`

c++预编译 命令总结(转)

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


  常用的预处理指令:
  #include       包含头文件

  #if               条件
  #else          否则
  #elif            否则如果
  #endif         结束条件

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

  #define        定义一个符号
  #undef         删除一个符号

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

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

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


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

  形式常有下面两种
  #include <iostream>
  #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<class T> 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()宏
  在标准库头文件<cassert>中声明
  用于在 程序中 测试一个逻辑表达式,如果逻辑表达式为false, 则assert()会终止 程序,并显示诊断 消息
  用于在条件不满足就会出现重大错误,所以应确保后面的语句不应再继续执行,所以它的应用非常灵活
  注意: assert不是错误处理 机制,逻辑表达式的结果不应产生负面效果,也不应超出 程序员的控制(如找开一个文件是否成功), 程序应提供适当的代码来处理这种情况
 assert(expression);
  assert(expression) && assert(expression2);
  可以使用#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;
  }
分享到:
评论

相关推荐

    C语言程序设计+研究生复试+求职+面试题

    汇总了计算机研究生复试有关C语言程序设计各章节简答题,使用了易于口头表达的语言进行了总结。...预编译命令作用; 论述含参数的宏与函数的优缺点; 文件操作过程; SOCKET基础之C/S通信过程; C和C++的区别; ... ...

    Tcl_TK编程权威指南pdf

    这些命令与你的应用程序所提供的C或C++过程关联。结果应用程序就分割成一组用编译语言编写的原语,并输出成为相应的Tcl命令。使用Tcl脚本程序可以将这些原语组装成完整的应用程序。脚本语言层可以存取与shell类似的...

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

    11.10 在c程序内部执行命令 11.11 在c程序内部使用管道 11.12 环境变量 11.13 c编译器 11.14 使用性能评估程序 11.15 函数库 11.16 对c代码进行计时 11.17 使用make 11.18 使用touch 11.19 其他有用的工具 11.20 ...

    精通SQL 结构化查询语言详解

    19.5.2 预编译文件  19.5.3 设置Visual C++ 6.0编译环境  19.5.4 编译运行程序  附录A SQL保留字  附录B 常用的SQL命令  附录C 关于运行环境的说明  C.1 SQL Server 2000  C.1.1 直接访问  C.1.2 ...

    C#微软培训资料

    18.2 在 C #代码中调用 C++和 VB 编写的组件 .240 18.3 版 本 控 制 .249 18.4 代 码 优 化 .252 18.5 小 结 .254 第五部分 附 录 .255 附录 A 关 键 字.255 附录 B 错 误 码.256 附录 C .Net 名字空间...

    精通SQL--结构化查询语言详解

    8.3.7 select语句各查询子句总结 156 第9章 多表查询 157 9.1 本章用到的实例表 157 9.2 表的基本连接 158 9.2.1 连接表的目的 158 9.2.2 简单的二表连接 159 9.2.3 多表连接 161 9.2.4 使用表别名 162 ...

    传智播客扫地僧视频讲义源码

    本教程共分为5个部分,第一部分是C语言提高部分,第二部分为C++基础部分,第三部分为C++进阶部分,第四部分为C、C++及数据结构基础部分,第五部分为C_C++与设计模式基础,内容非常详细. 第一部分 C语言提高部分目录...

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

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

    VTK User's Guide(中文完整版)

    C++---------------------------------------------------------------------------------------------20 Java Phthon Visual Basic/COM/ActiveX 3.3 在两种语言间转换 第二部分 通过例子学习VTK 第4章 基础...

    华为编程开发规范与案例

    1、系统配置、命令方式 第52页 【案例4.1.1】 第52页 【案例4.1.2】 第53页 2、设备对接 第54页 【案例4.2.1】 第54页 3、其他 第55页 【案例4.3.1】 第55页 五、版本控制问题 第58页 1、新老代码中同一全局变量不...

Global site tag (gtag.js) - Google Analytics