文件包含指令
#include
1.将所包含文件的内容粘贴到该指令处
2.尖括号包含:#include
先到 -l 目录找,再找系统目录( /usr/inlcude )
适用于系统文件
3.双引号包含:#include "xxx.h"
先到 -l 目录,再找当前目录,最后找系统目录
适用于自己编写的头文件
命令预处理: gcc -E -o xxx.i xxx,c
使用后,会将头文件中的内容全部放入预处理文件中
宏定义指令
#define分类:
无参宏定义(常量宏)
#define 宏名 (宏值)
例如:#define PI 3.14 将PI始终赋值3.14
当 printf("%lg\n",3.14*r*r); 可以替换为printf("lg\n",PI*r*r);
当需求更精密的圆周率时只需修改宏即可
1.宏替换只是简单的文本替换,编辑器对所替换的内容不做任何检查,更不会计算表达式或者调用函数
2.如有错误,只能在编译时发现
3.行末不必加分号,若加上分号连分号一起做宏替换
4.宏定义必须写在所有函数之外,其作用域从宏定义指令开始一直到源程序结束
5.宏名在源程序中若用引号括起来,则预处理器不对其做宏替换
6.在宏定义的字符串中可以使用已定义过的宏名
7.宏名习惯用大写字母表示,以区别于变量和函数,但也允许小写字母
8.定义常量,便于修改
9.定义类型,简化书写
用typedef定义类型别名效果更好
10.定义模式,语言扩展
不要滥用
有参宏定义
#define 分类 即宏函数
有参宏定义 (宏参数)
#define 宏名(参数表) (宏参)
例如: #define SQUARE(X) ((X)*(X)) 只做替换,不做运算
#define SQUARE_1(X) (X*X) //定义宏函数求一个数的平方
printf("%d\n", SQUARE_1(3+7));//3+7*3+7
这里就和原本目的相反了!!
1.宏名和形参表必须在同一行且中间不能有空格
2.宏形参不分配内存空间,因此无需说明其类型
3.调用带参宏只是符号替换,不存在参数传递问题
4.宏调用中的实参可以是表达式,但对实参表达式并不计算,直接用它替换宏定义中的参数
5.宏定义字符串内的形参,通常用括号括起来,避免出现错误
6.宏定义必须书写在一行中,如有必要可加续航符"\"
7.调用有参宏的实参表达式中不要用 ++/-- 运算符
8.宏定义字符串中的“#形参”表示将形参扩展为用双引号括起来的实参表达式,#N ->"N"
9.宏定义字符串中的"##形参"表示将形参扩展为实参表达式并与前面的字符粘连在一起
ID##1 -> ID1
10.用有参宏取代函数可以提高程序的执行效率,但会占用更多的磁盘和内存空间,
因此适用于实现那些频繁使用的简单功能
示例如下:
//宏函数演示
//gcc -E -o define.i define.c; vim define.i; gcc -o define define.i; ./define
#include <stdio.h>
#define SQUARE_1(X) (X*X) //定义宏函数求一个数的平方
#define SQUARE_2(X) ((X)*(X)) //定义宏函数求一个数的平方
#define SUB(X, Y) ((X)-(Y)) //定义宏函数求相减
#define MAX(x, y) ((x)>(y)?(x):(y)) //求最大数
#define IS_EVEN(n) ((n)%2 == 0)//判断是否是偶数,如果是返回1,否则返回0
int main(void) {
printf("%d\n", SQUARE_1(10));//10*10
printf("%d\n", SQUARE_1(3+7));//3+7*3+7
printf("%d\n", SQUARE_2(10));//(10)*(10)
printf("%d\n", SQUARE_2(3+7));//(3+7)*(3+7)
printf("%d\n", SUB(10, 5)); //(10)-(5)
int a = 10, b = 20;
printf("%d\n", MAX(a, b)); //(a)>(b)?(a):(b)
printf("%lg\n", MAX(3.2, 3.1)); //(3.2)>(3.1)?(3.2):(3.1)
printf("%d\n", IS_EVEN(a));//(a)%2 == 0
printf("%d\n", IS_EVEN(a+1));//(a+1)%2 == 0
printf("%d\n", IS_EVEN(a+2));//(a+2)%2 == 0
return 0;
}
取消宏、gcc -D设置宏和预定义宏
#undef
取消一个已定义的宏,令其宏名处于未定义状态
#undef PI
预定义宏有:
__FILE__ : 所在文件名%s
__LINE__: 所在行号 %d
__FUNCTION__/__func__ : 所在函数名 %s
__DATE__ : 编辑日期%s
__TIME__ : 编译时间 %s
gcc的 -D选项的妙用,功能:
可以通过-D选项给程序传递常量宏
示例:
//gcc的-D选项的妙用,功能:可以通过-D选项给程序传递常量宏
//gcc -DSIZE=5 -DEND=\"tarena\" -o define2 define2.c
#include <stdio.h>
int main(void) {
int a[SIZE] = {0};
//初始化数组
for(int i = 0; i < SIZE; i++)
a[i] = i + 1;
//打印数组
for(int i = 0; i < SIZE; i++)
printf("%d ", a[i]);
printf("\n");
printf("%s\n", END);
//调试宏
int *p = NULL;
if(!p) {
printf("空指针:%s-%s,%s,%s,%d\n",
__DATE__, __TIME__,
__FILE__, __FUNCTION__,
__LINE__);//前后各两个下划线
return -1;
}
return 0;
}
条件编译指令
#if 常量表达式 :如果常量表达式的值非零
#ifdef 宏名 :如果宏名已定义
#ifndef 宏名 :如果宏名未定义
#elif 常量表达式 :否则如果常量表达式的值非零
#else :否则
#endif :结束判断
常量表达式 :字面值/ defined(宏名) /&&/||
根据不同条件,编译不同部分,会产生不同目标代码
记住!!:当不满足编译条件时,会被 gcc -E 编译时忽略掉代码!!
#if 0
代码
#endif 中间的代码全被注释
示例如下:
//条件编译
#include <stdio.h>
int main(void) {
//#if演示
//gcc -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
//gcc -DA=1 -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
#if A==1
printf("1.\n");
#endif//必须和#if配对
//#if...#else
//gcc -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
//gcc -DB=1 -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
#if B==1
printf("2.\n");
#else
printf("3.\n");
#endif
//#ifdef或者#ifndef...#else演示
//gcc -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
//gcc -DC -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
//#ifdef C
#ifndef C
printf("4.\n");
#else //可以不加
printf("5.\n");
#endif
//#if defined ...#else演示
//gcc -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
//gcc -DD -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
//gcc -DE -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
//gcc -DD -DE -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
//gcc -DE -DD -E -o if.i if.c; vim if.i; gcc -o if if.i; ./if
#if defined(D)
printf("6.\n");
#elif !defined(D) && !defined(E)
printf("7.\n");
#else
printf("8.\n");
#endif
return 0;
}