C语言函数的定义、声明、调用 – 整理,归纳,总结
通过前面的举例分析,我们知道函数就像是一个工具,可以用来完成某项任务,例如printf函数可以把数据输出到终端,system函数可以执行linux系统中工具。那么,我们可以自己定义函数,来实现某项任务。函数的定义格式如下:
返回数据类型 函数名(形参列表)
{
函数体
}
函数定义格式说明:
(1) 函数定义的第一个单词是“返回数据类型”,它说明该函数的返回类型;如果是int类型,说明该函数返回一个整数类型数据;如果是char类型,说明该函数返回一个字符型数据;如果不想返回任何数据,可以使用void数据类型,表示没有返回数据。
(2) 在“返回数据类型”后面接着空格,然后,接着“函数名”;这个函数名必须是一个合法的标识符,标记这个函数的名字。
(3) 在“函数名”后面,接着小括号(),在小括号中定义“形参列表”,这个形参列表表示要传递给函数的数据。例如(int a)表示函数参数接收一个int类型的变量。如果不想给函数传递数据,那么,形参列表可以为空或者为void数据类型;
(4) 在小括号后面,接着大括号{},在大括号中存放的是函数体;
(5) 函数体就是函数需要执行的具体任务;例如main函数的函数体,就是我们自己编写的代码,用来实现具体的运算操作。
此时,我们了解了函数的定义,那么,怎么样使用函数?很简单,就是两种形式:
(1) 如果没有定义形参列表,函数的使用形式是:函数名();
(2) 如果定义形参列表,函数使用的形式是:函数名(参数列表);
下面我们会循序渐进地讲解不同函数类型的使用方式。函数的使用,一般也称为:函数的调用;使用函数,称为:调用函数。
在学习变量的时候,我们知道:变量在使用之前,必须先定义,例如:
int a = 168;
printf(\”a = %d\\n\”, a);
可以看到,在使用printf函数输出变量a的时候,就是使用了变量a。那么,在使用变量a之前,必须先对变量a进行定义。修改上面的代码如下:
printf(\”a = %d\\n\”, a);
int a = 168;
此时,编译代码异常,提示printf函数在使用变量a的时候,变量a没有定义。因为变量a的定义,放在了printf函数之后。
同理,函数在使用之前,也必须先定义或者声明。此时,我们就提到两个概念:函数的定义和函数的声明。总结如下:
(1) 函数的定义是实现整个函数的功能,包括函数体的实现,要定义函数体;
(2) 函数的声明,不要实现函数体,只是说明函数定义的格式;
例如,有如下的函数:
void func(int a, int b, int c)
{
printf(\”in func2, a = %d, b = %d, c = %d\\n\”, a, b, c);
}
可以看到,这一段代码说明了func函数的格式和函数体的定义,那么,这段代码就是func函数的定义。
那么,函数的声明,不需要函数体的定义,只需要函数的格式说明,所以func函数的声明如下:
void func(int a, int b, int c);
注意,就是函数定义时,是由函数的返回值类型、函数名和函数的形参列表组成。而且,最后是以引号“;”作为结束符。C语言的函数声明,还可以有另一种格式,就是函数的形参列表中,可以不用给出形参变量名,只给出形参变量类型即可。例如:
void func(int, int, int);
可以看到,此时声明func函数,在形参列表中,只给出形参列表的变量类型即可,不用给出变量名。所以,我们可以总结函数的声明有如下两种形式:
(1) 函数返回值类型 函数名(形参类型1变量名1, 形参类型2 变量名2, …);
(2) 函数返回值类型 函数名(形参类型1, 形参类型2, …);
程序测试例子如下:
深入学习,可以交个朋友,工人人人号:韦凯峰linux编程学堂
程序运行结果如下:
可以看到,在main函数中调用了func1、func2函数,那么,func1、func2函数是在main函数后面定义。就是说,在main函数中使用func1、func2函数之前,并没有看到对func1、func2函数的定义。
所以,就必须在main函数之前,对func1、func2函数进行“函数声明”。可以看到,对func1、func2函数的声明,我们使用了两种不同的格式。对func1函数的声明,在形参列表中,说明了形参变量的名称。对func2函数的声明,在形参列表中,没有给出形参变量的名称。
注意:函数定义的时候,函数的返回值类型,函数名和形参列表都定义完整了。那么,在函数声明的时候,函数的返回值类型和函数名必须相同,而且不能够省略。对于形参列表,可以只给出形参变量的类型,不用给出形参变量的名称。
那么,在对函数进行声明的时候,可以不用给出形参变量的名称;如果要写形参变量的名称,也可以与函数定义格式中,与形参变量名不同。程序测试代码如下:
深入学习,可以交个朋友,工人人人号:韦凯峰linux编程学堂
程序运行结果如下:
深入学习,可以交个朋友,工人人人号:韦凯峰linux编程学堂
可以看到,func1函数在声明的时候,形参变量的名称可以与函数定义时的形参变量名不同,只要形参变量的类型相同就可以了。
C语言的main函数的几种写法
main函数即为主函数,C程序总是从main()函数开始执行的。main函数是整个C程序的执行入口,一个程序想要运行起来,必须指定main函数。各种资料和书籍对main函数的写法各有不同,甚至很多的写法都有误区,本文整理了常见的mian函数写法,并逐一测试说明。
C89/C99/C11标准文档中只提供了两种main函数的写法:
int main(void) { /* … */ }
int main(int argc, char *argv[]) { /* … */ }
C99规定:main函数必须有返回值,而且必须是int 类型的,main函数return 0表示函数正常退出,否则表示错误退出,如果没有return语句,编译器自动在目标上添加return 0。
如果把main函数当成普通函数那么除了函数名main不可变,那么其他都有可以变,返回值类型,参数列表,返回值这三种搭配会产生各种形式。
样式一:int main(void){/* … */}
样式二:void main(void){/* … */}
样式二:main(void){/* … */}
样式一不必多说,那么其他两个是怎么出现的?样式二这种常见于老旧的教材,我记忆中那会谭浩强的书里就有这种写法。目前不少的编译器是能够编译通过的,但是这是编译器的处理并不是标准中的定义,甚至在C99之后明确规定这种就是错误写法!样式三不写返回值类型在C89标准中允许,C语言第一版中只有int一种类型,没有char,long,float等,既然只有一种类型,那么就可以不写,后来的改进版为了兼容以前的代码便规定:不明确标明返回值的,默认返回值为int,也就是说样式三等同于样式二。虽然可以编译,但在C99标准中要求编译器至少给 main() 这种用法警告,强烈不建议这么写!
使用以上的形式输出Hello World结果如下:
样式一和二的结果:C89和C99下编译运行正常
样式三的结果:C89和C99下编译运行正常,但是C99给出警告
样式四:int main(){/* … */}
样式五:void main(){/* … */}
样式六:main(){/* … */}
样式四到样式六对应着样式一到样式三,唯一不同在于里面的void省去了,很多人觉得这个应该和void一样的,都是表示不带参数的意思,但实际上并不是!在C语言中func()表示的是参数类型,数目不确定,并且可以重新对它的完整参数类型和个数重新声明。但是func(void)则完全限制了不允许任何参数,即使重新声明,也不能改变。
使用func():
func()任意参数列表都可以编译运行
使用func(void):
func(void)只要有参数直接报错
使用样式四到样式六输出Hello World结果与样式一到样式三一致,图略。
样式一:int main(int argc, char *argv[]){/* … */}
样式二:void main(int argc, char *argv[]){/* … */}
样式三:main(int argc, char *argv[]){/* … */}
样式四:int main(int argc){/* … */}
样式五:void main(int argc){/* … */}
样式六:main(int argc){/* … */}
样式七:int main(int argc, char *argv[], char *envp[]){/* … */}
样式八:void main(int argc, char *argv[], char *envp[]){/* … */}
样式九:main(int argc, char *argv[], char *envp[]){/* … */}
以上的各种形式中指针数组都可以使用二级指针替换:*argv[]->**argv,*envp[]->**envp.这里不一一列出来了。
标准的main函数中的参数最多有两个:第一个参数argc(argument count)是命令行中的字符串个数。第二个参数argv(argument value)是一个指针数组,一般程序名赋值给argv[0],从argv[1]开始对各个参数依次赋值。这里的参数可选,使用一个,使用两个都可以。
一个argc参数:
带上程序名,一共四个参数
两个标准参数:
输出所有的argv参数,这里的程序名包含路径
main函数还可以有第三个参数,用于获取环境变量,这种形式多源于编译器的扩展。但全局变量environ可以代替envp的作用,获取或者设置环境变量可以使用getenv或putenv,因此这种形式出现的很少。
三参数:
最后一个参数打印了全局环境变量
main函数的返回值最终会作为程序的退出状态,0表示函数正常退出,否则表示错误退出。这也是不推荐使用void作为返回值类型的原因,一旦声明为void,在程序退出后,想要获取其退出状态也就不可以了!如果main函数的最后没有写return语句的话,C99规定编译器要自动在生成的目标文件中加入return 0,表示程序正常退出。尽管省略return语句后,也能通过编译,但仍建议最好加上return语句,养成好习惯。
在其他领域以上规则不尽然完全适用,单片机中使用void main(void)还是很常见的。很多低级单片机的资源有限,常见的比如51单片机,基本上不会使用系统的,一般就是裸跑, 编译器初始化后直接转到main运行,不会给main传任何参数。进入main里面做一些硬件初化后,一般会进入while的死循环保持运行,直到掉电,不需要返回值,所以经常可以看到void main(void)这样的代码。
51单片机点灯:
「零基础学C语言」知识总结七:函数知识总结
有时候,如果程序的功能比较多。规模比较大,把所有的程序代码都写在一个主函数中,就会使得主函数太庞杂,所以为了方便阅读和维护程序,就引进了 组装程序 的概念,把某些功能都在其他分支完成,然后需要哪个功能的时候就组装那个分支到主函数,这些分支就叫它函数,组装就叫调用,这样就会使主程序简化了,哪个函数是做什么也都很清楚。哪里用得到这个功能就在哪个调用就可以了!
C 语言的函数可以直观地辨别出面向过程和面向对象的区别,C 语言的函数有一个特点,就是它有固定的格式和固定的模型。对于一个 C 程序而言,它所有的命令都包含在函数内。每个函数都会执行特定的任务。每个函数都只能被定义一次。但一个函数可以根据需要被多次的声明和调用。
在使用函数之前必须先定义,后使用。
定义函数要包括:
1、指定函数的数据类型,以便后续返回值的调用
2、指定函数的名字,以便后续调用
3、指定函数形参的类型和名字,以便后续传递数据,对于无参(void)可不用
函数类型+函数名+(参数表){
函数体
}
参数值的()起到了表示函数调用的重要作用即使没有参数也需要(),如果有参数,则需要给出正确的数量和顺序,这些值会按照一定的顺序(看编译环境一般是从右到左)依次来初始化函数中的参数
注意 如果调用函数时给的值与参数的类型不匹配有些编译环境可能会帮你类型强制转换好,但是有可能不是你想要的那样。所以 建议传递给函数的值要与声明一致
#include<stdio.h>//这里声明的是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),调用的时候不能做返回值的赋值
前面加有void 的函数,不能返回任何数据,这类函数应该将所有应该实现的功能在本函数内全部实现。但是并不是不能与外部交换数据,仍然可以通过引用型参数传递数据,只是调用时不能直接接受返回值,因为就没有返回值。
前面有int的函数,返回值是整型数,可能是结果是整数的数,也可能是运行状态,成功或失败的标识,函数调用时可以直接利用返回信息,实现一些功能。
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的指针)
#include <stdio.h>
int 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\\n\”, argv[i]);
}
return 0;
}
给main函数传递的这两个参数,argc和argv。argc是int类型的,它表示的是命令行参数的个数。不许要用户传递,它会根据用户从命令行输入的参数个数,自动确定。argv是char**类型的,它的作用是存储用户从命令行(黑窗口输入)传递进来的参数。它的第一个成员是用户运行的程序名字。
main函数的返回值,用于说明程序的退出状态。如果返回0,则代表程序正常退出;返回其他数字的含义则由系统决定,通常,返回非零代表程序异常退出。 (一般是由return返回)
(return 是C语言的关键字)函数定义为什么样的返回类型,该函数中return后就应该是相应类型的值。
在函数中,如果碰到return 语句,那么程序就会返回调用该函数的下一条语句执行,也就是说跳出函数的执行,回到原来的地方继续执行下去。但是如果是在主函数(main)中碰到return语句,那么整个程序就会停止。return表示从被调函数返回到主调函数或其他函数继续执行,返回时可 附带一个返回值,返回值可以是一个常量,变量,或是表达式。 传指针形式:直接传给函数的是变量的地址,由于被调函数在参数指针的作用域之内,此时直接改变变量的本体。
返回值: 计算结果表示函数执行的顺利与否(-1、0) 返回值可以为各种数据类型,如:int,float,double,char,a[数组],*a(指针),结构或类。写return是一种清晰的风格,可以防止一些意外的错误。有时候也是想中断函数执行,返回调用函数处。
返回本地变量的地址是危险的,返回全局变量或静态本地变量的地址是安全的,返回在函数malloc的内存是安全的,但是容易造成问题,最好的办法是返回传入的指针。
在函数中调用函数自己 :即自己return自己 = 递归
#include<stdio.h>//输出直到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函数之前。
注意:尽量不要使用全局变量来在函数之间传递参数和结果。尽量避免使用全局变量(丰田的案子),使用全局变量和静态的本地变量是线程不安全的。
作者:Mr_Li_
对啦对啦!另外的话为了帮助大家,轻松,高效学习C语言/C++,我给大家分享我收集的资源,从最零基础开始的教程到C语言项目案例,帮助大家在学习C语言的道路上披荆斩棘!可以来我粉丝群领取哦~
编程学习书籍分享:
编程学习视频分享:
整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!
对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。