函数

函数的概念

函数就是一段 C 语言语句的集合,通过将 C 语言函数封装到函数里可以实现代码复用和简化编程。

函数的分类

从定义角度分类(即是库函数还是自定义函数)

  1. 库函数;
  2. 自定义函数;
  3. 系统调用。

从参数角度分类

  1. 有参函数;
  2. 无参数函数;

从返回值角度分类

  1. 有返回值的函数;
  2. 无返回值的函数;

注意:

  1. 有返回值的函数必有一个 return 语句;
  2. 无返回值的函数可以有不必有 return 语句,可以使用 return 语句提取返回。

函数的定义

1
2
3
返回值类型 函数名(形参列表) {
函数体
}

注意:

  1. 函数名要符合命名规则;
  2. 形成列表不是必须有;
  3. 返回值类型可以是所有的基本数据类型和自定义数据类型,或者 void,也可以是指针;
  4. 函数不能同名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>

void myfun1() {

printf("hello world\n");
printf("nihao beijing\n");
printf("welcome to 1000phone\n");

return;
}

void myfun2(int a, int b) {

int sum;
sum = a + b;

printf("%d + %d = %d\n", a, b, sum);
}

int myfun3(int a, int b) {

int sum;
sum = a + b;

return sum;
}

函数声明

声明就是将函数头写在程序里。

函数头就是函数去掉函数体和花括号加上 ; 之后的东西,例如:int sum(int, int),函数头的形参列表可以只声明参数类型而不给出参数名。

编译器默认是自上而下进行编译的,如果主调函数在被调函数之上,这时候需要在主调函数声明前面声明被调函数,否则编译器找不到被调函数。

1
2
3
4
5
6
7
8
#include <stdio.h>

void fun() { printf("hello world\n"); }

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

上面情况就无需声明函数,而下面这种情况就必须要声明函数了:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

void fun();

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

void fun() { printf("hello world\n"); }

有时候一个函数的定义在另外一个 .c 文件里,这时候我们可以将函数声明在一个 .h 文件里,这样我们只需使用 #include 宏去引入这个 .h 文件就可以了,编译的时候将这两个 .c 文件一起编译,例如

1
2
3
4
5
6
7
8
9
// main.c

#include "fun.h"
#include <stdio.h>

int main(int argc, char *argv[]) {
printf("%d", sum(1, 2));
return 0;
}
1
2
3
4
5
6
7
8
// fun.h

#ifndef FUN_H
#define FUN_H

int sum(int, int);

#endif
1
2
3
4
5
// fun.c

#include "fun.h"

int sum(int a, int b) { return a + b; }

使用如下命令编译:

1
gcc main.c fun.c -o main

或者:

1
2
3
// fun.c

int sum(int a, int b) { return a + b; }
1
2
3
4
5
6
7
8
9
10
// main.c

#include <stdio.h>

extern int sum(int a, int b);

int main(int argc, char *argv[]) {
printf("%d", sum(1, 2));
return 0;
}

使用如下命令编译:

1
gcc main.c fun.c -o main

函数的调用

语法:

1
2
变量 = 函数名(实参列表); // 带返回值
函数名(实参列表);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>

void myfun1();
void myfun2(int a, int b);
int myfun3(int a, int b);

int main(int argc, char *argv[]) {

myfun1();
printf("**********************\n");
myfun2(100, 90);
int x = 10, y = 20;
myfun2(x, y);
printf("**********************\n");
int n;
n = myfun3(100, 90);
printf("n = %d\n", n);
printf("sum = %d\n", myfun3(90, 66));

return 0;
}

void myfun1() {

printf("hello world\n");
printf("nihao beijing\n");
printf("welcome to 1000phone\n");

return;
}

void myfun2(int a, int b) {
int sum;
sum = a + b;
printf("%d + %d = %d\n", a, b, sum);
}

int myfun3(int a, int b) {
int sum;
sum = a + b;
return sum;
}

输出结果:

image

变量的存储类别

内存的分区

内存分为物理内存和虚拟内存。

  • 物理内存:实实在在的存储设备;
  • 虚拟内存:操作系统虚拟出来的内存。

操作系统会将虚拟内存和物理内存进行映射。

在 32 位操作系统下,每个进程的寻址范围位 0x00000000~0xffffffff,即 4G,我们在编程时看到的内存地址都是虚拟地址。

在程序运行时,操作系统会将虚拟内存进行分区:

  • 堆:在动态申请内存时,会在堆区开辟空间;
  • 栈:主要存放局部变量;
  • 静态全局区:
    • 位初始化的静态全局区:没有初始化的静态变量(static 修饰的变量),或全局变量存放在此区;
    • 初始化了的静态全局区:初始化过的全局变量、静态变量存在此区。
  • 代码区:存放代码的区;
  • 文字常量区:存放常量的区;

普通的全局变量

在函数外部定义的变量就是一个全局变量。

1
2
int num = 100; // num就是一个全局变量
int main() { return 0; }

作用范围:全局变量作用范围时整个程序,在其他文件中要使用这个变量,需要使用 extern int num; 声明,声明时不要赋值。

生命周期:从程序开始运行到程序结束。

定义普通全局变量时,如果没有赋初始值,其有默认值 0.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

int num;

void myfun() { num = 888; }

int main(int argc, char *argv[]) {

printf("num = %d\n", num);
myfun();
printf("num = %d\n", num);

return 0;
}

输出结果:

image

静态全局变量

static 修饰的全局变量

1
2
static int num = 100; // num就是一个静态全局变量
int main() { return 0; }

作用范围:只在定义其的 .c 文件中有效。

生命周期:从程序开始运行,到程序结束。

定义静态全局变量时如果没有赋初始值,其有默认值 0.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

static int num;

void myfun() { num++; }

int main(int argc, char *argv[]) {

printf("num = %d\n", num);
myfun();
printf("num = %d\n", num);

return 0;
}

输出结果:

image

普通的局部变量

在函数内部定义的、或者在复合语句中定义的变量。

1
2
3
4
5
6
int main() {
int num; // 局部变量
{
int a; // 局部变量
}
}

作用域:从定义其的语句那里开始,到包含它的花括号结束。

生命周期:调用函数时才为局部变量开辟空间,函数结束时释放空间。复合语句块亦如此。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>

void myfun() {

int num = 100;
num++;
printf("num = %d\n", num);

return;
}

int main(int argc, char *argv[]) {

myfun();
myfun();
myfun();

return 0;

}

输出结果如下:

image

静态的局部变量

static 修饰的局部变量。

作用范围:在定义它的函数或复合语句块中有效。

生命周期:第一次调用函数的时候为其开辟空间,函数结束时不释放空间,下次在调用函数时不再为其开辟空间,直接使用之前开辟的那个空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <stdio.h>

void myfun() {

int a;
static int num;
printf("a = %d\n", a);
printf("num = %d\n", num);
}

void myfun1() {

static int num1 = 100;
num1++;
printf("num1 = %d\n", num1);
}

int main(int argc, char *argv[]) {

myfun();
myfun1();
myfun1();
myfun1();

return 0;
}

输出·结果:

image

注意:定义普通局部变量时,如果不赋初始值其值时随着的(它那块内存里的脏数据);定义静态局部变量时,如果不赋初始值,其值为 0.

外部函数

普通函数,都是外部函数。即函数可以在程序的任何一个文件中调用。

在分文件编程中,只需要将函数的实现过程写在指定的 .c 文件中,然后将其声明写在指定的 .h 文件中,其他文件只要包含了头文件,就可以使用外部函数。

内部函数

static 修饰的函数,又称静态函数。

内部函数只再定义其的 .c 文件中有效。

扩展:

  1. 在同一作用范围内,不允许变量重名。
  2. 作用范围不同的可以重名。
  3. 局部范围内,重名的全局变量不起作用。(就近原则)