• 数据的对齐


    数据的对齐

    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 
     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 <iostream>
       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 .
  • 相关阅读:
    后缀自动机在复习
    冬令营模拟day2总结
    割点(模板)
    黑匣子
    挤牛奶
    上白泽慧音
    lca最小公共祖先祖先
    最小花费
    牛的旅行(标程)
    骑马修栅栏
  • 原文地址:https://www.cnblogs.com/Qwells/p/5750350.html
Copyright © 2020-2023  润新知