函数是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。
您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的,它通常是用来执行某项特殊任务,相较于其他代码有一些独立性。
函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。
C 标准库提供了大量的程序可以调用的内置函数,也被称为库函数,它们一般需要头文件, strcmp() 函数就需要头文件 stdlib.h 。例如,函数 strcat() 用来连接两个字符串,函数 memcpy() 用来复制内存到另一个位置。
函数还有很多叫法,比如方法、子例程、子程序或程序,等等。
函数一般具有输入参数和返回值,提供对过程的封装和对细节的隐藏。这些代码通常被集成为软件库。
在 C 语言中,函数由一个函数头和一个函数主体组成。下面列出一个函数的所有组成部分:
参数:
实际参数(实参):
真实传给函数的参数叫做实参,实参可以是常量,变量,表达式,函数
无论是哪种形式的量,在进行函数调用时,他们都要有具体的值,以便传值给形参
形式参数(形参):
形式参数是指函数名之后的括号中的变量,它们只有在函数调用过程中才能实例化(分配内存单元),所以叫形式参数,形式参数当函数调用完成后就自动销毁了,因此形式参数只在函数中有效。
函数的功能应该足够单一,在封装之后更好调用。
函数的声明告诉编译器函数名、函数参数、函数返回类型。但是具体是否存在,函数声明决定不了。
函数的声明一般出现在函数使用前要满足先声明后使用。
函数的声明一般要放在头文件中。
函数声明会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。
需要先声明然后定义。函数声明告诉编译器我有这个函数,函数在后面,让编译器去找。
#include
int Add(int a, int b);//函数声明
int main()
{
int a = 10, b = 20;
int res = Add(a,b);
return 0;
}
int Add(int a,int b)//函数定义
{
return a + b;
}
或者只定义。
#include
int Add(int a, int b)//函数定义
{
return a + b;
}
int main()
{
int a = 10, b = 20;
int res = Add(a,b);
return 0;
}
函数声明包括以下几个部分:
return_type function_name( parameter list );
函数声明例子:
int max(int num1, int num2);
在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:
int max(int, int);
当您在一个源文件中定义函数且在另一个文件中调用函数时,函数声明是必需的。在这种情况下,您应该在调用函数的文件顶部声明函数。
例子:
#include
int Add(int x, int y)
{
return (x + y);
}
int main()
{
int a = 10, b = 20;
int res = Add(a,b);
printf("%d",res);
return 0;
}
C语言有很多库函数,但有些功能还是要自己写函数实现。
函数的定义是指函数的具体实现,交代函数的功能实现。
C 语言中的函数定义的一般形式如下:
return_type function_name( parameter list )
{
body of the function
}
函数的声明放在头文件中,函数的定义放在另一个源文件中,主函数文件只要预处理这个头文件就相当于函数声明,将主函数文件和具有函数定义的文件一起编译运行就可以实现调用函数,同时主函数文件最前面要预处理含函数声明的头文件。
把函数声明放在头文件中。
函数定义放在源文件中,可以编译为静态库,防止他人直接看到你写的函数本体。
在主函数源文件中要预处理头文件。
#include "Add.h"预处理头文件,不同于库函数的头文件,自己编写的头文件预处理是用""而不是<>。
这样的做法有较多的好处,可以使代码模块化,在使用是可以更加的方便,在源文件的开头预处理头文件就相当于声明了函数,在一开始就告诉编译器,我有这个函数,然后把函数定义放在另一个源文件中,就可以实现函数的调用。
编译成的静态库是二进制代码,可以反编译,但反编译后也只能反编译成汇编语言,不能反编译成高级语言。
例1:写一个函数取两个数的最大值
#include
int get_max(int x,int y)
{
return (x > y ? x : y);
}
int main()
{
int a = 0, b = 0;
scanf("%d %d",&a,&b);
int res = get_max(a,b);
printf("%d",res);
return 0;
}
在编程中,函数参数的传递方式主要有两种:传值(pass by value)和传引用(pass by reference)。当你传递一个参数给函数时,这两种方式决定了在函数内部对这个参数的任何修改是否会影响到函数外部的原始数据。
传值 (Pass by Value): 当传值给函数时,实际上传递的是数据的副本。这意味着函数内部对参数的任何改变都不会影响到原始数据。基本数据类型(如整数、浮点数、字符等)通常是通过传值的方式传递的。
传引用 (Pass by Reference): 当通过引用传递变量时,传递的是变量的内存地址,而不是数据的副本。这意味着函数内部对参数的任何更改都会直接影响到原始数据。
对于数组,大多数编程语言在传递数组到函数时通常是传引用。这是因为数组往往会占用较大的内存空间,将整个数组复制一份作为参数传递效率低下,所以传递指向数组首元素的指针要高效得多。例如在C语言中,当你将数组作为参数传递给函数时,实际传递的是指向数组第一个元素的指针。
例2:用函数交换两个变量的值
#include
void exchange(int *x,int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int a = 10, b = 20;
int* p1 = &a, * p2 = &b;
exchange(p1,p2);
printf("a = %d b = %d",a,b);
return 0;
}
例3:写一个函数判断输入的数字是否是素数
#include
#include
bool Judge(int x)
{
if (x == 0 || x == 1)
{
return false;
}
if (x == 2)
{
return true;
}
for (int i = 2; i <= x - 1; i++)
{
if (x % i == 0)
{
return false;
}
}
return true;
}
int main()
{
int num = 0;
scanf("%d",&num);
bool judge = Judge(num);
if (judge)
{
printf("是素数");
}
else
{
printf("不是素数");
}
return 0;
}
例4:输出100~200之内的素数
#include
#include
#include
bool Judge(int x)//用来判断是否是素数
{
if (x % 2 == 0)//除了2之外的偶数都不是素数,去掉偶数提升效率
{
return false;
}
//一个整数a,若它有一个因数大于等于sqrt(a),则它的另一个因数一定小于等于sqrt(a)
for (int i = 2; i <= sqrt(x); i++)
{
if (x % i == 0)
{
return false;
}
}
return true;
}
int main()
{
for (int i = 100; i <= 200; i++)//遍历100到200之内的数字
{
if (Judge(i))//函数在是素数时返回true,条件就满足,则打印此数
{
printf("%d ",i);
}
}
return 0;
}
例5:写一个函数判断是不是闰年
#include
#include
bool Judge(int x)
{
if ((x % 4 == 0 && x % 100 != 0) || x % 400 == 0)
{
return true;
}
return false;
}
int main()
{
int year = 0;
scanf("%d",&year);
if (Judge(year))
{
printf("闰年");
}
else
{
printf("平年");
}
return 0;
}
例6:输出1000年至2000年间的闰年
#include
#include
bool Judge(int x)
{
if ((x % 4 == 0 && x % 100 != 0) || x % 400 == 0)
{
return true;
}
return false;
}
int main()
{
int year = 0;
for(year = 1000;year <= 2000;year++)
{
if (Judge(year))
{
printf("%d ",year);
}
}
return 0;
}
例7:写一个二分法寻找有序数组中特定的数字函数
#include
void BinarySearch(int arr[],int num,int sz)
{
int left = 0;
int right = sz - 1;
while (left <= right)
{
int mid = left + (right - left) / 2;
if (arr[mid] > num)
{
right = mid - 1;
}
else if(arr[mid] < num)
{
left = mid + 1;
}
else
{
printf("找到了!下标是%d",mid);
break;
}
}
if (left > right)
{
printf("没找到!");
}
}
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr) / sizeof(arr[0]);//计算数组大小
int num = 7;//要找数字7
BinarySearch(arr,num,sz);
return 0;
}
例8:写一个函数,每调用一次函数,num就加一
#include
void fun(int *p)
{
(*p)++;//++的运算级高
}
int main()
{
int num = 0;
fun(&num);
printf("%d\n",num);
fun(&num);
printf("%d\n", num);
return 0;
}
把一个函数的返回值作为另一个函数(两函数可以是同一个函数)的参数。
需要在内部的函数有返回值。
例1:求(2 + 3) * 4
#include
int Add(int a,int b)
{
return (a + b);
}
int Mul(int x,int y)
{
return (x * y);
}
int main()
{
int num_f = 2, num_s = 3, num_t = 4;
int res = Mul(Add(num_f, num_s), num_t);
printf("%d",res);
return 0;
}
例2:打印一个字符串的长度
#include
#include
int main()
{
printf("%zu",strlen("hello"));
return 0;
}
这里的printf、strlen都是函数,这也是一个链式访问。
例3:求三个数的和
#include
int Add(int x, int y)
{
return (x + y);
}
int main()
{
int a = 10, b = 20,c = 30;
int res = Add(Add(a,b),c);
printf("%d",res);
return 0;
}
例4:经典例子
#include
int main()
{
printf("%d",printf("%d",printf("%d",43)));
return 0;
}
运行结果是4321。
例5:求三个数中的最大值
#include
int get_max(int x, int y)
{
return (x > y ? x : y);
}
int main()
{
int a = 10, b = 20,c = 30;
int res = get_max(get_max(a,b),c);
printf("%d",res);
return 0;
}
函数是可以嵌套调用的,但不能嵌套定义。
函数嵌套时,嵌套在内部的函数的定义要在外部函数的定义之前,不然会报错。
例1:在一个函数内调用另一个函数
#include
void print()
{
printf("hello\n");
}
void repeat()
{
for (int i = 1; i <= 3; i++)
{
print();
}
}
int main()
{
repeat();
return 0;
}
print函数的定义要在repeat函数之前。
函数的自调用称为递归,也是一种嵌套调用,不过嵌套的是此函数自身而不是其他函数。