精品课程建设网站,能注册账号的网站模板,网站分辨率自适应代码,关于网站建设项目实训报告目录 一、#和##
1.1#运算符
1.2## 运算符
二、命名约定
三、#undef
四、命令行定义
五、条件编译
六、头文件的包含
4.1 头文件被包含的方式#xff1a;
4.1.1 本地文件包含
Linux环境的标准头文件的路径#xff1a;
VS环境的标准头文件的路径
4.1.1 本地文件包含
Linux环境的标准头文件的路径
VS环境的标准头文件的路径
4.1.2库文件包含
4.2嵌套文件包含 一、#和##
1.1#运算符
#运算符 #运算符将宏的一个参数转换为字符串字面量。它仅允许出现在带参数的宏的替换列表中。 #运算符所执行的操作可以理解为”字符串化“。 当我们有一个变量 int a 10; 的时候我们想打印出 the value of a is 10 . 就可以写 #define PRINT(n) printf(the value of #n is %d, n); 当我们按照下面的方式调用的时候 PRINT(a);//当我们把a替换到宏的体内时就出现了#a而#a就是转换为a时一个字符串 代码就会被预处理为 printf(the value of a is %d, a); 运行代码就能在屏幕上打印 the value of a is 10 1.2## 运算符
## 可以把位于它两边的符号合成一个符号它允许宏定义从分离的文本片段创建标识符。 ## 被称 为记号粘合 这样的连接必须产生一个合法的标识符。否则其结果就是未定义的。 这里我们想想写一个函数求2个数的较大值的时候不同的数据类型就得写不同的函数。 比如
int int_max(int x, int y)
{return xy?x:y;
}
float float_max(float x, float y)
{return xyx:y;
}
但是这样写起来太繁琐了现在我们这样写代码试试
//宏定义
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{ \return (xy?x:y);\
}\
使用宏定义不同函数
GENERIC_MAX(int) //替换到宏体内后int##_max 生成了新的符号 int_max做函数名
GENERIC_MAX(float) //替换到宏体内后float##_max 生成了新的符号 float_max做函数名
int main()
{//调用函数int m int_max(2, 3);printf(%d\n, m);float fm float_max(3.5f, 4.5f);printf(%f\n, fm);return 0;
}在实际开发过程中##使用的很少很难取出非常贴切的例子。
//宏定义
#define GENERIC_MAX(type) \
type type##_max(type x, type y)\
{\return (xy?x:y);\
}\GENERIC_MAX(int) //替换到宏体内后int##_max 生成了新的符号 int_max做函数名
GENERIC_MAX(float)
int main()
{//调用函数int m int_max(2, 3);printf(%d\n, m);float fm float_max(3.5f, 4.5f);printf(%f\n, fm);return 0;
}二、命名约定
一般来讲函数的宏的使用语法很相似。所以语言本身没法帮我们区分二者。 那我们平时的一个习惯是 把宏名全部大写 函数名不要全部大写 但是也有例外: offsetof
三、#undef
这条指令用于移除一个宏定义。 #undef NAME //如果现存的一个名字需要被重新定义那么它的旧名字首先要被移除。 #define MAX 100int main()
{printf(%d\n, MAX);
#undef MAX //移除宏定义//printf(%d\n, MAX);#define MAX 1000printf(%d\n, MAX);return 0;
}四、命令行定义
许多C 的编译器提供了一种能力允许在命令行中定义符号。用于启动编译过程。 例如当我们根据同一个源文件要编译出一个程序的不同版本的时候这个特性有点用处。假定某个程序中声明了一个某个长度的数组如果机器内存有限我们需要一个很小的数组但是另外一个机器内存大些我们需要一个数组能够大些。
#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;
}编译指令 //linux 环境演示 gcc -D ARRAY_SIZE10 programe.c 在不改变代码的情况下,编译出不同的版本 五、条件编译
在编译一个程序的时候我们如果要将一条语句一组语句编译或者放弃是很方便的。因为我们有条件编译指令。在预处理阶段如果满足条件执行后续语句如果不满足就不执行 比如说调试性的代码删除可惜保留又碍事所以我们可以选择性的编译。
#include stdio.h
#define __DEBUG__int main(){int i 0;int arr[10] { 0 };for (i 0; i 10; i){arr[i] i;#ifdef __DEBUG__ //如果__DEBUG__为真就参与编译printf(%d\n, arr[i]);//为了观察数组是否赋值成功。 #endif //__DEBUG__}return 0;} int main(){#if 12printf(hehe\n);//不参与编译#endifreturn 0;} #define M 3
int main()
{int a 3;#if Ma//err a是局部变量,预编译时不参与printf(hehe\n);#endifreturn 0;
} #define M 2int main(){#if M1printf(hehe\n);#elif M2printf(haha\n);#elif M 3printf(heihei\n);#elseprintf(~~~~\n);#endifreturn 0;}常见的条件编译指令
1.#if 常量表达式//...
#endif
//常量表达式由预处理器求值。
如
#define __DEBUG__ 1
#if __DEBUG__//..
#endif
//结束条件编译2.多个分支的条件编译
#if 常量表达式//...
#elif 常量表达式//...
#else//...
#endif3.判断是否被定义
#if defined(symbol)
#ifdef symbol
#if !defined(symbol)
#ifndef symbol4.嵌套指令
#if defined(OS_UNIX)#ifdef OPTION1unix_version_option1(); 六、头文件的包含
4.1 头文件被包含的方式
4.1.1 本地文件包含 #include filename 查找策略先在源文件所在目录下查找如果该头文件未找到编译器就像查找库函数头文件一样在标准位置查找头文件。 如果找不到就提示编译错误。
Linux环境的标准头文件的路径 /usr/include VS环境的标准头文件的路径 C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include //这是VS2013的默认路径 注意按照自己的安装路径去找。
4.1.2库文件包含 #include filename.h 查找头文件直接去标准路径下去查找如果找不到就提示编译错误。 这样是不是可以说对于库文件也可以使用 “” 的形式包含 答案是肯定的可以但是这样做查找的效率就低些当然这样也不容易区分是库文件还是本地文件了。
4.2嵌套文件包含
我们已经知道 #include 指令可以使另外一个文件被编译。就像它实际出现于 #include 指令的 地方一样。 这种替换的方式很简单预处理器先删除这条指令并用包含文件的内容替换。 一个头文件被包含10次那就实际被编译10次如果重复包含对编译的压力就比较大。 如果直接这样写test.c文件中将test.h包含5次那么test.h文件的内容将会被拷贝5份在test.c中。 如果test.h 文件比较大这样预处理后代码量会剧增。如果工程比较大有公共使用的头文件被大家都能使用又不做任何的处理那么后果真的不堪设想。 如何解决头文件被重复引入的问题答案条件编译。 每个头文件的开头写 #ifndef __TEST_H__ #define __TEST_H__ //头文件的内容 #endif //__TEST_H__ 或者 #pragma once 就可以避免头文件的重复引入。
笔试题 1. 头文件中的 ifndef/define/endif是干什么用的?
答用于防止头文件的内容在同一个编译单元中被多次包含。 2. #include filename.h 和 #include filename.h有什么区别?
答#include filename.h这是用于包含系统提供的头文件的常用格式。编译器通常会在其预定义的系统头文件目录中查找这样的文件。
#include filename.h这是用于包含用户定义的头文件或项目特定的头文件的常用格式。编译器首先在当前文件或指定的用户目录中查找这样的文件如果找不到它可能会回退到系统目录。 其他预处理指令 #error #pragma #line ... 不做介绍自己去了解。 #pragma pack()在结构体部分介绍。 祝大家新年快乐 看到这里了还不给博主扣个 ⛳️ 点赞☀️收藏 ⭐️ 关注
你们的点赞就是博主更新最大的动力 有问题可以评论或者私信呢秒回哦。