c - 是否可以通过成员地址访问超过结构的大小,并分配足够的空间?

标签 c struct language-lawyer access-violation

具体来说,下面的代码,标记下面的行,行吗?

struct S{
    int a;
};

#include <stdlib.h>

int main(){
    struct S *p;
    p = malloc(sizeof(struct S) + 1000);
    // This line:
    *(&(p->a) + 1) = 0;
}

人们争论不休here ,但没有人给出令人信服的解释或引用。

他们的论据略有不同,但本质上是相同的

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);
//                ^^^^^^^

最佳答案

至少自 1989 年 C 标准化以来,其意图是允许实现检查数组边界以进行数组访问。

成员p->aint 类型的对象。 C11 6.5.6p7说是

7 For the purposes of [additive operators] a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.

因此

&(p->a)

是指向 int 的指针;但它也好像是一个指向长度为 1 的数组的第一个元素的指针,对象类型为 int

现在6.5.6p8允许计算 &(p->a) + 1 这是一个指向刚好超过数组末尾的指针,因此没有未定义的行为。但是,取消引用此类指针是无效的。来自 Appendix J.2在明确说明的地方,在以下情况下行为未定义:

Addition or subtraction of a pointer into, or just beyond, an array object and an integer type produces a result that points just beyond the array object and is used as the operand of a unary * operator that is evaluated (6.5.6).

在上面的表达式中,只有一个数组,一个(好像)只有 1 个元素的数组。如果 &(p->a) + 1 被取消引用,则长度为 1 的数组被越界访问并且 undefined behaviour发生,即

behavior [...], for which [The C11] Standard imposes no requirements

随着note saying that :

Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).

最常见的行为是完全忽略这种情况,即表现得好像指针刚刚引用了内存位置,但这并不意味着其他类型的行为是 Not Acceptable 标准的观点——标准允许每一个可以想象和无法想象的结果。


有人说C11的标准文本写得含糊不清,委员会的意图应该是确实允许这样,如果是以前就可以了。这不是真的。阅读委员会对 [缺陷报告 #017,日期为 1992 年 12 月 10 日至 C89] 的回应部分。

Question 16

[...]

Response

For an array of arrays, the permitted pointer arithmetic in subclause 6.3.6, page 47, lines 12-40 is to be understood by interpreting the use of the word object as denoting the specific object determined directly by the pointer's type and value, not other objects related to that one by contiguity. Therefore, if an expression exceeds these permissions, the behavior is undefined. For example, the following code has undefined behavior:

 int a[4][5];

 a[1][7] = 0; /* undefined */ 

Some conforming implementations may choose to diagnose an array bounds violation, while others may choose to interpret such attempted accesses successfully with the obvious extended semantics.

(bolded emphasis mine)

没有理由不将相同的 传递给结构的标量成员,尤其是当 6.5.6p7 规定指向它们的指针应被视为与 行为相同时指向长度为 1 的数组的第一个元素的指针,其元素类型为对象的类型

如果您想寻址连续的struct,您总是可以获取指向第一个成员 的指针并将其转换为指向struct< 的指针 并推进它:

*(int *)((S *)&(p->a) + 1) = 0;

关于c - 是否可以通过成员地址访问超过结构的大小,并分配足够的空间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47224138/

相关文章:

在c中从char*转换为char[]

c# 将结构转换为另一个结构

c# - 为什么在结构中使用 LINQ 时必须复制 "this"(如果我这样做可以)?

c - 需要帮助了解\n、\b 和\r 将如何呈现 printf 输出

c++ - 模棱两可的注入(inject)类名不是错误

c - 前景信号 tcsetpgrp c

c - 在 MPI 中发送/接收 3D 数组

c - 在 Arduino 上每 y 秒执行一次函数 x 秒

c - 从二进制文件读取结构体 (C)

c++ - 为什么这是 C++ 中的前向声明?