• c++ 预定义【转】


    预处理器(Preprocessor)定义了读取源代码、对代码预先翻译以及编写供编译器读取的新代码的过程。预处理先于编译器对源代码进行处理。
      C/C++语言没有内置工具在编译时间包含其他源文件、宏定义,或根据条件包含或排除一些代码行的编译时指令。预处理器提供了这些能力。虽然当前大多数编译器内部集成了预处理器,人们还是认为预处理独立于编译器的过程。预处理器读取源代码,查找预处理指令语句和宏调用,然后翻译源代码,它还去掉程序中的注释和多余的空白。
      在C++语言中预处理指令有:
      指令          功能描述
      #                 空指令,没有作用
      #include      在指令的位置包含一个源代码文件
      #define       定义一个宏
      #undef        取消宏定义
      #if               如果给定条件为真,则编译代码
      #ifdef          如果宏被定义,则编译代码 
      #ifndef        如果宏未被定义,则编译代码
      #elif            如果前面的#if...条件不为真而当前条件为真,则编译代码
      #endif         终止#if....#else条件块
      #error         终止编译并显示错误信息
      #line           修改编译器尾部用于消息报告的文件名和行号

      #pragma    功能取决于平台

     

     "#"串化运算符


      宏定义内的"#"运算符把位于其后的形参所对应的实参转化为字符串。
      例子:
     #include <iostream.h>
      #define Error(n) cout << "Error " << #n

      int main(void)
     {
         Error(53);
         return 0;   
     }
      
     程序运行结果:Error 53

      宏定义中的"#n"系列通知预处理器,把实参传递的内容转化为字符串。所以,Error(53)展开的效果如:
      cout << "Error " "53";
     根据字符串常量的用法,相邻字符串将被连接,应而实际的语句是:

    cout << "Error 53";

     

     "##"运算符


      "##"运算符把多个实参连接起来。

      #include <iostream.h>
       #deifne BookChapterVerse(b,c,v) b ## c ## v

      int main(void)
     {
         unsigned bcv=BookChapterVerse(5,12,43);
         cout << bcv;
         return 0;
     }
      

        程序的运行结果为:51243

     

       #include      在指令的位置包含一个源代码文件

      头文件通常以.h结尾,其 内容可使用#include预处理器指令包含到 程序

     头文件中一般包含: 函数原型与全局变量

      形式常有下面两种
      #include <iostream>
      #include "myheader.h"

      前者<>用来引用标准库头文件,后者""常用来引用自定义的头文件
      前者<>编译器只搜索包含标准库头文件的默认 目录,后者首先搜索正在编译的源文件所在的 目录,找不到时再搜索包含标准库头文件的默认 目录.

      如果把头文件放在其他 目录下,为了查找到它,必须在双引号中指定从源文件到头文件的完整路径

     

      #define  定义符号、宏

    符号
      #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)符号名不能限制在一个命名 空间

     

      用宏名中的参数带入语句中的参数
      

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

    宏后面没有;号

      Print(Var)中的Print和(之间不能有空格,否则(就会被解释为置换字符串的一部分 

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


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

      调用
      Print(ival, 15);

     #undef 删除#define定义的符号

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

      #undef PI

      之后不再有PI这个标识符

     

    逻辑预处理器指令#if  #else  #elif   #endif   #undef   #ifndef

     #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是独立没有关系的两个行

     

    #line   

      使用#line可以修改__FILE__返回的字符串
      如
      #line 1000    把当前行号设置为1000
      #line 1000 "the program file"      修改__FILE__返回的字符串行号改为了1000,文件名改为了"the program file"

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

     

     #error

      在预处理阶段,如果出现了错误,则#error指令可以生成一个诊断 消息,并显示为一个编译错误,同时中止编译
      #ifndef __cplusplus
      #error "Error -  Should be C++"
      #endif

     

     #pragma

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


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

      #include <iostream>
      #include <cassert>
      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;
      }

     

    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

     

     标准的预处理器宏

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

      __cplusplus  在编译C++ 程序时,它就定义为199711L

  • 相关阅读:
    MySQL分库分表环境下全局ID生成方案
    centos添加php及mysql环境变量
    shell中的常用通配符,字符类
    centos7 安装xinetd,telnet
    centos7 systemctl一些用法
    ps命令
    nginx与php-fpm通信的两种方式
    nginx常用功能
    MySQL安装
    MySql与MariaDB由来与历程
  • 原文地址:https://www.cnblogs.com/iable/p/4206875.html
Copyright © 2020-2023  润新知