• C++ 作用域与存储类型及预编译指令及文件结构


    一、作用域与存储类型

    作用域:

    1,对主函数来说其他文件中的子函数都是外部函数,所以要对各个子函数进行extern函数声明

    2,没有使用static的全局变量a 在子文件中可以使用。

    3,使用了static 存储类型符,即为静态全局变量,具有文件作用域。

    域运算符 ::

      

    int var=10;
    fun()
    {
    int var;
    var=::var; //将全局变量赋值给局部变量。
    }

    使用方法:

    1,namespace::subitem;

    2, using namespace namespace1;

    3, usign namespace1::subitem;

    程序文件结构:

    1,文件包含命令:

      #include "mymath.h";: 将文件内容替换此行。

      #include <mymath.h>; 到文件包含目录中去找, #include "mymath.h";先查当前目录,再查文件包含目录

    2,宏定义:

      #define PI 3.1415926   尾部不能有分号。#undef PI终止宏作用。

    带参宏定义:类函数宏:

      #define MAX(x,y) ((x)>(y)?(x):(y))

    #ifdef    #else  #endif

    #ifndef   #else #endif

    #if <expression 1> p1; #elif <expression2>  p2; #else  p3;

    预编译指令:

      #pragma once  用法:头文件头中添加表示只编译一次。

          #if !defined (AFX_TEXTPROGRESSCTRL_H...)

      #define afx_....

      #endif    //为了节省第二次编译的速度

    预处理命令#pragma和预定义宏--转载

    一、C预定义宏
    C标准指定了一些预定义宏,编程中常常用到。
    __DATE__     进行预处理的日期
    __FILE__     代表当前源代码文件名的字符串
    __LINE__     代表当前源代码文件中行号的整数常量
    __STDC__     设置为1时,表示该实现遵循C标准
    __STDC_HOSTED__  为本机环境设置为,否则设为0
    __STDC_VERSION__ 为C99时设置为199901L
    __TIME__     源文件的编译时间
    __func__     C99提供的,为所在函数名的字符
    对于__FILE____LINE____func__这样的宏,在调试程序时是很有用的,因为你可以很容易的知道程序运行到了哪个文件的那一行,是哪个函数.

    头文件中的这几句:

    #if _MSC_VER >=1000

    #pragma once 

    #endif    //这三句是条件预编译语句,进行编译时本文件只打开一次。

    #define VC_EXTRALEAN //一个定义语句,他的功能是删除头文件中的

    二、#line和#error
    #line用于重置由__LINE__和__FILE__宏指定的行号和文件名。
    用法如下:#line number filename
    例如:#line 1000 //将当前行号设置为1000
         #line 1000 "lukas.c"   //行号设置为1000,文件名设置为lukas.c

    #error指令使预处理器发出一条错误消息,该消息包含指令中的文本.这条指令的目的就是在程序崩溃之前能够给出一定的信息。

    三、#pragma
    在所有的预处理指令中,#Pragma 指令可能是最复杂的了。#pragma的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
    其格式一般为: #Pragma Para
    其中Para 为参数,下面来看一些常用的参数。

    (1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:
    #Pragma message(“消息文本”)
    当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
    当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
    #ifdef _X86
    #Pragma message(“_X86 macro activated!”)
    #endif
    当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_
    X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。

    (2)另一个使用得比较多的pragma参数是code_seg。格式如:
    #pragma code_seg( ["section-name"[,"section-class"] ] )
    它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。

    (3)#pragma once (比较常用)
    只要在头文件的最开始加入这条指令就能够保证头文件被编译一次。这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。

    (4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。
    有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。

    (5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体外观的定义。

    (6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )等价于:
    #pragma warning(disable:4507 34) /* 不显示4507和34号警告信息。如果编译时总是出现4507号警告和34号警告,
                                        
    而认为肯定不会有错误,可以使用这条指令。*/
    #pragma warning(once:4385) // 4385号警告信息仅报告一次
    #pragma warning(error:164) // 把164号警告信息作为一个错误。
    同时这个pragma warning 也支持如下格式:
    #pragma warning( push [ ,n ] )
    #pragma warning( pop )
    这里n代表一个警告等级(1---4)。
    #pragma warning( push )保存所有警告信息的现有的警告状态。
    #pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。
    #pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。例如:
    #pragma warning( push )
    #pragma warning( disable : 4705 )
    #pragma warning( disable : 4706 )
    #pragma warning( disable : 4707 )
    //.......
    #pragma warning( pop )
    在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。

    (7)pragma comment(...)
    该指令将一个注释记录放入一个对象文件或可执行文件中。
    常用的lib关键字,可以帮我们连入一个库文件。 

    (8)progma pack(n)
       
     指定结构体对齐方式!#pragma pack(n)来设定变量以n字节对齐方式n 字节对齐就是说变量存放的起始地址的偏移量有两种情况:第一、如果n大于等于该变量所占用的字节数,那么偏移量必须满足默认的对齐方式,第二、如果n小于该变量的类型所占用的字节数,那么偏移量为n的倍数,不用满足默认的对齐方式。结构的总大小也有个约束条件,分下面两种情况:如果n大于所有成员变量类型所占用的字节数,那么结构的总大小必须为占用空间最大的变量占用的空间数的倍数;否则必须为n的倍数。下面举例说明其用法。

    #pragma pack(push) //保存对齐状态
    #pragma pack(4)//设定为4字节对齐
    struct test
    {
    char m1;
    double m4;
    int m3;
    };
    #pragma pack(pop)//恢复对齐状态
    为测试该功能,可以使用sizeof()测试结构体的长度!

    在你写dll的时候,因为对于C和C++,编译器会有不同的名字解析规则,所以可以这样用

    #ifndef   __STDC__

    extern "C "   void   function();

    #else

    void   function();

    #endif

     

     __LINE__           在源代码中插入当前源代码行号
      __FILE__           在源代码中插入当前源代码文件名
      __DATE__           在源代码中插入当前编译日期〔注意和当前系统日期区别开来〕
      __TIME__           在源代码中插入当前编译时间〔注意和当前系统时间区别开来〕  
      __STDC__           当要求程序严格遵循ANSIC标准时该标识符被赋值为1。
    ----------------------------------------------------------------------------

    标识符__LINE__和__FILE__通常用来调试程序;标识符__DATE__和__TIME__通常用来在编译后的程序中加入一个时间标志,以区分程序的不同版本;当要求程序严格遵循ANSIC标准时,标识符__STDC__就会被赋值为1;当用C++编译程序编译时,标识符__cplusplus就会被定义。

    #include

     

    int main ()

    {

        printf("该输出行在源程序中的位置:%d\n", __LINE__ );

        printf("该程序的文件名为:%s\n", __FILE__ );

        printf("当前日期为:%s\n", __DATE__ );

        printf("当前时间为:%s\n", __TIME__ );

     

        return 0;

    }
     

    #include

    void main(void)

    {

        printf("%d",__LINE__); // Line 5

    }

    结果为:5

     

     

    // 标准预定义宏宏.cpp : Defines the entry point for the console application.
    //

    #include "stdafx.h"

    #include

    void main(void)

    {
     
        printf("%d",__LINE__); // Line 5
     

     

    }

     

     

     

    编译器宏使用总结
     
     
     

    C/C++中宏总结C程序的源代码中可包括各种编译指令,这些指令称为预处理命令。虽然它们实际上不是C语言的一部分,但却扩展了C程序设计的环境。本节将介绍如何应用预处理程序和注释简化程序开发过程,并提高程序的可读性。ANSI标准定义的C语言预处理程序包括下列命令:

     

    #define,#error,#include,#if,#else,#elif,#endif,#ifdef,#ifndef,#undef,#line,#pragma等。非常明显,所有预处理命令均以符号#开头,下面分别加以介绍。

     

    命令#define定义了一个标识符及一个串。在源程序中每次遇到该标识符时,均以定义的串代换它。ANSI标准将标识符定义为宏名,将替换过程称为宏替换。命令的一般形式为:

     

    #define identifier string

     

    注意:

    ? 该语句没有分号。在标识符和串之间可以有任意个空格,串一旦开始,仅由一新行结束。

    ? 宏名定义后,即可成为其它宏名定义中的一部分。

    ? 宏替换仅仅是以文本串代替宏标识符,前提是宏标识符必须独立的识别出来,否则不进行替换。例如:

    #define XYZ this is a tes

    使用宏printf("XYZ");//该段不打印"this is a test"而打印"XYZ"。因为预编译器识别出的是"XYZ"

    ? 如果串长于一行,可以在该行末尾用一反斜杠' \'续行。

     

    处理器命令#error强迫编译程序停止编译,主要用于程序调试。

     

    #include 命令#i nclude使编译程序将另一源文件嵌入带有#i nclude的源文件,被读入的源文件必须用双引号或尖括号括起来。例如:

     

    #i nclude"stdio.h"或者#i nclude

     

    这两行代码均使用C编译程序读入并编译用于处理磁盘文件库的子程序。

    将文件嵌入#i nclude命令中的文件内是可行的,这种方式称为嵌套的嵌入文件,嵌套层次依赖于具体实现。

     

    如果显式路径名为文件标识符的一部分,则仅在哪些子目录中搜索被嵌入文件。否则,如果文件名用双引号括起来,则首先检索当前工作目录。如果未发现文件,则在命令行中说明的所有目录中搜索。如果仍未发现文件,则搜索实现时定义的标准目录。

     

    如果没有显式路径名且文件名被尖括号括起来,则首先在编译命令行中的目录内检索

     

    如果文件没找到,则检索标准目录,不检索当前工作目录。

     

    条件编译命令

    有几个命令可对程序源代码的各部分有选择地进行编译,该过程称为条件编译。商业软件公司广泛应用条件编译来提供和维护某一程序的许多顾客版本。

     

    #if、#else,#elif及#endif

     

    #if的一般含义是如果#if后面的常量表达式为true,则编译它与#endif之间的代码,否则跳过这些代码。命令#endif标识一个#if块的结束。

     

    #if constant-expression

    statement sequence

    #endif

     

    跟在#if后面的表达式在编译时求值,因此它必须仅含常量及已定义过的标识符,不可使用变量。表达式不许含有操作符sizeof(sizeof也是编译时求值)。

     

    #else命令的功能有点象C语言中的else;#else建立另一选择(在#if失败的情况下)。注意,# else属于# if块。

     

    #elif命令意义与ELSE IF 相同,它形成一个if else-if阶梯状语句,可进行多种编译选择。#elif 后跟一个常量表达式。如果表达式为true,则编译其后的代码块,不对其它#elif表达式进行测试。否则,顺序测试下一块。

     

    #if expression

    statement sequence

    #elif expression1

    statement sequence

    #endif

     

    在嵌套的条件编译中#endif、#else或#elif与最近#if或#elif匹配。

     

    # ifdef 和# ifndef

     

    条件编译的另一种方法是用#ifdef与#ifndef命令,它们分别表示"如果有定义"及"如果无定义"。# ifdef的一般形式是:

     

    # ifdef macroname

    statement sequence

    #endif

     

    #ifdef与#ifndef可以用于#if、#else,#elif语句中,但必须与一个#endif。

     

    命令#undef 取消其后那个前面已定义过有宏名定义。一般形式为:

    #undef macroname

     

    命令# line改变__LINE__与__FILE__的内容,它们是在编译程序中预先定义的标识符。命令的基本形式如下:

     

    # line number["filename"]

     

    其中的数字为任何正整数,可选的文件名为任意有效文件标识符。行号为源程序中当前行号,文件名为源文件的名字。命令# line主要用于调试及其它特殊应用。注意:在#line后面的数字标识从下一行开始的数字标识。

     

    预定义的宏名

     

    ANSI标准说明了C中的五个预定义的宏名。它们是:

     

    __LINE__

    __FILE__

    __DATE__

    __TIME__

    __STDC__

     

    如果编译不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。

     

    __LINE__及__FILE__宏指令在有关# line的部分中已讨论,这里讨论其余的宏名。

    __DATE__宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。

    源代码翻译到目标代码的时间作为串包含在__TIME__中。串形式为时:分:秒。

    如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何其它数,则实现是非标准的。编译C++程序时,编译器自动定义了一个预处理名字__cplusplus,而编译标准C时,自动定义名字__STDC__。

     

    注意:宏名的书写由标识符与两边各二条下划线构成。

     

    (部分内容出自:http://www.bc-cn.net/Article/kfyy/cyy/jc/200511/919.html) 8、

     

     

    C、C++宏体中出现的#,#@,##

     

    宏体中,#的功能是将其后面的宏参数进行字符串化操作(Stringfication),简单说就是在对它所引用的宏变量通过替换后在其左右各加上一个双引号。

     

    ##被称为连接符(concatenator),用来将两个Token连接为一个Token。注意这里连接的对象是Token就行,而不一定是宏的变量。比如你要做一个菜单项命令名和函数指针组成的结构体

     
    上一篇文章中,我演示了几个常用的宏定义和预处理指令,但可以说这些都是相当常规的技巧。下面要介绍的宏定义与预处理指令的用法也是ATL,MFC以及LINUX中使用得比较多的非常重要的技巧。

      ## 连接符与# 符

      ## 连接符号由两个井号组成,其功能是在带参数的宏定义中将两个子串(token)联接起来,从而形成一个新的子串。但它不可以是第一个或者最后一个子串。所谓的子串(token)就是指编译器能够识别的最小语法单元。具体的定义在编译原理里有详尽的解释,但不知道也无所谓。同时值得注意的是#符是把传递过来的参数当成字符串进行替代。下面来看看它们是怎样工作的。这是MSDN上的一个例子。

      假设程序中已经定义了这样一个带参数的宏:

    #define paster( n ) printf( "token" #n " = %d", token##n )

      同时又定义了一个整形变量:

    int token9 = 9;

      现在在主程序中以下面的方式调用这个宏:

    paster( 9 );

      那么在编译时,上面的这句话被扩展为:

    printf( "token" "9" " = %d", token9 );

      注意到在这个例子中,paster(9);中的这个”9”被原封不动的当成了一个字符串,与”token”连接在了一起,从而成为了token9。而#n也被”9”所替代。

      可想而知,上面程序运行的结果就是在屏幕上打印出token9=9

      在ATL的编程中,我们查看它的源代码就会经常看见这样的一段:

    #define IMPLEMENTS_INTERFACE(Itf) \
    {&IID_##Itf, ENTRY_IS_OFFSET,BASE_OFFSET(_ITCls, Itf) },

      我们经常不假思索的这样使用它:

    ……
    IMPLEMENTS_INTERFACE(ICat)
    ……

      实际上IID_ICat 已经在别的地方由ATL向导定义了。当没有向导的时候,你只要遵循把IID_加在你的接口名前面来定义GUID的规则就也可以使用这个宏。在实际的开发过程中可能很少用到这种技巧,但是ATL使用得如此广泛,而其中又出现了不少这样的源代码,所以明白它是怎么一回事也是相当重要的。我的一个朋友就是因为不知道IMPLEMENTS_INTERFACE宏是怎么定义的,而又不小心改动了IID_ICat的定义而忙活了一整天。

      Linux的怪圈

      在刚开始阅读Linux的时候有一个小小的宏让我百思不得其解:

    #define wait_event(wq,condition) \
    do{ \
    if(condition) \
    break; \
    __wait_event(wq,condition); \
    }while(0)

      这是一个奇怪的循环,它根本就只会运行一次,为什么不去掉外面的do{..}while结构呢?我曾一度在心里把它叫做“怪圈”。原来这也是非常巧妙的技巧。在工程中可能经常会引起麻烦,而上面的定义能够保证这些麻烦不会出现。下面是解释:

      假设有这样一个宏定义

    #define macro(condition) \

    if(condition) dosomething();

      现在在程序中这样使用这个宏:

    if(temp)
    macro(i);
    else
    doanotherthing();

      一切看起来很正常,但是仔细想想。这个宏会展开成:

    if(temp)
    if(condition) dosomething();
    else
    doanotherthing();

      这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。

      为了避免这个错误,我们使用do{….}while(0) 把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。

      几个小小的警告

      正如微软声称的一样,宏定义与预编译器指令是强大的,但是它又使得程序难以调试。所以在定义宏的时候不要节省你的字符串,一定要力争完整的描述这个宏的功能。同时在定义宏的时候如有必要(比方使用了if语句)就要使用do{…}while(0)将它封闭起来。在宏定义的时候一定要注意各个宏之间的相互依赖关系,尽量避免这种依赖关系的存在。下面就有这样一个例子。

      设有一个静态数组组成的整型队列,在定义中使用了这样的方法: int array[]={5, 6, 7, 8};

      我们还需要在程序中遍历这个数组。通常的做法是使用一个宏定义

    #define ELE_NUM 4
    …………………………..
    ……………………………..

    for(int I=0;I<ELE_NUM;I++)
    {
    cout<<array[I];
    }

      由于某种偶然的原因,我们删除了定义中的一个元素,使它变成:

    array[]={5,6,7}

      而却忘了修改ELE_NUM的值。那么在上面的代码中马上就会发生访问异常,程序崩溃。然后是彻夜不眠的调试,最后发现问题出在这个宏定义上。解决这个问题的方法是不使用

    array[]={….}这样的定义,而显式的申明数组的大小:

    array[ELE_NUM]={….}

      这样在改动数组定义的时候,我们就不会不记得去改宏定义了。总之,就是在使用宏定义的时候能够用宏定义的地方统统都用上。

      我发现的另一个有趣的现象是这样的:

      假设现在有一个课程管理系统,学生的人数用宏定义为:

    #define STU_NUM 50

      而老师的人数恰好也是50人,于是很多人把所有涉及到老师人数的地方通通用上STU_NUM这个宏。另一个学期过去,学生中的一个被开除了,系统需要改变。怎么办呢?简单的使用#define STU_NUM 49 么?如果是这样,一个老师也就被开除了,我们不得不手工在程序中去找那些STU_NUM宏然后判断它是否是表示学生的数目,如果是,就把它改成49。天哪,这个宏定义制造的麻烦比使用它带来的方便还多。正确的方法应该是为老师的数目另外定义一个宏:

    #define TEA_NUM 50

      当学生的数目改变以后只要把STU_NUM 定义为49就完成了系统的更改。所以,当程序中的两个量之间没有必然联系的时候一定不要用其中的一个宏去替代另一个,那只会让你的程序根本无法改动。

      最后,建议C/C++语言的初学者尽可能多的在你的程序中使用宏定义和预编译指令。多看看MFC,ATL或者LINUX的源代码,你会发现C语言强大的原因所在。
     
     
     
     

    VC 编译连接(#if #ifdef #ifndef #else #endif defened)  

    2009-11-23 14:49:10|  分类:技术 |  标签:|字号 订阅

     

    一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。

    条件编译的一般形式:

    (1)                         (2)                               (3)

    #ifdef 标识符          #ifndef 标识符              #if 表达式

    程序段1                 程序段1                       程序段1

     #else                     #else                            #else

       程序段2                程序段2                       程序段2

     #endif                    #endif                         #endif

    形式(1),它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序1:开关作用,条件编程
    #ifdef A
    程序段
    #endif
    2:避免重复定义,重复包舍
    #ifndef A
    #defined A
    程序段
    #endif段2。例如,我们有一个数据类型,在Windows平台中,应该使用long类型表示,而在其他平台应该使用float表示,这样往往需要对源程序作必要的修改,这就降低了程序的通用性。可以用以下的条件编译:

        #ifdef WINDOWS

        #define MYTYPE long

        #else

        #define MYTYPE float

        #endif

         如果在windows平台上编译程序,则在程序开始时定义windows。#define WINDOWs则在程序中MYTYPE long就会被定义,#define MYTYPE long。如果 #define WINDOWS 0 则在程序中MYTYPE floa就会被定义,#define MYTYPE float。

        形式(2),只是第一行与第一种形式不同:将“ifdef”改为“ifndef”。它的作用是:若标识符未被定义则编译程序段1,否则编译程序段2。这种形式与第一种形式的作用相反。

       

        形式(3),当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。可以事先给定一定条件,使程序在不同的条件下执行不同的功能。例如,咱们在VC的头文件中程序中经常看到这样的例子:

    #if !defined(AFX_LOAD_H__98934971_8314_4B1A_AFC6_7033D48189B1__INCLUDED_)

    #define AFX_LOAD_H__98934971_8314_4B1A_AFC6_7033D48189B1__INCLUDED_

    #if _MSC_VER > 1000

    #pragma once

    #endif // _MSC_VER > 1000

        如果!defined后面的没有定义,则执行下面语句进行定义。

    常用方法:

    1:开关作用,条件编程
                #ifdef A
                程序段
                #endif
            2:避免重复定义,重复包舍
               #ifndef A
               #defined A
               程序段
               #endif

    #ifdef,指示符,宏定义

    使用#ifdef指示符,我们可以区隔一些与特定头文件、程序库和其他文件版本有关的代码。代码举例:新建define.cpp文件
     
      #include "iostream.h"
     
      int main()
     
      {
     
      #ifdef DEBUG
     
      cout<< "Beginning execution of main()";
     
      #endif
     
      return 0;
     
      }
     
      运行结果为:
     
      Press any key to continue
     
      改写代码如下:
     
      #include "iostream.h"
     
      #define DEBUG
     
      int main()
     
      {
     
      #ifdef DEBUG
     
      cout<< "Beginning execution of main()";
     
      #endif
     
      return 0;
     
      }
     
      运行结果为:
     
      Beginning execution of main()
     
      Press any key to continue
     
      更一般的情况是,#define语句是包含在一个特定的头文件中。比如,新建头文件head.h,在文件中加入代码:
     
      #ifdef DEBUG
     
      #define DEBUG
     
      #endif
     
      而在define.cpp源文件中,代码修改如下:
     
      #include "iostream.h"
     
      #include "head.h"
     
      int main()
     
      {
     
      #ifdef DEBUG
     
      cout<< "Beginning execution of main()";
     
      #endif
     
      return 0;
     
      }
     
      运行结果如下:
     
      Beginning execution of main()
     
      Press any key to continue
     
      结论:
     
      通过使用#ifdef指示符,我们可以区隔一些与特定头文件、程序库和其他文件版本有关的代码。
     
      C语言之详解#ifdef等宏
     
      这几个宏是为了进行条件编译。一般情况下,源程序中所有的行都参加编译。但是有时希望对其中一部分内容只在满足一定条件才进行编译,也就是对一部分内容指定编译的条件,这就是“条件编译”。有时,希望当满足某条件时对一组语句进行编译,而当条件不满足时则编译另一组语句。
     
      条件编译命令最常见的形式为:
     
      #ifdef 标识符
     
      程序段1
     
      #else
     
      程序段2
     
      #endif
     
      它的作用是:当标识符已经被定义过(一般是用#define命令定义),则对程序段1进行编译,否则编译程序段2。
     
      其中#else部分也可以没有,即:
     
      #ifdef
     
      程序段1
     
      #endif
     
      这里的“程序段”可以是语句组,也可以是命令行。这种条件编译可以提高C源程序的通用性。如果一个C源程序在不同计算机系统上系统上运行,而不同的计算机又有一定的差异。例如,我们有一个数据类型,在Windows平台中,应该使用long类型表示,而在其他平台应该使用float表示,这样往往需要对源程序作必要的修改,这就降低了程序的通用性。可以用以下的条件编译:
     
      #ifdef WINDOWS
     
      #define MYTYPE long
     
      #else
     
      #define MYTYPE float
     
      #endif
     
      如果在Windows上编译程序,则可以在程序的开始加上
     
      #define WINDOWS
     
      这样则编译下面的命令行:
     
      #define MYTYPE long
     
      如果在这组条件编译命令之前曾出现以下命令行:
     
      #define WINDOWS 0
     
      则预编译后程序中的MYTYPE都用float代替。这样,源程序可以不必作任何修改就可以用于不同类型的计算机系统。当然以上介绍的只是一种简单的情况,可以根据此思路设计出其它的条件编译。
     
      例如,在调试程序时,常常希望输出一些所需的信息,而在调试完成后不再输出这些信息。可以在源程序中插入以下的条件编译段:
     
      #ifdef DEBUG
     
      print ("device_open(%p)\n", file);
     
      #endif
     
      如果在它的前面有以下命令行:
     
      #define DEBUG
     
      则在程序运行时输出file指针的值,以便调试分析。调试完成后只需将这个define命令行删除即可。有人可能觉得不用条件编译也可达此目的,即在调试时加一批printf语句,调试后一一将printf语句删除去。的确,这是可以的。但是,当调试时加的printf语句比较多时,修改的工作量是很大的。用条件编译,则不必一一删改printf语句,只需删除前面的一条“#define DEBUG”命令即可,这时所有的用DEBUG作标识符的条件编译段都使其中的printf语句不起作用,即起统一控制的作用,如同一个“开关”一样。
     
      有时也采用下面的形式:
     
      #ifndef 标识符
     
      程序段1
     
      #else
     
      程序段2
     
      #endif
     
      只是第一行与第一种形式不同:将“ifdef”改为“ifndef”。它的作用是:若标识符未被定义则编译程序段1,否则编译程序段2。这种形式与第一种形式的作用相反。
     
      以上两种形式用法差不多,根据需要任选一种,视方便而定。
     
      还有一种形式,就是#if后面的是一个表达式,而不是一个简单的标识符:
     
      #if 表达式
     
      程序段1
     
      #else
     
      程序段2
     
      #endif
     
      它的作用是:当指定的表达式值为真(非零)时就编译程序段1,否则编译程序段2。可以事先给定一定条件,使程序在不同的条件下执行不同的功能。
     
      例如:输入一行字母字符,根据需要设置条件编译,使之能将字母全改为大写输出,或全改为小写字母输出。
     
      #define LETTER 1
     
      main()
     
      {
     
      char str[20]="C Language",c;
     
      int i=0;
     
      while((c=str[i])!='\0'){
     
      i++;
     
      #if LETTER
     
      if(c>='a'&&c<='z') c=c-32;
     
      #else
     
      if(c>='A'&&c<='Z') c=c+32;
     
      #endif
     
      printf("%c",c);
     
      }
     
      }
     
      运行结果为:C LANGUAGE
     
      现在先定义LETTER为1,这样在预处理条件编译命令时,由于LETTER为真(非零),则对第一个if语句进行编译,运行时使小写字母变大写。如果将程序第一行改为:
     
      #define LETTER 0
     
      则在预处理时,对第二个if语句进行编译处理,使大写字母变成小写字母(大写字母与相应的小写字母的ASCII代码差32)。此时运行情况为:
     
      c language
     
      有人会问:不用条件编译命令而直接用if语句也能达到要求,用条件编译命令有什么好处呢?的确,此问题完全可以不用条件编译处理,但那样做目标程序长(因为所有语句都编译),而采用条件编译,可以减少被编译的语句,从而减少目标的长度。当条件编译段比较多时,目标程序长度可以大大减少。
     
    详细讲解   Visual   C++   中的预处理器的编译指示指令   #pragma   pack([n])

      该指令指定结构和联合成员的紧凑对齐。而一个完整的转换单元的结构和联合的紧凑对齐由   /Zp   选项设置。紧凑对齐用   pack   编译指示在数据说明层设置。该编译指示在其出现后的第一个结构或联合说明处生效。该编译指示对定义无效。当你使用   #pragma   pack(n)   时,这里   n   为   1、2、4、8   或   16。第一个结构成员之后的每个结构成员都被存储在更小的成员类型或   n   字节界限内。如果你使用无参量的   #pragma   pack,结构成员被紧凑为以   /Zp   指定的值。该缺省   /Zp   紧凑值为   /Zp8。 

      编译器也支持以下增强型语法: 
    #pragma   pack([[{push|pop},][标识符,]][n]) 
      若不同的组件使用   pack   编译指示指定不同的紧凑对齐,这个语法允许你把程序组件组合为一个单独的转换单元。 
      带   push   参量的   pack   编译指示的每次出现将当前的紧凑对齐存储到一个内部编译器堆栈中。编译指示的参量表从左到右读取。如果你使用   push,则当前紧凑值被存储起来;如果你给出一个   n   的值,该值将成为新的紧凑值。若你指定一个标识符,即你选定一个名称,则该标识符将和这个新的的紧凑值联系起来。
      带一个   pop   参量的   pack   编译指示的每次出现都会检索内部编译器堆栈顶的值,并且使该值为新的紧凑对齐值。如果你使用   pop   参量且内部编译器堆栈是空的,则紧凑值为命令行给定的值,并且将产生一个警告信息。若你使用   pop   且指定一个   n   的值,该值将成为新的紧凑值。
      若你使用   pop   且指定一个标识符,所有存储在堆栈中的值将从栈中删除,直到找到一个匹配的标识符,这个与标识符相关的紧凑值也从栈中移出,并且这个仅在标识符入栈之前存在的紧凑值成为新的紧凑值。如果未找到匹配的标识符,将使用命令行设置的紧凑值,并且将产生一个一级警告。缺省紧凑对齐为8。

      pack   编译指示的能让你编写头文件,确保在遇到该头文件的前后的紧凑值是一样的。 
    /* 
    File   name:   include1.h 
    */ 
    #pragma   pack(push,enter_include1) 
    /*   你的包括文件代码...   */ 
    #pragma   pack(pop,   enter_include1) 
    /*   include1.h结束   */ 
      在上面的例子中,当前紧凑值与标识符   enter_include1   联系起来,并被压入头文件的项中。头文件末尾的   pack   编译指示删除所有可能出现在头文件中的干预紧凑值,并且删除与   enter_include1   相关的紧凑值。因此确保该头文件的前后的紧凑值是相同的。

      这种功能也允许你使用代码,例如头文件,它可以使用   pack   编译指示设置不同于在你的代码中设置的紧凑值的紧凑对齐: 
    #pragma   pack(push,before_include1) 
    #include   "include1.h " 
    #pragma   pack(   pop,before_include1) 
      在上面的例子中,对于出现在   include.h   中的紧凑值的任何变化,你的代码是受到保护的。
     
     
     
     
     
    • pragma是прагматика和практика的词根,意为行为、事情。  
         引申义有附注、标记,在这里就用来作为编译指示。
    • 希腊文,pragma,原意是“行动”、“实践”的意思.  
        
    • 这是以前我给人解答的  
         #pragma    
         是编译器的预处理指令,   各个编译器的用法不一样  
         #pragma   warning   (disable:4068)  
         是告诉编译器4068编号的警告就不要告诉我了  
         这样你编译室就不用看到一大堆警告  
          
         #pragma   hdrstop   [(   "filename"   )]      
         这个是用于指定头文件预编译的的文件名  
         用于缓存头文件的编译结果,   可以提高编译速度,    
         不用每次编译都要去编译一些没改动过的头文件,   很有用的;  
         具体VC中有stdafx.h   stdafx.cpp的一个重要作用就是这个  
          
         还有很多  
         #pragma   comment(   lib,   "emapi"   )  
         比如这个就是告诉编译器我要用到emapi.lib这个库文件  
         #pragma   pack  
         这是对齐字节设置  
          
        
    • #pragma    
         预处理指令,  
         标准C++未定义其用途,  
         不同的编译器有不同的意思。

      文章来源: http://blog.csdn.net/jx_kingwei/archive/2005/04/28/367312.aspx

          在所有的预处理指令中,#pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。
      #pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。
      依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。 
          其格式一般为: #pragma para 
          其中para为参数,下面来看一些常用的参数。 

      (1)message 参数

          message参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输出相应的信息,
      这对于源代码信息的控制是非常重要的。其使用方法为: 
          #pragma message("消息文本") 
          当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 
          当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,
      此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏,
      可以用下面的方法:
          #ifdef _X86 
          #pragma message("_X86 macro activated!") 
          #endif 
          我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示"_86 macro activated!"。
      我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。 
            

      (2)另一个使用得比较多的pragma参数是code_seg

          格式如: 
          #pragma code_seg( ["section-name" [, "section-class"] ] ) 
          它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。 

      (3)#pragma once (比较常用)

          只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,
      但是考虑到兼容性并没有太多的使用它。

             
      (4)#pragma hdrstop

          表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,
      但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。    
          有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。
      你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init),
      BCB就会根据优先级的大小先后编译。   

             
      (5)#pragma resource "*.dfm"

          表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体 
      外观的定义。   

               
      (6)#pragma warning( disable: 4507 34; once: 4385; error: 164 )

          等价于: 
          #pragma warning( disable: 4507 34 )    // 不显示4507和34号警告信息 
          #pragma warning( once: 4385 )          // 4385号警告信息仅报告一次 
          #pragma warning( error: 164 )          // 把164号警告信息作为一个错误。

          同时这个pragma warning 也支持如下格式: 
          #pragma warning( push [, n ] ) 
          #pragma warning( pop ) 
          这里n代表一个警告等级(1---4)。 
          #pragma warning( push )保存所有警告信息的现有的警告状态。 
          #pragma warning( push, n )保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。    
          #pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。例如: 
          #pragma warning( push ) 
          #pragma warning( disable: 4705 ) 
          #pragma warning( disable: 4706 ) 
          #pragma warning( disable: 4707 ) 
          //....... 
          #pragma warning( pop )    
          在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。


      (7)#pragma comment(...)

          该指令将一个注释记录放入一个对象文件或可执行文件中。 
      常用的lib关键字,可以帮我们连入一个库文件。如:
          #pragma comment(lib, "comctl32.lib")
          #pragma comment(lib, "vfw32.lib")
          #pragma comment(lib, "wsock32.lib")

         
         
      每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。

      例如,对循环优化功能: 
      #pragma loop_opt(on)     // 激活 
      #pragma loop_opt(off)    // 终止

      有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,
      如“Parameter xxx is never used in function xxx”,可以这样: 
      #pragma warn —100         // Turn off the warning message for warning #100 
      int insert_record(REC *r) 
      { /* function body */ } 
      #pragma warn +100          // Turn the warning message for warning #100 back on 
      函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。

      每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。


      补充 —— #pragma pack 与 内存对齐问题


          许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k
      (通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。

          Win32平台下的微软C编译器(cl.exe for 80x86)在默认情况下采用如下的对齐规则: 
          任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),
      就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。

          Linux下的GCC奉行的是另外一套规则(在资料中查得,并未验证,如错误请指正):
          任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型
      (比如long,double)都以4为对齐模数。

          ANSI C规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部的填充区大小之和。
      填充区就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。那么结构体本身有什么对齐要求吗?
      有的,ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。


      如何使用c/c++中的对齐选项

          vc6中的编译选项有 /Zp[1|2|4|8|16] ,/Zp1表示以1字节边界对齐,相应的,/Zpn表示以n字节边界对齐。
      n字节边界对齐的意思是说,一个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上,取它们中的最小值。
      也就是:
          min ( sizeof ( member ), n)

          实际上,1字节边界对齐也就表示了结构成员之间没有空洞。
          /Zpn选项是应用于整个工程的,影响所有的参与编译的结构。
          要使用这个选项,可以在vc6中打开工程属性页,c/c++页,选择Code Generation分类,在Struct member alignment可以选择。

          要专门针对某些结构定义使用对齐选项,可以使用#pragma pack编译指令:


      (1) #pragma pack( [ n ] )

          该指令指定结构和联合成员的紧凑对齐。而一个完整的转换单元的结构和联合的紧凑对齐由/Zp 选项设置。
      紧凑对齐用pack编译指示在数据说明层设置。该编译指示在其出现后的第一个结构或联合说明处生效。
      该编译指示对定义无效。
          当你使用#pragma pack ( n ) 时, 这里n 为1、2、4、8 或16。
          第一个结构成员之后的每个结构成员都被存储在更小的成员类型或n 字节界限内。
      如果你使用无参量的#pragma pack, 结构成员被紧凑为以/Zp 指定的值。该缺省/Zp 紧凑值为/Zp8 。


      (2) 编译器也支持以下增强型语法:
          #pragma pack( [ [ { push | pop } , ] [ identifier, ] ] [ n] )

          若不同的组件使用pack编译指示指定不同的紧凑对齐, 这个语法允许你把程序组件组合为一个单独的转换单元。
      带push参量的pack编译指示的每次出现将当前的紧凑对齐存储到一个内部编译器堆栈中。
          编译指示的参量表从左到右读取。如果你使用push, 则当前紧凑值被存储起来; 
      如果你给出一个n 的值, 该值将成为新的紧凑值。若你指定一个标识符, 即你选定一个名称, 
      则该标识符将和这个新的的紧凑值联系起来。

          带一个pop参量的pack编译指示的每次出现都会检索内部编译器堆栈顶的值,并且使该值为新的紧凑对齐值。
      如果你使用pop参量且内部编译器堆栈是空的,则紧凑值为命令行给定的值, 并且将产生一个警告信息。
      若你使用pop且指定一个n的值, 该值将成为新的紧凑值。若你使用p o p 且指定一个标识符, 
      所有存储在堆栈中的值将从栈中删除, 直到找到一个匹配的标识符, 这个与标识符相关的紧凑值也从栈中移出, 
      并且这个仅在标识符入栈之前存在的紧凑值成为新的紧凑值。如果未找到匹配的标识符, 
      将使用命令行设置的紧凑值, 并且将产生一个一级警告。缺省紧凑对齐为8 。

         pack编译指示的新的增强功能让你编写头文件, 确保在遇到该头文件的前后的
      紧凑值是一样的。


      (3) 栈内存对齐

          在vc6中栈的对齐方式不受结构成员对齐选项的影响。它总是保持对齐,而且对齐在4字节边界上。

      文章来源: http://blog.csdn.net/jx_kingwei/archive/2005/04/28/367312.aspx

          在所有的预处理指令中,#pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。
      #pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。
      依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。 
          其格式一般为: #pragma para 
          其中para为参数,下面来看一些常用的参数。 

      (1)message 参数

          message参数是我最喜欢的一个参数,它能够在编译信息输出窗口中输出相应的信息,
      这对于源代码信息的控制是非常重要的。其使用方法为: 
          #pragma message("消息文本") 
          当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。 
          当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,
      此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏,
      可以用下面的方法:
          #ifdef _X86 
          #pragma message("_X86 macro activated!") 
          #endif 
          我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示"_86 macro activated!"。
      我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了。 
            

      (2)另一个使用得比较多的pragma参数是code_seg

          格式如: 
          #pragma code_seg( ["section-name" [, "section-class"] ] ) 
          它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。 

      (3)#pragma once (比较常用)

          只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,
      但是考虑到兼容性并没有太多的使用它。

             
      (4)#pragma hdrstop

          表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,
      但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。    
          有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。
      你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init),
      BCB就会根据优先级的大小先后编译。   

             
      (5)#pragma resource "*.dfm"

          表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体 
      外观的定义。   

               
      (6)#pragma warning( disable: 4507 34; once: 4385; error: 164 )

          等价于: 
          #pragma warning( disable: 4507 34 )    // 不显示4507和34号警告信息 
          #pragma warning( once: 4385 )          // 4385号警告信息仅报告一次 
          #pragma warning( error: 164 )          // 把164号警告信息作为一个错误。

          同时这个pragma warning 也支持如下格式: 
          #pragma warning( push [, n ] ) 
          #pragma warning( pop ) 
          这里n代表一个警告等级(1---4)。 
          #pragma warning( push )保存所有警告信息的现有的警告状态。 
          #pragma warning( push, n )保存所有警告信息的现有的警告状态,并且把全局警告等级设定为n。    
          #pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的一切改动取消。例如: 
          #pragma warning( push ) 
          #pragma warning( disable: 4705 ) 
          #pragma warning( disable: 4706 ) 
          #pragma warning( disable: 4707 ) 
          //....... 
          #pragma warning( pop )    
          在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。


      (7)#pragma comment(...)

          该指令将一个注释记录放入一个对象文件或可执行文件中。 
      常用的lib关键字,可以帮我们连入一个库文件。如:
          #pragma comment(lib, "comctl32.lib")
          #pragma comment(lib, "vfw32.lib")
          #pragma comment(lib, "wsock32.lib")

         
         
      每个编译程序可以用#pragma指令激活或终止该编译程序支持的一些编译功能。

      例如,对循环优化功能: 
      #pragma loop_opt(on)     // 激活 
      #pragma loop_opt(off)    // 终止

      有时,程序中会有些函数会使编译器发出你熟知而想忽略的警告,
      如“Parameter xxx is never used in function xxx”,可以这样: 
      #pragma warn —100         // Turn off the warning message for warning #100 
      int insert_record(REC *r) 
      { /* function body */ } 
      #pragma warn +100          // Turn the warning message for warning #100 back on 
      函数会产生一条有唯一特征码100的警告信息,如此可暂时终止该警告。

      每个编译器对#pragma的实现不同,在一个编译器中有效在别的编译器中几乎无效。可从编译器的文档中查看。


      补充 —— #pragma pack 与 内存对齐问题


          许多实际的计算机系统对基本类型数据在内存中存放的位置有限制,它们会要求这些数据的首地址的值是某个数k
      (通常它为4或8)的倍数,这就是所谓的内存对齐,而这个k则被称为该数据类型的对齐模数(alignment modulus)。

          Win32平台下的微软C编译器(cl.exe for 80x86)在默认情况下采用如下的对齐规则: 
          任何基本数据类型T的对齐模数就是T的大小,即sizeof(T)。比如对于double类型(8字节),
      就要求该类型数据的地址总是8的倍数,而char类型数据(1字节)则可以从任何一个地址开始。

          Linux下的GCC奉行的是另外一套规则(在资料中查得,并未验证,如错误请指正):
          任何2字节大小(包括单字节吗?)的数据类型(比如short)的对齐模数是2,而其它所有超过2字节的数据类型
      (比如long,double)都以4为对齐模数。

          ANSI C规定一种结构类型的大小是它所有字段的大小以及字段之间或字段尾部的填充区大小之和。
      填充区就是为了使结构体字段满足内存对齐要求而额外分配给结构体的空间。那么结构体本身有什么对齐要求吗?
      有的,ANSI C标准规定结构体类型的对齐要求不能比它所有字段中要求最严格的那个宽松,可以更严格。


      如何使用c/c++中的对齐选项

          vc6中的编译选项有 /Zp[1|2|4|8|16] ,/Zp1表示以1字节边界对齐,相应的,/Zpn表示以n字节边界对齐。
      n字节边界对齐的意思是说,一个成员的地址必须安排在成员的尺寸的整数倍地址上或者是n的整数倍地址上,取它们中的最小值。
      也就是:
          min ( sizeof ( member ), n)

          实际上,1字节边界对齐也就表示了结构成员之间没有空洞。
          /Zpn选项是应用于整个工程的,影响所有的参与编译的结构。
          要使用这个选项,可以在vc6中打开工程属性页,c/c++页,选择Code Generation分类,在Struct member alignment可以选择。

          要专门针对某些结构定义使用对齐选项,可以使用#pragma pack编译指令:


      (1) #pragma pack( [ n ] )

          该指令指定结构和联合成员的紧凑对齐。而一个完整的转换单元的结构和联合的紧凑对齐由/Zp 选项设置。
      紧凑对齐用pack编译指示在数据说明层设置。该编译指示在其出现后的第一个结构或联合说明处生效。
      该编译指示对定义无效。
          当你使用#pragma pack ( n ) 时, 这里n 为1、2、4、8 或16。
          第一个结构成员之后的每个结构成员都被存储在更小的成员类型或n 字节界限内。
      如果你使用无参量的#pragma pack, 结构成员被紧凑为以/Zp 指定的值。该缺省/Zp 紧凑值为/Zp8 。


      (2) 编译器也支持以下增强型语法:
          #pragma pack( [ [ { push | pop } , ] [ identifier, ] ] [ n] )

          若不同的组件使用pack编译指示指定不同的紧凑对齐, 这个语法允许你把程序组件组合为一个单独的转换单元。
      带push参量的pack编译指示的每次出现将当前的紧凑对齐存储到一个内部编译器堆栈中。
          编译指示的参量表从左到右读取。如果你使用push, 则当前紧凑值被存储起来; 
      如果你给出一个n 的值, 该值将成为新的紧凑值。若你指定一个标识符, 即你选定一个名称, 
      则该标识符将和这个新的的紧凑值联系起来。

          带一个pop参量的pack编译指示的每次出现都会检索内部编译器堆栈顶的值,并且使该值为新的紧凑对齐值。
      如果你使用pop参量且内部编译器堆栈是空的,则紧凑值为命令行给定的值, 并且将产生一个警告信息。
      若你使用pop且指定一个n的值, 该值将成为新的紧凑值。若你使用p o p 且指定一个标识符, 
      所有存储在堆栈中的值将从栈中删除, 直到找到一个匹配的标识符, 这个与标识符相关的紧凑值也从栈中移出, 
      并且这个仅在标识符入栈之前存在的紧凑值成为新的紧凑值。如果未找到匹配的标识符, 
      将使用命令行设置的紧凑值, 并且将产生一个一级警告。缺省紧凑对齐为8 。

         pack编译指示的新的增强功能让你编写头文件, 确保在遇到该头文件的前后的
      紧凑值是一样的。


      (3) 栈内存对齐

          在vc6中栈的对齐方式不受结构成员对齐选项的影响。它总是保持对齐,而且对齐在4字节边界上。


  • 相关阅读:
    UVA 129 Krypton Factor (困难的串)(回溯法)
    UVA 524 Prime Ring Problem(素数环)(回溯法)
    【POJ 2559】Largest Rectangle in a Histogram【栈】
    【POJ 2559】Largest Rectangle in a Histogram【栈】
    向右看齐【栈】
    向右看齐【栈】
    向右看齐【栈】
    【模板】最近公共祖先【LCA】
    【模板】最近公共祖先【LCA】
    【模板】最近公共祖先【LCA】
  • 原文地址:https://www.cnblogs.com/netact/p/2339639.html
Copyright © 2020-2023  润新知