C语言基础之函数1(基础篇)

函数计划分成3篇来讲: 上篇是函数的基础篇,中篇是函数的进阶篇,下篇是函数的高级篇。

一、函数介绍

        函数其实在任何编程语言下,作用都只有一个: 就是用来包裹共同完成同一个功能的代码,以便这份代码看起来成一个独立的整体, 并且未来如果需要多次使用这份代码,就不需要重复编写,只需要简单的一行调用代码。即可完成功能的复用,极大的方便我们管理代码和提升了代码的复用率。


#include <stdio.h>

// 打印HELLO
void printHello() {
    printf("H   H  EEEEE  L      L       OOO   \n");
    printf("H   H  E      L      L      O   O  \n");
    printf("HHHHH  EEEE   L      L      O   O  \n");
    printf("H   H  E      L      L      O   O  \n");
    printf("H   H  EEEEE  LLLLL  LLLLL   OOO   \n");
}

int main() {
    // 调用打印HELLO的函数
    printHello();
    // 可以多次调用
    printHello();
    return 0;
}

二、函数基本使用

        与变量一样,函数要想使用必须先完成声明定义,然后才能使用。所不同的是函数远比变量要复杂的多,但是万法不离其宗,我们时钟按照先定义,后使用的规则去使用函数即可。

2.1 函数语法

        C语言的函数语法并不难记,从1972年C语言诞生至今就一直是这个样子。如果我们多写几次,它的构成部分还是很容易记住的。

//定义函数
函数返回值类型 函数名称(参数列表){
   函数体
   return 返回值;
}
//调用函数
函数名称(具体的参数);

柯南有话说:

1. 函数返回值: 这个函数执行结束之后需要返回什么数据吗?如果有数据返回,就写数据的类型,如果没有数据返回就写 void 。

2. 函数名称: 可以自定义、只要不是关键字,通俗易懂即可。如果有多个单词可以使用下划线连接,也可以使用驼峰式命名。

3. 参数列表:   调用这个函数的时候需要传递什么数据给它吗?如果有需要则需要声明参数,声明语法为 参数类型 参数名称。可以声明多个参数,使用 逗号 间隔即可。若没有传递参数的必要,则空着不写,保留()即可。

4. 函数体: 这个函数要包装的代码。具体实现的什么功能,把代码写在 {} 里面。

5. 返回值:   表示函数执行结束要返回什么数据出去给调用者吗?如果有则使用 return 关键字 搭配需要返回的数据写上即可。

6. 调用函数: 调用函数需要使用函数名称配合 () 来调用, 这个() 在这里称之为调用符号。目的就是为了运行函数的代码。如果函数有参数的话,可以在调用的时候从() 传递进去具体的参数。多个参数使用 逗号 间隔。

2.2 函数示例


#include <stdio.h>

// 定义 say_hello 函数
void say_hello() {
    printf("Hello, World!\n");
}

int main() {
    // 调用 say_hello 函数
    say_hello();

    return 0;
}

三、函数种类(四种写法)

        根据函数返回值和函数参数列表,可有可无的情况,我们可以组合出来函数的4种分类,分别是: 无参无返回值、无参有返回值、有参无返回值、有参有返回值。所有的函数都被囊括在这这种函数分类中,虽然对于大牛、老人家程序员来说,这多么简单呀,但是对于小白还是先熟悉熟悉函数的分类写法,未来写多了也就自然而然成为大牛了(^_^)

3.1 无参无返回值

   无参无返回值即调用函数时不需要传递参数,函数运行结束也不会有返回值,声明为 void 即可

#include <stdio.h>

// 定义函数
void fun01() {
    printf("fun01...\n");
}

int main() {
    // 调用 fun01函数
    fun01();

    return 0;
}

3.2 无参有返回值

        无参有返回值即没有参数,但是可以有返回值,返回值的类型由返回值来决定。 不建议随便写返回值类型,返回值类型和返回值匹配上,这样方便告诉外界调用者,调用我们的函数最终会返回什么样的数据。

#include <stdio.h>

// 定义函数
int fun01(){
    return 6;
}

int main() {
    // 调用 fun01函数
    int result = fun01();
    printf("调用函数得到结果: %d \n" , result);
    return 0;
}

3.3 有参无返回值

        有参无返回值即有参数传递,但是没有返回值。有多个参数的话,需要使用逗号间隔声明。没有返回值时,则可以声明为 void

#include <stdio.h>

// 定义函数
void fun01(int a, int b , char c) {
    printf("fun01:: %d, %d, %c\n" , a , b ,c );
}

int main() {
    // 调用 fun01函数
    fun01(3,4 , 'a');
    return 0;
}

3.4 有参有返回值

        有参有返回值,与无参无返回值是两个极端,注意参数和返回值即可,其他与前面3个一样

#include <stdio.h>

// 定义函数 计算两数之和
int fun01(int a, int b ) {
   return a + b;
}

int main() {

    // 调用 fun01函数
    int result = fun01(3,4);
    printf("调用函数得到结果: %d \n" , result); // 打印 7

    return 0;
}

四、函数声明

        函数在某种程度上与变量、指针一样,都需要实现声明定义,然后才能使用。只不过相比于后两者,函数的体量更大一些。在上面的函数4种分类写法的示例中,我们都是先给出了函数的定义(也就是函数到底是执行什么代码),给出了函数的全貌,然后才在后面的main函数中调用。假设我们颠倒一下两者的顺序会出现什么现象呢?

4.1 函数声明的原因

下面的代码演示了,把函数fun01定义在后面,但是选择在前面调用函数fun01。

#include <stdio.h>

int main() {
    // 调用 fun01函数
    fun01();
    return 0;
}

// 定义函数
void fun01() {
    printf("fun01...\n");
}

柯南有话说:

1. 编译器编译代码,属于自上而下编译,在执行到调用函数 fun01()的时候,依然没有发现过它的踪迹。在不同的编译器里可能会出现如下情况:

1.1 出现函数没有定义的提示,或者不认识这个符号 fun01的提示。

1.2 会出现重复声明的问题,在前面调用fun01的时候,由于前面没有发现这个函数fun01的踪迹,所以默认根据调用信息,隐式生成了一个函数声明,但是到后面又发现了正儿八经的fun01的函数定义,就出现了假李鬼遇上真李逵的场景了。

2. 编译器之所以这么做,也是有原因的,自上而下的编译,如果在前面遇到了函数fun01的声明定义、那么即可收集到该函数的特征,比如: 函数返回值类型、函数名称、函数参数列表(类型和个数) 这些信息。这就使得在编译后面调用函数 fun01() 的时候识别出我们是否是按照规矩来调用,以便给出错误的提示。

3. 既然有这个缘由在,那么不能把所有的函数都放在上面统一写就,然后把main函数放在最后不行么? 答案是可以的。但是我们也要意识到这种问题的存在,假设以后有多个函数相互纠缠存在这样的调用关系(箭头表示调用)A -->B , C--->D 和 E , B ---> D 和 F 。我们会疲于应付符合先声明定义、后使用的原则,因为我们不得不去计算调用的先后顺序。

4. 好吧,那么有没有什么办法可以根本不用操心这种调用顺序呢?即便有一百个函数,一千个函数,甚至更多,我们都不担心这个问题,该怎么解决呢? 此时就不得不说: 函数声明(或者叫做函数原型 ,在C++里面就是这么叫的)的概念了。

4.2 函数声明示例

        函数其实可以做成与变量一般,先声明后赋值,函数只要在前面表示出来:返回值类型、函数名称、参数列表即可,编译器掌握这些信息之后,才能够在后面正常编译我们调用函数的代码。

#include <stdio.h>

//声明函数:: 只要表示出函数的返回值、函数名称、参数列表即可
void fun01()

//声明函数:: 只要表示出函数的返回值、函数名称、参数列表即可
int fun02(int a , int b);

int main() {
    // 调用 fun01函数
    fun01();

    //调用 fun02函数
    int result = fun02(3 ,4);
    printf("result=%d\n",result);

    return 0;
}

// 定义函数:: 给出函数的具体运行代码
void fun01() {
    printf("fun01...\n");
}

// 定义函数:: 给出函数的具体运行代码
int fun02(int a , int b) {
    return a + b;
}

柯南有话说:

1. 上面的示例就是掩饰了先声明函数,后调用函数的写法。我们一般会按照这种标准来写代码,大家要勤加练习,即便未来在学习C++的时候,也完全是使用这种风格。

2. 虽然这么写函数,看起来代码量变多了,工作量随之也多了起来。但是注意观察,我们从把代码从上往下看。从我们声明函数到调用函数,中间可以不用出现函数的全貌(也就是函数运行什么代码),只要未来这个函数的代码能够正常参与到程序编译中来即可。

3. 这就为了头文件和源文件的出现提供了契机,我们可以把关于函数的声明统一放在头文件中,然后把函数的定义(函数的全貌)放在源文件中。只要有哪个地方需要用到这个函数,只需要引入头文件即可(这可以看作是完成函数的声明)。当编译运行整个程序的时候,顺便编译刚才我们存放函数代码的源文件即可。这样子做,极大的方便我们管理代码,可以使用模块化的思维来管理代码。【不知道小伙伴能不能理解~~若不能理解,请务必给我来信,我一定会耐心解答~】。

五、总结

好啦,欢乐的时光总是如此的短暂,又到了要说再见的时候。在这我们稍微总结一下基础篇的内容,看起来文章篇幅挺长,包括了函数的介绍、作用体现、函数的语法,以及函数的四种分类。这些内容大家完全可以通过多写几遍来熟悉它们,尤其对于新手小白来说,更要这么做。至于后面的函数原型声明这小节内容,除了需要去动手实践之外,还需要大家多思考为什么需要这么做。很多时候我们的编程思想就是这么锻炼出俩的,如果觉得谁又想到这茬呢,那么好好跟着咱们得C语言基础连载篇走下去,相信你在提升的同时,思维逻辑也会有质的飞跃。


六、思考

按照惯例还是留一些思考题吧:

1. 函数为什么要先声明,后使用呢?(自己尝试组织一下语言回答)

2. 能否出现同名的函数呢?

  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值