《小菜狗 C 语言入门 + 进阶笔记》(12)函数进阶 -- 声明与调用

文章目录:《小菜狗 C 语言入门 + 进阶笔记》(0)简介



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 语言入门 + 进阶笔记》(0)简介

每日一更!

公众号、优快云等博客:小菜狗编程笔记

谢谢点赞关注哈!目前在飞书持续优化更新~

日更较慢有需要完整笔记请私我,C/C++/数据结构-算法/单片机51-STM32-GD32-ESP32/嵌入式/Linux操作系统/uboot/Linux内核-驱动-应用/硬件入门-PCB-layout/Python/后期小程序和机器学习!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小鹅编程笔记

你的鼓励将是我最大的动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值