数据的对齐

数据的对齐

cpu在读写内存的时候,如果所给的地址是机器字长的整数倍,则操作效率会比较高,这可以称之为地址的对齐。

在一般的32bits机器上,地址对齐的界线默认是4的整数倍。

struct my_struct{
char ch1; //1字节
char ch2; //1字节
int n; //4字节
char ch3; 1字节
};//整个结构体在32bits系统占12个字节
对结构体类型采用sizeof操作符,得到的是结构体占用的内存字节数,包括所有的空闲字节,显然,这个值并不一定等于它的所有成员的大小之和。

pragma pack(1) //将地址对齐界线改为1的整数

struct my_struct{
char ch1; //1字节
char ch2; //1字节
int n; //4字节
char ch3; 1字节
};//整个结构体占7个字节
#pragma pack() //将地址对齐界线改回原来的值

对齐规则参考

该博文也给出了#pragma pack(show) 和#pragma pack(num)的说明以及在VS中的设置;默认num=8.

这内存宝贵的应用场合,比如嵌入式系统,一般要考虑对齐,一般原则,可以是从小到大排,把多个小字节变量放一起,并且跟对齐的大小对齐。

一般来说,

  • 结构体的对齐规则是先按数据类型自身进行对齐,这里需要比较#pragma pack(num)中num的数值和数据类型的大小,取小的作为基准;
  • 然后再按整个结构体进行对齐,对齐值必须是2的幂,比如1,2, 4, 8, 16。准确来讲,结构的总大小是其成员中最大类型的sizeof(该类型)整数倍。
  • 如果一个类型按n字节对齐,那么该类型的变量起始地址必须是n的倍数。比如int按四字节对齐,那么int类型的变量起始地址一定是4的倍数,比如0x0012ff60,0x0012ff48等。

查看系统是多少位的

file /sbin/init

/sbin/init: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=7a4c688d009fc1f06ffc692f5f42ab09e68582b2, stripped

file /bin/ls

/bin/ls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=3bf9ca54ea5e261943509c2e47bc814bb1248921, stripped

uname -a

Linux ubuntu 3.13.0-79-generic #123-Ubuntu SMP Fri Feb 19 14:27:58 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

cat /proc/version

Linux version 3.13.0-79-generic (buildd@lcy01-24) (gcc version 4.8.2 (Ubuntu 4.8.2-19ubuntu1) ) #123-Ubuntu SMP Fri Feb 19 14:27:58 UTC 2016

cat /etc/issue

Ubuntu 15.04 \n \l

测试数据所占字节

因为在数据对齐的时候需要考虑是在什么系统下,各数据类型所占字节大小,纠正网上一些有问题的解答,比如long是只占4bits的。

cout << "char: " << sizeof(char) << endl;
cout << "short: " << sizeof(short) << endl;
cout << "int: " << sizeof(int) << endl;
cout << "long: " << sizeof(long) << endl;
cout << "unsigned long: " << sizeof(unsigned long) << endl;
cout << "long long: " << sizeof(long long) << endl;
//在vc6.0下不支持 long long 数据类型。
cout << "double: " << sizeof(double) << endl;
cout << "char: " << sizeof(char) << endl;

32bit windows xp (VC6.0 IDE 32bits) 测试:

char: 1
short: 2
int: 4
long: 4
unsigned long: 4
double: 8
char*: 4

64bit windows 10 (Dev-C++ 5.11 64bits的)测试:

Window系统下的MinGW,总是编译为32位代码。因为MinGW只支持32位代码。

char: 1
short: 2
int: 4
long: 4 // 不知道为什指针又是8字节的!!!
unsigned long: 4
long long: 8
double: 8
char*: 8 //指针所占内存大小有区别,每字节8位,跟总线搭配

64bit ubuntu15.04 (g++ 4.8.4)测试:

char: 1
short: 2
int: 4
long: 8 // 8 !!
unsigned long: 8
long long: 8
double: 8
char*: 8 //指针所占内存大小有区别,每字节8位,跟总线搭配

地址对齐的概念:

  • 由于硬件设计的特点,CPU在读写内存时,如果所给的地址是机器字长的整数倍,则操作效率会比较高。
  • 有的CPU甚至不支持读写地址不对齐的内存单元。
  • 为了提高程序运行的效率,编译器会尽量避免一个变量(包括结构体成员)的存储空间跨越地址对齐的界线。
  1. 实验(64bits 环境)

    include

    using namespace std;
    typedef struct{

    }test_null; // test 1

    typedef struct{
    double d1; //8
    char c1; //1
    short s1; //补1+2
    char ch2; //1
    int i1; //补3+4+补4 => 24
    }testdouble; // test 2

    typedef struct{
    int d1; //4
    char c1; //1
    short s1; //fill 1 + 2
    char ch2; //1
    int i1; //fill 3 + 4 => 16
    }testint; // test 3

    pragma pack(2)

    typedef struct{
    int d1; //4
    char c1; //1
    short s1; //fill 1 +2
    char ch2; //1
    int i1; //fill 1 + 4 => 14
    }testint2;

    class BU{
    int number; //4
    union UBffer{
    char buffer[13];//13
    int number; //4
    }ubuf; //->13
    void foo(){} //0
    typedef char(f)(void); //0
    enum{hdd,ssd,blueray}disk; //fill 1 + 4
    int
    a; //8 => 30
    }bu; // test 4
    #pragma(4)

    pragma pack(1) //

    typedef struct {
    char ch1; //1
    char ch2; //1
    int n; //4
    char ch3; //1
    }stch3_1; // -> 7

    class clt_1{
    char ch1; //1
    int a; //4
    short b; //2
    char c; //1
    stch3_1 st1;//7 => 15
    }st_inside; // test 5
    #pragma pack(4) //

输出控制:

  • 64bits win10(GCC 4.9.2)测试结果:

    test null struct: 1
    sizeof(bu): 30
    sizeof(testdouble):24
    sizeof(testint):16
    sizeof(testint2):14
    st_inside: 15, inside stch3_1: 7

  • 64bits ubuntu (gcc version 4.8.4 ) 测试结果:

    test null struct: 1
    sizeof(bu): 30
    sizeof(testdouble):24
    sizeof(testint):16
    sizeof(testint2):14
    st_inside: 15, inside stch3_1: 7

注意:

  • 空的结构体在linux系统中并不是0,它和windows 一样,均是占一个字节;
  • long类型在32bits 是占4个字节,64bits 编译器中是8字节;
  • 在32bits 系统中指针是4字节,64bits系统占8字节,由于总线物理跟指针的空间是一样大的:4*8 -> 32 ; 8*8 -> 64 .

posted on 2016-08-30 18:50  xweel  阅读(364)  评论(0)    收藏  举报

导航