- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
在这两个示例中,通过偏移其他成员的指针来访问结构成员是否会导致未定义/未指定/实现定义的行为?
struct {
int a;
int b;
} foo1 = {0, 0};
(&foo1.a)[1] = 1;
printf("%d", foo1.b);
struct {
int arr[1];
int b;
} foo2 = {{0}, 0};
foo2.arr[1] = 1;
printf("%d", foo2.b);
C11 § 6.7.2.1 的第 14 段似乎表明这应该由实现定义:
Each non-bit-field member of a structure or union object is aligned in an implementation-defined manner appropriate to its type.
然后继续说:
There may be unnamed padding within a structure object, but not at its beginning.
但是,像下面这样的代码似乎相当普遍:
union {
int arr[2];
struct {
int a;
int b;
};
} foo3 = {{0, 0}};
foo3.arr[1] = 1;
printf("%d", foo3.b);
(&foo3.a)[1] = 2; // appears to be illegal despite foo3.arr == &foo3.a
printf("%d", foo3.b);
该标准似乎保证 foo3.arr
与 &foo3.a
相同,并且以一种方式引用它是合法且没有意义的另一个不是,但同样地,将外部 union 与数组添加应该突然使 (&foo3.a)[1]
合法是没有意义的。
因此,我思考第一个例子的推理也必须是合法的:
foo3.arr
保证与 &foo.a
foo3.arr + 1
和&foo3.b
指向相同的内存位置&foo3.a + 1
和 &foo3.b
因此必须指向相同的内存位置(从 1 到 2)&foo1.a
和&foo1.b
的布局应该和&foo3.a
完全一样> 和 &foo3.b
&foo1.a + 1
和 &foo1.b
因此必须指向相同的内存位置(来自 3 和 4)我发现一些外部资源表明 foo3.arr[1]
和 (&foo3.a)[1]
示例都是非法的,但是我一直无法在标准中找到具体的声明来实现这一点。即使它们都是非法的,也可以使用灵活的数组指针构造相同的场景,据我所知,确实具有标准定义的行为。
union {
struct {
int x;
int arr[];
};
struct {
int y;
int a;
int b;
};
} foo4;
原始应用程序正在考虑从一个结构字段到另一个结构字段的缓冲区溢出是否严格按照标准定义:
struct {
char buffer[8];
char overflow[8];
} buf;
strcpy(buf.buffer, "Hello world!");
println(buf.overflow);
我希望它能在几乎任何现实世界的编译器上输出 "rld!"
,但是这种行为是否由标准保证,或者它是未定义的还是实现定义的行为?
最佳答案
简介:这方面的标准是不充分的,关于这个话题和严格的别名有几十年的争论历史,没有令人信服的解决方案或建议来解决。
这个答案反射(reflect)了我的观点,而不是标准的强加。
首先:人们普遍认为,您的第一个代码示例中的代码是未定义行为,因为通过直接指针算法访问数组边界之外。
规则是 C11 6.5.6/8 。它说指针的索引必须保留在“数组对象”(或末尾的一个对象)内。它没有说明 哪个 数组对象,但通常认为在 int *p = &foo.a;
的情况下,“数组对象”是 foo .a
,而不是任何以 foo.a
为子对象的更大对象。
其次:人们普遍认为您的两个 union
示例都是正确的。该标准明确表示可以读取 union 体的任何成员;并且相关内存位置的任何内容都被解释为正在读取的 union 成员的类型。
您建议 union
正确意味着第一个代码也应该正确,但事实并非如此。问题不在于指定读取的内存位置;问题在于我们如何得出指定该内存位置的表达式。
即使我们知道 &foo.a + 1
和 &foo.b
是相同的内存地址,访问 int
也是有效的通过第二个并且通过第一个访问 int
无效。
人们普遍认为,您可以通过以其他不违反 6.5.6/8 规则的方式计算其地址来访问 int,例如:
((int *)((char *)&foo + offsetof(foo, b))[0]
或
((int *)((uintptr_t)&foo.a + sizeof(int)))[0]
关于 ((int *)&foo)[1]
是否有效,未普遍达成共识。有人说它与您的第一个代码基本相同,因为标准说“指向对象的指针,经过适当转换,指向元素的第一个对象”。其他人说它与我上面的 (char *)
示例基本相同,因为它遵循指针转换规范。一些人甚至声称这是一个严格的别名违规行为,因为它将结构别名为数组。
可能相关的是 N2090 - Pointer provenance proposal .这并不直接解决问题,也不建议废除 6.5.6/8。
关于c - 通过来自其他结构成员的偏移指针访问结构成员是否合法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51737910/
(注意:这个问题的动机是试图提出预处理器黑客来生成无操作分配来回答另一个问题: Macro that accept new object ...请记住这一点!) 这是一个人为的类: class foo
所以我尝试用java编写一个井字棋游戏。大部分已经完成,但是,如果有人选择了已被占用的空间,我无法返回无效的移动。 这是我试图弄清楚的代码。我认为由于空间是由数字 0 表示的(我的教授告诉我们这一点)
我想使用 Java 代码获取某些 Google 搜索引擎查询(在整个网络上)的估计结果数。 我每天只需要做很少的查询,所以一开始 Google Web Search API ,虽然已被弃用,但似乎已经
C++ Primer, 5e 的练习 4.20 询问表达式 iter++->empty(); 是否成立是合法的。假设iter是 vector::iterator . 这个表达式是合法的。我用gcc编译
C++ Primer, 5e 的练习 4.20 询问表达式 iter++->empty(); 是否成立是合法的。假设iter是 vector::iterator . 这个表达式是合法的。我用gcc编译
在 C++ 中,动态内存(取消)分配(malloc-free/new-delete)显然可以重复获得相同的内存范围,该内存范围被释放并按顺序再次分配。在多线程 C++ 中,这可能发生在多个线程中。 显
AesCryptoServiceProvider.LegalKeySizes 字段显示允许的位大小。 但是我不明白的是,如果这些都是真的,我如何才能成功利用 2048 位 key 长度(256 字节)
我有一个结构: typedef struct _n { int type; union { char *s; int i; }; } n; 当我
这个问题在这里已经有了答案: 关闭10年前. Possible Duplicate: int var = 1; void main() { int i = i; } 以下代码在g++和Visual C
根据 C++03 标准 (5.3.4/7): When the value of the expression in a direct-new-declarator is zero, the allo
我正在尝试创建一个生成器,使用 scalacheck 1.6.6 和规范 1.7 (scala 2.8.1) 生成(非零长度)合法的 unicode 字符串。 我希望我可以创建像这样的生成器: obj
这确实是一个关于 C++ 的问题,而不是 OpenGL。我正在关注 this OpenGL 教程(我刚开始),作者使用 C++(不是 C)。我的问题是 glVertexAttribPointer ta
由于 C++ 别名规则,您不能随意将 (一个 int*) 指向 char 数组,这似乎达成了一些共识。 从另一个问题——Generic char[] based storage and avoidin
我正在寻找一个通过 HTTP 接口(interface)发送文本消息的 SMS 网关。 但是,有些短信网关不允许设置发件人ID,允许的要么要求用户证明发件人ID是自己的手机号码,要么手动检查每个发件人
我们注意到您的应用要求用户注册个人信息才能访问非基于帐户的功能。在允许访问与用户无关的应用内容和功能之前,应用不能要求用户注册。 我们在注册时采用电子邮件 ID,因为所有数据都代表电子邮件 ID 在后
我一直在阅读 Bloch 和 Gafter 的 Java Puzzlers,并读到了第 10 个谜题 (Tweedledee)。这个谜题的本质是 provide declarations for th
这个问题确实符合标题:我很想知道造成这种差异的技术原因是什么,以及基本原理? std::shared_ptr sharedToVoid; // legal; std::unique_ptr uniqu
我试图做的是将数据框数据附加到现有的合法 excel 文件中。我使用了 openpyxl 中的 load_workbook() 函数,但它系统地返回了一个错误。这是一些在我的机器上崩溃的代码: fro
App 多次被 Apple 拒绝 “经过进一步审查,我们仍然发现您的应用使用了后台定位服务,但并未按照 iOS 人机界面指南的要求在位置模式警报中阐明其使用目的。” 在“NSLocationAlway
据我所知,像这样在 C++ 中实例化一个整数是合法的: int x = int(5); 作为一名 Java 程序员,我会假设这行代码调用传递“5”作为参数的整数的构造函数。我读到 int 不是一个类,
我是一名优秀的程序员,十分优秀!