c 语言 函数学习

240 阅读3分钟

1. 函数

函数是什么?

  • 在计算机中子程序,是一个大型程序中的某部分代码,由一个或者多个语句块组成,负责完成某个特定任务,具备相对的独立性,一般会有输入参数并有返回值,提供对过程的封装和细节的隐藏。

c 语言函数分类:

  • IO 函数 (输入输出函数)
  • 字符串操作函数 (strlen strcmp)
  • 字符操作函数 (toupper)
  • 内存操作函数 (memcpy memset)
  • 时间/日期操作函数 (time)
  • 数学函数 (sqrt pow)
  • 其他库函数

T$T_CN8C8Y@6BFS(AUTYY_P.png

自定义函数

自定义函数和库函数一样,有函数名,返回值类型和函数参数。但是不一样的是这些都是我们自己设计的。

函数的基本组成形式:返回类型 函数名 函数参数 函数体

函数小栗子

// 定义函数 ,类型,参数
int get_Max(int a ,int b)
{
	if (a > b)
		return a;
	else
		return b;
}

int main()
{
	int a = 10;
	int b = 20;
	// 函数调用 找出最大的值 
	int max = get_Max(a, b);
	printf("%d", max); 
	return 0;
};

// -----------------------------------分割线--------------------------------------

// 函数返回类型的地方写:
// void 表示这个函数不返回任何值
// 实际参数可以是:常量 变量 表达式 函数等
// 形参当函数调用完成后就会自动销毁,因此形式参数只在函数中有效。
void exch(int * pa ,int * pb)// 指针变量
{
	int z = 0;// 临时变量
	z = *pa;
	*pa = *pb;
	*pb = z;
}

int main()
{
	int a = 10;
	int b = 20;
	// 交换两个整型变量的值
	exch(&a, &b); // 地址
	printf("%d %d", a,b);
	return 0;
};

函数的调用

传值调用:

函数的形参和实参分别占有不同内存块,对形参的修改不会影响实参。

传址调用

  • 传址调用是把函数外部创建的变量的内存地址传递给函数参数的一种调用函数的方式。
    
  • 这种传参方式可以让函数和函数外边的变量建立起真正的联系,也就是函数内部 可以直接操作函数外部的变量。
    
void exch(int * pa ,int * pb)// 指针变量
{
	int z = 0;// 临时变量
	z = *pa;
	*pa = *pb;
	*pb = z;
}

int main()
{
	int a = 10;
	int b = 20;
	//exch(a, b); // 传值 只是一份拷贝
	printf("-------------------");
	exch(&a, &b); // 传址
	printf("%d %d", a,b);
	return 0;
};

函数小练习

// 函数的功能要单一 独立
int is_pram(int n)
{
	for (int j = 2; j < n; j++)
	{
		if (n % j == 0)
		{
			return 0;
		}
	}
	return 1;
}

int main()
{
	// 100 - 200 之间的素数
	int cop = 0;
	for (int i = 100; 200 >= i; i++)
	{
		if (is_pram(i))
		{
			cop++;
			printf(" %d ", i);
		}
	}
	printf("\n cop = %d \n", cop);
	return 0;
};

// ----------------------分割线----------------------------

// 如果 不写返回类型 int,默认返回int
// 但是写 void 表示不返回类型
int is_leap_year(int n)
{       // 判断是否为润年 
	if ((n % 4 == 0 && n % 100 != 0) || n % 400 == 0)
	{
		return 1;
	}
	return 0;
}

int main()
{
	int cop = 0;
	for (int i = 1000; 2000 >= i; i++)
	{
		if (is_leap_year(i) == 1)
		{
			cop++;
			printf(" %d ", i);
		}
	}
	printf("\n %d ", cop);
	return 0;
};

// ----------------------分割线----------------------------

int bin_seach(int n[], int tar, int len)
{
	int left = 0; 
	int right = len - 1;

	while (left <= right)
	{
		int mid = (left + right) / 2;
		if (n[mid] > tar)
		{
			right = mid - 1;
		}
		else if (n[mid] < tar)
		{
			left = mid + 1;
		}
		else {
			return mid;
		}
	}
	return -1;
}

int main()
{
	// 整型有序的二分查找
	int arr[] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
	int key = 19;
	int sz = sizeof(arr) / sizeof(arr[0]);
	// 找到返回位置的下标
	// 找不到返回 -1
	int ret = bin_seach(arr,key,sz);// 这里arr的传参 只是把arr的第一个元素地址传过去了
	if (-1 == ret)
	{
		printf("\n 找不到");
	}
	else
	{
		printf("\n 找到了:%d", ret);
	}
	return 0;
};

// ----------------------分割线----------------------------

void Add(int* p)
{
	(*p)++;
}

int main()
{
	int i = 0;
	Add(&i); // 传地址
	printf("%d", i);// 1
	return 0;
};

函数的嵌套调用 和 链式访问

函数是不能嵌套 定义 的,可以 嵌套调用

void test3()
{
	printf("hehe \n");
}

int test2()
{
	test3();
	return 0;
}

int main()
{
	// 函数的嵌套
	test2(); // 打印 hehe
	return 0;
};

链式访问

把一个函数的返回值作为另外一个函数的参数

int main()
{
	// 字符传长度
	int i = strlen("abcd");
	printf("%d \n", i);

	// 链式访问
	printf("%d", strlen("abcd"));
	return 0;
};

// ---------------------------------分割线----------------------------------------

int main()
{
        // printf 返回一个打印在屏幕上的字符个数
	printf("%d", printf("%d", printf("%d", 43)));// 43 2 1
	return 0;
};

函数的声明 和 定义

函数的声明

  1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么,但是具体是否存在无关紧要。
  2. 函数的声明一般出现在函数的使用之前,要满足先声明后使用
  3. 函数的声明一般放在头文件中。

函数的定义: 函数的定义是指函数的具体实现,交代函数的功能实现。

int main()
{
	int a = 10;
	int b = 20;
	// 函数的声明 告知有Add这个函数 
	// 因为函数定义到了后面,不声明的话会出警告

	int Add(int, int);
	int c = Add(a, b);
	printf("%d \n", c);// 打印 30
	return 0;
};
// 函数的定义
int Add(int a, int b)
{
	return a + b;
}

// ----------------------------分割线---------------------------------

// 头文件声明
int Add(int x, int y);
// 新建文件定义 add 函数
int Add(int x, int y)
{
	return x + y;
}
// 在main 函数内引入头文件 ,使用 add函数
# include "add.h";
int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	printf("%d", c); // 30
	return 0;
};


递归

什么是递归: 程序调用自身的编程技巧称为递归。递归的主要思考方式在于把大事化小。

递归两个必要条件:

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件。
void print(int n)
{
	if (9 < n)
	{
		print(n / 10);
	}
	printf("%d ", n % 10);// 1 2 3 4
}
int main()
{
	int n = 123;
	// 递归 函数自己调用自己
	print(n);// print 函数可以打印参数部分数的每一位
	return 0;
};

/*
	print(n);进入函数
	1. 判断 n 是否大于9 ,大于 执行 print(n / 10); n = 12
	2. 再判断 n 是否大于9 ,大于 执行 print(n / 10); n = 1
	3. 再判断 n 是否大于9 ,小于 执行 printf("%d ", n % 10); 打印:1
	4. 继续往上 执行 printf("%d ", 12 % 10); 打印 :2
	5. 继续往上 执行 printf("%d ", 123 % 10); 打印 :3
*/

06.png 08.png 09.png

递归层数过深出现栈溢出 (Stack overflow)

state.png

栈空间总有被占满的时候,就会出现Stack overflow现象

669.png

写递归时:

  1. 不能死递归,都有跳出条件,每次递归逼近跳出条件。
  2. 递归层次不能太深,否则会出现 Stack overflow。
int my_strlen(char* str)
{
	if ((*str) != '\0')
	{
		return 1 + my_strlen(str + 1);
	}
	else
	{
		return 0;
	}
}

int main()
{
	char str[] = "bit";
	// ['b'] ['i'] ['t'] ['\0']
	// 模拟实现 strlen()
	printf("%d \n", my_strlen(str));// 打印:3
	return 0;
};

d222.png

递归阶乘

int fac(int n)
{
	if (n <= 1)
	{
		return 1;
	}
	else
	{
		return n * fac(n - 1);
	}
}

int main()
{
	int n = 5;
	int ch = fac(n);
	printf("%d \n ", ch);// 120
	return 0;
};

斐波那契数

-递归会进行重复大量的计算

// 递归法
int deep(int n)
{
	if (2 >= n)
	{
		return 1;
	}
	else
	{
		return deep(n - 1) + deep(n - 2);
	}
}

// 迭代法
int fac(int n)
{
	int a = 1;
	int b = 1;
	int c = 1;
	while (2 < n)
	{
		c = a + b;
		a = b;
		b = c;
		n -= 1;
	}
	return c;
}

int main()
{
	int n = 10;
	int ch = deep(n);
	printf("%d \n ", ch);
	return 0;
};

// 左下三角乘法表
int main()
{
	for (int i = 1; 9 >= i; i++)
	{
		for (int j = 1; i >= j; j++)
		{
			printf("%d * %d = %d ", j,i , (j * i));
		}
		printf("\n");
	}
	return 0;
};

// -----------------分割线 ------------------------------------

// 左上三角乘法表
int main()
{
	for (int i = 1; 9 >= i; i++)
	{
		for (int j = i; 9 >= j; j++)
		{
			printf("%d * %d = %d ", j,i , (j * i));
		}
		printf("\n");
	}
	return 0;
};

// -----------------分割线 ------------------------------------

// 右上三角乘法表
int main()
{
	for (int i = 1; 9 >= i; i++)
	{
		for (int j = 1; 9 >= j; j++)
		{
			if (j < i)
			{
				printf("        ");
			}
			else 
			{
				printf("%d * %d = %d ", j, i, (j * i));
			}
		}
		printf("\n");
	}
	return 0;
};

// -----------------分割线 ------------------------------------

// 右下三角乘法表
int main()
{
	int j = 0;
	for (int i = 1; 9 >= i; i++)
	{
		for (j = 1; j <= 9 - i; j++)
		{
			printf("        ");
		}
		for (int n = 1; n <= i; n++)
		{
			printf("%d * %d = %d ", j, i, (j * i));
		}
		printf("\n");
	}
	return 0;
};