文章目录
1、函数声明
1.1、函数声明的背景
C 语言代码由上到下依次执行,而且函数定义要出现在函数调用之前,否则就会报错。
在实际开发中,经常会在函数定义之前使用它们,这个时候就需要提前声明。
1.1.1、举例1
在之前文章(2.10)函数入门 – 保证入门 里面有个例子是这样的:
#include <stdio.h>
int Add(int x, int y)
{
return x+y;
}
int main()
{
int a = 0;
int b = 0;
int sum = 0;
scanf("%d %d", &a, &b);
sum = Add(a, b);
printf("%d\n", sum);
return 0;
}
上面代码中橙色的部分是函数的定义,绿色的部分是函数的调用。
这种场景下是函数的定义在函数调用之前,没啥问题。
1.1.2、举例2
那如果我们将函数的定义放在函数的调用后边,如下:
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
int sum = 0;
scanf("%d %d", &a, &b);
sum = Add(a, b);
printf("%d\n", sum);
return 0;
}
int Add(int x, int y)
{
return x+y;
}
代码编译后你就会发现报错!
这是因为 C 语⾔编译器对源代码进行编译的时候,从第⼀行往下扫描的,当遇到第 11 行的 ``Add`` 函数调用的时候,并没有发现前面有 Add 函数的定义,就报错退出。
1.1.3、举例3
把怎么解决这个问题呢?就是函数调用之前先声明⼀下 Add 函数就 ok 了。
如:int Add(int x, int y); 这就是函数声明,具体解释看下面解释。
代码变成这样就能正常编译了:
#include <stdio.h>
int Add(int x, int y);
int main()
{
int a = 0;
int b = 0;
int sum = 0;
scanf("%d %d", &a, &b);
sum = Add(a, b);
printf("%d\n", sum);
return 0;
}
int Add(int x, int y)
{
return x+y;
}
1.2、函数声明的格式
函数声明的格式非常简单,相当于函数头并在最后加上分号;,如下所示:
dataType functionName( dataType1 param1, dataType2 param2 ... );
在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此也可以不写形参,只写数据类型,如下所示:
dataType functionName( dataType1, dataType2 ... );
针对上面的函数声明举例如下:
int max(int num1, int num2);
int max(int, int);
因此:
函数的调用⼀定要满足:
先声明后使用;函数的定义也是⼀种特殊的声明,所以如果函数定义放在调用之前也是可以的。
1.3、函数原型
函数声明给出了函数名、返回值类型、参数列表(重点是参数类型)等与该函数有关的信息,称为函数原型。
函数声明和**函数原型**二者通常是相同的,实际上函数原型也是一种函数声明,但函数原型这个术语强调了参数信息。
1.4、函数声明的作用
初学者编写的代码都比较简单,顶多几百行,完全可以放在一个源文件中。对于单个源文件的程序,通常是将函数定义放到 main() 的后面,将函数声明放到 main() 的前面,这样就使得代码结构清晰明了,主次分明。
白话文解释函数声明的作用:告诉编译器与该函数有关的信息,让编译器知道函数的存在以及存在的形式,即使函数暂时没有定义,编译器请不要报错,稍后我会把定义补上。 有了函数声明,函数定义就可以出现在任何地方了,甚至是其他文件、静态链接库、动态链接库等。
2、跨文件的声明
2.1、头文件
还记得之前文章(2.3)头文件 的内容吧?
头文件的作用是方便其它源文件引用,引用头文件相当于复制头文件的内容。
2.2、声明举例
同时⼀般在企业中我们写代码时候,代码工程比较大,往往都是几千行、上万行、百万行的代码,将这些代码都放在一个源文件中简直是灾难,不但检索麻烦,而且打开文件也很慢,所以必须将这些代码分散到多个文件中。
我们往往会按功能模块划分为多个文件,方便代码编写、工程维护。通常是将函数定义放到源文件(.c文件)中,将函数的声明放到头文件(.h文件)中,使用函数时引入对应的头文件就可以,编译器会在链接阶段找到函数体。
因此:
⼀般情况下,函数的声明、类型的声明、常量的定义是放在头文件(.h)中,函数的实现是放在源文件(.c)文件中。
add.c
//函数的定义
int Add(int x, int y)
{
return x+y;
}
add.h
//函数的声明
int Add(int x, int y);
test.c
#include <stdio.h>
#include "add.h"
int main()
{
int a = 10;
int b = 20;
//函数调⽤
int c = Add(a, b);
printf("%d\n", c);
return 0;
}
运行结果:
30
2.3、扩展
前面我们在使用 printf()、puts()、scanf() 等函数时引入了 stdio.h 头文件,很多初学者认为 stdio.h 中包含了函数定义(也就是函数体),只要有了头文件就能运行?
其实不然,头文件中包含的都是函数声明,而不是函数定义,函数定义都放在了其它的源文件中,这些源文件已经提前编译好了,并以动态链接库或者静态链接库的形式存在,只有头文件没有系统库的话,在链接阶段就会报错!程序根本不能运行。
只是我们的编译环境里默认都有 C 库文件,我们没感觉到而已!
3、函数的链式调用(嵌套调用)
所谓链式调用就是将⼀个函数的返回值作为另外⼀个函数的参数,像链条⼀样将函数串起来就是函数的链式调用,也称嵌套调用。
比如:
#include <stdio.h>
#include <string.h>
int main()
{
int len = strlen("abcdef"); //1. strlen 求⼀个字符串的⻓度
printf("%d\n", len); //2. 打印⻓度
return 0;
}
结果毋庸置疑是 6 个字符。
前面的代码写了 2 条语句,把如果把 strlen 的返回值直接作为 printf 函数的参数呢?
如下:
#include <stdio.h>
#include <string.h>
int main()
{
printf("%d\n", strlen("abcdef")); //链式调用或嵌套调用
return 0;
}
发现结果是是 6 个字符,两句语句结合成一个语句了,功能是一样的。
3.1、⼀个有趣的代码
#include <stdio.h>
int main()
{
printf("%d", printf("%d", printf("%d", 43)));
return 0;
}
这个代码的关键是明白 printf 函数的返回值是啥?
printf 函数返回的是
打印在屏幕上的字符的个数。
上面的例子中,我们就第⼀个 printf 打印的是第⼆个 printf 的返回值,第⼆个 printf 打印的是第三个 printf 的返回值。
结果如下:
第三个 printf 打印 43,在屏幕上打印 2 个字符,再返回 2
第⼆个 printf 打印 2,在屏幕上打印 1 个字符,再返回 1
第⼀个 printf 打印 1
所以屏幕上最终打印:4321
每日一更!
公众号、优快云等博客:小菜狗编程笔记
谢谢点赞关注哈!目前在飞书持续优化更新~
日更较慢有需要完整笔记请私我,C/C++/数据结构-算法/单片机51-STM32-GD32-ESP32/嵌入式/Linux操作系统/uboot/Linux内核-驱动-应用/硬件入门-PCB-layout/Python/后期小程序和机器学习!
1581

被折叠的 条评论
为什么被折叠?



