zoukankan      html  css  js  c++  java
  • 【C++】预处理过程与语句总结

    转载请保留: http://www.cnscn.org(CNS电脑与英语学习网)
    Author: cnscn http://www.cnscn.org

    1)预处理
    根据已放置在文件中的预处理指令来修改源文件的 内容
    预处理器会分析执行所有的预处理器指令,然后删除他们,得到一个仅包含C++语句的转换单元
    预处理指令以#号开头

    常用的预处理指令:

    include 包含头文件

    if 条件

    else 否则

    elif 否则如果

    endif 结束条件

    ifdef 或 #if defined 如果定义了一个符号, 就执行操作

    ifndef 或 #if !defined 如果没有定义一个符号, 就指执行操作

    define 定义一个符号

    undef 删除一个符号

    line 重新定义当前行号和文件名

    error 输出编译错误 消息, 停止编译

    pragma 提供 机器专用的特性,同时保证与C++的完全兼容

    2)#include  在 程序中包含头文件
     头文件通常以.h结尾,其 内容可使用#include预处理器指令包含到 程序中
     头文件中一般包含: 函数原型与全局变量

    形式常有下面两种

    include

    include "myheader.h"

    前者<>用来引用标准库头文件,后者""常用来引用自定义的头文件
    前者<>编译器只搜索包含标准库头文件的默认 目录,后者首先搜索正在编译的源文件所在的 目录,找不到时再搜索包含标准库头文件的默认 目录.
    如果把头文件放在其他 目录下,为了查找到它,必须在双引号中指定从源文件到头文件的完整路径

    3)#define 定义符号、宏
    1>符号

    define PI 3.1415925 定义符号PI为3.1415925

    define PI      取消PI的值

    这里PI看起来像一个变量,但它与变量没有任何关系,它只是一个符号或标志,在 程序代码编译前,此符号会用一组指定的字符来代替
    3.14159265 不是一个数值,只是一个字符串,不会进行检查

    在编译前,预处理器会遍历代码,在它认为置换有意义的地方,用字符串PI的定义值(3.14159265)来代替
     在注释或字符串中的PI不进行替换

    在C中常以#define来定义符号常量,但在C++中最好使用const 来定义常量

    define PI 3.14159265

    const long double PI=3.14159265;
    两者比较下,前者没有类型的指定容易引起不必须的麻烦,而后者定义清楚,所以在C++中推荐使用const来定义常量

     #define的缺点:
    1)不支持类型检查
    2)不考虑作用域
    3)符号名不能限制在一个命名 空间中

    2>#undef 删除#define定义的符号

    define PI 3.14159265

    ... //之间所有的PI都可以被替换为3.14159265

    undef PI

    之后不再有PI这个标识符

    3>定义宏

    define Print(Var) count<<(Var)<<endl

    用宏名中的参数带入语句中的参数
    宏后面没有;号
    Print(Var)中的Print和(之间不能有空格,否则(就会被解释为置换字符串的一部分

    define Print(Var, digits) count << setw(digits) << (Var) << endl

    调用
    Print(ival, 15)
    预处理器就会把它换成
    cout << setw(15) << (ival) << endl;

    所有的情况下都可以使用内联函数来代替宏,这样可以增强类型的检查
    template inline void Print (const T& var, const int& digits)
    {
    count<<setw(digits)<<var<<endl;
    }

    调用
    Print(ival, 15);

    使用宏时应注意的易引起的错误:

    define max(x,y) x>y?x:y;+

    调用 result = max(myval, 99); 则换成 result = myval>99?myval:99; 这个没有问题是正确的
    调用 result = max(myval++, 99); 则换成 result = myval++>99?myval++:99; 这样如果myval>99那么myval就会递增两次,这种情况下()是没什么用的如result=max((x),y)则 result = (myval++)>99?(myval++):99;

    再如

    define product(m,n) m*n

    调用
    result = product(5+1,6);则替换为result = 5+1*6; 所以产生了错误的结果,此时应使用()把参数括起

    define product(m,n) (m)*(n)

    则result = product(5+1,6);则替换为result = (5+1)*(6); 所以产生了错误的结果,此时应使用()把参数括起

    结论: 一般用内联函数来代替预处理器宏

    技巧:
    1)给替换变量加引号
    #define MYSTR "I love you"

    cout << MYSTR ; //I love you而不是"I love you"
    如果
    cout << "MYSTR" ; //则会输出"MYSTR"而不是"I love you"
    
    可以这样做
    cout << #MYSTR ;  //则会输出 "I love you"即cout << ""I love you"";
    
    2)在宏表达式中连接几个参数
    如
      #define join(a,b) ab 这样不会理解为参数a的值与参数b的值的连接,即如join(10,999)不会理解为10999而是把ab理解为字符串,即输出ab
    这时可以
    #define join(a,b) a##b
      则join(10,999)就会输出10999
    

    3)逻辑预处理器指令
     #if defined CALCAVERAGE 或 #ifdef CALCAVERAGE
    int count=sizeof(data)/sizeof(data[0]);
    for(int i=0; i<count; i++)
    average += data;
    average /= count;

    endif

    如果已经定义符号CALCAVERAGE则把#if与#endif间的语句放在要编译的源代码内

    防止重复引入某些头文件

    ifndef COMPARE_H

    define COMPARE_H 注意: 这里只是定义一个没有值的符号COMPARE_H, 下面的namespace compare不是COMPARE_H的 内容,这里的定义不像是定义一个常量或宏,仅仅定义一个符号,指出此符号已定义,则就会有下面的 内容namespace compare{...

    namespace compare{
    double max(const double* data, int size);
    double min(const double* data, int size);
    }

    endif

    比较

    define VERSION

    3
    因为有换行符 所以上句等价于 #define VERSION 3
    由此可以看出#define COMPARE_H与namespace compare是独立没有关系的两个行

    也可以这样用

    if defined block1 && defined block2

    ...

    endif

    if CPU==PENTIUM4

    ...
    

    endif

    if LANGUAGE == ENGLISH

    define Greeting "Good Morning."

    elif LANGUAGE == GERMAN

    define Greeting "Guten Tag."

    elif LANGUAGE == FRENCH

    define Greeting "Bonjour."

    else

    define Greeting "Hi."

    endif

    std::cout<<Greeting << std::endl;

    if VERSION == 3

    ...

    elif VERSION == 4

    ...

    else

    ...

    endif

    5)标准的预处理器宏
    LINE 当前源文件中的代码行号,十进制整数
    __FILE__  源文件的名称,字符串字面量
    __DATE__  源文件的处理日期,字符串字面量,格式mmm dd yyyy其中mmm是月份如Jan、Feb等 dd是01-31 yyyy是四位的年份
    TIME 源文件的编译 时间,也是字符串字面量格式是hh:mm:ss
    STDC 这取决于实现方式,如果编译器选项设置为编译标准的C代码,通常就定义它,否则就不定义它
    __cplusplus 在编译C++ 程序时,它就定义为199711L

    使用#line可以修改__FILE__返回的字符串

    line 1000 把当前行号设置为1000

    line 1000 "the program file" 修改__FILE__返回的字符串行号改为了1000,文件名改为了"the program file"

    line LINE "the program file" 修改__FILE__返回的字符串行号没变,文件名改为了"the program file"

    cout << "program last complied at "<<TIME
    << " on " << DATE
    << endl;

    6)#error
    在预处理阶段,如果出现了错误,则#error指令可以生成一个诊断 消息,并显示为一个编译错误,同时中止编译

    ifndef __cplusplus

    error "Error - Should be C++"

    endif

    7)#pragma
     专门用于实现预先定义好的选项,其结果在编译器说明文档中进行了详细的解释。编译器未识别出来的#pragma指令都会被忽略

    8)assert()宏
    在标准库头文件中声明
    用于在 程序中 测试一个逻辑表达式,如果逻辑表达式为false, 则assert()会终止 程序,并显示诊断 消息
    用于在条件不满足就会出现重大错误,所以应确保后面的语句不应再继续执行,所以它的应用非常灵活
    注意: assert不是错误处理 机制,逻辑表达式的结果不应产生负面效果,也不应超出 程序员的控制(如找开一个文件是否成功), 程序应提供适当的代码来处理这种情况
     assert(expression);
    assert(expression) && assert(expression2);
    可以使用#define NDEBUG来关闭断言 机制

    include

    include

    using std::cout;
    using std::endl;

    int main()
    {
    int x=0;
    int y=0;

     cout<<endl;
    
     for(x=0; x<20; x++)
     {
        cout<<"x= "<<x <<" y= "<<y<<endl;
        assert(x<y); //当x>=y与x==5时,就报错,并终止 程序的执行
     }
     return 0;
    

    }

  • 相关阅读:
    Openstack Paste.ini 文件详解
    Keystone controller.py & routers.py代码解析
    YARN源码分析(三)-----ResourceManager HA之应用状态存储与恢复
    YARN源码分析(四)-----Journalnode
    YARN源码分析(四)-----Journalnode
    YARN源码分析(四)-----Journalnode
    YARN源码学习(五)-----NN,DN,RM在Ganglia上的监控实现机理
    Confluence 6 配置一个 Confluence 环境
    Confluence 6 审查日志的对象
    Confluence 6 审查日志
  • 原文地址:https://www.cnblogs.com/cknightx/p/6922707.html
Copyright © 2011-2022 走看看