【问题标题】:C char pointer lengthC char 指针长度
【发布时间】:2017-05-27 14:33:54
【问题描述】:

这是 Coursera 上的测验(未评分)。问题是,以下代码可能评估为什么?正确的答案是 127 和 0(其他选项是崩溃,-1、128。为什么下面的代码可能评估为 0?我理解为什么它会评估为 127。它是否与 char 字节未初始化一样简单,因此随机的?它也可能计算为 0 到 127 之间的任何 # 吗?

int foo(void) {

    char bar[128];

    char *baz = &bar[0];

    baz[127] = 0;

    return strlen(baz);

}

【问题讨论】:

  • 没有一个您列出的可能答案是正确的。
  • coursera 上有什么课程?
  • 软件安全。这是一个“资格测验”,旨在衡量您是否可以完成课程。
  • 扩展@tofro 的评论:您可能的结果之一是偏离了一个,但这个想法(我认为)是正确的。

标签: c arrays initialization strlen


【解决方案1】:

代码的行为是不确定的。我的意思是答案可以是 0 到 127 之间的任何值。

strlen 将读取直到 bar[127] 的未初始化内存,但不包括 bar[127],这 将作为终止条件。

但由于该数组由char 元素组成,读取这些数据不是未定义,因为char 类型不能有陷阱表示。只是它们包含 indeterminate 值。

(如果bar 具有静态存储持续时间,情况将完全不同。那么答案将始终为零)。


下面的大部分 cmets 对此答案的错误表述做出了反应,该表述表明行为未定义。

【讨论】:

  • 我不确定它是否未定义。没有超出缓冲区限制的访问。它只是读取不确定的值。
  • @StoryTeller 我想说你必须在标准中提供一个引用,以排除 char 具有陷阱值。
  • OK,6.2.6.1,第 5 段似乎相关:某些对象表示不需要表示对象类型的值。如果对象的存储值具有这样的表示形式并且由不具有字符类型的左值表达式读取,则行为未定义。如果这种表示是由通过不具有字符类型的左值表达式修改对象的全部或任何部分的副作用产生的,则行为未定义。这种表示称为陷阱表示。提到了“字符类型”。
  • @StoryTeller:这并不矛盾,但它通过字符类型(即char 系列的任何成员)而不是 UB 进行阅读。我怀疑这是(除其他原因外)memcpystruct 复制为块,即使存在具有不确定值的填充字节。
【解决方案2】:

之前这个答案有错误信息,这个案例不调用undefined behavior


编辑答案:

TL;DR我们无法给出明确的答案,代码包含不确定的行为。

详细地说,char bar[128]; 是一个自动局部变量,如果没有显式初始化,将包含 indeterminate 值。

引用C11,第 §6.7.9 章

如果具有自动存储持续时间的对象未显式初始化,则其值为 不定。 [....]

在您的代码中,您只为数组的一个成员赋值,在索引 127 处。剩余元素的值仍然不确定。

尝试将该数组(基本上是指向数组的第一个元素的指针)传递给strlen(),导致读取这些值(以搜索空终止符)并且由于不确定的值,不能保证它会在任何特定位置找到空终止符。

  • 它可以很好地在第一个元素中找到一个空终止符(ASCII 值 0)并返回 0。
  • 它也无法在任何其他数组元素中找到任何空终止符(ASCII 值 0),直到最后一个并返回 127。
  • 它可以在数组的任意位置找到一个空终止符并返回该计数。

所以,这个问题没有明确的答案。


注意: 弥补我的错误理解,防止读者进一步陷入同样的​​陷阱)支持>

这里,读取未初始化的值(即不确定的值)不会引发未定义的行为,正如人们可能认为的那样。

单行:为对象取地址。

关于这个话题有详细的讨论,参考here

【讨论】:

  • char 数组将包含介于 -128 和 127 之间的不确定值。读取它们不是 UB。返回的值将在 0 到 127 之间不确定,因为它将返回遇到的第一个 \0 的位置。由于 baz[127] = 0,没有 UB。
  • @neuro(或以上评论的支持者)怎么回事?我正在查看附件 J.2 “具有自动存储持续时间的对象的值在不确定时使用”。我错过了什么?
  • @neuro 你说得对,这里的问题是不确定的值。但是阅读它们可能是UB。说可能是 UB,不是 UB,是错误的。最好将中间值视为 UB。只有在明确假设该类型在实现中不能有陷阱值的情况下,答案才能说“no-UB”。
  • @neuro:没有 C1x。而C标准是C11,仅此而已。自C89甚至C99以来已经有很长一段时间了。没有进一步的注释/标签,我们假设问题的当前标准。而且标准也很好地允许陷阱表示,但读取字符有一个例外。不确定如何同时遵守这两者或有什么用途,但从标准的措辞来看,它可以有一个陷阱表示。
  • @Olaf 对不起,先生耽搁了,但据我所知,签名的char 可以有陷阱表示,所以就是这样。是什么让这个非 UB?
【解决方案3】:

有两件事可以使此代码成为 UB,如 here 所列。这是一个自动存储时长的变量,它的地址被占用了,所以第一种情况肯定不适用。

该变量也不允许包含陷阱表示,根据陷阱表示的定义 C11 6.2.6.1/5 强调我的:

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

这意味着该数组包含未指定的值。这种未指定值的一种情况可能是值 0,在数组中的任何位置,都被视为空终止符。

【讨论】:

  • @Bathsheba 好吧,这件事的真相必须在语言律师沼泽的深处找到。而 C++ 则不同。
猜你喜欢
  • 2021-11-26
  • 2021-01-26
  • 1970-01-01
  • 2012-12-27
  • 1970-01-01
  • 2012-03-19
  • 2021-08-11
  • 1970-01-01
  • 2016-06-10
相关资源
最近更新 更多