您可以使用严格的 C 来执行此操作,但您需要采取某些预防措施以确保符合标准。我将在下面解释这些,但您需要采取的预防措施是:
(0) 通过包含此声明确保没有填充:
extern int CompileTimeAssert[
sizeof(some_struct) == NumberOfMembers * sizeof(char *) ? 1 : -1];
(1)从结构的地址初始化指针,而不是成员的地址:
char **p = (char **) (char *) &struct1;
(我怀疑上述内容不是必需的,但我必须从 C 标准中插入更多推理。)
(2) 按以下方式递增指针,而不是使用++
或加一:
p = (char **) ((char *) p + sizeof(char *));
以下是解释。
(0) 中的声明充当编译时断言。如果结构中没有填充,则结构的大小等于成员数乘以成员的大小。然后三元运算符的计算结果为 1,声明有效,编译器继续。如果有填充,则大小不相等,三元运算符的计算结果为 -1,声明无效,因为数组不能有负大小。然后编译器报告错误并终止。
因此,包含此声明的程序只有在结构没有填充时才会编译。此外,该声明不会占用任何空间(它只声明一个从未定义过的数组),并且它可能会与其他表达式重复(如果它们的条件为真,则评估为数组大小为 1),因此可以使用不同的断言使用相同的数组名称进行测试。
第 (1) 项和 (2) 项处理指针算术通常保证仅在数组中工作的问题(包括末尾的概念标记元素)(根据 C 2011 6.5.6 8)。但是,C 标准在 C 2011 6.3.2.3 7 中对字符类型做出了特殊保证。指向结构的指针可以转换为指向字符类型的指针,并且它将产生指向结构的最低寻址字节的指针. 结果的连续增量,直到对象的大小,产生指向对象剩余字节的指针。
在(1)中,我们从 C 2011 6.3.2.3 7 中知道,这(char *) &struct1
是一个指向struct1
. 当转换为 时(char **)
,它必须是指向第一个成员的指针struct1
(特别感谢 C 2011 6.5.9 6,它保证相等的指针指向同一个对象,即使它们具有不同的类型)。
最后,(2)解决了数组算术不能直接保证在我们的指针上工作的事实。也就是说,p++
将增加一个不严格在数组中的指针,因此 6.5.6 8 不能保证算术。所以我们将其转换为 a char *
,对于它的增量保证在 6.3.2.3 7 时有效,我们增加它四次,我们将其转换回char **
. 这必须产生一个指向下一个成员的指针,因为没有填充。
有人可能会声称添加char **
(比如 4)的大小与 1 的四个增量不同char
,但该标准的意图当然是允许以合理的方式处理对象的字节。但是,如果您想避免这种批评,您可以更改+ sizeof(char *)
为+1+1+1+1
(在大小为 4 的实现上)或+1+1+1+1+1+1+1+1
(在大小为 8 的地方)。