3

在 C 标准的 Annex K(边界检查接口)中不断出现一句话:

....复制不应发生在重叠的对象之间。

例如,考虑 ,strcpy_s( char * restrict s1, rsize_t s1max, char const * restrict s2 )其中指定启用边界检查s1max的最大容量。s1

此时的“对象”究竟是什么s1,不能与“对象”重叠s2

会不会是...

  • s1[0]..s1[s1max](到缓冲区的末尾,即内存对象),

或者

  • s1[0]..s1[strnlen(s1, s1max)](到字符串的末尾,即字符串对象)?

如果是前者,我想知道缺乏一致性,因为我不知道s2缓冲区的大小,并且必须对“对象”应用不同的定义。

如果是后者,我想知道它是否不会破坏给出的“承诺”,因为可以想象,如果源字符串比原始字符串长,源字符串和最终(复制后)目标字符串可能会重叠。

这里“对象”的意图/预期定义是什么?

4

2 回答 2

3

我相信意图是这样的,s1max从 开始的字符在包括空终止符s1时不能与任何字符重叠。K.3.7.1.3p5说:s2

  1. strcpy_s在 s1max 指向的字符数组中写入的终止空字符(如果有)之后的所有元素在返回s1时采用未指定的值。strcpy_s[418]

脚注418说

  1. 这允许实现将字符从 复制s2s1,同时检查这些字符中的任何一个是否为空。s1这种方法可能会在发现第一个元素应该设置为空字符之前将一个字符写入每个元素。

但是,微软表示“如果重叠sourcedest则行为未定义”,因此这暗示实际上在这种情况下可能会发生任何事情。这似乎否定了边界检查接口的有用性。

于 2019-06-15T09:26:57.217 回答
0

这在标准中随处可见,不仅在可选的边界检查接口中,而且在强制库函数中,例如strcpy. 边界检查接口函数只是继承了相同的文本。

对象的正式定义是:

3.15
object
region 执行环境中的数据存储区,其内容可以表示值

基于此,字符串必须是包括空终止符在内的整个数组。因为如果在复制过程中以某种方式覆盖了空终止符,则诸如此类的函数strcpy会中断-它必须被视为(数组)对象的一部分。

似乎没有“重叠”一词的定义,但意图相当明确:防止出现这样的情况:

  char str[] = "foobar";
  strcpy(str+3,str);

一种可能的实现strcpywhile(*dst++ = *src++){}. 这会中断,因为它永远不会碰到空终止符,我们最终会写出越界。

restrict值得注意的是,您已经向编译器保证,当您将参数传递给需要指针的函数时,它们不会重叠。标准中关于重叠未定义的文本只是使其更加清晰。

在这个strcpy例子中,任何对什么点的左值访问dst都不允许修改什么str点,或者我们违反了restrict(C17 6.7.3)的定义,从而调用了未定义的行为。

据我所知,这始终是程序员的责任。restrict据我所知,没有编译器会针对调用方的违规提供诊断消息。

于 2019-06-17T08:27:22.400 回答