ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

【C语言】函数全解析

【C语言】函数全解析 目录1.函数的概念2.库函数3.自定义函数4.形参和实参5.return语句6.数组做函数的参数7.嵌套调用和链式访问8.函数的声明和定义1.函数的概念函数也叫子程序C语言的函数就是一个完成某项特定任务的一小段代码。C语言中的两类函数库函数自定义函数2.库函数库函数的学习工具C/C官⽅的链接https://zh.cppreference.com/w/c/header中⽂版https://cppreference.cn/w/cplusplus.comhttps://legacy.cplusplus.com/reference/clibrary/3.自定义函数3.1函数的语法形式ret_type fun_name(形式参数){}函数头由函数名、参数、返回值类型组成ret_type表示计算结果的类型返回类型是void表示什么都不返回fun_name函数名尽量有意义函数的参数函数如果有参数就需要交代清楚参数的类型和名字以及参数个数函数的参数也可以是void表示函数没有参数{ }括起来的是函数体完成计算过程return语句函数中可能有也可能没有return语句返回类型是void时return语句可以不写也可以写成 return;注一个函数必须要有函数名、返回类型/void和函数体3.2举例写⼀个加法函数完成2个整型变量的加法操作#includestdio.h int Add(int x, int y) { int z x y; return z; } int main() { int a 0; int b 0; scanf(%d%d, a, b); int s Add(a, b);// ab的和调用加法函数完成a和b的相加 printf(%d\n, s); return 0; }add函数部分也可以简化为int Add(int x,int y) { return x y;//return后边可以是数值也可以是表达式如果是表达式则先执行表达式再返回表达式的结果 }4.形参和实参实参实参是真实传递给函数的参数形参上述例子中在函数名Add 后的括号中写的 x 和 y 就是形参。当没有调用Add函数时不会创建xy变量只是形式上存在不会向内存申请空间—形参是实参的临时拷贝只有在函数被调用的过程中为了存放实参传来的值才向内存申请空间这就是形参的实例化。形参和实参的关系实参是传递给形参的但是形参和实参各自是独立的内存空间int Add(int x, int y)//形参有自己独立的空间是实参的临时拷贝 { return x y; } int main() { int a 0; int b 0; scanf(%d%d, a, b);//实参是传递给形参的但是形参和实参各自是独立的内存空间 int c Add(a, b); printf(%d\n, c); return 0; }5.return语句1函数返回类型是void时return语句可以不写也可以写成 return2返回类型是非void时若没有使用return语句那么函数的返回值是未知的3return后边可以是⼀个数值也可以是⼀个表达式如果是表达式则先执⾏表达式再返回表达式的结果4return语句执⾏后函数就彻底返回后边的代码不再执⾏5return返回的值和函数返回类型不⼀致系统会⾃动将返回的值隐式转换为函数的返回类型int test() { return 3.14; } int main() { int r test(); printf(%d\n, r);//3 return 0; }6如果函数中存在if等分⽀的语句则要保证每种情况下都有return返回否则会出现编译错误7函数的返回类型如果不写编译器会默认函数的返回类型是int8小练习判断一个年份是否是闰年int is_leap_year(int y) { if ((y % 4 0) (y % 100 ! 0) || (y % 400 0)) return 1; else return 0; } int main() { int year 0; scanf(%d, year); if (is_leap_year(year) 1) { printf(是闰年\n); } else { printf(不是闰年\n); } return 0; }6.数组做函数的参数将数组作为参数传递给函数在函数内部对数组进⾏操作写⼀个函数将⼀个整型数组的内容全部置为-1再写⼀个函数打印数组的内容void set_arr(int arr[],int sz) { int i 0; for (i 0;i sz;i) { arr[i] -1; } } print_arr(int arr[],int sz) { int i 0; for (i 0;i sz;i) { printf(%d , arr[i]); } } int main() { int arr[10] { 0 }; int sz sizeof(arr) / sizeof(arr[0]); set_arr(arr,sz); print_arr(arr, sz); return 0; }数组传参• 函数的形式参数要和函数的实参个数匹配• 函数的实参是数组形参也是可以写成数组形式的• 形参如果是⼀维数组数组大小可以省略不写• 形参如果是二维数组行可以省略但是列不能省略•数组传参形参是不会创建新的数组的•形参操作的数组和实参的数组是同⼀个数组7.嵌套调用和链式访问1嵌套调用就是函数之间的互相调用假设我们计算某年某月有多少天如果要函数实现可以设计2个函数//确定是否是闰年——is_leap_year()//再根据月计算这个月的天数——get days of monthint is_leap_year(int y)//闰年判断 { if ((y % 4 0) (y % 100 ! 0) || (y % 400 0)) return 1; else return 0; } int get_days_of_month(int y,int m)//某年某月有多少天 { int days[] { 0,31,28,31,30,31,30,31,31,30,31,30,31 };//从0开始月份正好和下标对应 int day days[m]; if ((is_leap_year(y) 1) m 2)//嵌套调用 day 1;//闰年的2月有29天 return day; } int main() { int year 0; int month 0; scanf(%d%d, year, month); int d get_days_of_month(year,month); printf(%d\n, d); return 0; }(2) 链式访问将一个函数的返回值作为另一个函数的参数像链条一样串起来int main() { /*6*/ printf(%d\n, strlen(abcdef));//链式访问 return 0; }int main() { printf(%d , printf(%d , printf(%d , 46)));//46 3 2 return 0; } //printf()的返回值是字符个数 //注意有没有空格int main() { printf(%d, printf(%d, printf(%d, 46)));//4621 return 0; }8.函数的声明和定义8.1单个文件一般在写函数的时候会将函数的定义放在函数调用之前如下橙色部分是函数的定义绿色部分是函数的调用这种写法没有问题但是当把函数的定义放在函数调用之后就会产生问题这是因为C语⾔编译器对源代码进⾏编译的时候从第⼀⾏往下扫描的当遇到第7⾏的函数调⽤的时候并没有发现前⾯有 is_leap_year的定义就报出了上述的警告解决方法只要在调用前声明一下is_leap_year这个函数就可以了声明函数要交代清楚函数名函数的返回类型和函数的参数。函数声明中参数只保留类型省略掉名字也是可以的。如上述例子中将函数的声明 bool is_leap_year(int y); 放在函数调用之前就可以解决总结函数的使用一定要先声明后定义函数的定义也是一种特殊的声明所以如果函数定义放在调用之前也是可以的8.2多个文件一般情况下函数的声明、类型的声明放在头文件(.h)中函数的实现是放在源文件(.c)文件中。那么该如何使用呢只要在调用函数之前包含头文件就可以了如下8.3 static和extern先了解作用域和生命周期1作用域和生命周期作用域⼀段程序代码中所⽤到的名字并不总是有效可⽤ 的⽽限定这个名字的可⽤性的代码范围就是这个名字的作⽤域1.局部变量的作用域是变量所在的局部范围2.全局变量的作用域是整个工程项目生命周期指的是变量的创建申请内存到销毁收回内存之间的一个时间段1.局部变量的生命周期是进入作用域变量创建生命周期开始出作用域生命周期结束2.全局变量的生命周期是整个程序的生命周期2static和externstatic和extern都是C语言中的关键字static可以修饰局部变量修饰全局变量修饰函数extern是用来声明外部符号的1 - externextern是用来声明外部符号的如果一个全局的符号在A文件中定义在B文件想使用就可以使用extern进行声明//声明来自外部的变量//声明来自外部的函数1 - static修饰局部变量在上面代码中n 是局部变量当进入 test() 这个函数之后变量n才创建此时生命周期开始出这个函数之后变量销毁生命周期结束所以这里只会打印 11 11 11 11 11而static修饰局部变量可以改变生命周期如下static拉长了生命周期总结static修饰局部变量可以拉长生命周期相当于全局变量的生命周期但是作用域不会改变。生命周期的改变本质是改变了变量的存储类型本来局部变量是存在内存的栈区的但是被static修饰后存到了静态区。存在静态区的变量和全局变量是一样的生命周期就和程序的生命周期一样了使用建议未来⼀个局部变量出了函数后我们还想保留值等下次进⼊函数继续使用就可以使用 static 修饰2 - static修饰全局变量全局变量默认具有外部链接属性在上述代码中文件 lesson_test.c 中test变量被static修饰则该变量在文件lesson_8_4_20.c中不能被使用结论一个全局变量被static修饰这个全局变量就只能在该文件中使用不能在其他文件中使用(全局变量默认具有外部链接属性而被static修饰的全局变量变成内部链接属性)使用建议如果一个全局变量只想在所在的源文件内部使用不想被其他文件发现就可以用static修饰3 - static修饰函数结论static修饰函数和修饰全局变量是一样的一个函数被static修饰后变成了内部链接属性使函数只能在自己所在的源文件内部使用使用建议一个函数只想在所在的源文件内部使用不想被其他源文件使用就可以用static修饰
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进