`

C语法总结 预处理器

 
阅读更多

 

预处理器可以在编译前处理C程序

//include 指令告诉预处理器打开一个特定的文件
#include <stdio.h>

//用define 指令定义了一个宏,用来代表其他东西的名字
#define PI  3.14

//可以用  /  符号换行
#define DISK_CAPACITY (SIDES *                            \
                                        TRACKS_PER_SIDE *        \
                                        SECTORS_PER_TRACK *   \
                                        BYTES_PER_SECTOR)

//带参数的宏
#define MAX(x,y)     ((x)>(y)?(x):(y))
//展开后为
i = MAX(j+k, m-n)
i = ((j+k)>(m-n)?(j+k):(m-n))

大多数预处理器都属于下面三种类型之一:

宏定义       #define指令定义一个宏, #undef指令删除一个宏定义

文件包含   #include指令导致一个指定文件的内容被包含到程序中

条件编译   #if、#ifdef、#ifndef、#elif、#else 和 #endif 指令可以根据预处理器可以测试的条件来确定是将

                一段文本块包含到程序中还是将其排除在程序之外

 

适用于所有指令的规则

指定都以  # 开头

在指令的符号之间可以插入任意数量的空格或水平制表符

指令总是在一个换行符处结束,除非明确地指明要延迟,可以在末尾使用 \  字符

指令可以出现在程序中的任何地方

注释可以与指令放在同一行

 

使用#define来为常量命名有许多优点

1.程序会更容易读

2.程序会更容易修改

3.可以帮助避免前后不一致或者键盘输入错误

4.可以对C语法做小的修改

5.对类型重命名

6.控制条件编译

 

带参数的宏优缺点

1.程序可能会更快

2.宏更通用(宏的参数没有类型,可以接收int,long,float等类型)

3.编译后的代码通常会变大

4.宏参数没有类型检查

5.无法用一个指针来指向一个宏

6.宏可能会不止一次地计算它的参数

//MAX宏的副作用
n = MAX(i++,j);
//展开后为
n = ((i++)>(j)?(i++):(j));
//如果i大于j,那么i可能会增加两次

 

宏定义中的圆括号

#define SQ(y) y*y
//如果参数y没有加括号,那么下面的表达式就会有问题
sq=SQ(a+1);
//展开后为
sq=a+1*a+1;
//所以需要携程 SQ(y)   (y)*(y)


//这里有两个规则
//1.如果宏的替换列表中有运算符,那么始终将替换列表放在括号中
#define TWO_PI (2*3.14)
        //如果不加括号,#define TWO_PI  2*3.14 在处理时就有问题
        conversion_factor = 360/TWO_PI
        //展开后为
        conversion_factor = 360/2*3.14

2.如果宏有参数,每个参数每次在替换列表中出现时都要放在圆括号中

 

 

 

宏的通用属性

1.宏的替换列表可以包含对其他宏的调用

#define PI             3.14
#define TWO_PI    (2*PI)

2.预处理只会替换完整的记号,不会替换记号的片断

#define SIZE 256
int BUFFER_SIZE;
if(BUFFER_SIZE > SIZE) {
    puts("error : SIZE exceeded");
}
//展开后为
int BUFFER_SIZE;
if(BUFFER_SIZE > 256) {
     puts("error : SIZE exceeded");
}
//尽管标识符BUFFER_SIZE和字符串"error : SIZE exceeded"都包含SIZE,但他们没有被预处理影响

3.宏定义的作用范围通常到出现这个宏的文件末尾(定义在函数中的宏不是仅在函数内起作用,而是作用到文件末尾)

4.宏不可以被定义两遍,除非新的定义与旧的定义是一样的

5.宏可以使用#undef指令“取消定义”

 

 

 

 

 

# 和 ##运算符

// #运算符会将一个参数转换为字符串字面量
#define PINRT_X(n) printf(#n" = %d",n)
PINRT_X(i+j);
//展开后为
printf("i+j = %d",i+j);

// ##运算符可以将两个记号(如标识符)“粘合”在一起
#define MK_ID(n)   i##n
int MK_ID(1), MK_ID(2), MK_ID(3);
//展开后为
int i1, i2, i3;
//由于c语言不允许同名函数,可以为每个max函数构造不同的名字
#define GENERIC_MAX(type)                        \
              type type##max(type x, type y){   \
                  return x > y ? x:y;                       \
              }
GENERIC_MAX(float)
//展开后为
float float_max(float x, floaty) {
    return x > y ? x:y;
}

              

 

 

预定义宏(名字是两个下划线+name+两个下划线组成)

名字 描述
__LINE__ 被编译的文件中的行号
__FILE__ 被编译的文件名
__DATE__ 被编译的日期(格式"Mmm dd yyyy")
__TIME__ 被编译的时间(格式"hh:mm:ss")
__STDC__

如果编译器符合C标准(C89或C99),

那么值为1

 

C99中新增的预定义宏

名字 描述
__STDC_HOSTED__ 如果是托管式实现,值为1;如果是独立式实现,值为0
__STDC_VERSION__ 支持的C标准版本
__STDC_IEC_599__' 如果支持IEC60599浮点算术运算,则值为1
__STDC_IEC_599)COMPLEX' 如果支持IEC60599复数算术运算,则值为1
__STDC_ISO_10646__'

如果wchar_t的值与指定年月的ISO 10646标准相匹配,

则值为yyyymmL

 

 

 

条件编译

//定义一个宏  
#define DEBUG 1  
  
//if指令会计算常量表达式的值,如果是0那么#if与#endif之间的行将在预处理过程中  
//从程序中删除;否则#if和#endif之间的行就会被保留在程序中  
#if DEBUG  
   printf("ok~~");  
#endif  
  
//defined运算符用于检测某个宏是否被定义过,如果是则返回1;否则返回0  
#if defined(DEBUG)  
    printf("ok~~~");  
#endif  
//也可以简写成  
#if defined DEBUG  
  
  
//#ifdef指令用于测试一个标识符是否已经定义为宏  
//#ifndef指令相反,用于测试一个标识符是否没有被定义为宏  
#ifdef DEBUG  
    printf("ok~");  
#endif  
//严格来说,可以不需要#ifdef指令,用 #if defined(DEBUG)可以替代  
  
#ifndef DEBUG
//等价于
#if !defined DEBUG
  
  
//#elif 和 #else指令  
// #elif 常量表达式  
#define WIN  
#if defined(WIN)  
    printf("win...");         
#elif defined(LINUX)  
    printf("linux");  
#elif defined(MAC)  
    printf("mac");  
#else  
    printf("unkownd");  
#endif  

 

 

 

使用条件编译的场景:

1.编写在多态机器或多重操作系统之间可移植的程序

2.编写可以用不同的编译器编译的程序

3.为宏提供默认定义

4.临时屏蔽包含注释的代码

 

 

其他指令

#error
#line
#pragma
//_Pragma运算符(C99)
//__func__标识符(C99)
//空的宏参数(C99)
//参数个数可变的宏(C99)

 

 

参考:

C语言带参数的宏定义

 

 

 

分享到:
评论

相关推荐

    Microsoft Visual C 6.0语言参考手册

    第一部分Visual C 6.0语言参考手册,以6章、4个附录的篇幅介绍了C语言的基本元素、程序结构、说明和类型、表达式和赋值、语句及函数,附录给出了C语言语法总结和C定义的实现。第二部分Visual C++ 6.0语言参考手册,...

    VC++6语言参考手册

    本书是美国微软出版社授权的Microsoft Visual Studio 系列中文版图书 之一, 它是Visual C++ 6.0 开发人员的实用参考书。...给出了C++ 6.0 的预处理器和预处理器的编译器的编译指示指令以及语法总结。

    你必须知道的495个C语言问题

    作者在网络版C FAQ列表的基础上进行了大幅度的扩充和丰富,结合代码示例,权威而且详细深入地解答了实际学习和工作中最常遇到的495个C语言问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等...

    你必须知道的495个C语言问题.pdf

    作者在网络版C FAQ列表的基础上进行了大幅度的扩充和丰富,结合代码示例,权威而且详细深入地解答了实际学习和工作中最常遇到的495个C语言问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等...

    你必须知道的495个C语言问题清晰中文版PDF

    作者在网络版C FAQ列表的基础上进行了大幅度的扩充和丰富,结合代码示例,权威而且详细深入地解答了实际学习和工作中最常遇到的495个C语言问题,涵盖了初始化、数组、指针、字符串、内存分配、库函数、C预处理器等...

    C语言解析教程(原书第4版)(美) 凯利.pdf

    《c语言教程(原书第4版)》是一本优秀的c程序设计语言教材,完整描述了ansi c语言及其语法特性,并对c语言的高级特性和应用作了深入阐述,介绍了从c到c++和java过渡的相关知识。《c语言教程(原书第4版)》的一个...

    c/c++ 学习总结 初学者必备

    (C语言里参数传递都是传值,是一个拷贝,修改指针,只是改变了拷贝的指向,原指针指向并没有改变,而修改指针的内容则是可以的。)如果函数的参数是一个指针,不要指望用该指针去申请动态内存。(即上面所说的修改...

    嵌入式Linux C编程入门(第2版) PPT

    1.3.4 s3c2410处理器简介 18 1.4 嵌入式系统硬件平台选型 22 1.4.1 硬件平台的选择 22 1.4.2 arm处理器选型 23 1.5 嵌入式系统开发概述 25 1.5.1 嵌入式系统开发流程 25 1.5.2 嵌入式软件开发...

    aybook.cn_c++jiaocheng0105.rar

    模板 第17章 异常处理 第18章 C++的I/O系统 第19章 运行时类型识别与强制转换运算符 第20章 名字空间和其他高级主题 第21章 标准模板库 第22章 C++预处理器 附录A 基于C的I/O 附录B 使用旧的C++编译器 附录C .NET对...

    freemarker总结

    这是一个典型的分支控制指令,该指令的作用完全类似于Java语言中的if,if指令的语法格式如下: &lt;#if condition&gt;... &lt;#elseif condition&gt;... &lt;#elseif condition&gt;... &lt;#else&gt; ... 例子如下: (age&gt;60)&gt;老年人 ...

    Oracle SQL高级编程(资深Oracle专家力作,OakTable团队推荐)--随书源代码

    作者通过总结各自多年的软件开发和教学培训经验,与大家分享了掌握Oracle SQL所独有的丰富功能的技巧所在,内容涵盖SQL执行、联结、集合、分析函数、子句、事务处理等多个方面。读者可以学习到以下几个方面的技巧:...

Global site tag (gtag.js) - Google Analytics