c——预编译

来源:互联网 发布:akka java 简明教程 编辑:程序博客网 时间:2024/05/22 05:05

预编译

预编译,又称预处理,顾名思义预先编译,发生于真正编译之前,预编译不属于c语言组成部分,因此预编译无需遵守c语言语法规则
预编译系统本质是文本处理系统,预编译系统对原始source file进行文本处理,处理原始source file中预处理命令,预编译处理后生成的中间source file不再含有预处理命令

预处理命令

预处理命令存在于原始source file中,以#字符起始,预处理命令遵守以下规则:
  • 预处理命令以#字符起头,#字符占据行首字符,即#字符前不允许有任何前导字符(空格符,tab符等空白字符除外)
预处理命令有三类:
  • 文件包含
  • 宏定义
  • 条件编译

文件包含

  • #include
两种使用格式:
  • #include <file>
  • #include "file"
文件包含,本质是文件复制,复制#include预处理命令指定的目标文件内容到#include预处理命令处,删除#include预处理命令
注:#include预处理命令中file允许任何文件扩展名(包括无文件扩展名),但习惯上使用.h文件扩展名,表示header文件
两种使用格式区别:
  • #include <file>优先search标准header目录,一旦search到,search terminate,用search到的目标文件内容替换#include预处理命令
  • #include "file"优先search自定义header目录,一旦search到,search terminate,用search到的目标文件内容替换#include预处理命令
ctype.h是标准header文件,含isalpha函数对象声明
int isalpha(int ch);
#include <file>头文件包含
#include <ctype.h>void call_isalpha(){    printf("isalpha(65) = %d\n", isalpha(65));}
编译正确,运行正确
现在自定义header文件ctype.h,含自定义isalpha函数对象声明
int isalpha(int* pch);
#include "file"头文件包含
#include "ctype.h"void call_isalpha(){    printf("isalpha(65) = %d\n", isalpha(65));}
编译error,参数类型不匹配

宏定义

  • #define:宏定义
  • #undef:宏定义取消
#define macro,宏定义,作用是标识符(包括类型标识符,对象标识符,宏定义内容中标识符,不包括宏定义标识符)文本替换,用宏定义内容替换source file中与宏定义相同标识符,替换范围从宏定义处到宏定义取消处或当前source file结束处(如果没有宏定义取消)
#define三种格式
  • 无宏替换的宏定义,如#define NAME
  • 不带参数的宏定义,如#define NAME "Martin"
  • 带参数的宏定义,如#define NAME(first) first,#define NAME(first, last) first" "last
注意:
  • 无宏替换的宏定义仅仅表示定义宏,但这个宏定义的宏替换无或不确定(并非默认为空字符串),无宏替换不同于宏替换为空字符串
  • 如果同一source file中重复定义同一宏,则从重复宏定义处始更新宏定义,旧有宏定义失效
  • 宏定义的标识符文本替换包括宏定义内容中标识符,此处宏定义内容为当前宏定义之后的后续宏定义内容,不包括当前宏定义内容(否则宏定义无穷递归展开)
void define_macro(){#define NAME    //printf("name = %s\n", NAME);    #define NAME ""    printf("name = %s\n", NAME);    #define NAME "Martin"        printf("name = %s\n", NAME);    #define NAME "Harry"        printf("name = %s\n", NAME);    #define NAME NAME        //printf("name = %s\n", NAME);    #define NAME(first) first        printf("name = %s\n", NAME("Tom"));    //printf("name = %s\n", NAME(Tom));    #define NAME(first, last) first" "last        printf("name = %s\n", NAME("Bob", "George"));    #define NAME_CARD(first, last) "name: "NAME(first, last)        printf("%s\n", NAME_CARD("Bob", "George"));    #define COUNTRY_CODE 86        printf("country code = %d\n", COUNTRY_CODE);    #undef COUNTRY_CODE        //printf("country code = %d\n", COUNTRY_CODE);}
output:
name = name = Martinname = Harryname = Tomname = Bob Georgename: Bob Georgecountry code = 86

条件编译

defined(macro)表达式在预编译期判断在当前位置处宏macro是否定义过,表达式值为布尔型,!defined(macro)则是对defined(macro)表达式值取逻辑非
条件编译三种形式:
  • #if:后跟条件表达式,#if condition_exp
  • #ifdef:后跟宏,#ifdef macro
  • #ifndef:后跟宏,#ifndef macro
#if格式和用法类似于if语句,如果if语句称之为编译版的if语句,则#if可称之为预编译版的if语句,前者存在于编译期,后者存在于预编译期
#if语法和格式为:
#if condition//code list[#elif condition_1//code list ]...[#elif condition_n//code list ][#else//code list ]#endif
注:[]表示optional
根据条件语句匹配逻辑,保留符合条件匹配的分支代码,忽略其他分支代码
#ifdef macro等价于#if defined(macro),#ifdef macro语法和格式为:
#ifdef macro//code list[#elif condition_1//code list ]...[#elif condition_n//code list ][#else//code list ]#endif
#ifndef macro等价于#if !defined(macro),#ifndef macro语法和格式为:
#ifndef macro//code list[#elif condition_1//code list ]...[#elif condition_n//code list ][#else//code list ]#endif
应用:
#define COUNTRY_CODE 86void if_condition(){#if COUNTRY_CODE == 1        printf("#if country America\n");    #elif COUNTRY_CODE == 81        printf("#if country Japan\n");    #elif COUNTRY_CODE == 86        printf("#if country China\n");    #else        printf("#if country unkonwn\n");    #endif    #ifdef COUNTRY_CODE        printf("#ifdef macro COUNTRY_CODE defined\n");    #if COUNTRY_CODE == 1        printf("#ifdef country America\n");    #elif COUNTRY_CODE == 81        printf("#ifdef country Japan\n");    #elif COUNTRY_CODE == 86        printf("#ifdef country China\n");    #else        printf("#ifdef country unkonwn\n");    #endif    #endif    #ifndef COUNTRY_CODE        printf("#ifndef macro COUNTRY_CODE undefined\n");    #elif COUNTRY_CODE == 1        printf("#ifndef country America\n");    #elif COUNTRY_CODE == 81        printf("#ifndef country Japan\n");    #elif COUNTRY_CODE == 86        printf("#ifndef country China\n");    #else        printf("#ifndef country unkonwn\n");    #endif}
output:
#if country China#ifdef macro COUNTRY_CODE defined#ifdef country China#ifndef country China
总结:
  • #if作为预处理命令发生于预编译期,因此#if中条件表达式在预编译期必须能确定值(true或false),因此上述COUNTRY_CODE必须为宏
  • 预编译不属于c语言组成部分,预编译系统本质是文本处理系统,因此预编译期无需遵守c语言语法规则,也没有对象和内存概念,因此#if中条件表达式中不允许出现对象(包括const对象),因为对象值只有在编译期或运行期才能确定,而#if中条件表达式值必须在预编译期确定
  • #if中条件表达式支持逻辑运算符(&&,||,!),支持内置类型常量转布尔型常量规则(非0->true,0->false),条件表达式值类型必须为布尔型,否则编译error
  • #ifdef和#ifndef是#if的特殊版本
void if_condition(){#if COUNTRY_CODE == 1        printf("#if country America\n");    #elif COUNTRY_CODE == 81 || COUNTRY_CODE == 86        printf("#if country Japan or China\n");    #else        printf("#if country unkonwn\n");    #endif}

预编译过程

预编译系统作为文本处理系统对原始source file进行文本处理,处理原始source file中预处理命令,预编译处理后生成的中间source file不再含有预处理命令
预编译系统把原始source file当做文本文件进行处理,预编译不是c语言组成部分,因此无需考虑c语言语法规则,预编译系统处理单位为单个原始source file,从头到尾顺序遍历,处理遇到的预处理命令,过程如下:
  • 遇到文件包含(#include)预处理命令,复制#include预处理命令指定的目标文件内容到#include预处理命令处,删除#include预处理命令
  • 遇到宏定义(#define)预处理命令,读取宏定义,如果宏定义list中无该宏定义,把该宏定义插入到宏定义list,如果宏定义list中已存在该宏定义,用该宏定义进行替换更新,删除#define预处理命令
  • 遇到宏定义取消(#undef)预处理命令,读取宏名字,如果宏定义list中已存在该宏名字,在宏定义list中删除该宏名字对应宏定义,删除#undef宏定义取消预处理命令
  • 遇到条件编译(#if,#ifdef,#ifndef)预处理命令,把符合条件匹配的分支代码插入到条件编译(#if,#ifdef,#ifndef)预处理命令处,删除条件编译(#if,#ifdef,#ifndef)预处理命令
  • 遇到标识符(包括类型标识符,对象标识符,宏定义内容中标识符,不包括宏定义标识符),在宏定义list中search,如果search到,进行宏替换
注:预编译时内存中维护着宏定义list

预编译技巧

防止文件重包含

header file或source file都可使用#include预处理命令,因此可能会形成意大利面条似的文件包含关系,眼花缭乱,无法理清文件之间复杂的包含关系,可能会产生下面错误:
  • 同一source file中多次包含同一header file,造成数据类型重定义error(为了数据类型复用,header file中常放数据类型定义)
  • 同一header file直接或间接包含自身,造成无穷递归包含
可使用宏定义预处理命令和条件编译预处理命令来避免这些错误,比如animal头文件,可在animal头文件中放置下面结构:
#ifndef ANIMAL_H#define ANIMAL_H//code list#endif
0 0
原创粉丝点击