函数专题【C语言从入门到精通系列(九)】


1. 函数的声明、定义 与 调用

函数的定义是对函数整体功能的确定,即完整地定义一个函数;C语言的函数可以嵌套调用,但是不能嵌套定义(即在函数中再定义函数,这种做法是不可行的)。

函数的声明则相对简略些,具体包括把函数的名字、函数类型及形参的类型、个数和顺序通知编译系统,以便在调用该函数时编译系统能正确识别函数并检查调用是否合法。
 ;
在做函数声明的时候,要尽可能避免做出隐式声明。隐式声明即省略一些可以省略的声明信息,使其使用默认值。如:函数如果不显式地声明返回值的类型,那么它默认返回整型。隐式声明不是好的习惯。

主函数可以调用其他子函数。

子函数之间也可以互相调用。

但是子函数不可以调用主函数。


直接定义一个子函数,然后在主函数中调用它。代码示例如下。

#include <stdio.h>

void print_hello(){
    printf("%s\n","hello");
}

int main() {
    print_hello();
    return 0;
}

作为规范的写法,函数必须先被声明或者定义,后被调用。而不能先调用后定义,不然会产生警告。


当调用未被定义的子函数的时候,默认返回的是int类型。如果先调用,后定义子函数,且子函数的返回值类型不为int,则会发生类型不匹配的警告。

虽然也可以先声明,再调用,最后再定义。但是不建议这样写。一般建议主函数写在最后。
下边便是,合理但是不推荐的写法的示例:

#include <stdio.h>

//先声明
void print_hello();

int main() {
    print_hello(); //调用
    return 0;
}

// 定义
void print_hello(){
    printf("%s\n","hello");
}

2.多文件写法

对于较大的程序项目,通常将程序内容分别放在若干源文件中,再由若干源程序文件组成一个 C 程序。
这样处理便于分别编写、分别编译,进而提高调试效率。一个源程序文件可以为多个C 程序共用。

下边做一个简单示例。首先创建一个项目文件夹,以test为例。
这里以VScode为工具为例。多文件写法具体包含三部分:

2.1 头文件(xxx.h)

在test文件夹内,新建一个后缀为.h的文件,即头文件。这里以func.h为例。
假设我想要定义的是两个函数,分别是print_hello()print_sum()
则需要在func.h中声明这两个函数。
如果这两个函数有需要引用的头文件,则也应在func.h中引用。
具体代码示例如下:

#include <stdio.h>

void print_hello();
int print_sum(int a,int b);

2.2 定义函数的源文件(xxx.c)

头文件中只做了函数的声明,并未做函数的定义。函数的定义应体现在若干个(.c)源文件中。

以一个源文件func.c为例,
func.c中定义print_hello()print_sum()的相关代码示例如下:

#include "func.h"

void print_hello(){
    printf("%s\n","hello");
}

int print_sum(int a,int b){
    int result;
    result = a+b;
    printf("%d\n",result);
    return result;
}

因为我们在这里引用了头文件"func.h",而头文件中也引用了已经引用<stdio.h>,所以在func.c中不需要再重复引用,只需要引用"func.h"即可。

统一将每个源文件要引用的头文件都写在一个固定的头文件(func.h)中,然后每个源文件 引用该固定的头文件。这样写的好处是,降低了代码的冗余度,也有利于每个.c源文件之间都能够实现交叉引用。而不是在需要的时候再手动一个一个引用。


2.3 主函数文件(main.c)

主函数文件,同命名为main.c。C程序的执行是从main函数开始的,也结束于main函数。

主文件也是需要引用头文件的,且需要确认头文件func.h存在,且位于与主程序文件相同的目录下。

#include "func.h"

int main(){
    print_hello();
    print_sum(100, 200);
    return 0;
    }

2.4 程序执行

编辑好后的三个文件在VSCode中如下:
                在这里插入图片描述


要执行程序,可以通过命令函来实现。首先通过命令行进入到test文件夹下,
然后执行以下命令,即可得到一个可执行的程序文件:

 gcc main.c func.c -o program

得到exe程序后,可以双击执行(双击执行可能不会有任何显示效果)。
也可以继续在命令行中执行,以查看程序输出效果。

program.exe

程序输出结果:

hello

300

3.函数的递归调用

递归,即函数在满足或不满足一定约束条件的情况下,函数自身调用自身的使用方法。
以定义一个求阶乘的函数为例。代码示例如下:

# include <stdio.h>

int fun(int n){
    if (1==n){
        return 1;
    }
    return n*fun(n-1);
}


int main(){
    int num;
    scanf("%d",&num);
    printf("f(%d)=%d",num,fun(num));
    return 0;
}

输入;

10

输出:

f(10)=3628800

这是递归的基本用法,使用递归的核心便是找公式,尤其是当面对较为复杂的问题的时候。

n=1    f(1)=?
n=2    f(2)=?
n=3    f(3)=?

n=x    f(x)=?


下边给出一个例子,以一个上台阶的问题为例。
假设台阶一共有n阶,每一步可以走1阶,也可以走两个台阶,不可以走更多。问一共有多少种上台阶的方法。
代码实现如下:

# include <stdio.h>

int fun(int n){
    if (1==n||2==n){
        return n;
    }
    return fun(n-1)+fun(n-2);
}


int main(){
    int num;
    scanf("%d",&num);
    printf("fun(%d)=%d",num,fun(num));
    return 0;
}

输入;

5

输出:

fun(10)=8


4.全局变量

数据传递的方式有三种:
①参数
②返回值
③全局变量

全局变量定义在函数之外,也称外部变量全程变量。一般不建议使用。全局变量可以在全局使用,在定义的函数内也可以使用。
下边定义全局变量i示例如下。

# include <stdio.h>

int i=100;

void print(){
    printf("i=%d\n", i);
}

int main(){
    print();
}

程序输出结果:

i=100


如果在某个函数中修改全局变量,以主函数为例:

# include <stdio.h>

int i=100;

void print(){
    printf("i=%d\n", i);
}

int main(){
    int i=10;
    print();
}

程序输出结果依然是100,而不是10.

i=100

这是因为,全局变量是存储在数据段中的,而函数内的局部变量是存储在栈空间内的。主函数中定义的变量i存在与栈空间内,而print函数中的数据存储于另一个栈空间内。因为print函数对应的栈空间内没有定义局部变量i,所以访问的是全局变量中的变量i
 ;
如果是在子函数print中定义了变量i=10,则在print函数中打印的i则为10。此时此处访问的i则为print函数对应的栈空间内的变量i

局部变量只在其当前作用于内有效,即其所在的大括号内。局部变量也是有层级的,即多层大括号嵌套的时候。内层大括号内没有定义某局部变量时,可以在这里访问外层大括号内定义的该局部变量的。但是外层大括号不能访问内层大括号内定义的变量。(如,for循环括号内定义的变量在该for循环外不可用。)
 ;
一般要避免全局变量与局部变量同名。或者避免使用全局变量。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值