我正在寻找一个关于C++内存对齐的好(全面)文档,典型方法,编译器之间的差异以及常见的陷阱.只是为了检查我对这个主题的理解是否正确并学习新的东西.
这个问题的灵感来自于我使用以下构造的另一个问题的答案:
char const buf[1000] = ...;
unsigned int i = *reinterpret_cast<unsigned int*>(buf + shift); // shift can be anything
Run Code Online (Sandbox Code Playgroud)
它被批评为不符合内存对齐规则.你可以解释为什么这种方法从内存对齐的角度来看是有缺陷的?一个不起作用的例子将受到高度赞赏.我知道这通常是一种糟糕的方法,但我经常在网络协议实现中使用它,所以它比理论问题更实际.
另外请不要在这里提到严格别名,这是另一个问题.
我理解结构成员之间发生的填充,以确保各个类型的正确对齐.但是,为什么数据结构必须是最大成员对齐的倍数?我不明白最后需要填充.
该SP_DEVICE_INTERFACE_DETAIL_DATA结构:
typedef struct _SP_DEVICE_INTERFACE_DETAIL_DATA {
DWORD cbSize;
TCHAR DevicePath[ANYSIZE_ARRAY];
} SP_DEVICE_INTERFACE_DETAIL_DATA, *PSP_DEVICE_INTERFACE_DETAIL_DATA;
Run Code Online (Sandbox Code Playgroud)
如何在C#中声明它才能Marshal.SizeOf正常工作?
我没有分配动态缓冲区的问题.我只想以cbSize适当的,非硬编码的方式进行计算.
PInvoke.net的定义是错误的.
PInvoke.net的解释也是错误的:
Run Code Online (Sandbox Code Playgroud)SP_DEVICE_INTERFACE_DETAIL_DATA didd = new SP_DEVICE_INTERFACE_DETAIL_DATA(); didd.cbSize = 4 + Marshal.SystemDefaultCharSize; // trust me :)
不要相信他.
4 + Marshal.SystemDefaultCharSize仅在x86上有效.同样的sizeof(int) + Marshal.SystemDefaultCharSize.在x64上它失败了.
这是非托管C++给出的:
x86
结构尺寸A:5
设备路径偏移A:4
结构尺寸W:6
设备路径偏移W:4x64
结构尺寸A:8
设备路径偏移A:4
结构尺寸W:8
设备路径偏移W:4
我想每一个可能的组合StructLayout和MarshalAs参数,但我不能让它返回上述值.
什么是正确的声明?
我已经看到了无数问题的形式"我不喜欢填充如何关闭它",但还没有找到任何关于强制编译器提供额外填充的内容.
我的具体情况看起来像
struct particle{
vect2 s;
vect2 v;
int rX;
int rY;
double mass;
int boxNum;
};
Run Code Online (Sandbox Code Playgroud)
哪里vect2很简单struct {double x; double y;} vect2.为了使用SSE2,我需要能够加载一对双精度数,对齐到16字节边界.这曾经工作,直到我添加额外的int,将我的结构大小从48字节推到56字节.结果是段错误.
是否有某种编译器指令我可以使用"填充此结构使其成为16字节长的倍数",或"此结构具有16字节的对齐"?我知道我可以手动完成(例如,添加一个额外的字符[12]),但我真的只是告诉编译器(GCC,最好是ICC兼容),如果我改变它,就不必手动完成结构在未来.
我正在编写一个程序(在C++中),我需要在其中分配其起始地址应与缓存行大小对齐的数组.当我分配这些数组时,我也希望将内存初始化为零.
现在我使用posix_memalign函数工作了.这适用于获取内存对齐的数组,但数组未经过限制.有什么更好的函数可以用来在我初始化它们时将数组清零,或者我是否只需要为我编写一个单独的循环来解决它?
我已经在ideone.com上测试了这个代码并且16它应该输出.但是,当我在Visual Studio 2013中尝试它时,它显示8.它是编译器的错误还是缺少C++ 11支持?
#include <iostream>
#include <type_traits>
using namespace std;
using float_pack = aligned_storage<4 * sizeof(float), 16>::type;
int main() {
cout << alignment_of<float_pack>::value << endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我使用了alignment_of,因为MSVC不支持alignof.
编辑:我看到我无法16与之对齐aligned_storage.但为什么这个片段没问题呢?
#include <iostream>
#include <type_traits>
#include <xmmintrin.h>
using namespace std;
__declspec(align(16)) struct float_pack {
float x[4];
};
int main()
{
cout << alignment_of<float_pack>::value << endl;
}
Run Code Online (Sandbox Code Playgroud)
输出是16.这是否意味着编译器在使用扩展时可以提供更大的对齐?为什么我不能达到同样的效果aligned_storage?只是因为MSVC没有提供那个aligned_storage?
cppreference有一个引用:
每个对象类型都有称为对齐要求的属性,它是一个整数值(类型为std :: size_t,总是2的幂),表示可以分配此类对象的连续地址之间的字节数.
据我所知,这个参考文献是非规范性的.但是alignof(T)标准中没有关于价值的东西,而不是它alignof(std::max_align_t).
显然,对齐是2的幂.为什么对齐不是3?
许多C / C ++编译器(包括gcc和clang)都具有称为打包结构的功能。由于许多原因,它派上用场,但必须谨慎使用。一个潜在的陷阱是,您将指向结构成员的指针用作另一个函数的参数。现在,该函数不知道未对齐的指针。让我用一些代码说明一下我的意思:
#pragma pack(1)
typedef struct { int x; } uas;
#pragma pack()
void foo(int *f) {
// some code using the value of *f
}
void bar(uas *b) {
foo(&(b->x));
}
Run Code Online (Sandbox Code Playgroud)
int在32位计算机上的对齐方式通常为4。foo()如果f没有4字节对齐,则编译器现在可能会为此生成代码。在较旧的ARM体系结构中就是这种情况。
现在struct uas和其中的所有成员都具有1的对齐保证。显然,传递b->xto 的地址foo()是一个坏主意。
GCC和clang具有编译器警告(-Wcast-align),例如,通过将其转换char*为来触发该警告int*。使用指向压缩结构成员的指针,即使两者都支持,似乎也不会触发此警告。我也试过-Wall和-Wextra,但他们甚至不包括-Wcast-align。
我的主要问题是GCC,clang或任何其他支持打包结构的编译器是否有警告,该警告将由上述特定示例触发。看起来,如果编译器支持压缩结构,则必须发出这样的警告。
考虑在具有以下类型对齐方式的x64位Windows操作系统上工作:
据我了解,做这样的事情非常不好:
struct X_chaotic
{
bool flag1;
double d1;
bool flag2;
double d2;
bool flag3;
double d3;
//... and so on ...
};
Run Code Online (Sandbox Code Playgroud)
根据C ++对齐,缓存行和最佳实践 以及数据结构对齐,编写此代码应该更好/更快并且更紧凑:
struct X_alignOrder
{
double d1;
double d2;
double d3;
//... all other doubles ...
bool flag1;
bool flag2;
bool flag3;
//... all other bools ...
};
Run Code Online (Sandbox Code Playgroud)
成员以比对大小的顺序声明,从最高比对开始。
可以肯定地说按对齐大小对数据成员的声明进行排序是个好主意吗?您会说这是最佳做法吗?还是没有区别?
(由于C ++标准,我听说编译器无法重新排列定义的顺序,这甚至适用于在类的访问说明符块中声明的所有数据成员)
因为我从没读过这本书,所以无论是在Scott Meyers的书还是Bjarne Stroustrup的书中,我都不知道应该为我的日常工作重新排列数据声明的顺序。