c - 零大小数组在结构内未对齐
问题描述
当在未打包的结构中声明时,我发现关于零大小数组的内存对齐的奇怪行为。我突然想到抛出零大小的数组来标记结构的某些部分。我不认为它有什么特别的用途,但我仍然觉得可能会产生一些有趣的代码。
这种行为是编译器特定的还是所有编译器都会给出相同的结果(它们不会对齐零大小的数组)?
以下面的代码为例:
#include <stdio.h>
struct tagOBJ
{
char Label1[0];
int A;
char Label2[0];
int B;
int C;
};
typedef struct tagOBJ OBJ;
int main()
{
OBJ a;
printf("%d == %d\n", &a.A, a.Label1);
printf("%d == %d\n", &a.B, a.Label2);
return 0;
}
在上述情况下,输出如预期:
6422292 == 6422292
6422296 == 6422296
但是,如果我将另一个变量添加到暗示填充的结构中,则给定的地址是不同的:
struct tagOBJ
{
char Label1[0];
int A;
char D;
char Label2[0];
int B;
int C;
};
typedef struct tagOBJ OBJ;
生产
6422288 == 6422288
6422296 == 6422293 // ! false
解决方案
这种行为是编译器特定的还是所有编译器都会给出相同的结果(它们不会对齐零大小的数组)?
C 语言本身不允许大小为零的数组。您的代码无效,并且代码的行为是"undefined"。任何编译器都可以做任何事情,所以答案是肯定的,它是特定于编译器的,并且也没有要求行为应该是一致的。
还:
printf("%d == %d\n", &a.A, a.Label1);
也是无效的。%d
期望int
-&a.A
是一个指针。
本着 C 的精神,添加访问器函数会更好:
void *OBJ_public(const OBJ *t) { return (void*)&t->A; }
void *OBJ_private(const OBJ *t) { return (void*)&t->C; }
如果您真的想使用宏,那么您可以这样做:
#define OBJ_PUBLIC A
#define OBJ_PRIVATE C
#define PUBLIC(TYPE, VAR) (void*)(&(VAR).TYPE##_PUBLIC)
#define PRIVATE(TYPE, VAR) (void*)(&(VAR).TYPE##_PRIVATE)
int main() {
OBJ a;
printf("%p\n", PUBLIC(OBJ, a));
}
6422296 == 6422293 // ! false
Gcc 具有零长度数组的扩展。而且我认为-肯定是错误的。char
member 在最后一个 struct 成员结束时开始,所以它“粘合”并指向前一个成员之后的下一个字节 - 所以它指向(char*)&a.D + 1
,它可能等于也可能不等于下一个成员的地址,所以它取决于关于填充。在这种情况下,下一个成员是,因此和int
之间需要一些填充。char
int
推荐阅读
- sql - 在 avg() over () 子句内四舍五入不起作用 - SQL Server
- reactjs - 测试反应模态不能使关闭模态测试通过
- javascript - Highcharts Gantt - 在依赖路径悬停时显示工具提示
- css - 如何垂直对齐 div 中的文本(boostrap 4)?
- node.js - nodejs如何加载.env文件?
- powershell - 在线程作业中使用 SetImage
- r - 如何在 R 中为积分()做 for 循环
- python - 从第一个时代开始,我在 Keras 的损失函数中得到了一个 NaN
- python - 试图从网站上抓取数据,必须导航多个标签
- python - 使用 Paramiko 在远程机器中通过 sudo 执行 SFTP 操作