C语言模块化灵魂——函数

张开发
2026/6/26 2:09:08 15 分钟阅读
C语言模块化灵魂——函数
1、函数的概念说起函数我们在数学中已经有所了解比如一次函数ykxbk和b都是常数给一个任意的x就可以得到一个y值。在C语言中也有函数的概念有人认为它是子程序。C语言中的函数就是一个完成某个任务的一小段代码。这段代码是有特殊的写法和调用方法的。C语言的程序其实是由无数个小的函数而组成的也可以这么说一个大的计算任务可以分解成若干个较小的函数完成。同时一个函数如果能完成某项特定任务的话这个函数也是可以复用的这样就提升了开发软件的效率。在C语言中我们一般会见到两类函数库函数和自定义函数2、库函数2.1标准库和头文件C语言标准规定了C语言的各种语法规则C语言并不提供库函数C语言的国际标准ANSI C规定了一些常用的函数的标准被称为标准库那不同的编译器厂根据ASNI提供的C语言标准就给出了一系列函数的实现这些函数就被称为库函数。我们前面提到的内容printfscanf都是库函数库函数也是函数不过这些函数已经是现成的我们只要学会就能直接使用了。有了库函数一些常见的功能就不需要程序员自己实现了一定提升了效率。2.2库函数的使用方法库函数的学习和查看有很多的工具比如cplusplus.com - The C Resources Network举个例子sqrtdouble sqrt(double x) //sqrt是函数名 //x是函数的参数表示调用sqrt函数需要一个double类型的值 //double 是返回值的类型表示计算的结果是double类型的值2.21功能sqrt函数能计算平方根2.22头文件包含库函数是在标准库中对应的头文件中声明的所以库函数的使用务必包含对应的头文件不包含是会出现一些问题的。2.23实践#includestdio.h #includemath.h int main() { double d 16.0; double r sqrt(d); printf(%lf\n, r); return 0; }3、自定义函数3.1函数的语法形式其实自定义函数和库函数是一样的形式如下ret_type name(形式参数) { }//ret_type是函数返回类型name是函数名括号里放的是形式参数(我们也叫形参){}括起来的是函数体我们可以把函数想像成一个小型工厂工厂得输入原材料经过工厂加工才能生产出产品那函数也是一样的函数一般会输入一些值0到多个都有经过函数的计算得出结果。其中ret_type是用来表示函数计算结果的类型有时候返回类型可以是void表示什么都不返回。name是为了方便使用函数就像人的名字一样有了名字方便称呼函数有了名字方便调用我们一般根据函数的功能来命名。函数的参数相当于送入工厂的原材料函数的参数可以是void明确表示函数没有参数。如果有参数要交代参数的类型和名字以及个数。{}括起来的就是函数体函数体就是完成计算的过程。3.2函数的举例写一个加法函数#includestdio.h int Add(int x, int y) { int z 0; z x y; return z; } int main() { int a 0; int b 0; scanf(%d %d, a, b); int r Add(a, b); printf(%d, r); return 0; }当然Add函数也可以进行简化int Add(int x,int y) { return xy; }这里需要注意的是函数的参数部分函数参数的个数每个参数的类型形参的名字4、形参和实参在函数使用的过程中把函数的参数分为形参和实参。再看看我们前面写的代码1 #includestdio.h 2 int Add(int x, int y) 3 { 4 int z 0; 5 z x y; 6 return z; 7 } 8 int main() 9 { 10 int a 0; 11 int b 0; 12 scanf(%d %d, a, b); 13 int r Add(a, b); 14 printf(%d, r); 15 16 return 0; 17 }4.1实参在上面的代码中第2~7行是Add函数的定义有了Add函数了之后我们再看第13行对Add函数的调用。我们把第13行调用Add函数时传递给参数的a和b称为实际参数也叫实参。实际参数就是真是传递给函数的参数。4.2形参在上面的代码中第2行定义函数的时候在括号里的x和y被称为形式参数也叫形参。我们为什么叫它形式参数呢如果只是定义Add函数而不调用的话Add函数的参数x和y只能是形式上存在的不会向内存申请空间不会真实存在所以叫形式参数。形式参数只有在函数被调用的过程中为了存放实参传递过来的值才会向内存申请空间这个过程就叫形参的实例化。4.3形参和实参的关系虽然我们提到了实参是传递给形参的他们之间是有关系的但是形参和实参各自是独立的内存空间。实参的地址和形参的地址是不一样的所以我们可以理解为形参是实参的一份临时拷贝。5、return语句在函数的设计中函数经常会出现return语句这里讲一下return语句使用的注意事项。return后面可以是一个数值也可以是一个表达式如果是表达式则先执行表达式再返回表达式的结果。return后面也可以什么都没有直接写return这种写法适合函数返回类型为void的情况。return语句执行后函数就会彻底返回后面的代码将不再执行。return返回的值和函数返回类型不一致系统会自动将返回的值隐式转换为函数的返回类型。如果函数中存在if等分支语句要保证每一种情况都有return返回否则会出现编译错误。函数的返回类型如果不写编译器会默认为int类型。函数写了返回类型但是函数中没有使用return返回值那么函数的返回值是未知的。6、数组做函数参数在使用函数解决问题的时候难免会将数组作为参数传递给函数在函数内部对数组进行操作。比如写一个函数将一个整型数组的内容全部置为-1再写一个打印数组的内容。#includestdio.h int main() { int arr[]{1,2,3,4,5,6,7,8,9,10}; int numsizeof(arr)/sizeof(arr[0]); set_arr(arr,num);//设置数组内容为-1 print_arr(arr,num);//打印数组 }数组作为参数传递给set_arr和print_arr函数了那这两个函数应该如何设计呢这里我们需要知道数组传参的几个知识点函数的形式参数要和函数的实参个数匹配函数的实参是数组形参也可以写成数组形式的形参如果是一维数组数组大小可以考虑不写形参如果是二维数组行可以省略列不可以省略数组传参形参不会创建新的数组的形参操作的数组和实参的数组是同一个数组现在我们就可以实现这两个函数了#includestdio.h void set_arr(int arr[], int n) { int i 0; for (i 0; i n; i) { arr[i] -1; } } void print_arr(int arr[], int n) { int i 0; for (i 0; i n; i) { printf(%d , arr[i]); } } int main() { int arr[] { 1,2,3,4,5,6,7,8,9,10 }; int num sizeof(arr) / sizeof(arr[0]); set_arr(arr, num); print_arr(arr, num); return 0; }7、嵌套调用和链式访问7.1嵌套调用嵌套调用是指函数之间的相互调用每一个函数就像是一块积木正是因为多个积木相互无缝的配合才能搭建出精美的物件也正是因为函数之间的相互调用才能写出相对大型的程序。假如我们计算某年某月有多少天如果要用函数实现可以设计两个函数leap_year():根据年份来确定是否是闰年get_days_of_month():确实是否为闰年后再根据月份计算天数#includestdio.h int leap_year(int y) { if((y % 4 0)(y % 100 ! 0)||(y%4000)) { return 0; } else { return 1; } } int get_days_of_month(int y, int m) { int day[] { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; int d day[m]; if (leap_year(y) 0) { d 1; } return d; } int main() { int year 0; int month 0; int days 0; scanf(%d %d, year,month); days get_days_of_month(year,month); printf(%d, days); return 0; }在这个程序中在get_days_of_month函数中调用了leap_year函数当然在main函数中也有printf和scanf函数的调用等等。7.2链式访问所谓链式访问就是将一个函数的返回值作为另一个函数的参数这样两个函数就像链条一样串在了一起这就是函数的链式访问。比如这样#includestdio.h int main() { int lengthstrlen(abcdef); printf(%d\n,length); return 0; } #includestdio.h int main() { printf(%d\n,strlen(abcdef)); return 0; }8、函数的声明和定义8.1单个文件一般我们使用函数时直接将函数写出来使用。比如像上面我们写的函数其中第475~484行是函数的定义第489行是函数的调用。这种场景下函数的定义在函数的调用之前没有问题。如果我们将函数的定义放在函数的调用后面会怎样呢这个代码在VS2022上编译就会出现上面的警告。因为编译器在进行编译时是从第一行往下扫描的当遇到leap_year函数的调用时并没有发现前面的定义就会发出警告。要解决这个问题我们只需要在调用函数之前先声明一下leap_year函数这个声明只需要交代清楚函数的返回类型参数以及函数名就可以了通俗一点就是先给编译器提个醒告诉它我等下会用到这个函数的。这样先定义函数代码就可以正常编译了。函数的定义也算是一种特殊的声明所以先定义再调用也是可以的。8.2多个文件一般在企业我们写代码时代码可能会比较多不会将所有的代码都放在一个文件里我们往往会根据其功能放在多个文件中。一般情况下函数的声明都放在头文件中(.h)函数的调用放在源文件中(.c)。9、结语再次更新共同进步

更多文章