大小和对齐有什么区别?
What's the difference between sizeof and 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);
}
将输出
1/11/12/22/24/44/44/44/44/48/88/88/8
我想我不明白对齐是什么...?
嗯,"内存"基本上是一个巨大的字节数组。但是,大多数较大的东西(如整数)需要超过 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 是不正确的;试试结构。结构也将对齐(因为它们的各个成员需要最终位于正确的地址上),但它们的大小会大得多。
对于提供的答案,似乎对对齐实际上是什么存在一些混淆。出现混淆可能是因为有两种对齐方式。
1. 杆件对齐
这是一个定性度量,用于说明结构/类类型中成员的特定排序的实例的大小(以字节数为单位)。通常,如果成员在结构中按字节大小降序排序(即最大的第一个,最小的成员在最后),编译器可以压缩结构/类实例。考虑:
struct A
{
char c; float f; short s;
};
struct B
{
float f; short s; char c;
};
两种结构包含完全相同的信息。为了这个例子;浮点型需要 4 个字节,短类型需要 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个填充机构在工作以实现包装。
首先,如果生成的字节大小小于或等于字节对齐,则字节大小小于字节对齐的每个成员将与下一个成员"合并"。在结构 B 中,成员 s 和 c 可以通过这种方式合并;它们的组合大小为 S 的 2 个字节 + C = 3 个字节的 1 个字节 <= 4 字节对齐。对于结构 A,不会发生此类合并,并且每个成员在结构的打包中有效地消耗 4 个字节。
再次填充结构的总大小,以便下一个结构可以从对齐边界开始。在示例 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 **memptr, size_t alignment, size_t size);//POSIX(Linux/UX)
- 空 *aligned_alloc( size_t对齐,size_t大小); C++11
- void *std::aligned_alloc( size_t alignment, size_t size );//c++17
- 空 *aligned_malloc( size_t 大小,size_t对齐); 微软VS2019
这两个运算符执行根本不同的事情。 sizeof
给出类型的大小(需要多少内存),而alignof
给出类型必须对齐到多少字节。碰巧的是,您测试的基元具有与其大小相同的对齐要求(如果您考虑一下,这是有道理的)。
想想如果你有一个结构体会发生什么:
struct Foo {
int a;
float b;
char c;
};
alignof(Foo)
将返回 4。
老问题(虽然没有标记为已回答..)但认为这个例子除了克里斯蒂安·斯蒂伯的答案之外,这个例子使区别更加明确。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
它还表明,最好按最大在前(在本例中为双精度)的大小对成员进行排序,因为其他成员受该成员的约束。
对齐值与基本类型的 sizeof 值相同。
区别在于使用定义的数据类型,例如使用结构;例如
typedef struct { int a; double b; } S;
//cout<<alignof(s); outputp: 8;
//cout<<sizeof(S); output: 12;
因此,sizeof 值是给定数据类型所需的总大小;而对齐值是结构中最大元素的对齐要求。
对齐方式的使用:在特定对齐边界上分配内存。
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
sizeof
运算符为您提供实际类型或类型实例的大小(以字节为单位)。
alignof
运算符为您提供给定类型的任何实例所需的对齐方式(以字节为单位)。
数据在内存中按特定顺序排列,以使 CPU 更容易访问它。
-
alignof
通知对象在内存中的位置 -
sizeof
表示物体有多大,同时考虑到其部件的位置
基本类型对于alignof
和sizeof
具有相同的结果,因为它们仅作为其自身的一部分:例如。 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
P. 允许的简化有利于理解这个想法:)
- 向量 <int> a {N, 0} 和 int arr a[N] = {0} 的时间复杂度有什么区别
- 在 .h 文件中的类中声明静态变量和在.cpp文件中声明"global"变量有什么区别
- 我是C++编程的新手,这些代码之间有什么区别,我应该使用哪一个
- 返回常量对象引用 (getter) 和仅返回字符串有什么区别?
- Qt:remove() 和 rmdir() 有什么区别
- 这 4 个 lambda 表达式之间有什么区别?
- 将向量作为类>(值)<向量启动和向量<类>[值]有什么区别
- typedef 枚举和枚举类有什么区别?
- &C::c 和 &(C::c) 有什么区别?
- ascii 和 unicode 在处理级别有什么区别吗?
- C 中的常量限定符和 C++ 中的常量限定符有什么区别?
- "ABC" 和 "ABC" ) 在C++中有什么区别?
- 空指针常量 (nullptr)、空指针值和空成员指针值之间有什么区别?
- 引用捕获和在 lambda 中通过引用发送参数有什么区别 (C++)
- 两种访问I2C总线的方法有什么区别?
- 两种模板示例有什么区别?
- 这两种C++语法之间有什么区别?
- lua 5.0.2 模块和 5.3.5 有什么区别?
- C++中"typedef"、"using"、"namespace"和"using namespace"有什么区别?
- std::enable_if 和 std::enable_if_t 有什么区别?