第二章 预处理
一、文件的包含方式
1)#include"文件名" 2)#include<文件名>
查找文件的顺序不同,一个先从当前目录开始查找;一个先从系统目录开始查找。
二、宏定义
0.基础介绍
宏定义又称为宏替换,是在预处理阶段用预先定义的字符串替代标示符的过程。其定义一般形式为:
#define 标示符 字符串
宏定义的标示符一般情况下都大写,这是一种约定俗成的习惯。使用宏定义需要注意以下几点:
1)宏替换不做语法检查,使用的时候要特别小心
2)宏替换通常在文件开头部分,写在函数的花括号外边,作用域为气候的程序,直到#undef命令终止宏定义的作用域
3)不要在字符串中使用宏,如果宏名出现在字符串中,那么将按字符串进行处理
1.简单宏替换
宏定义的优点:1)减少不必要的修改,提升程序的可阅读性 2)提升代码的可移植性
使用宏定义需要注意的问题:宏定义只是简单的替换,要清楚的理解其替换后的意义
2.带参数的宏替换
带参数的宏替换,其定义的一般形式为:
#define 宏名(参数表) 字符串
使用带参数的宏时需要注意以下几点:
1)宏名和参数表的括号间不能有空格
2)宏替换只做替换,不做计算和表达式求解
3)函数调用在编译后程序运行时进行,并且分配内存,宏替换在编译前进行,不分配内存
4)宏的哑实结合(哑实结合类似于函数调用过程中实参替代形参的过程)不存在类型,也没有类型转换
两个标准的宏,但是只能在linux下运行:
#define min(x,y) ({typeof(x)_x = x;typeof(y)_y=y;(void)(&_x==&_y);_x<_y?_x:_y;}) #define max(x,y) ({typeof(x)_x = x;typeof(y)_y=y;(void)(&_x==&_y);_x>_y?_x:_y;})
可以细细去体会其中的奥妙:a.typeof的意义是什么?b.void(&_x==&_y)的意义是什么?c.为什么要加{}?4.为什么外面需要()?
3.宏定义实现变参
带变参的宏的一般形式为:
#define print(......) printf(__VA_ARGS__)
在这个宏中,“......”指可变参数。可变参数的实现方式就是使用“......”所代表的内容替代__VA_ARGS__,下面代码演示:
#include<stdio.h> #define print(......) printf(__VA_ARGS__) int main(int argc, char *argv) { print("hello world----%d\n",1111); return 0; }
三、条件编译指令的使用
预处理程序提供了条件编译的功能,用户可以选择性地编译程序,进而产生不同的目标代码文件,这对程序的移植性和调试来说是非常有用的。下面来看条件编译命令的几种使用方式:
1)第一种方式: #if 常量表达式 程序段1; #else 程序段2; #endif 2)第二种方式: #ifdef 标示符 程序段1; #else 程序段2; #endif 3)第三种方式: #ifndef 标示符 程序段1; #else 程序段2; #endif
四、#pragma指定的使用
预处理#pragma指令的作用不只是指定字节对齐,其作用是设置编译器的状态或指示编译器完成一些特定的动作。
1.#pragma mesage("消息")
该打印输出的信息是在编译期输出,不会出现在程序最终的运行结果中。通过这种方式,可以在代码中输出想要的信息,也可以看某个宏是否已经被定义过。
#include<stdio.h> #define STR void main(int argc, _TCHAR* argv[]) { printf("学习#pragma命令中message参数的使用!\n"); #ifdef STR #pragma message("STR已经定义过了。") #endif return; }
2.#pragma once
如果在头文件的开头部分加入这条指令,那么就能保证头文件只被编译一次
3.#pragma hdrstop
该指令表示编译头文件到此为止,后面的无需再编译了
4.#pragma pack()
设定字节对齐
5.#pragma warning()