分配的内存少于指向数组的指针的指定大小

Jac*_*lan 24 c malloc language-lawyer pointer-to-array

在 C 中,如果我们只访问分配内存中的元素,那么向数组指针分配内存不足是“合法的”吗?或者这会调用未定义的行为吗?

int (*foo)[ 10 ];                  //Pointer to array of 10 ints
foo = malloc( sizeof( int ) * 5 ); //Under-allocation! 
                                   //Only enough memory for 5 ints
//Now we only ever access (*foo)[ 0 - 4 ]
Run Code Online (Sandbox Code Playgroud)

如果这本身不是未定义的行为,那么访问另一个不相关的对象(其内存地址恰好落在数组未分配部分的地址空间内)是否会导致严格别名冲突?

dbu*_*ush 11

这是未定义的行为

\n

foo应该指向 类型的对象(或对象数组中的第一个)int[10]这被认为是数组类型的对象,在C 标准第 6.2.5p20 节中定义

\n
\n

数组类型描述 具有特定成员对象类型(称为元素类型)的连续分配的非空对象集。只要指定数组类型,元素类型就应该是完整的。数组类型的特征在于其元素类型和数组中的元素数量。据说数组类型是从其元素类型派生的,如果其元素类型是 T ,则该数组类型有时称为 \xe2\x80\x98\xe2\x80\x98array of T \xe2\x80\x99\ xe2\x80\x99。从元素类型\n构造数组类型称为 \xe2\x80\x98\xe2\x80\x98array\n类型推导\xe2\x80\x99\xe2\x80\x99

\n
\n

我用粗体突出显示的部分是重要的部分。因此Anint[10]是连续分配的 10 个类型对象的集合int

\n

您没有分配足够的空间,因此*foo具有类型的表达式int[10]访问该类型的对象,但这样做会读取已分配内存段的末尾。

\n


Joh*_*ger 7

正如 @dbush 在他的回答中所描述的,数组被定义为元素类型(C17 6.2.5/20)的连续分配的非空对象集。显然,malloc( sizeof( int ) * 5 )没有为int[10].

但我发现很难正式支持该答案的最后部分,声称尺寸差异使得(例如)(*foo)[4]具有未定义的行为。这个结论似乎有道理,但标准在哪里实际上是这么说的呢?

这里的主要问题之一是(动态)分配的对象没有声明的类型,只有在某些情况下,有效类型由它们的访问方式和访问方式决定。(C17 6.5/6 和脚注 88)。我们确实知道成功时,malloc(n)返回一个指向大小对象的指针n(C17 7.22.3.4/2),但是我们如何将未定义的行为专门归因于与描述大小大于的对象的有效类型的对象的关联n

我最终决定将这些点连接起来的最佳方法如下。假设 是o一个大小为 的已分配对象nT是一个具有 的完整类型sizeof(T) > n,并且o通过类型为 的左值进行读取或写入T。然后第 6.5/6 段将有效类型归属T于 object o,但由于o的大小不足,我们必须得出结论,它的表示构成了类型的陷阱表示T(C17 3.19.4)。第 6.2.6.1/5 段重申了“陷阱表示”的定义,并让我们到达我们想要去的地方:

某些对象表示不需要表示对象类型的值。如果对象的存储值具有此类表示形式,并且由不具有字符类型的左值表达式读取,则行为未定义。如果这样的表示是由副作用产生的,即通过不具有字符类型的左值表达式修改对象的全部或任何部分,则行为是未定义的。这种表示称为陷阱表示。

(强调是添加的。)

  • @alinsoar,正如这个答案已经说过的,C 定义“陷阱表示”在 C17 第 3.19.4 节中给出。即:“不需要表示对象类型的值的对象表示”。就是这样。您会发现上面引用的第 6.2.6.1/5 段开头重申了这一点。如果通过“具体链接”您询问与硬件架构或行为的关联,则不存在具体链接。规范中的其他大部分内容也没有具体的链接。创建此类链接是*实现*的作用。 (2认同)