一、预处理
定义:在编译之前所做的处理,主要包括:
头文件(预处理会展开)、宏定义(预处理会替换掉)、条件编译
二、头文件
作用:将一些公用代码,如函数原型声明,类型声明,全局变量声明,宏定义等,放到一个文件中,以提供跨工程代码复用,减少代码重复书写。
处理方式:内容展开(若头文件中有递归包含逐级展开)
能否重复包含:不能,容易出现重复定义
可以使用条件编译,如下方式加以解决
#ifndef 宏
#define 宏
#endif
#include <>:编译器去系统默认指定目录查找
#include“”:编译器先在当前目录查找,找不到再去系统默认目录
三、宏定义
定义:用一个简单易记的字符集合代替复杂的内容
格式:#define 字符集合 内容
处理方式:简单的完全的字符替换
优点:增强代码可读性,提高可移植性
注意:以下两行是不等价的【见3.c】
#define IP int * //预处理前替换掉 IP a,b 指:int*a;int b不等价
typedef int * ip; //编译时 ip a,b 指:int*a; int* b;
带参宏:
1、整体的返回值要添加括号
2、使用参数时也要添加括号
四、关键字
【typedef】
作用:给类型起别名,可以提高代码的可移植性
1、typedef int ** ipp;
2、typedef int (*a2p)[5];
3、typedef int (*fp)(int, int);
【static】
1、static修饰局部变量
延长局部变量的生命周期至整个程序
2、static修饰全局变量
限定了全局变量的作用域为当前定义的源文件
3、static修饰函数
限定了函数的使用范围为当前定义的源文件
总结:static虽然限定了全局变量和函数的作用域,
但是其它源文件可以定义同名变量或函数。
【const】
1、const int *a 和 int const *a
a的指向可以改变,a指向的内容不可改变
2、int *const a
a的指向不可改变,a指向的内容可以改变
3、const int *const a
a的指向不可改变,a指向的内容也不可改变
1、const int *a; int b; a = &b; *a = 100; 错
2、int *const a; int b; a = &b; *a = 100; 错
3、int b; int *const a = &b; *a = 100; 对
如果有const int *a;int *b;那么语句a=b;是否可行?
反过来b=a;又是否可行?为什么呢?
结论:const指针不可以通过地址改变数据,而非const指 针可以通过地址修改数据,所以在指针变量赋值时只能 是非const指针赋值给const指针,反过来则有风险。
总结:const修饰谁,谁就不可改变
五、条件编译
定义:给某段代码指定编译条件
处理方式:条件成立参与编译,条件不成立相当于删除
格式一:注释成块代码
#if 表达式
#endif
格式二:测试代码
#if 表达式
#else
#endif
格式三:
#ifdef 宏
#endif
格式四:
#ifndef 宏
#endif
六、复杂的指针声明
1、二级指针(多级指针)
int a = 250;
int *p = &a;
int **q = &p;
p <===> &a
*p <==> *(&a) <==> a
q <===> &p
*q <==> *(&p) <==> p <==> &a
**q <==> *p <==> a
总结:&使数据埋藏的更深,*使数据重见天日
2、指向二维数组的指针
int a[5];
int *p = a<==> &a[0]
int a[2][5];
int [5] *p => int (*p)[5] = p=a=&a[0]
a+1 <==> p+1 <==> &a[1]
*(p + i) <==> &a[i][0]
&a[i][j] <==> *(p+i)+j
a[i][j] <==> *(*(p+i)+j)
3、函数指针
void test(int a, char *b);
void (*p)(int, char *) = test;
p(250, NULL);
总结:作为函数参数的时候使用
定义:在编译之前所做的处理,主要包括:
头文件(预处理会展开)、宏定义(预处理会替换掉)、条件编译
二、头文件
作用:将一些公用代码,如函数原型声明,类型声明,全局变量声明,宏定义等,放到一个文件中,以提供跨工程代码复用,减少代码重复书写。
处理方式:内容展开(若头文件中有递归包含逐级展开)
能否重复包含:不能,容易出现重复定义
可以使用条件编译,如下方式加以解决
#ifndef 宏
#define 宏
#endif
#include <>:编译器去系统默认指定目录查找
#include“”:编译器先在当前目录查找,找不到再去系统默认目录
三、宏定义
定义:用一个简单易记的字符集合代替复杂的内容
格式:#define 字符集合 内容
处理方式:简单的完全的字符替换
优点:增强代码可读性,提高可移植性
注意:以下两行是不等价的【见3.c】
#define IP int * //预处理前替换掉 IP a,b 指:int*a;int b不等价
typedef int * ip; //编译时 ip a,b 指:int*a; int* b;
带参宏:
1、整体的返回值要添加括号
2、使用参数时也要添加括号
四、关键字
【typedef】
作用:给类型起别名,可以提高代码的可移植性
1、typedef int ** ipp;
2、typedef int (*a2p)[5];
3、typedef int (*fp)(int, int);
【static】
1、static修饰局部变量
延长局部变量的生命周期至整个程序
2、static修饰全局变量
限定了全局变量的作用域为当前定义的源文件
3、static修饰函数
限定了函数的使用范围为当前定义的源文件
总结:static虽然限定了全局变量和函数的作用域,
但是其它源文件可以定义同名变量或函数。
【const】
1、const int *a 和 int const *a
a的指向可以改变,a指向的内容不可改变
2、int *const a
a的指向不可改变,a指向的内容可以改变
3、const int *const a
a的指向不可改变,a指向的内容也不可改变
1、const int *a; int b; a = &b; *a = 100; 错
2、int *const a; int b; a = &b; *a = 100; 错
3、int b; int *const a = &b; *a = 100; 对
如果有const int *a;int *b;那么语句a=b;是否可行?
反过来b=a;又是否可行?为什么呢?
结论:const指针不可以通过地址改变数据,而非const指 针可以通过地址修改数据,所以在指针变量赋值时只能 是非const指针赋值给const指针,反过来则有风险。
总结:const修饰谁,谁就不可改变
五、条件编译
定义:给某段代码指定编译条件
处理方式:条件成立参与编译,条件不成立相当于删除
格式一:注释成块代码
#if 表达式
#endif
格式二:测试代码
#if 表达式
#else
#endif
格式三:
#ifdef 宏
#endif
格式四:
#ifndef 宏
#endif
六、复杂的指针声明
1、二级指针(多级指针)
int a = 250;
int *p = &a;
int **q = &p;
p <===> &a
*p <==> *(&a) <==> a
q <===> &p
*q <==> *(&p) <==> p <==> &a
**q <==> *p <==> a
总结:&使数据埋藏的更深,*使数据重见天日
2、指向二维数组的指针
int a[5];
int *p = a<==> &a[0]
int a[2][5];
int [5] *p => int (*p)[5] = p=a=&a[0]
a+1 <==> p+1 <==> &a[1]
*(p + i) <==> &a[i][0]
&a[i][j] <==> *(p+i)+j
a[i][j] <==> *(*(p+i)+j)
3、函数指针
void test(int a, char *b);
void (*p)(int, char *) = test;
p(250, NULL);
总结:作为函数参数的时候使用