zoukankan      html  css  js  c++  java
  • 一些语言特性整理——预处理指令、volatile、标准预定义宏

    [GCC官方文档:http://gcc.gnu.org/onlinedocs/cpp/index.html]

     

    一、预处理

    C语言的预处理主要有三个方面的内容:

    1.宏定义;#define[分无参宏和带参宏]

    #undef 取消定义宏(之前的宏定义的作用域在该行之后失效)[宏能够被重定义,在重定义前,必须使用#undef指令取消原来的宏定义

    使用简单宏定义定义常量符号起源于C语言,但在C++中,定义常量可以用const关键字,并且还附加类型检查的功能,因此C++中已经尽量避免使用宏定义来定义常量了。

    带参宏:

    定义形式:#define 宏名(形参表) 字符串 

    如:#define MAX(a,b) (a>b)?a:b 

    注:

    带参数宏定义在C++中的使用同样也在减少,因为:1,C++的内联函数提供了和带参数宏同样高的代码执行效率,同时没有后者那样的语义歧义;

    2,C++模板提供了和带参数宏同样高的灵活性,还能够执行语法分析和类型检查。

    引用操作符#:将操作对象替换为带引号的字符串  如:

    #define CheckPtr(ptr) \

    if ((ptr) == 0) cout << #ptr << " is zero!\n"

    则CheckPtr(tree->left);

    扩展为:

    if ((tree->left) == 0) cout << "tree->left" << " is zero!\n";

    拼接操作符##:用来连接宏中两个实际参数

    在一般编程时很少用到拼接操作符,但在编写编译器程序或源代码生成器时特别有用,因为它能轻易的构造出一组标识符。

    宏定义写成多行时,需要在一行的行末添加“\”字符,如#define CheckError if (error) exit(1)可以写成:

    #define CheckError \

    if (error) \

    exit(1)

    #define dlogh(...)  NSLog(@"DEBUGLog File=%s Line%d >>",                \

    __FILE__,__LINE__),                                                  \

    NSLog(__VA_ARGS__);

    对于宏定义,我们要说明以下几点。

    ①宏定义是用宏名来表示一个字符串,在宏展开时又以该字符串取代宏名,这只是一种简单的代换。字符串中可以含任何字符,可以是常数,也可以是表达式。预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。

    ②宏定义不是说明语句,在行末不必加分号。如加上分号,则连分号也一起置换。

    ③宏定义必须写在方法之外,其作用域为宏定义命令起到源程序结束。如要终止其作用域,则可使用#undef命令。

    ④宏名在源程序中若用引号括起来,则预处理程序不对其作宏代换。

    ⑤宏定义允许嵌套,在宏定义的字符串中可以使用已经定义的宏名。在宏展开时由预处理程序层层代换。例如:

    1. #define PI 3.1415926  
    2. #define S PI*y*y /* PI 是已定义的宏名*/ 

    ⑥习惯上宏名用大写字母表示,以便于与变量区别,但也允许用小写字母。

    ⑦可用宏定义表示数据类型,方便书写。例如:

    1. #define INTEGER int 

    在程序中即可用INTEGER作整型变量说明:

    1. INTEGER a,b; 

    应注意用宏定义表示数据类型和用typedef定义数据说明符的区别。

    ⑧对输出格式作宏定义,可以减少书写麻烦。

    2.文件包含;#include

    3.条件编译。 

    #if 常量  //这种方式常用于添加测试代码,如做测试时将常量设为1,发布时将其改为0
    #ifdef 宏名  //常用于打开和关闭某项功能
    #ifndef 宏名  //同#ifdef

    4、其他指令

    #line 改变当前行号或者文件名

    #error 输出一条错误信息[强迫编译程序停止编译,主要用于程序调试。]

    #warning 同#error,不过编译器将继续执行下去[常见使用场景:在废弃的头文件中使用#warning,输出信息来指示用户应该使用的正确文件名]

    #progma,主要功能是为编译程序提供非常规的控制流信息。

    二、c语言中volatile的使用:

    作用:

    1、告诉compiler不能做任何优化;

    2、表示用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能把他放在cache或寄存器中重复使用。

    [volatile关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。

    遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

    使用该关键字的例子如下:volatile int nVint;]

    下面是volatile变量的几个例子:

    1) 并行设备的硬件寄存器(如:状态寄存器)

    2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

    3) 多线程应用中被几个任务共享的变量

      这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,这些都要求volatile变量。

    三、标准预定义宏

    ANSI C标准中有几个标准预定义宏:
    __LINE__:在源代码中插入当前源代码行号;
    __FILE__:在源文件中插入当前源文件名;
    __DATE__:在源文件中插入当前的编译日期
    __TIME__:在源文件中插入当前编译时间;
    __func__ 当前所在函数名,在编译器的较高版本中支持
    __FUNCTION__ 当前所在函数名

    __STDC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;
    __cplusplus:当编写C++程序时该标识符被定义。
    
    

    GCC中其他的预定义宏:

    __GNUC__、__GNUC_MINOR__、__GNUC_MINOR__、__GNUC_PATCHLEVEL__:用于得到GNU版本:

    __VERSION__:用于得到编译器的版本

    __COUNTER__:自身计数器,用于记录以前编译过程中出现的__COUNTER__的次数,从0开始计数。常用于构造一系列的变量名称,函数名称等。

    __INCLUDE_LEVEL__:用于表示文件被包含的计数,从0开始递增,常作为递归包含的限制条件。

  • 相关阅读:
    关于WorkFlow的使用以及例子
    11 个用来创建图形和图表的 JavaScript 工具包
    产品经理看程序员的自我修养
    extern "C" 的作用
    DLL 演示
    C++中L和_T()之区别
    VMware:Configuration file was created by a VMware product with more features than this version
    使用内存映射来对文件排序
    平衡二叉树的插入删除操作
    volatile关键字的使用
  • 原文地址:https://www.cnblogs.com/zhulin/p/2425159.html
Copyright © 2011-2022 走看看