零长度数组
我正在重构一些旧代码,并发现一些包含零长度数组的结构(如下)。 当然,警告被 pragma 抑制,但我无法通过包含此类结构的“新”结构创建(错误 2233)。 数组“byData”用作指针,但为什么不使用指针呢? 或者长度为1的数组? 当然,没有添加任何评论来让我享受这个过程...... 有什么理由使用这样的东西吗? 重构这些有什么建议吗?
struct someData
{
int nData;
BYTE byData[0];
}
注意:它是 C++、Windows XP、VS 2003
I am working on refactoring some old code and have found few structs containing zero length arrays (below). Warnings depressed by pragma, of course, but I've failed to create by "new" structures containing such structures (error 2233). Array 'byData' used as pointer, but why not to use pointer instead? or array of length 1? And of course, no comments were added to make me enjoy the process...
Any causes to use such thing? Any advice in refactoring those?
struct someData
{
int nData;
BYTE byData[0];
}
NB It's C++, Windows XP, VS 2003
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
是的,这是一个 C-Hack。
创建任意长度的数组:
现在您有一个 someData 对象,其中包含指定长度的数组。
Yes this is a C-Hack.
To create an array of any length:
Now you have an object of someData with an array of a specified length.
不幸的是,有多种原因导致您在结构末尾声明零长度数组。 它本质上使您能够从 API 返回可变长度结构。
Raymond Chen 就此主题发表了一篇出色的博客文章。 我建议您看一下这篇文章,因为它可能包含您想要的答案。
请注意,在他的帖子中,它处理的是大小为 1 而不是 0 的数组。
这是因为零长度数组是标准的较新条目。他的帖子应该仍然适用于您的问题。http://blogs.msdn.com/oldnewthing/archive/2004 /08/26/220873.aspx
编辑
注意:尽管 Raymond 的帖子说 0 长度数组在 C99 中是合法的,但实际上在 C99 中仍然不合法。 您应该使用长度为 1 的数组,而不是此处的长度为 0 的数组
There are, unfortunately, several reasons why you would declare a zero length array at the end of a structure. It essentially gives you the ability to have a variable length structure returned from an API.
Raymond Chen did an excellent blog post on the subject. I suggest you take a look at this post because it likely contains the answer you want.
Note in his post, it deals with arrays of size 1 instead of 0.
This is the case because zero length arrays are a more recent entry into the standards.His post should still apply to your problem.http://blogs.msdn.com/oldnewthing/archive/2004/08/26/220873.aspx
EDIT
Note: Even though Raymond's post says 0 length arrays are legal in C99 they are in fact still not legal in C99. Instead of a 0 length array here you should be using a length 1 array
这是一个古老的 C hack,允许灵活大小的数组。
在 C99 标准中,这不是必需的,因为它支持 arr[] 语法。
This is an old C hack to allow a flexible sized arrays.
In C99 standard this is not neccessary as it supports the arr[] syntax.
您关于“为什么不使用大小为 1 的数组”的直觉是正确的。
该代码执行了“C struct hack”错误,因为零长度数组的声明违反了约束。 这意味着编译器可以在编译时立即拒绝您的黑客攻击,并提供停止翻译的诊断消息。
如果我们想要进行黑客攻击,我们必须偷偷地通过编译器。
进行“C struct hack”(与可追溯到 1989 年 ANSI C 的 C 方言兼容,可能更早)的正确方法是使用大小为 1 的完全有效的数组:
此外,而不是
sizeof struct someData
,byData
之前部分的大小计算方法如下:在
byData
中分配一个struct someData
,空间为42字节,然后我们将使用:请注意,即使在数组大小为零的情况下,此
offsetof
计算实际上也是正确的计算。 你看,sizeof
整个结构可以包含填充。 例如,如果我们有这样的内容:由于
ul
成员,struct hack
的大小很可能被填充以进行对齐。 如果unsigned long
是四个字节宽,那么sizeof (struct hack)
很可能是8,而offsetof(struct hack, foo)
几乎是8当然可以 5.offsetof
方法是获取数组之前结构体前一部分的准确大小的方法。这就是重构代码的方法:使其符合经典的、高度可移植的结构黑客。
为什么不使用指针呢? 因为指针占用了额外的空间并且必须进行初始化。
不使用指针还有其他充分的理由,即指针需要地址空间才能有意义。 struct hack 是可外部化的:也就是说,在某些情况下,这种布局符合外部存储(例如文件、数据包或共享内存区域),在这些情况下您不需要指针,因为它们没有意义。
几年前,我在内核和用户空间之间的共享内存消息传递接口中使用了 struct hack。 我不想在那里有指针,因为它们只对生成消息的进程的原始地址空间有意义。 软件的内核部分使用不同地址处的自己的映射来查看内存,因此一切都基于偏移计算。
Your intution about "why not use an array of size 1" is spot on.
The code is doing the "C struct hack" wrong, because declarations of zero length arrays are a constraint violation. This means that a compiler can reject your hack right off the bat at compile time with a diagnostic message that stops the translation.
If we want to perpetrate a hack, we must sneak it past the compiler.
The right way to do the "C struct hack" (which is compatible with C dialects going back to 1989 ANSI C, and probably much earlier) is to use a perfectly valid array of size 1:
Moreover, instead of
sizeof struct someData
, the size of the part beforebyData
is calculated using:To allocate a
struct someData
with space for 42 bytes inbyData
, we would then use:Note that this
offsetof
calculation is in fact the correct calculation even in the case of the array size being zero. You see,sizeof
the whole structure can include padding. For instance, if we have something like this:The size of
struct hack
is quite possibly padded for alignment because of theul
member. Ifunsigned long
is four bytes wide, then quite possiblysizeof (struct hack)
is 8, whereasoffsetof(struct hack, foo)
is almost certainly 5. Theoffsetof
method is the way to get the accurate size of the preceding part of the struct just before the array.So that would be the way to refactor the code: make it conform to the classic, highly portable struct hack.
Why not use a pointer? Because a pointer occupies extra space and has to be initialized.
There are other good reasons not to use a pointer, namely that a pointer requires an address space in order to be meaningful. The struct hack is externalizeable: that is to say, there are situations in which such a layout conforms to external storage such as areas of files, packets or shared memory, in which you do not want pointers because they are not meaningful.
Several years ago, I used the struct hack in a shared memory message passing interface between kernel and user space. I didn't want pointers there, because they would have been meaningful only to the original address space of the process generating a message. The kernel part of the software had a view to the memory using its own mapping at a different address, and so everything was based on offset calculations.
值得指出的是,在我看来,进行尺寸计算的最佳方法是在上面链接的 Raymond Chen 文章中使用的。
第一个条目相对于所需分配末尾的偏移量也是所需分配的大小。 在我看来,这是一种非常优雅的尺寸计算方式。 可变大小数组的元素类型是什么并不重要。 offsetof(或Windows中的FIELD_OFFSET或UFIELD_OFFSET)始终以相同的方式编写。 没有 sizeof() 表达式会意外混乱。
It's worth pointing out IMO the best way to do the size calculation, which is used in the Raymond Chen article linked above.
The offset of the first entry off the end of desired allocation, is also the size of the desired allocation. IMO it's an extremely elegant way of doing the size calculation. It does not matter what the element type of the variable size array is. The offsetof (or FIELD_OFFSET or UFIELD_OFFSET in Windows) is always written the same way. No sizeof() expressions to accidentally mess up.