函数的概念
函数就是一段 C 语言语句的集合,通过将 C
语言函数封装到函数里可以实现代码复用和简化编程。
函数的分类
从定义角度分类(即是库函数还是自定义函数):
- 库函数;
- 自定义函数;
- 系统调用。
从参数角度分类:
- 有参函数;
- 无参数函数;
从返回值角度分类:
- 有返回值的函数;
- 无返回值的函数;
注意:
- 有返回值的函数必有一个
return
语句;
- 无返回值的函数可以有不必有
return
语句,可以使用
return
语句提取返回。
函数的定义
1 2 3
| 返回值类型 函数名(形参列表) { 函数体 }
|
注意:
- 函数名要符合命名规则;
- 形成列表不是必须有;
- 返回值类型可以是所有的基本数据类型和自定义数据类型,或者
void
,也可以是指针;
- 函数不能同名。
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
|
#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
|
#ifndef FUN_H #define FUN_H
int sum(int, int);
#endif
|
1 2 3 4 5
|
#include "fun.h"
int sum(int a, int b) { return a + b; }
|
使用如下命令编译:
1
| gcc main.c fun.c -o main
|
或者:
1 2 3
|
int sum(int a, int b) { return a + b; }
|
1 2 3 4 5 6 7 8 9 10
|
#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; 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; 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
文件中有效。
扩展:
- 在同一作用范围内,不允许变量重名。
- 作用范围不同的可以重名。
- 局部范围内,重名的全局变量不起作用。(就近原则)