为什么会起这么一个题目?今天在写网络原理的实验,BGP状态机变来变去弄得我晕头转向,好不容易理清了头绪,写完了代码,却总是卡在一个位置,运行之后总是报BGP消息头的错误。我看文档看了一遍又一遍,检查了一次又一次,没有发现什么问题啊?于是我决定把能输出的东西全都输出来,最终发现消息头的大小莫名其妙地多出了几字节。原来是编译器搞的鬼,问题就在这个编译选项上。

  • 什么是对齐,以及为什么要对齐?
    现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。 对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据。显然在读取效率上下降很多。这也是空间和时间的博弈。

  • 对齐的实现
    通常,我们写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法。 但是,正因为我们一般不需要关心这个问题,所以因为编译器对数据存放做了对齐,而我们不了解的话,常常会对一些问题感到迷惑。最常见的就是struct数据结构的sizeof结果,出乎意料。为此,我们需要对对齐算法所了解。

  • 作用:
    指定结构体、联合以及类成员的packing alignment;

  • 语法:
    #pragma pack( [show] | [push | pop] [, identifier], n )

  • 说明:
    pack提供数据声明级别的控制,对定义不起作用;
    调用pack时不指定参数,n将被设成默认值;
    一旦改变数据类型的alignment,直接效果就是占用memory的减少,但是performance会下降;

    • 语法具体分析:

      • show:可选参数;显示当前packing aligment的字节数,以warning message的形式被显示;

      • push:可选参数;将当前指定的packing alignment数值进行压栈操作,这里的栈是the internal compiler stack,同时设置当前的packing alignment为n;如果n没有指定,则将当前的packing alignment数值压栈;

      • pop:可选参数;从internal compiler stack中删除最顶端的record;如果没有指定n,则当前栈顶record即为新的packing alignment数值;如果指定了n,则n将成为新的packing aligment数值;如果指定了identifier,则internal compiler stack中的record都将被pop直到identifier被找到,然后pop出identitier,同时设置packing alignment数值为当前栈顶的record;如果指定的identifier并不存在于internal compiler stack,则pop操作被忽略;

      • identifier:可选参数;当同push一起使用时,赋予当前被压入栈中的record一个名称;当同pop一起使用时,从internal compiler stack中pop出所有的record直到identifier被pop出,如果identifier没有被找到,则忽略pop操作;
        +n:可选参数;指定packing的数值,以字节为单位;缺省数值是8,合法的数值分别是1、2、4、8、16。


    #include <iostream>
    using namespace std;
    
    struct node
    {
        char a;
        int b;
    };
    
    int main(int argc, char const* argv[])
    {
        cout<<sizeof(char)<<endl;
        cout<<sizeof(int)<<endl;
        cout<<sizeof(node)<<endl;
        return 0;
    }

输出结果为

1
4
8

如果添加相应的预编译选项


    #include <iostream>
    using namespace std;
    #pragma pack(1)
    struct node
    {
        char a;
        int b;
    };
    
    int main(int argc, char const* argv[])
    {
        cout<<sizeof(char)<<endl;
        cout<<sizeof(int)<<endl;
        cout<<sizeof(node)<<endl;
        return 0;
    }

输出结果为

1
4
5

cppowboy
20 声望1 粉丝

引用和评论

0 条评论