常用的C++预处理

  预处理指令提供按条件跳过源文件中的节、报告错误和警告错误条件,以及描绘源文件代码的不同区域的能力。是整个编译过程的最先做的工作。预处理不做语法检查,预处理命令以符号“#”开头。

宏定义:#define

头文件:#include

条件编译:#if、#elif、#ifndef、#else、#endif、#undef

错误信息指令:#error

#line指令

布局控制:#pragma

 

宏定义——“#”

宏定义简称宏。宏定义只做替换,不做计算,不做表达式求解。宏定义分带参数宏定义和不带参数宏定义。在带参数的宏定义,宏名和参数的括号间不能有空格。

宏定义不分配内存,变量定义分配内存。

宏展开不占运行时间,只占编译时间:函数调用占运行时间(分配内存、保留现场、值传递、返回值)。

出现在宏定义中的#运算符把跟在其后的参数转换成一个字符串,有时把这种用法的#称为字符串化运算符。例如:

#include<iostream>
using namespace std;
#define STR(n) "很好"#n

int main(){
    cout <<STR(10) <<endl;          
}

 

输出:很好10

 

宏定义——“##”

##运算符用于把参数连接到一起,预处理程序把出现在##两侧的参数合并成一个符号。例如:

 

复制代码
#include <iostream>
using namespace std;

#define STR(a,b,c)  a##b##c

int main(){
    int STR(a,1,2) = 10;
    cout << a12 <<endl;
}
复制代码

 

输出:10

 

条件编译

使用条件编译可以使目标程序变小,运行时间变短。

#undef指令,用来删除事先定义的宏定义,其一般形式为:#undef宏替换名

 

错误信息指令

 

#error指令,该指令用于程序的调试,输出一个错误信息,当编译中遇到#error指令就停止编译,其一般形式为:#error出错信息。例如:

复制代码
#ifndef __cplusplus
#error this is not a C++ complier.
#endif
#include <iostream>
using namespace std;

int main(){
   cout << "hh" <<endl;
}
复制代码

 

#line指令

命令#line改变__LINE__与__FILE__的内容,他么是在编译程序中预先定义的标识符。

其格式为:#line number [filename] ,这条指令可以改变当前的行号和文件名。

复制代码
#include <iostream>
using namespace std;

#line 100 "a.cpp"

int main()
{
    cout <<__LINE__<<'\t'<<__FILE__<<endl;
}    
复制代码

 

输出:103          a.cpp

 

布局控制指令#pragma para

message 参数

Message 参数能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:

#pragma message(“消息文本”)

当人们在程序中定义了许多宏来控制源代码版本的时候,人们自己有可能都会忘记有没有正确的设置这些宏,此时人们可以用这条指令在编译的时候就进行检查。假设人们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法:

#ifdef _X86

#pragma message("_X86 macro activated!")

#endif

#pragma once

只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。#pragma once是编译相关,就是说这个编译系统上能用,但在其他编译系统不一定可以,也就是说移植性差,不过基本上已经是每个编译器都有这个定义了。

#ifndef,#define,#endif这个是C++语言相关,这是C++语言中的宏定义,通过宏定义避免文件多次编译。所以在所有支持C++语言的编译器上都是有效的,如果写的程序要跨平台,最好使用这种方式。

 

#pragma data_seg

用#pragma data_seg建立一个新的数据段并定义共享数据,其具体格式为:

  #pragma data_seg ("shareddata")

  HWND sharedwnd=NULL;//共享数据

  #pragma data_seg()

第一个#pragma叙述建立数据段,这里命名为shared。您可以将这段命名为任何一个您喜欢的名字。在这里的#pragma叙述之后的所有初始化了的变量都放在shared数据段中。第二个#pragma叙述标示段的结束。对变量进行专门的初始化是很重要的,否则编译器将把它们放在普通的未初始化数据段中而不是放在shared中。

  1,#pragma data_seg()一般用于DLL中。也就是说,在DLL中定义一个共享的有名字的数据段。最关键的是:这个数据段中的全局变量可以被多个进程共享,否则多个进程之间无法共享DLL中的全局变量。

  2,共享数据必须初始化,否则微软编译器会把没有初始化的数据放到.BSS段中,从而导致多个进程之间的共享行为失败。例如:

#pragma data_seg("MyData")

int g_Value; // Note that the global is not initialized.

#pragma data_seg()

DLL提供两个接口函数:

int GetValue()
{
  return g_Value;
}
void SetValue(int n)
{
  g_Value = n;
}

       然后启动两个进程A和B,A和B都调用了这个DLL,假如A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值不一定是5,而是一个未定义的值。因为DLL中的全局数据对于每一个调用它的进程而言,是私有的,不能共享的。假如你对g_Value进行了初始化,那么g_Value就一定会被放进MyData段中。换句话说,如果A调用了SetValue(5); B接着调用int m = GetValue(); 那么m的值就一定是5,这就实现了跨进程之间的数据通信。

#pragma pack(n)

#pragma pack规定的对齐长度,实际使用的规则是: 结构,联合,或者类的 数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和这个数据成员自身长度中,比较大的那个进行。 但是,当#pragma pack的值等于或超过最长数据成员的长度的时候,这个值的大小将不产生任何效果。 而结构整体的对齐,则按照结构体中最大的数据成员 和 #pragma pack指定值 之间,较小的那个进行。

posted @   放弃吧  阅读(146)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示