C 语言字节对齐详析

C语言字节对齐与GNU __attribute__、__align()__选项使用说明 1、什么是字节对齐? 对齐跟数据在内存中的位置有关。如果一个变量的内存地址正好等于它长度的整数倍,他就被称做自然对齐。比如在32位cpu下,假设一个int 型变量iNum的地址为0x00000004或0x00000008,那它就是自然对齐的;但是如果iNum的地址为 0x0000 0006或0x0000 00009(假定从0x0000 0000 ---0x0000 0005内存存放的是其他数据),那么就不是自然对齐。 2、字节对齐的优缺点 优点: 需要字节对齐的根本原因在于CPU访问数据的效率问. 阅读详情

一、概述

    我们知道,在缺省情况下,C编译器会为每一个 变量 或者 数据单元 按其自然边界对齐(natural    

        alignment) 的方式分配空间。

    本文先介绍 自然边界对齐 方式,接着介绍4种改变C编译器的缺省字节对齐方式的方法 -- 即指定边界对齐。

 

二、自然边界对齐

    什么是 自然边界对齐 呢?
    我们知道,在C语言中,结构是一种 复合数据类型,它的 构成元素 可以是基本数据类型(如int 、long、float等)的变量,也可以是一些 复合数据类型(如数组、结构、联合体等)的数据单元。

 

    首先让我们来了解下 结构体  字节对齐有哪些特点?

    ①  对于结构体,编译器会自动进行 成员变量 的对齐, 以提高运算效率。

          缺省情况下,编译器为结构体的每个成员按其自然边界对齐方式分配空间 -- 自然边界对齐方式是编译器的默认对齐方式;

    ②  各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构体的地址相同;

    ③  结构体整体的默认字节对齐大小是它的所有成员中对齐参数最大的一个;

    ④  结构体长度的计算必须取所用过的所有对齐参数的整数倍,不够的话就填充(pad)空字节;

          也就是说,最后整个结构体的大小是所有用过的对齐参数中最大的那个值的整数倍。对齐参数一般都是2的n次方,这样在处理数组时可以保证每一项都边界对齐。

 

    下面一段英文摘自(http://en.wikipedia.org/wiki/Data_structure_alignment),注意其中的红色字体,下文还会涉及到。

    The type of each member of the structure usually has a default alignment, meaning that it will, unless

    otherwise requested by the programmer, be aligned on a pre-determined boundary. The following

    typical alignments are valid for compilers fromMicrosoft (Visual C++),Borland/CodeGear (C++Builder),

    Digital Mars (DMC)   andGNU (GCC) when compiling for 32-bit x86:

  • A char (one byte) will be 1-byte aligned.
  • A short (two bytes) will be 2-byte aligned.
  • An int (four bytes) will be 4-byte aligned.
  • A long (four bytes) will be 4-byte aligned.
  • A float (four bytes) will be 4-byte aligned.
  • A double (eight bytes) will be 8-byte aligned on Windows and 4-byte aligned on Linux (8-byte with

             -malign-double compile time option).

  • A long double (ten bytes with C++Builder and DMC, eight bytes with Visual C++, twelve bytes with GCC) will be 8-byte aligned with C++Builder, 2-byte aligned with DMC, 8-byte aligned with Visual C++ and 4-byte aligned with GCC.
  • Any pointer (four bytes) will be 4-byte aligned. (e.g.: char*, int*)

    The only notable difference in alignment for a 64-bit system when compared to a 32-bit system is:

  • A long (eight bytes) will be 8-byte aligned.
  • A double (eight bytes) will be 8-byte aligned.
  • A long double (eight bytes with Visual C++, sixteen bytes with GCC) will be 8-byte aligned with Visual C++ and 16-byte aligned with GCC.
  • Any pointer (eight bytes) will be 8-byte aligned.

 

    好了,让我们先看一个具体的例子:

    例如, 下面的 结构体 各成员空间分配情况:

[cpp]  view plain copy
  1. struct test  
  2. {  
  3.             char  x1;  
  4.             short x2;  
  5.             float x3;  
  6.             char  x4;  
  7. };  

    结构体的第一个成员x1,其偏移地址为0,占据了第一个字节。

    第二个成员x2为short类型,其起始地址必须 2字节边界对齐(2-byte aligned.) , 因此, 编译器在x2和x1之间填充了一个空字节。

    结构体的第三个成员x3和第四个成员x4恰好落在其边界对齐地址上,在它们前面无需填充额外的空字节。

    看下图:不同的颜色代表不同的结构体成员,白色代表填充的空白字节。

    

   

 

 三、指点边界对齐

    我们可以通过不同的方法来改变编译器的缺省对齐方式。

   

    方法1: 使用#pragma pack

    作用:

    指定结构体、联合以及类成员的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。

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

    而结构体的对齐,则按照结构体中size最大的数据成员和#pragma pack指定值之间较小的值来对齐。

    总结一下:

    1.    每个成员采用#pragma pack指定的数值和自身的自然边界对齐方式大小 中较小的值来对齐。

    2.    将结构体看做一个整体,那么这个整体的对齐方式采用 #pragma pack指定的数值 和结构体中size最大的数据成员的自然边界对齐大小 中较小的值来对齐。

    这一切都是为了最小化长度,并且提高程序的效率。

   

    说了这么多,有点晕了,让我们来看几个实例吧。  

[cpp]  view plain copy
  1. // 例1  
  2. #pragma pack(2)  
  3. struct TestA  
  4. {  
  5. public:  
  6.     int   a; // 第一个成员,放在[0,3]偏移的位置。  
  7.   
  8.     char  b; // 第二个成员sizeof(char)=1, #pragma pack(2), 取小值也就是1  
  9.          // 所以这个成员按1字节对齐,放在偏移[4]的位置。  
  10.   
  11.     short c; // 第三个成员sizeof(short)=2, #pragma pack(2),取小值也就是2  
  12.          // 所以这个成员按2字节对齐,所以放在偏移[6,7]的位置。  
  13.   
  14.     char  d; // 第四个成员sizeof(short)=1, #pragma pack(2), 取小值也就是1  
  15.          // 所以这个成员按1字节对齐,放在[8]的位置。  
  16.   };  
  17. #pragma pack()  
  18.     // struct TestA中size最大的数据成员(4),#pragma pack(2),   
  19.     // 取小值也就是2,所以sizeof(TestA)应当按照2来对齐,为10。  

 内存布局示意图(白色框为填充字节):

                                                                                           

[cpp]  view plain copy
  1. // 例2  
  2. #pragma pack(4)  
  3. struct TestB  
  4. {  
  5. public:  
  6.     int   a; // 第一个成员,放在[0,3]偏移的位置。  
  7.   
  8.     char  b; // 第二个成员sizeof(char)=1, #pragma pack(4), 取小值也就是1  
  9.          // 所以这个成员按1字节对齐,放在偏移[4]的位置。  
  10.   
  11.     short c; // 第三个成员sizeof(short)=2, #pragma pack(4),取小值也就是2  
  12.          // 所以这个成员按2字节对齐,所以放在偏移[6,7]的位置。  
  13.   
  14.     char  d; // 第四个成员sizeof(short)=1, #pragma pack(4), 取小值也就是1  
  15.          // 所以这个成员按1字节对齐,放在[8]的位置。  
  16. };  
  17. #pragma pack()  
  18.     // struct TestB中size最大的数据成员(4),#pragma pack(4)  
  19.     // 取小值也就是4,所以sizeof(TestB)应当按照4来对齐,为12。  

 内存布局示意图(白色框为填充字节):

                                    

[cpp]  view plain copy
  1. // 例3(如果在GCC中编译,请加上-malign-double选项)  
  2. #pragma pack(8)  
  3. struct s1  
  4. {  
  5.     short a; // 第一个成员,放在[0, 1]偏移的位置。  
  6.     long  b; // 第二个成员sizeof(long)=4, #pragma pack(8), 取小值也就是4  
  7.          // 所以这个成员按4字节对齐,放在偏移[4~7]的位置。  
  8. };  
  9.     // struct s1中size最大的数据成员(4), #pragma pack(8)  
  10.     // 取小值也就是4,所以sizeof(struct s1)应当按照4来对齐,为8  
  11.   
  12. struct s2  
  13. {  
  14.     char      c; // 第一个成员,放在[0]偏移的位置。  
  15.     struct s1 d; // 第二个成员为struct s1,其对齐方式是它的所有成员使用的对齐参数中最大的一个,即4。  
  16.              // 所以第二个成员d按4字节对齐,由于sizeof(d)=8, 放在偏移[4~11]的位置。  
  17.     long long e; // 第三个成员sizeof(long long)=8, #pragma pack(8), 取小值也就是8  
  18.              // 所以这个成员按8字节对齐,放在偏移[16~23]的位置。  
  19. };  
  20. #pragma pack()  


 问:
    1. sizeof(struct s2) = ?
    2. s2的c后面空了几个字节接着是d?
    
    答案1:
        struct s1中size最大的数据成员(4),#pragma pack(8),取小值也就是4,所以sizeof(struct s1)应当按照4来对齐,为8。
        struct s2中size最大的数据成员(8),#pragma pack(8),取小值也就是8,所以sizeof(struct s2)应当按照8来对齐,为24。
    答案2:
        s2的c后面空了3个字节接着是d。

    s1的内存布局示意图:

                                                          

    s2的内存布局示意图:

                                           

    例3中,我们如果将#pragma pack(8)改成#pragma pack(2), 结果会怎样呢?  

[cpp]  view plain copy
  1. //(如果在GCC中编译,请加上-malign-double选项)  
  2. #pragma pack(2)  
  3. struct s1  
  4. {  
  5.     short a; // 第一个成员,放在[0, 1]偏移的位置。  
  6.     long  b; // 第二个成员sizeof(long)=4, #pragma pack(2), 取小值也就是2  
  7.          // 所以这个成员按4字节对齐,放在偏移[2~5]的位置。  
  8. };  
  9.     // struct s1中size最大的数据成员(4), #pragma pack(2)  
  10.     // 取小值也就是2,所以sizeof(struct s1)应当按照2来对齐,sizeof(struct s1) = 6  
  11.   
  12. struct s2  
  13. {  
  14.     char      c; // 第一个成员,放在[0]偏移的位置。  
  15.     struct s1 d; // 第二个成员为struct s1,其对齐方式在这里就不是4了, 为什么呢?  
  16.              // 由上文的总结2得知,这里应该是2  
  17.              // 所以第二个成员d按2字节对齐,由于sizeof(d)=6, 放在偏移[2~7]的位置。  
  18.     long long e; // 第三个成员sizeof(long long)=8, #pragma pack(2), 取小值也就是2  
  19.              // 所以这个成员按2字节对齐,放在偏移[8~15]的位置。  
  20. };  
  21.     // 所以,sizeof(s2) = 16  
  22. #pragma pack()  


 

 

 再看一个实例: 

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. struct test  
  5. {  
  6.         char struc_char;  
  7.         int struc_int;  
  8.         short struc_short;  
  9.         double struc_double;  
  10. }TEST;  
  11.   
  12. int main(int argc, const char *argv[])  
  13. {  
  14.         printf("sizeof(TEST) = %d\n", sizeof(TEST));  
  15.         return 0;  
  16. }  

    在gcc中得到的结果是20,在VC中输出的却是24,这是怎么回事呢?

    原来gcc和VC关于double的对齐方式不同:在gcc中double类型的变量4字节对齐,而在VC中却是8字节对齐。(见上文中英文中红色字体部分)

    如果想使double类型的变量在gcc中也是8字节对齐,编译时加上 -malign-double选项即可。

    最后值得一提的是,对于数组,比如:char a[3];这种,它的对齐方式和分别写3个char是一样的.也就是说它还是按1个字节对齐.
    如果写: typedef char Array3[3];
    Array3这种类型的对齐方式还是按1个字节对齐,而不是按它的长度.
    不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64....中的一个。

 

 

    方法2: 使用__attribute__((aligned (alignment))) 

    __attribute__((aligned (alignment)))  告诉编译器一个结构体或者类或者联合或者一个类型的变量(对象)分配地址空间时的地址对齐方式。也就是说,如果将__attribute__((aligned (alignment)))  作用于一个类型,那么该类型的变量在分配地址空间时, 其存放的地址一定按照alignment字节对齐(alignment必须是2的幂次方)。并且其占用的空间,即大小,也是alignment的整数倍,以保证在申请连续存储空间的时候,每一个元素的地址也是按照m字节对齐。__attribute__((aligned(alignment)))也可以作用于一个单独的变量。

    看几个实例: 

[cpp]  view plain copy
  1. struct A  
  2. {  
  3.     char               a;  
  4.     int                b;  
  5.     unsigned short     c;  
  6.     long               d;  
  7.     unsigned long long e;  
  8.     char               f;  
  9. };  

    
    因为什么限制也没有,所以采用默认处理方式。因为其中有unsigned long long e,其在VC下8字节对齐,GCC默认4字节对齐,如果加上-malign-double选线则也8字节对齐。

    所以GCC默认情况下,sizeof(struct A) =  4(a, 1-->4)+ 4 + 4(c, 2-->4) + 4 + 8 + 4(f, 1-->4) = 28。VC下sizeof(struct A) = 32
 

[cpp]  view plain copy
  1. struct B  
  2. {  
  3.     char               a;  
  4.     int                b;  
  5.     unsigned short     c;  
  6.     long               d;  
  7.     unsigned long long e;  
  8.     char               f;  
  9. }__attribute__((aligned));  

    在struct B中,aligned没有参数,表示“让编译器根据目标机制采用最大最有益的方式对齐"。
    当然,最有益应该是运行效率最高吧,呵呵。其结果是与采用__attribute__((aligned(8)))相同。
    sizeof(struct B) = 8(1+4+2 ,即a, b, c)+ 8(d, 4-->8) + 8 + 8(f, 1-->8) = 32。

 

[cpp]  view plain copy
  1. struct C  
  2. {  
  3.     char               a;  
  4.     int                b;  
  5.     unsigned short     c;  
  6.     long               d;  
  7.     unsigned long long e;  
  8.     char               f;  
  9. }__attribute__((aligned(1)));  

    在struct C中,试图使用__attribute__((aligned(1)))来使用1个字节方式的对齐,不过并未如愿,仍然采用了默认4个字节的对齐方式。这是与#pragma pack的区别 -- 选择其中值大的那个。

   总结一下,或者说__attribute__((aligned (alignment)))  与 #pragma pack(m)有什么区别呢?

    最大的区别在 变量的自然边界对齐值 与 它们的指定值 之间选择哪个值! 

    #pragma pack(m)告诉编译器: 结构体或者C++类内部的成员变量相对于第一个变量的地址偏移的对齐方式。缺省情况下,编译器按照自然边界对齐,否则选择其中较小的值。

    __attribute__((aligned (alignment)))  作用于一个类型,那么该类型的变量在分配地址空间时,其存放的地址一定按照alignment字节对齐(alignment必须是2的幂次方),并且其所占用的空间也是alignment的整数倍,以保证在申请连续存储空间的时候,每一个元素的地址也都是按照alignment字节对齐。__attribute__((aligned (alignment)))  也可以作用于一个单独的变量。当变量自然边界对齐值小于指定值时,选择指定的值,也就是寻则其中较大的值。

    来看一个具体的例子: 

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. #pragma pack(16)  
  5. struct testa  
  6. {  
  7.         unsigned int number;  
  8. };  
  9. #pragma pack()  
  10. struct testa arr_testa[4] = { {65535}, {65535}, {65535}, {65535} };  
  11.   
  12. struct testb  
  13. {  
  14.         unsigned int number;  
  15. } __attribute__ ((aligned (16))) ;  
  16.   
  17. struct testb arr_testb[4] = { {65535}, {65535}, {65535}, {65535} };  
  18.   
  19.   
  20. int main(int argc, const char *argv[])  
  21. {  
  22.         printf("sizeof(struct testa) = %d, sizeof(arr_testa) = %d\n",  
  23.                         sizeof(struct testa), sizeof(arr_testa));  
  24.         printf("sizeof(struct testb) = %d, sizeof(arr_testb) = %d\n",  
  25.                         sizeof(struct testb), sizeof(arr_testb));  
  26.   
  27.         printf("address of arr_testa[0] = %p\n", &arr_testa[0]);  
  28.         printf("address of arr_testa[1] = %p\n", &arr_testa[1]);  
  29.         printf("address of arr_testa[2] = %p\n", &arr_testa[2]);  
  30.         printf("address of arr_testa[3] = %p\n", &arr_testa[3]);  
  31.         puts("");  
  32.         printf("address of arr_testb[0] = %p\n", &arr_testb[0]);  
  33.         printf("address of arr_testb[1] = %p\n", &arr_testb[1]);  
  34.         printf("address of arr_testb[2] = %p\n", &arr_testb[2]);  
  35.         printf("address of arr_testb[3] = %p\n", &arr_testb[3]);  
  36.         return 0;  
  37. }  

在我的机器上(ubuntu 11.10, gcc 4.4.6)输出如下:  

[cpp]  view plain copy
  1. sizeof(struct testa) = 4, sizeof(arr_testa) = 16  
  2. sizeof(struct testb) = 16, sizeof(arr_testb) = 64  
  3. address of arr_testa[0] = 0x804a040  
  4. address of arr_testa[1] = 0x804a044  
  5. address of arr_testa[2] = 0x804a048  
  6. address of arr_testa[3] = 0x804a04c  
  7.   
  8. address of arr_testb[0] = 0x804a060  
  9. address of arr_testb[1] = 0x804a070  
  10. address of arr_testb[2] = 0x804a080  
  11. address of arr_testb[3] = 0x804a090  

可以看出#pragma pack(m)对数组元素的存放并没有什么影响,而__attribute__((aligned (alignment)))  影响了数组每个元素的地址,都是alignment字节对齐,好处就是增强了copy操作的效率。

 

 

 

    方法三:  使用__attribute__ ((packed))取消结构在编译过程中的优化对齐

     __attribute__ ((packed))作用于结构成员,表示该成员与前一个结构成员之间没有空洞。
     举例:  

[cpp]  view plain copy
  1. struct foo  
  2.     {  
  3.        char a;  
  4.        int x[2] __attribute__ ((packed));  
  5.     };  

    这里packed属性作用于成员x,因而在结构成员a后没有空洞,而是立即紧跟着成员x。

    __attribute__ ((packed))作用于整个结构,等同于为结构中的每个成员指定__attribute__ ((packed)),与#pragma pack(1)的作用等效。

 

    举例: 

[cpp]  view plain copy
  1. struct F{  
  2.    char               a;  
  3.    int                b;  
  4.    unsigned short     c;  
  5.    long               d;  
  6.    unsigned long long e;  
  7.    char               f;  
  8. __attribute__((packed));  

    sizeof(struct F) = 1 + 4 + 2 + 4 + 8 + 1 = 20。
    注意:在使用了packed属性限定之后,GCC编译器将用字节存取命令(ARM中为LDRB或STRB指令)来访问该结构成员,而不是按照自然边界对齐方式来访问结构成员。


 

    方法四:GCC编译选项中使用-fpack-struct[=n]

     如果没有指定n, 则去除所有结构中的空洞(注意这里会影响到所有的结构),即编译器不能在成员之间填充边界对齐的空字节。
     如果指定n, 则n表示maximum alignment (that is, objects with default alignment requirements larger than this will be output potentially unaligned at the next fitting location)。

    但通常不应当使用该选项,因为这会使访问结构成员的效率降低,代码量增大(通常会增加1/3左右,当Flash空间很有限时就要认真考虑了),而且使生成的代码与没有使用该编译选项的系统库不兼容。

 

    补充:

    还有一个__declspec( align(#) ),它有点类似于__attribute__((aligned (alignment))) 

    __declspec( align(#) )和#pragma pack(n)有密切联系, 并且__declspec( align(#) )的优先级比#pragma pack(#)的要高。

    规则是:

    ①    结构体每个成员的对齐值依然是 自身自然边界对齐值 与 #pragma pack(n)指定值 中较小的那个。

    ②    结构体整体的大小受__declspec( align(#) )的影响,最终取值是 结构体中size最大值 与 # 中较大的那个

    举个例子吧:

   

[cpp]  view plain copy
  1. #pragma pack( 4 )   
  2. __declspec( align(2) )  struct C   
  3.   
  4. {   
  5.     int i1;     // 第一个成员,放在[0~3]  
  6.     double d1;  // 第二个成员,vc默认8字节对齐,4比8小,所以最终4字节对齐,放在[4~11]  
  7.     int i2;     // 第三个成员,大小是4,所以4字节对齐,放在[12~15]  
  8.     int i3;     // 第四个成员,大小是4,所以4字节对齐,放在[16~19]  
  9. };   
  10.   
  11. // 现在在定义含有C的D  
  12.   
  13. __declspec( align(16) ) struct D   
  14. {   
  15.     int i1;     // 第一个成员,放在[0~3]  
  16.     struct C m_d;   // 第二个成员,受之前的__declspec( align(32) )的影响,优先级最高,  
  17.     // 偏移量是32,所以这里用32对齐, 所以m_d放在[32~63]  
  18.     int i2;     // 第三个成员,大小是4,所以4字节对齐,放在[64~67]  
  19. };   
  20. // 当计算结构体的最后大小时,因为结构体接受 __declspec( align(16) ) 影响,  
  21. // 比较 32 和 #, 32 比 #=16大,用32补完,所以最后的大小应该是大于68最小的32倍数,即96  
  22. #pragma pack()  

     __attribute__((aligned (alignment)))有所不同:

   

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3.   
  4. #pragma pack(4)  
  5. struct __attribute__ ((aligned (32))) C  
  6. {  
  7.         int i1;  
  8.         double d1;  
  9.         int i2;  
  10.         int i3;  
  11. };  
  12.   
  13. struct __attribute__ ((aligned (16))) D  
  14. {  
  15.         int i1;  
  16.         struct C m_d;  
  17.         int i2;  
  18. };  
  19. #pragma pack()  
  20.   
  21. int main(int argc, const char *argv[])  
  22. {  
  23.         printf("sizeof(struct C) = %d\n", sizeof (struct C));  
  24.         printf("sizeof(struct C) = %d\n", sizeof (struct D));  
  25.         return 0;  
  26. }  


输出:

[cpp]  view plain copy
  1. sizeof(struct C) = 32  
  2. sizeof(struct C) = 48  


 

    参考链接:

    http://kapok.blog.51cto.com/517862/127218  

    ( 细说结构字节对齐 )

    http://blog.163.com/tyw_andy/blog/static/11679021200910635047652/   

    (#pragma pack(n)和__attribute__((aligned(m)))的区别)
    http://blog.youkuaiyun.com/jamesf1982/article/details/4375719

    (pragma pack(非常有用的字节对齐用法说明))

    http://blog.youkuaiyun.com/jamesf1982/article/details/4384186

    (#pragma pack( n )和__declspec( align(#) ) 的偏移量计算方法)


【嵌入式底层知识修炼】结构体成员对齐之#pragma pack(n)和__attribute__((aligned (n)))的含义和区别 文章目录01 - 提出问题02 - 对齐规则2.1 - 自然对齐规则2.2 - 自定义对齐之#pragma pack(n)2.3 - 自定义对齐之__attribute__((aligned (n)))2.4 - 两种自定义对齐方式的区别03 - 解答问题04 - 问题扩展05 - 总结 不要迷信书、考题、老师、回帖; 要迷信CPU、编译器、调试器、运行结果。 ————优快云赵4老师  ... 阅读详情

相关推荐

C语言之字节对齐

本文为博主个人学习总结记录,如有不正,欢迎指正。

qq_45583706的博客 1661

4 __attribute__((aligned))说明

摘要: attribute__是GNUC特有的语法机制,用于设置函数、变量和类型的属性。其语法格式为__attribute((attribute-list))。作为修饰符时,适用于对象(类似const等类型限定符)、函数(类似inline等说明符)或结构体/联合体/枚举(需置于类型标识符前)。常用的aligned属性可指定函数首地址的最小对齐字节数。__attribute__机制扩展了C语言的语法功能,是GNUC的重要特色之一。(98字)

u012906122的专栏 2537

C语言字节对齐问题详解

一、何谓字节对齐?       现代计算机中内存空间都是按照字节(byte)划分的,从理论上讲,似乎对任何类型变量的访问都可以从任何地址开始,但实际情况是在访问特定变量的时候,经常在特定的内存地址访问,而不是顺序的一个接一个的排放。为了使CPU能够对变量进行快速访问,变量的起始地址应该具有某些特性,即所谓的“字节对齐”。比如4字节的int型,其起始地址应该位于4字节的边界上,即起始地址能够被4整

thomashtq的专栏 1066

C语言字节对齐

一、概述 我们知道,在缺省情况下,C编译器会为每一个 变量 或者 数据单元 按其自然边界对齐(natural alignment)的方式分配空间。 本文先介绍 自然边界对齐 方式,接着介绍4种改变C编译器的缺省字节对齐方式的方法 -- 即指定边界对齐。 二、自然边界对齐 什么是 自然边界对齐 呢? 我们知道,在C语言中,结

astrotycoon 3390

c语言关键字_ _align()和_ _packed的使用以及字节对齐原理

字节对齐(Byte Alignment)是计算机内存中的一种存储优化技术,用于确保数据在内存中的存储位置满足特定的硬件要求,以提高内存访问的效率。在现代计算机体系结构中,许多处理器要求数据以特定的方式对齐,以便在访问内存时能够更快地读取数据。字节对齐的原则是确保特定数据类型的起始地址是它自身大小的倍数。如果数据没有按照正确的对齐方式存储在内存中,处理器在访问这些数据时可能需要额外的操作来解决不对齐的情况,从而增加了访问数据的时间和计算开销。

Known668的博客 3082

C语言字节对齐介绍

C语言字节对齐介绍 我们常提到的1字节、4字节对齐等具体是什么意思? 回答:对齐指存放当前数据的地址能否被n整除(n就是对齐值,如1,4等)。比如:地址0x0004中存放int型数据。那么它满足4字节和1字节的对齐。但地址0x0003中存放int型数据,它只满足1字节对齐,不满足4字节对齐。 我们先谈谈为什么会有字节对齐这个概念? 回答:我们应该都听过8位、16位、32位、64位等位数的处理器。如我们最常见的STM32就是32位单片机。我们所说的n位其实指的就是地址总线的宽度,与其相关的还有数据总

guoweilkd的博客 1358

C语言结构体的字节对齐

本文详细介绍了C语言中结构体字节对齐的规则,并根据该规则拓展了一些CPU读取系统内存的一些知识,联系到实际开发中,鼓励读者对提高自己编程能力的思考

Royi的学习博客 1596

【AI落地】AI生成测试用例,claude or gpt?(提效至少 50%)

gpt生成测试用例?当然好使啦,用好了提效50%+,用不好就让他变好!

青禾tester 1969

c语言字节对齐

c语言驱动变成,网络通讯内核模块编程

zouweisj的博客 3787

美团mtgsig 3.1.0算法还原:从加密入口到d1生成的完整链路拆解

本文详细拆解了美团mtgsig 3.1.0签名算法的完整逆向过程。通过定位加密入口函数eH,理清a6、a5、a8到d1的生成依赖链,并运用插桩技术,在JSVMP混淆环境下逐步还原了核心参数的生成逻辑,为逆向工程实践提供了清晰的纯算还原方法论。

oo7890的博客 777

FOC时序及电流采样注意事项

摘要:本文介绍了无刷电机FOC(磁场定向控制)的实现方法,重点解析了电流采样时序与PWM同步的关键技术。在S32K144平台上,需使用两个独立的ADC组件实现双路电流同步采样,采样时刻必须严格对应低边MOS管开启时间并位于PWM波形中央。文章详细说明了硬件触发时序设计、采样电阻配置原则,并通过示波器实测波形验证了电流反馈电路的正确性,指出正弦电流波形应是理想输出形态。关键点包括ADC-PWM时序同步、低边MOS采样及正确的PWM映射配置。

qq_40916548的博客 2622

C语言字节对齐详解

C语言字节对齐12345不同系统下的C语言类型长度 Data Type ILP32 ILP64 LP64 LLP64 char 8 8 8 8 short 16 16 16 16 int 32 64 32 32 long 32 64 64 32 long long 64 64 64

czw 6万+

从零构建DDS信号发生器:STM32 DAC与DMA的完美交响

本文详细介绍了如何利用STM32的DAC模块和DMA控制器从零构建高性能DDS信号发生器。通过深入解析DDS技术原理、STM32 DAC特性及DMA高效数据传输,提供了完整的波形生成算法、硬件电路设计和软件实现方案,帮助开发者掌握数字信号合成的核心技术。

tree8的博客 705

C语言的字节对齐

一、基本概念 1. 什么是自然对齐 对于一个存储在内存中的变量,如果它的内存地址等于该变量长度的整数倍,则称该变量是自然对齐的。 例子:对于32位的CPU,如果一个int型变量A的地址是0x00000004,A是自然对齐。 2. 关于CPU存取数据的效率问题 假设有一片连续的内存地址,如下图: 每个地址上可存储1个字节的数据。 例子:以32位的CPU为例,32位的CPU,默认是4字节[32 / 8 = 4]对齐。 当整型数据A的存储起始地址为0x2,则它在内存中的数据占据了4..

渣渣小码农的博客 3199

Zemax单模光纤耦合仿真避坑指南:数值孔径、模式匹配与镀膜影响详解

本文详细解析了Zemax单模光纤耦合仿真中的关键避坑要点,包括数值孔径的多重定义与换算、模式匹配度评估及镀膜影响分析。通过实际案例和技术公式,帮助工程师避免常见仿真误差,提升激光通信和光纤传感系统的设计精度。特别针对OpticStudio用户提供了实用的参数设置建议和优化策略。

weixin_30836759的博客 465

消息平台接入实战:Hermes Agent 实现微信/钉钉日常任务自动化的 4 步配置

揭示 Hermes Agent 接入微信/钉钉时高频失效的根本原因——非 API 调用问题,而是上下文边界缺失;详解通过 soul.md 锁定人格基线、skills/ 目录原子化封装技能、CLAUDE.md 控制记忆节奏、Docker 环境适配四步构建可信赖自动化信任链,并给出身份证号过滤、中文标点

weixin_42520239的博客 165

浅谈C语言字节对齐

首先,我们得知道为什么要进行内存对齐,它的意义何在?在这儿可以先看这样一张图。(手绘请见谅!!!) 我们知道,在32位CPU下,一个读取周期可以读取四个字节。一个字符变量在内存中占一个字节,而整型为在内存中占4个字节。 那么CPU一个读写周期刚好能读取一个整形,所以如上边左图,两个整型变量分别读写一次即可;而上面中间的图,第一个读写周期读取的四个字节为一个char变量

ljx_5489464的博客 1949

LeRobot 入门教程(十)LIBERO 机器人终身学习评估

LIBERO是一项专注于机器人终身学习(LLDM)的基准测试平台,包含130项任务,分为5大套件(空间推理、物体操作、目标约束等)。LeRobot框架已集成LIBERO,支持单/多套件评估,并兼容SmolVLA模型。训练需使用预处理数据集(HuggingFaceVLA/libero),观测数据需符合规范(8维状态+双摄像头图像)。该平台采用MuJoCo模拟,支持无头服务器运行,旨在推动机器人持续学习算法的发展与标准化评估。

2506_90492529的博客 1663

c语言4字节对齐,理一理C语言字节对齐的那些事

前言字节对齐是我们初学 C语言 就会接触到的一个概念,但是到底什么是字节对齐?对齐准则又是什么?为什么要字节对齐呢?字节对齐对我们编程有什么启示?本文将简单理一理字节对齐的那些事。什么是字节对齐计算机中内存大小的基本单位是字节(byte),理论上来讲,可以从任意地址访问某种基本数据类型,但是实际上,计算机并非逐字节大小读写内存,而是以2,4,或8的 倍数的字节块来读写内存,如此一来就会对基本数据类...

weixin_33634341的博客 1801

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

c语言强制4字节对齐,C语言字节对齐4

非字节对齐类型的字节对齐规则我们可以使用“__packed”、“__attribute__((packed))”、“#pragma”等方式控制结构体的字节对齐,这些结构体的内部结构在定义时就已经确定了,当它们被包含在其它结构体内部时它们被当做一个整体来看待,其内部结构不受外面字节对齐控制方式的影响,但它们的对齐方式却受外面结构体的对齐方式控制,来看下面的例子:#pragmapack(2)typed...

weixin_42151729的博客 2405

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

C语言结构体_字节对齐

C语言_结构体 _ 字节对齐 - 详解 文章目录C语言_结构体 _ 字节对齐 - 详解@[toc]0 . 说明1.结构体__字节对齐的背景(本质)2.结构体__字节对齐的处理方式总结3.`4字节对齐`__实例展示a.发生字节对齐b.改变书写顺序c.体会4.拓展5.`8字节对齐`实例展示6.强制转换 个别结构体 字节对齐的方式 0 . 说明 ​ 看滴水逆向视频总结笔记 ​ 编译器VC++6.0 1....

qq_35289660的博客 851

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C语言编程之字节对齐

一.什么是字节对齐 现代计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。 但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐。

热土程序园,利他愉己~ 975
上一篇: OpenCV — 人脸识别
下一篇: 自我修养——静态链接
南丶烟
博客等级 码龄14年 569粉丝 98原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值