63

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);
}

将输出

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

我想我不明白对齐是什么......?

4

8 回答 8

81

好吧,“内存”基本上是一个巨大的字节数组。然而,大多数像整数这样的较大的东西需要超过 1 个字节来存储它们——例如,一个 32 位的值将使用 4 个连续字节的内存。

现在,您计算机中的内存模块通常不是“字节”;它们还以“并行”的几个字节组织,例如 4 个字节的块。

对于 CPU,在读取整数之类的内容时,不“跨越”这样的块边界会更容易 = 更高效 = 更好的性能:

memory byte    0 1 2 3     4 5 6 7       8 9 10 11
 integer       goooood
                   baaaaaaaaad

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

您观察到 sizeof==alignof 不正确;尝试结构。结构也将对齐(因为它们的各个成员需要以正确的地址结束),但它们的大小会大得多。

于 2012-07-08T21:48:52.003 回答
24

对于提供的答案,似乎对对齐实际上是什么感到有些困惑。混淆可能是因为有两种对齐方式。

1. 会员对齐

这是一种定性度量,它说明对于结构/类类型中成员特定排序,实例的字节数有多大。一般来说,如果成员在结构中按字节大小降序排列(即最大的在前,最小的成员在后),编译器可以压缩结构/类实例。考虑:

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

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

两种结构都包含完全相同的信息。为了这个例子;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;

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

有 2 种填充机制在工作以实现打包。

  1. 首先,如果结果字节大小小于或等于字节对齐,则每个字节大小小于字节对齐的成员将与下一个成员“合并”。在结构B中,成员s和c可以这样合并;它们的组合大小是 s 的 2 个字节 + c 的 1 个字节 == 3 个字节 <= 4 字节对齐。对于结构 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)

2.分配对齐

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

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

该代码会将类型 T 的计数实例块放置在以 4096 的倍数结尾的地址上。

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

请注意,语言版本和平台通常会提供这种对齐分配功能的特定变体。例如,Unix/Linux 兼容的posix_memalign()函数通过 ptr 参数返回内存,并在失败的情况下返回非零错误值。

  • int posix_memalign(void **mempr, size_t 对齐, size_t 大小); // POSIX(Linux/UX)
  • void *aligned_alloc(size_t 对齐,size_t 大小); // C++11
  • void *std::aligned_alloc(size_t 对齐,size_t 大小); // c++17
  • void *aligned_malloc(size_t 大小, size_t 对齐); 微软VS2019
于 2016-12-01T11:14:13.883 回答
19

这两个运营商做着根本不同的事情。sizeof给出一个类型的大小(它需要多少内存),而alignof给出一个类型必须对齐的字节数。碰巧您测试的基元具有与其大小相同的对齐要求(如果您考虑一下,这是有道理的)。

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

struct Foo {
     int a;
     float b;
     char c;
};

alignof(Foo)将返回 4。

于 2012-07-08T21:45:31.110 回答
15

老问题(虽然没有标记为已回答..),但认为除了 Christian Stieber 的回答之外,这个例子使差异更加明确。此外,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

它还表明最好按大小对成员进行排序,最大的在前(在这种情况下为双倍),因为其他成员受该成员的约束。

于 2014-12-11T12:52:41.733 回答
5

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

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

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

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

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

于 2012-08-28T07:24:21.093 回答
3

sizeof和有什么区别alignof

两者都是运营商。两者都返回一种size_t.

sizeof是对象的“字节”大小 - 对其进行编码所需的内存空间。

alignof是对象的“字节”中的地址对齐要求。值 1 表示没有对齐限制。2 意味着地址应该是偶数地址。4 意味着地址应该是四元地址。等等

当尝试不满足对齐要求的对象引用时,结果是未定义的行为
例子:
。访问可能有效,只是速度较慢。
. 访问尝试可能会终止程序。

// Assume alignof(int) --> 2
char a[4];   // It is not known that `a` begins on an odd or even address
int *p = a;  // conversion may fail
int d = *p;  // *p is UB.

OP 代码的示例扩展和输出。

  SIZEOF_ALIGNOF(double);
  SIZEOF_ALIGNOF(complex double);
  SIZEOF_ALIGNOF(div_t);
  SIZEOF_ALIGNOF(max_align_t);

8/8
16/8
8/4
32/16
于 2019-04-27T17:46:49.177 回答
2

The sizeof operator gives you the size in bytes of an actual type or instance of a type.

The alignof operator gives you the alignment in bytes required for any instance of the given type.

于 2012-07-08T21:43:16.273 回答
0

数据在内存中按特定顺序排列,以使 CPU 更容易访问它。

  • alignof通知对象在内存中的位置
  • sizeof表示物体有多大,考虑到它的部件所在的位置

基本类型具有相同的结果alignofsizeof因为它们只有它们自己的一部分:例如。short占用 2 个字节并从 2 的地址倍数开始(对 CPU 有利)。对于用户定义的数据类型,请查看它们的部分:

class Foo {
  char c1; // first member (imagine adr=0, which is multiple of 1, CPU is happy)
  int i; // adr=1, but adr has to be a multiple of 4, so reserve helper bytes for CPU (padding) before `i` = 3bytes. **so adr=4**
  short s; // adr=8 (adr of `i` + its size), 8 % 2 == 0, good
  double d; // adr=10 (adr of `s` + its size), 10 % 8 != 0, need 6 more, **so adr=16**
  char c2; // adr=24 (adr of `d` + its size), 24 % 1 == 0, ok

  // and surprise, after c2 padding is also needed! why?
  // imagine you have an array (which is sequentially data in memory) of 2 elements,
  // what is adr `d` of the second element? (let me use index=2 here)  
  // arr[2].c1 adr=25, arr[2].i adr=29 (?do you remember about padding=3bytes?, and this field already feels bad), ..., arr[2].d adr=31
  // must be padding after `c2` to satisfy everyone
  // calc p (padding), where (adr`c2` + size`c2` + p) % max(alignof of every member type) = (24+1+p) % 8 == 0; p=7 (minimum value that satisfies)   
  // YEAH! Now all members of all elements will be aligned!
};

如上所述alignof(type)是 CPU 将数据放在哪里的偏好,在示例中:alignof(Foo)== alignof(double)=8 [否则有些成员会伤心]。
并且sizeof(Foo)==(每个成员大小的总和 + 填充)=32
ps。允许的简化有利于理解这个想法:)

于 2021-06-26T11:29:57.100 回答