有时候,如果程序的功能比较多。规模比较大,把所有的程序代码都写在一个主函数中,就会使得主函数太庞杂,所以为了方便阅读和维护程序,就引进了 组装程序 的概念,把某些功能都在其他分支完成,然后需要哪个功能的时候就组装那个分支到主函数,这些分支就叫它函数,组装就叫调用,这样就会使主程序简化了,哪个函数是做什么也都很清楚。哪里用得到这个功能就在哪个调用就可以了!
C 语言的函数可以直观地辨别出面向过程和面向对象的区别,C 语言的函数有一个特点,就是它有固定的格式和固定的模型。对于一个 C 程序而言,它所有的命令都包含在函数内。每个函数都会执行特定的任务。每个函数都只能被定义一次。但一个函数可以根据需要被多次的声明和调用。
函数的定义
在使用函数之前必须先定义,后使用。
定义函数要包括:
1、指定函数的数据类型,以便后续返回值的调用
2、指定函数的名字,以便后续调用
3、指定函数形参的类型和名字,以便后续传递数据,对于无参(void)可不用
函数类型+函数名+(参数表){ 函数体 }
参数值的()起到了表示函数调用的重要作用即使没有参数也需要(),如果有参数,则需要给出正确的数量和顺序,这些值会按照一定的顺序(看编译环境一般是从右到左)依次来初始化函数中的参数
注意 如果调用函数时给的值与参数的类型不匹配有些编译环境可能会帮你类型强制转换好,但是有可能不是你想要的那样。所以 建议传递给函数的值要与声明一致
#include//这里声明的是int的形参 int a(int a);//但是如果换成浮点类型输出将会不一样 int main(void){ double t = 23.8; a(t);//这里传进去的是浮点数(实参) } int a(int a){//这里接收到的是整型的实参 int t = a; //所以即使换成double t 也一样 printf("%d", t);//输出23 } //但是更高级的语言会检查比较严格如 java C++
函数的传值
每个函数都有自己的变量空间,参数也位于这个独立的空间中,和其他函数没有关系
值的传递:传递给函数的值可以是表达式的结果(包括):字面量、变量、函数的返回值、计算的结果。但是在调用函数时,永远只能传 值 给函数,在传值的时候实际上只是把实参的值传递到形参处,做的只是一个复制的过程(但是指针就不一样了)
在函数定义的形参中,它们是不占用内存的,在主函数内调用形参的值时才会被临时分配内存。实参给形参传递的是值的传递,属于单向传递。把实参的值传递给形参,在调用结束后,形参的存储单元被释放,而形参值的任何变化都不会影响到实参的值,实参的存储单元仍保留并维持数值不变。
地址的传递:形参为指针变量时函数之间的数据传递,如果函数的形参为指针类型时,对应的实参类型必须与形参保持一致
这种方式使用数组名或者指针作为函数参数,传递的是该数组的首地址或指针的值,而形参接收到的是地址,即指向实参的存储单元,形参和实参占用相同的存储单元,这种传递方式称为“参数的地址传递”。
地址传递的特点是形参并不会占用存储空间,数组名或指针就是一组连续空间的首地址。因此在数组名或指针作函数参数时所进行的传送只是地址传送,形参在取得该首地址之后,与实参共同存在一个存储单元,形参的变化也就是实参的变化。
函数的返回值
在定义的时候,如果不需要返回值的时候定义void类型,其他时候定义其他数据类型。但是使用void类型的时候,不能使用带值的return(可以没有return),调用的时候不能做返回值的赋值
int 和void的不同:
前面加有void 的函数,不能返回任何数据,这类函数应该将所有应该实现的功能在本函数内全部实现。但是并不是不能与外部交换数据,仍然可以通过引用型参数传递数据,只是调用时不能直接接受返回值,因为就没有返回值。
前面有int的函数,返回值是整型数,可能是结果是整数的数,也可能是运行状态,成功或失败的标识,函数调用时可以直接利用返回信息,实现一些功能。
main()函数的揭秘
main()函数原形:int main(int argc,char const*argv [ ] )
里边的两个参数,允许从执行环境中传递任意的多字节字符串 (argv[0])是命令本身,命令行参数是保存在argv[ ]里的,C/C++语言规定,可执行程序程序本身的文件名和地址 ,其中的一个描述了命令行参数的个数,通常称为argc;另一个是命令行参数的数组,通常称为argv。命令行参数都是字符串,所以argv的类型是char * [argc+1]。该程序的名字也作为argv[0]传进来,这个参数的表总以0结束,也就是说,argv[argc]==0。(argv数组的最后一个元素存放了一个NULL的指针)
#includeint main(int argc, char * argv[]){ //argv[0]== 调用函数时使用的程序名和地址 //argv[1]==参数1 //argv[2]==参数2 //argv[3]==参数3 //依次类推... //argc 就是计算并保存总共有多少个参数的 int i; for (i = 0; i < argc; i++) { printf("%s ", argv[i]); } return 0; }
给main函数传递的这两个参数,argc和argv。argc是int类型的,它表示的是命令行参数的个数。不许要用户传递,它会根据用户从命令行输入的参数个数,自动确定。argv是char**类型的,它的作用是存储用户从命令行(黑窗口输入)传递进来的参数。它的第一个成员是用户运行的程序名字。
main函数的返回值,用于说明程序的退出状态。如果返回0,则代表程序正常退出;返回其他数字的含义则由系统决定,通常,返回非零代表程序异常退出。(一般是由return返回)
return 的妙用
(return 是C语言的关键字)函数定义为什么样的返回类型,该函数中return后就应该是相应类型的值。
在函数中,如果碰到return 语句,那么程序就会返回调用该函数的下一条语句执行,也就是说跳出函数的执行,回到原来的地方继续执行下去。但是如果是在主函数(main)中碰到return语句,那么整个程序就会停止。return表示从被调函数返回到主调函数或其他函数继续执行,返回时可 附带一个返回值,返回值可以是一个常量,变量,或是表达式。 传指针形式:直接传给函数的是变量的地址,由于被调函数在参数指针的作用域之内,此时直接改变变量的本体。
返回值: 计算结果表示函数执行的顺利与否(-1、0) 返回值可以为各种数据类型,如:int,float,double,char,a[数组],*a(指针),结构或类。写return是一种清晰的风格,可以防止一些意外的错误。有时候也是想中断函数执行,返回调用函数处。
返回本地变量的地址是危险的,返回全局变量或静态本地变量的地址是安全的,返回在函数malloc的内存是安全的,但是容易造成问题,最好的办法是返回传入的指针。
在函数中调用函数自己 :即自己return自己 = 递归
#include//输出直到n项的斐波那契数列 int add(int n);int i; int main(void) { int n; scanf("%d", &n); int c; for (i = 1; i <=n; i++) {//输出数列 c = add(i);//调用函数 printf("%d ", c); } return 0; }int add(int n) {//递归 if (n == 1) {//第一位数是1 return 1; } else if (n == 2) {//第二位数也是1 return 1; } else { return (add(n-1) + add(n - 2));//第三位开始 等于它前两位相加 1+1=2 所以第三位是2 } }
本地变量和全局变量的补充
本地变量的规则:
本地变量是定义在块内的:它可以是定义在函数的块内,也可以是定义在语句的块内,甚至可以随便拉一对大括号来定义变量。
但是程序进入这个块之前,在这个块内定义的变量它是不存在的,离开这个块,它也随之消失。
所以,在块外面定义的变量在变量仍然有效,然而在块里边定义的变量出去块外边就无效了。
注意 :如果在块外边定义和里边同名的变量,里边的变量会覆盖外边的值(小覆盖大的)。
不能再同一个块内定义同名变量,本地变量不会被初始化,参数再进入函数的时候就被初始化了。
本地变量的规则:
没有做初始化的全局变量会得到零值,指针会得到NULL,只能用编译时刻已知道的值来初始化全局变量,它们的初始化再main函数之前。
注意:尽量不要使用全局变量来在函数之间传递参数和结果。尽量避免使用全局变量(丰田的案子),使用全局变量和静态的本地变量是线程不安全的。
审核编辑:汤梓红
评论
查看更多