iBu*_*Bug 21 c struct access-violation language-lawyer
具体来说,是下面的代码,标记下方的行,好吗?
struct S{
int a;
};
#include <stdlib.h>
int main(){
struct S *p;
p = malloc(sizeof(struct S) + 1000);
// This line:
*(&(p->a) + 1) = 0;
}
Run Code Online (Sandbox Code Playgroud)
人们在这里争论,但没有人给出令人信服的解释或参考.
他们的论点略有不同,但基本相同
typedef struct _pack{
int64_t c;
} pack;
int main(){
pack *p;
char str[9] = "aaaaaaaa"; // Input
size_t len = offsetof(pack, c) + (strlen(str) + 1);
p = malloc(len);
// This line, with similar intention:
strcpy((char*)&(p->c), str);
// ^^^^^^^
Run Code Online (Sandbox Code Playgroud)
Ant*_*ala 24
至少从1989年C的标准化以来的意图是允许实现检查数组访问的数组边界.
该成员p->a是类型的对象int.C11 6.5.6p7说
7出于[附加运算符]的目的,指向不是数组元素的对象的指针与指向长度为1的数组的第一个元素的指针的行为相同,其中对象的类型为其元素类型.
从而
&(p->a)
Run Code Online (Sandbox Code Playgroud)
是一个指向int; 但它也好像是一个指向长度为1的数组的第一个元素的指针,int作为对象类型.
现在6.5.6p8允许一个计算&(p->a) + 1哪个是指向刚好超过数组末尾的指针,因此没有未定义的行为.但是,这种指针的取消引用是无效的.从附录J.2中拼写出来,在以下情况下行为未定义:
将指针加到或减去数组对象和整数类型会产生一个指向数组对象之外的结果,并用作
*被计算的一元运算符的操作数(6.5.6).
在上面的表达式中,只有一个数组,一个(好像)只有一个元素.如果&(p->a) + 1被解除引用,则访问超出边界的数组,并且发生未定义的行为,即
行为[...],[C11]标准没有要求
随着说明说:
可能的未定义行为包括完全忽略具有不可预测结果的情况,在转换或程序执行期间以环境特征(有或没有发出诊断消息)的特定文档执行,终止翻译或执行(发布时)一条诊断信息).
最常见的行为是完全忽略这种情况,即表现就好像指针刚刚引用了内存位置,并不意味着从标准的角度来看其他类型的行为是不可接受的 - 标准允许每一个可以想象的并且难以想象的结果.
有人声称C11标准文本含糊不清,委员会的意图应该是确实允许这样做,以前它本来就没问题.这不是真的.阅读委员会对[1992年12月10日至C89的缺陷报告#017 ]的答复.
问题16
[...]
响应
对于数组数组,通过将单词object的使用解释为表示由指针的类型和值直接确定的特定对象,可以理解子条款6.3.6,第47页,第12-40行中的允许指针算法,而不是通过邻接与其相关的其他对象.因此,如果表达式超过这些权限,则行为未定义.例如,以下代码具有未定义的行为:
Run Code Online (Sandbox Code Playgroud)int a[4][5]; a[1][7] = 0; /* undefined */一些符合要求的实现可以选择诊断数组边界违规,而其他实现可以选择使用明显的扩展语义成功地解释这种尝试访问 .
(加粗强调我的)
没有理由不将同一个转移到结构的标量成员,特别是当6.5.6p7表示指向它们的指针应该被认为与指向长度为1的数组的第一个元素的指针相同时对象的类型作为其元素类型.
如果你想寻址连续的structs,你总是可以把指针指向第一个成员并将其作为指针转换为struct并向前推进:
*(int *)((S *)&(p->a) + 1) = 0;
Run Code Online (Sandbox Code Playgroud)
这是未定义的行为,因为您正在访问不是数组(int a内部struct S)作为数组的内容,并且超出范围.
实现您想要的正确方法是使用没有大小的数组作为最后一个struct成员:
#include <stdlib.h>
typedef struct S {
int foo; //avoid flexible array being the only member
int a[];
} S;
int main(){
S *p = malloc(sizeof(*p) + 2*sizeof(int));
p->a[0] = 0;
p->a[1] = 42; //Perfectly legal.
}
Run Code Online (Sandbox Code Playgroud)