椋鸟C语言笔记#12:函数、参数、多文件协作、static与extern

萌新的学习笔记,写错了恳请斧正。


目录

函数的概念

库函数

标准库

自定义函数

自定义函数的语法格式

形参与实参

实参

形参

形参与实参的关系

return语句

数组作为函数的参数

嵌套调用与链式访问

嵌套调用

链式访问

函数的声明与定义

单文件

多文件

static与extern

static

static修饰局部变量

static修饰全局变量

static修饰函数

extern


函数的概念

完成特定功能的小段代码(子程序)。将一个大的程序拆解为实现不同功能的函数,有利于提高开发效率,也方便部分功能的反复使用。

一般函数分两类:

  • 库函数
  • 自定义函数

库函数

标准库

C语言规定了一些常用的函数(比如printf、scanf)的标准,即标准库。然后各个编译器厂商按照标准给出这些函数的实现,即库函数。这些函数不需要程序员自己实现,只要会用即可。这大大提高了开发效率。

这些库函数根据功能的划分,在不同的头文件中进行了声明。如果想要使用,只要包含头文件即可。

C语言自带头文件:头文件

自定义函数

自定义函数很重要,它赋予了代码更多的可能性

自定义函数的语法格式
returntype func_name(形参)
{
    ...
}
  • returntype是函数返回值的类型,可以是任何数据类型,如果写void则不返回值
  • func_name是函数名,自定义
  • 括号里放形式参数,是函数的“原材料”,可以是各种类型的数据,如果写void则函数没有参数
  • 花括号里放函数体,是具体的“加工”、计算的过程

比如说,我们可以写一个加法函数并在主函数中使用:

#include 

int Add(int x, int y)
{
    int sum = x + y;
    return sum;
}

int main()
{
    int a = 0, b = 0;
    scanf("%d%d", &a, &b);
    printf("%d", Add(a, b));
    return 0;
}

其中,return是确定函数返回值的关键字,下面会讲

上方加法函数代码也可简化为:

int Add(int x, int y)
{
    return x + y;
}
形参与实参
实参

在函数使用时我们把函数的参数分为形参与实参

上方示例中主函数中向Add函数传递的a、b即为实参,是真实传递给函数的参数

形参

上方代码中定义函数时,函数名后面括号里的x与y即为形式参数

形式参数只在形式上存在,不会向内存申请空间

只有在运行时函数被调用时,为了存放实参传递过来的值,才会向内存申请一块临时的空间——这个过程被称为形参的实例化

形参与实参的关系

在程序运行到调用函数时,就会在内存开辟一块空间,并复制一份实参的值在函数中使用

所以说形参是实参的一份临时拷贝

return语句
  • return后面可以是一个数值,也可以是一个表达式(如果是表达式就先执行后返回)
  • return后面可以什么都没有,那么直接结束函数并不返回值
  • 如果return后面的值的类型与函数的返回值类型不一致,就会自动进行强制类型转换
  • return语句将直接结束函数,后面的代码不会执行(可以理解为用于函数的break)
  • 如果函数中存在分支语句,应使每一条分支后都有return返回,否则编译不通过
数组作为函数的参数

如果将数组作为参数传递给函数,那么情况会有所不同:

  • 函数的形式参数与传递过来的实参个数一致
  • 实参是数组时,形参可以写成数组形式
  • 形参如果是一维数组,数组大小不用写,写了也会被忽略
  • 形参如果是二维数组,行可以省略,但是列不可以
  • 数组传参时,形参不会创建新的拷贝,所以函数中对数组的操作会被保留

嵌套调用与链式访问

嵌套调用

每一个函数组件施行某个特定功能,可以看做一个零件

而函数中也可以调用函数,甚至调用自身,以达成复杂目的

但是函数可以嵌套调用但不能嵌套定义

而且注意不要互相调用造成死循环

链式访问

链式访问就是将一个函数的返回值作为另一个函数的参数

比如说上方举例的Add函数的返回值就作为了printf函数的参数

这里就有一道有趣的题:

printf("%d", printf("%d", printf("%d", 123)));

上方代码的输出结果是什么?

是:12331

为什么?

先执行了最里层的printf打印123,而printf的返回值是打印字符的长度,则中间的printf接收到123的长度3并打印出来,最终外层的printf接受3的长度1并打印出来

类似的,下方代码将输出123 4 2 (3、4、2后有空格)

printf("%d \n", printf("%d ", printf("%d ", 123)));

函数的声明与定义

单文件

一般我们直接就把函数在调用函数的函数前面写出来用了

如上方举例的Add函数的定义直接写在调用其的主函数(main)前

如果将函数的定义放在调用它的函数的后面,就会报警告了,如下方示例:

#include 

int main()
{
    int a = 0, b = 0;
    scanf("%d%d", &a, &b);
    printf("%d", Add(a, b));
    return 0;
}

int Add(int x, int y)
{
    int sum = x + y;
    return sum;
}

编译器在编译时从上往下扫描结果突然遇到没见过的函数,不知道这个函数的返回类型,就会警告

像解决这个问题,除了把函数挪到前面,也可以直接在前面对函数进行声明

函数的声明需要包括函数名、返回类型、和参数

比如说,把上面的代码改成这样就能正常运行了:

#include 

int Add(int x, int y);

int main()
{
    int a = 0, b = 0;
    scanf("%d%d", &a, &b);
    printf("%d", Add(a, b));
    return 0;
}

int Add(int x, int y)
{
    int sum = x + y;
    return sum;
}

由于函数声明只需要告诉编译器参数的类型,不需要定义一个形参,所以也可以直接写成这样:

int Add(int, int);

综上,函数必须要先声明再使用(函数的定义本身也是一种声明,不能重定义但是可以重声明)

多文件

在企业中,程序员实际编写代码时,不会把所有代码放在一个文件中,而是将代码按照功能拆分在多个文件中。这即方便了程序员之间的协作、增强了代码的泛用性,也能起到保密的作用(需要结合其他内容实现)

一般来说函数的声明与类型的声明会放在头文件(.h)中,而函数的实现放在源文件(.c)中

比如我们把上方Add的实例代码拆分:

add.h

//加法声明
int Add(int, int);

add.c

//加法定义
int Add(int x, int y)
{
    return x + y;
}

test.h

#include 
#include "add.h"

int main()
{
    int a = 0, b = 0;
    scanf("%d%d", &a, &b);
    printf("%d", Add(a, b));
    return 0;
}

这样我们依旧能正常运行编译出来的可执行程序

注意这样多文件协作需要在test这里包含我们自己写的头文件add.h

包含自己的头文件应该用双引号而不是尖括号!!!

注意,头文件可以包含头文件,如果我们在add.h前面加上包含stdio.h

那我们就不要在test.c中包含stdio.h了,直接包含add.h就同时包含了stdio.h

static与extern

static与extern都是C语言中的关键字

static

static是静态的意思,可以用来修饰局部变量、全局变量、函数

static修饰局部变量

static修饰局部变量改变了变量的生命周期,本质上改变了变量的存储类型。将本身存储在栈区的局部变量改为存储在静态区。这样,被修饰的变量就与全局变量的生命周期一样了,直到程序结束才会被销毁内存回收。但是static不会改变变量的作用域,这点与全局变量不同。

如下方代码的运行结果为1 2 3 4 5 ,而不是1 1 1 1 1 

#include 

void test()
{
    //static修饰局部变量
    static int i = 0;
    i++;
    printf("%d ", i);
}

int main()
{
    int i = 0;
    for(i=0; i<5; i++)
        test();
    return 0;
}

如果一个变量出了函数我们还想保留其值等下一次进入函数使用,就可以用static修饰

static修饰全局变量

全局变量本身可以在整个工程中被调用(需要extern声明),但是被static修饰后就只能在本文件中使用了,如果被其他文件声明只会报链接错误

static修饰函数

与static修饰全局变量相同,被static修饰的函数将不能在其他文件中调用。

extern

extern用来声明外部符号,如果在a文件定义了一个全局的符号,想在b文件使用,就要用extern声明,然后再使用,如:

a.c

int val = 114514;

test.c

#include 

extern int val;

int main()
{
    printf("%d\n", val);
    return 0;
}

你可能感兴趣的:(C语言笔记,笔记,c语言,开发语言)