导购网站开发 源码,wordpress 获取总页数,哈尔滨网站建设推广公司,做网站如何备案目录 预定义符号介绍
编辑
预处理指令 #define
#define 定义标识符
#define 定义宏
#define 替换规则
#define中#和##的使用
带副作用的宏参数
宏和函数的对比
命令行定义
预处理指令 #undef
预处理指令 #include
头文件被包含的方式#xff1a;
本地文件包含 …目录 预定义符号介绍
编辑
预处理指令 #define
#define 定义标识符
#define 定义宏
#define 替换规则
#define中#和##的使用
带副作用的宏参数
宏和函数的对比
命令行定义
预处理指令 #undef
预处理指令 #include
头文件被包含的方式
本地文件包含
库文件包含
预处理指令 条件编译
1.常量条件编译 2.多个分支的条件编译 3.判断符号是否被定义 4.嵌套使用条件编译 预定义符号介绍
__FILE__ //进行编译的源文件
__LINE__ //文件当前的行号
__DATE__ //文件被编译的日期
__TIME__ //文件被编译的时间
__STDC__ //如果编译器遵循ANSI C其值为1否则未定义
__FUNCTION__//当前代码所在函数 这些符号都是内置可以通过这些符号记录一些代码信息或者说是日志信息可以根据这些信息分析代码哪里出现问题
预处理指令 #define
#define 定义标识符 原型 define name stuff举例 define MAX 100 如果在写代码的过程中使用了该符号它会在预编译的阶段把该符号替换成相对应的stuff部分 简单来说就是在代码中使用MAX他会在预编译阶段把它替换成100 define不止可以定义常量还可以定义类性相当于重新起个名字甚至可以定义语句 #define MAX 1000
#define reg register //为 register这个关键字创建一个简短的名字
#define do_forever for(;;) //用更形象的符号来替换一种实现
#define CASE break;case //在写case语句的时候自动把 break写上。
// 如果定义的 stuff过长可以分成几行写除了最后一行外每行的后面都加一个反斜杠(续行符)。
#define DEBUG_PRINT printf(file:%s\tline:%d\t \date:%s\ttime:%s\n ,\__FILE__,__LINE__ , \__DATE__,__TIME__ ) 其实就是把标识符后面的内容在预编译阶段全部替换上去可以减少一定代码冗余以及方便修改 可以加 ‘;’ 但是会把 ‘;’ 一起替换上去可能会影响原代码例如 #define M 100;
int main()
{
int a M;
//预编译之后就会变成
int a 100;;
}define定义不是语句不用加 ‘;’它的单位是行如果嫌写在一行太长就可以用斜杠续行方便自己与他人阅读 #define 定义宏 原型 #define name( parament-list ) stuff 举例 #define SQUARE( x ) x * x name后面不能跟空格要不然它会被认为是定义标识符从而把后面的内容理解为stuff与定义标识符不同宏是有参数的它可以分为两个部分替换一个是参数部分替换一个是除参数部分替换 这里需要注意的是宏与函数不同宏本质是替换如果我们在参数部分传进去一个表达式这个表达式不会算出一个结果而是直接放进去可能会因为操作符的优先级不同而导致结果不是预期所想例如 #define SQUARE( X ) X * X
int main()
{
int a SQUARE(31);//预期结果16
return 0;
}
//预编译之后
#define SQUARE( X ) X * X
int main()
{
int a 31*31;//7
return 0;
} 要避免这种问题本质上是要在替换时避免歧义要想避免歧义就应该把宏定义的内容分离出来就是加上括号而宏内部又有参数的替换所以又要避免参数替换与除参数部分替换这里就称宏的框架吧发生歧义所以参数部分又要从宏的框架中分离出来。例如 #define SQUARE( x ) x*x #define 替换规则 在程序中扩展 #define 定义符号和宏时需要涉及几个步骤。 1. 在调用宏时首先对参数进行检查看看是否包含任何由 #define 定义的符号。如果是它们首先 2. 替换文本随后被插入到程序中原来文本的位置。对于宏参数名被他们的值替换。 3. 最后再次对结果文件进行扫描看看它是否包含任何由 #define 被替换。 定义的符号。如果是就重复上 述处理过程。 #define中#和##的使用 # 当我们在定义宏时想在字符串中使用参数就可以使用#它可以把参数转成字符串此操作可以做到一些字符串做不到的事情例如我想实现一个根据不同参数输出不同字符串的功能 使用用函数 #includestdio.h
void My_Print(char name)
{printf(我是%c\n, name);
}
int main()
{char a a;My_Print(a);char b b;My_Print(b);char c c;My_Print(c);char d d;My_Print(d);} 使用宏 #includestdio.h
#define PRINT(X) printf(我是#X\n);
int main()
{PRINT(a)//相当于将a替换成a,就变成printf(我是a\n);PRINT(b)PRINT(c)PRINT(d)} 我们发现使用宏就不用大费周章去定义变量和函数直接传文本进去就能使用 ## 可以把两个传进来的文本合成为一个文本 例如 #includestdio.h
#define UNION(X,Y) X##Y;
int main()
{int a UNION(12, 3)printf(%d, a);//输出123
} 带副作用的宏参数 当宏参数在宏的定义中出现超过一次的时候如果参数带有副作用那么你在使用这个宏的时候就可能出现危险导致不可预测的后果。副作用就是表达式求值的时候出现的永久性效果。 例如 x1;//不带副作用
x;//带有副作用 MAX 宏可以证明具有副作用的参数所引起的问题。 #define MAX(a, b) ( (a) (b) ? (a) : (b) )
...
x 5;
y 8;
z MAX(x, y);
printf(x%d y%d z%d\n, x, y, z);//输出的结果是什么 这里我们得知道预处理器处理之后的结果是什么 z ( (x) (y) ? (x) : (y)); 所以输出的结果是 x6 y10 z9 宏和函数的对比 宏通常被应用于执行简单的运算。比如在两个数中找出较大的一个。 #includestdio.h
#define MAX(a, b) ((a)(b)?(a):(b))
int Max(int a, int b)
{return a b ? a : b;
}
int main()
{int a 10;int b 20;int retMax(a,b);printf(%d %d, ret, MAX(a, b));return 0;} 那为什么不用函数来完成这个任务 原因有二 1. 用于调用函数和从函数返回的代码可能比实际执行这个小型计算工作所需要的时间更多。 所以宏比 函数在程序的规模和速度方面更胜一筹 。我们进入反汇编看一下比较一下就清楚了 2. 更为重要的是函数的参数必须声明为特定的类型。所以函数只能在类型合适的表达式上使用。反之 这个宏怎可以适用于整形、长整型、浮点型等可以用于 来比较的类型。 宏是类型无关的 。 当然和宏相比函数也有劣势的地方 1. 每次使用宏的时候一份宏定义的代码将插入到程序中。除非宏比较短否则可能大幅度增加程序的长度。 2. 宏是没法调试的因为宏是在预编译阶段就完成替换而调试是在程序运行时调试也就是已经变成可执行文件所以在调试阶段就看不见宏做的那些工作。 3. 宏由于类型无关也就不够严谨。 4. 宏可能会带来运算符优先级的问题导致程容易出现错。 宏有时候可以做函数做不到的事情。比如宏的参数可以出现类型 但是函数做不到。 总结当需要实现一个简单的功能使用宏好复杂就用函数好
命令行定义
在命令行时定义就是预处理指令不在代码内部而是用于启动编译过程添加也就是预编译之前简单来说就是不会出现在源文件。 例如当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候这个特性有点用处。假 定某个程序中声明了一个某个长度的数组如果机器内存有限我们需要一个很小的数组但是另外一 个机器内存大写我们需要一个数组能够大写。 #include stdio.h
int main()
{int array [ARRAY_SIZE];int i 0;for(i 0; i ARRAY_SIZE; i ){array[i] i;}for(i 0; i ARRAY_SIZE; i ){printf(%d ,array[i]);}printf(\n );return 0;
} 编译指令 gcc -D ARRAY_SIZE10 programe.c 预处理指令 #undef 用于取消define定义 #undef NAME
//如果现存的一个名字需要被重新定义那么它的旧名字首先要被移除。 预处理指令 #include 我们已经知道 #include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的地方 一样。 这种替换的方式很简单 预处理器先删除这条指令并用包含文件的内容替换。 这样一个源文件被包含 10 次那就实际被编译 10 次。 头文件被包含的方式
本地文件包含 #include filename.h 本地文件包含查找策略先在源文件所在目录下查找如果该头文件未找到编译器就像查找库函数头文件一样在标 准位置查找头文件。 如果找不到就提示编译错误。 库文件包含 查找头文件直接去标准路径下去查找如果找不到就提示编译错误。 这样是不是可以说对于库文件也可以使用 “” 的形式包含 答案是肯定的 可以 。 但是这样做查找的效率就低些当然这样也不容易区分是库文件还是本地文件了。 #include filename.h 避免嵌套文件包含 comm.h 和 comm.c 是公共模块。 test1.h 和 test1.c 使用了公共模块。 test2.h 和 test2.c 使用了公共模块。 test.h 和 test.c 使用了 test1 模块和 test2 模块。 这样最终程序中就会出现两份 comm.h 的内容。这样就造成了文件内容的重复 如何解决这个问题呢加上一行代码就行就可以避免头文件的重复引入。 #pragma once 例如 #includestdio.h
#define MAX(a, b) ((a)(b)?(a):(b))
#undef MAX
int main()
{int a 10;int b 20;printf(%d , MAX(a, b));return 0;} 预处理指令 条件编译 条件满足就编译不满足被条件编译框住的部分就不让编译用法与if语句类似但是if存在于可执行文件中也就是说程序无论走不走if都会在可执行文件中而条件编译不同条件满足就编译不满足在预编译阶段就删除了它不会存在于预编译之后文件。 1.常量条件编译 //格式
#if 常量表达式
//...
#endif//例如------------------------------------
int main()
{
#define __DEBUG__ 0
#define __RELEASE__ 1
#if __DEBUG____RELEASE__//常量表达式由预处理器求值。printf(hello world);
#endif
} 2.多个分支的条件编译
//格式
#if 常量表达式
//...
#elif 常量表达式
//...
#else
//...
#endif//例如-----------------------------------------
#define VERSION 2#if VERSION 1#define MESSAGE Version 1.0
#elif VERSION 2#define MESSAGE Version 2.0
#else#define MESSAGE Unknown version
#endif#include stdio.hint main() {printf(%s\n, MESSAGE);return 0;
} 3.判断符号是否被定义
//格式--------------------------------------------------
//两种不同的写法和两种不同的操作
//如果符号存在
#if defined(symbol)
//...
#endif#ifdef symbol
//...
#endif//如果符号不存在
#if !defined(symbol)
//...
#endif#ifndef symbol
//...
#endif//例如-----------------------------------------------------
//如果符号存在
#includestdio.h
#define DEBUG_MODE
int main() {
#if defined(DEBUG_MODE)printf(This is a debug message.\n);
#endif#ifdef DEBUG_MODEprintf(This is a debug message.);
#endifreturn 0;
}//如果符号不存在
int main() {
#if !defined(DEBUG_MODE)printf(N0 debug message.\n);
#endif#ifndef DEBUG_MODEprintf(No debug message.);
#endifreturn 0;
} 4.嵌套使用条件编译
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1();#endif#ifdef OPTION2unix_version_option2();#endif
#elif defined(OS_MSDOS)#ifdef OPTION2msdos_version_option2();#endif
#endif