sizeof和alignof有什么区别?

use*_*506 45 c++ sizeof c++11 alignof

sizeof和alignof有什么区别?

#include <iostream>

#define SIZEOF_ALIGNOF(T) std::cout<< sizeof(T) << '/' << alignof(T) << std::endl

int main(int, char**)
{
        SIZEOF_ALIGNOF(unsigned char);
        SIZEOF_ALIGNOF(char);
        SIZEOF_ALIGNOF(unsigned short int);
        SIZEOF_ALIGNOF(short int);
        SIZEOF_ALIGNOF(unsigned int);
        SIZEOF_ALIGNOF(int);
        SIZEOF_ALIGNOF(float);
        SIZEOF_ALIGNOF(unsigned long int);
        SIZEOF_ALIGNOF(long int);
        SIZEOF_ALIGNOF(unsigned long long int);
        SIZEOF_ALIGNOF(long long int);
        SIZEOF_ALIGNOF(double);
}
Run Code Online (Sandbox Code Playgroud)

将输出

1/1 1/1 2/2 2/2 4/4 4/4 4/4 4/4 4/4 8/8 8/8 8/8

我想我不知道对齐是什么......?

Chr*_*ber 68

好吧,"内存"基本上是一个巨大的字节数组.但是,大多数较大的东西(如整数)需要超过1个字节来存储它们 - 例如,32位值将使用4个连续字节的内存.

现在,计算机中的内存模块通常不是"字节"; 它们也被组织成几个字节"并行",就像4个字节的块一样.

对于CPU来说,它更容易=效率更高=在读取类似整数时不会"交叉"这样的块边界的更好性能:

memory byte    0 1 2 3     4 5 6 7       8 9 10 11
 integer       goooood
                   baaaaaaaaad
Run Code Online (Sandbox Code Playgroud)

这就是"对齐"所说的:4的对齐意味着这种类型的数据应该(或必须,取决于CPU)从4的倍数地址开始存储.

你观察到sizeof == alignof是不正确的; 尝试结构.结构也将对齐(因为它们各自的成员需要最终在正确的地址上),但它们的大小会更大.

  • 额外的一点 - 虽然x86会对大多数事情做不对齐的读写(缓慢但正确),但有些架构要求所有操作都要对齐,即使在x86中也有一些必须对齐的特殊情况(我认为是SIMD指令). (16认同)
  • @ user1494506 - 如果此答案或任何其他答案正确回答了您的问题,*考虑*将其标记为正确.(当然这只是*纯粹你的选择*.不要被迫接受答案,因为人们这样说(例如我现在说的)):) (2认同)

tsk*_*zzy 15

这两个运营商做了根本不同的事情.sizeof给出一个类型的大小(它需要多少内存),然后alignof给出一个类型必须对齐的字节数.事实上,您测试的基元的对齐要求与它们的大小相同(如果您考虑它,这是有意义的).

想想如果你有一个结构会发生什么:

struct Foo {
     int a;
     float b;
     char c;
};
Run Code Online (Sandbox Code Playgroud)

alignof(Foo) 将返回4.

  • 为什么?`alignof`是什么? (3认同)
  • @tskuzzy 你说`alignof(Foo)` 将返回4。但这取决于目标ABI。所以这在 ia32 (x86) 上可能是正确的,但在 ARM、MIPS、PowerPC 等上则不然。 (3认同)

eol*_*lso 12

旧问题(尽管没有标记为已回答......)但是认为这个例子除了克里斯蒂安施蒂伯的答案之外还有点明显.Meluha的答案也包含一个错误,因为sizeof(S)输出是16而不是12.

// c has to occupy 8 bytes so that d (whose size is 8) starts on a 8 bytes boundary
//            | 8 bytes |  | 8 bytes  |    | 8 bytes |
struct Bad  {   char c;      double d;       int i;     }; 
cout << alignof(Bad) << " " << sizeof(Bad) << endl;   // 8 24

//             | 8 bytes |   |   8 bytes    |    
struct Good {   double d;     int i; char c;          };
cout << alignof(Good) << " " << sizeof(Good) << endl; // 8 16
Run Code Online (Sandbox Code Playgroud)

它还表明,最好按大小排序成员,最大的是第一个(在这种情况下为double),因为其他成员受到该成员的约束.


Sta*_*ine 11

对于提供的答案,似乎存在一些关于实际对齐的混淆.可能会出现混淆,因为有两种对齐方式.

1.成员对齐

这是清楚地说明一个实例多大在用于部件的特定排序的字节数定性度量的结构/类类型.通常,如果成员按结构中的降序(即最大的第一个,最小的成员)按字节大小排序,则编译器可以压缩结构/类实例.考虑:

struct A
{
  char c; float f; short s;
};

struct B
{
  float f; short s; char c;
};
Run Code Online (Sandbox Code Playgroud)

两种结构都包含完全相同的信息.为了这个例子; float类型需要4个字节,short类型需要2个字符,字符需要1个字节.但是,第一个结构A具有随机顺序的成员,而第二个结构B根据它们的字节大小对成员进行排序(这在某些体系结构上可能不同,我假设x86 intel CPU体系结构在此示例中具有4字节对齐).现在考虑结构的大小:

printf("size of A: %d", sizeof (A)); // size of A: 12;
printf("size of B: %d", sizeof (B)); // size of B: 8;
Run Code Online (Sandbox Code Playgroud)

如果您希望大小为7个字节,则可以假设成员使用1字节对齐方式打包到结构中.虽然有些编译器允许这样做,但由于历史原因(大多数CPU使用DWORD(双字)或QWORD(四字)通用寄存器),大多数编译器通常使用4字节甚至8字节对齐.

有2个填充机制在工作以实现包装.

  1. 首先,如果结果字节大小小于或等于字节对齐,则字节大小小于字节对齐的每个成员与下一个成员"合并".在结构B中,成员s和c可以这种方式合并; 对于c == 3字节<= 4字节对齐,它们的组合大小为s + 1字节的2个字节.对于结构A,不会发生这样的合并,并且每个成员在结构的打包中有效地消耗4个字节.

  2. 再次填充结构的总尺寸,以便下一个结构可以在对齐边界处开始.在示例B中,总字节数将是7.下一个4字节边界位于字节8,因此结构填充1个字节以允许阵列分配作为紧密的实例序列.

请注意,Visual C++/GCC允许1个字节的不同对齐,2个字节的2倍和更高倍数.了解这会影响编译器为您的架构生成最佳代码的能力.实际上,在下面的示例中,对于每个读取操作,使用单字节指令将每个字节读取为单个字节.实际上,硬件仍将获取包含读入高速缓存的每个字节的整个存储器行,并执行指令4次,即使4个字节位于同一个DWORD中并且可以在1个指令中加载到CPU寄存器中.

#pragma pack(push,1)
struct Bad
{
  char a,b,c,d;
};
#pragma pack(pop)
Run Code Online (Sandbox Code Playgroud)

2.分配对齐

这与前一节中解释的第二个填充机制密切相关,但是,可以在malloc/memalloc分配函数的变体中指定分配对齐,例如calloc.因此,可以在与结构/对象类型的字节对齐建议不同的(通常更高的2倍)对齐边界处分配对象.

size_t blockAlignment = 4*1024;  // 4K page block alignment
void* block = std::aligned_alloc(blockAlignment, sizeof(T) * count);
Run Code Online (Sandbox Code Playgroud)

代码将把类型为T的计数实例块放在以4096的倍数结束的地址上.

使用这种分配对齐的原因再次纯粹是架构.例如,从页面对齐的地址读取和写入块更快,因为地址范围非常适合缓存层.分割在不同"页面"上的范围在跨越页面边界时会丢弃缓存.不同的媒体(总线架构)具有不同的访问模式,并且可以受益于不同的对齐.通常,4,16,32和64 K页面大小的对齐并不罕见.

  • 哎呀!不,“calloc()”不会那样进行对齐。上面的返回值将针对任何类型进行对齐(在现代 CPU 上很可能是 8 或 16 或 64 对齐),并且这完全独立于第二个参数。上面您实际上所做的是为“T”的“count * 4 * 1024”个实例分配空间,例如,您可能想要的数量的 4096 倍。请记住,“calloc(x, y)”分配的内存量与“malloc(x * y)”完全相同。`calloc` 的第二个参数*不是*用于指定对齐方式!!! (2认同)

Mel*_*uha 5

alignof值与基本类型的sizeof值相同.

不同之处在于使用定义的数据类型,例如使用struct; 例如

typedef struct { int a; double b; } S;
//cout<<alignof(s);                              outputp: 8;
//cout<<sizeof(S);                               output: 12;
Run Code Online (Sandbox Code Playgroud)

因此sizeof值是给定数据类型所需的总大小; 和alignof值是结构中最大元素的对齐要求.

使用alignof:在特定的对齐边界上分配内存.


Man*_*Way 1

sizeof运算符为您提供实际类型或类型实例的大小(以字节为单位)。

alignof运算符为您提供给定类型的任何实例所需的字节对齐方式。