写出边界矩阵

Writing out of boundary matrix

提问人:MRn0b0dy 提问时间:11/14/2023 最后编辑:ChrisMRn0b0dy 更新时间:11/14/2023 访问量:94

问:

考试中的练习需要在纸上写下执行以下程序后记忆获得的值。问题是在这个程序中,当 for 循环到达元素 3 时,我们正在访问未分配的内存。写入未分配内存的值是实际存储的还是未定义的行为,因此不写入它是正确的,因为我们不知道实际发生了什么。按照程序:

#include <stdio.h>

int main()
{
  int k[5][7] = {0};
  int *p, *z;
 
  p = k[1];
  *p = 12;
 
  for (int i = 0; i < 4; i++){
    z = p + 8;
    *z = *p + 2;
    p = z;
  }

  return 0;
}

运行程序时,我遇到了分段错误,但在考试更正中,教授仍然分配了未分配内存区域中写入的值。那么,将我们正在写入未分配内存的值写在纸上是否正确?

c 内存 未定义行为

评论

4赞 Ted Lyngmo 11/14/2023
我没有看到这个问题
1赞 Eugene Sh. 11/14/2023
从可读性的角度来看,这段代码非常糟糕。它应该展示什么?
4赞 Weather Vane 11/14/2023
超出数组边界的写入是未定义的行为。因此,不写它是不正确的,除非它是 UB 的演示。
1赞 mrn0b0dy 11/14/2023
@tadman 因此,就我而言,由于我们正在执行像 char x[N] 这样的分配,因此我们被限制在分配边界内。
1赞 Lundin 11/14/2023
如果通过,@tadman仍然会调用未定义的行为,从而执行超过 7 的指针算术。存储在那里并不重要。但是,会有所不同,因为从 malloc 返回的块还没有有效的类型,因此我们可以像普通的一维数组一样访问它。通常,对于数组类型,严格别名/有效类型的标准规则是模糊指定的。int (*k)[7] = malloc(sizeof(int[5][7]); ... z = p + 8;k[1]intint (*k)[7] = malloc(sizeof(int[5][7]); int*p = (int*)k; p+=8;

答:

2赞 John Bollinger 11/14/2023 #1

那么,将我们正在写入未分配内存的值写在纸上是否正确?

C 语言规范没有为该问题的任何特定答案提供任何理由,因为程序执行了多个未定义的行为。

但这并没有直接关系,因为是你的教授决定他们会接受什么答案。他们可能会接受一个答案,解释为什么行为是未定义的,而不是给出请求的内存布局,但他们更有可能希望你假设一些特定的行为并基于此做出答案。

我不太在意这样的问题,但如果我面对它,那么我可能会做上述所有事情:解释所涉及的未定义的行为,然后描述一个特定的、合理的行为选择,作为对记忆结果内容的答案的答案,然后给出该行为所暗示的答案。这涵盖了所有基础。

2赞 Lundin 11/14/2023 #2

指针算术的规则(包括 的使用)在加法运算符的 C 标准章节 (C17 6.5.6 §8) 中指定。最重要的部分:[]

如果指针操作数和结果都指向 对同一数组对象的元素,或超过数组对象的最后一个元素的元素,计算 不得产生溢流;否则,行为是未定义的。如果结果指向过去 数组对象的最后一个元素,它不应用作一元 * 运算符的操作数,即 评价。

  • p指向数组 。对于上述规则,多维数组无关紧要。k[1]k
  • p只允许在此数组的范围内进行指针算术。k[1]
  • 作为特殊规则,允许在数组末尾的 1 项正好执行指针算术。所以会很明确。pp + 7
  • 但这是未定义的行为。p + 8

基本上,多维数组可以保证在内存中相邻分配。在内部数组 的末尾之后有一个有效的分配位置。但是,我们可能无法使用基于指向 的指针的指针算术来计算该“过了末尾”项的地址。指针算术本身可能会导致生成不正确的代码,因为编译器可以自由地假设对 through / 的任何访问都不会更改数组中的项。intk[1]k[1]k[1]pzk[2]

例如,我们可以将以下代码添加到循环的末尾:

if(k[2][1]==0)
  puts("zero");

理论上,编译器可以自由地将其替换为非条件,因为代码中的任何内容都不允许更改此数组项。puts("zero")

因此,考试问题的正确答案是:这是未定义的行为,任何事情都可能发生。