vbe*_*nar 17 c undefined-behavior language-lawyer
我有以下代码:
uint8_t buffer[16];
uint8_t data[16];
uint8_t buffer_length = 16;
uint8_t data_length = 0;
memcpy(buffer + buffer_length, data, data_length);
Run Code Online (Sandbox Code Playgroud)
memcpy
应该是空操作,因为data_length
为零。然而buffer + buffer_length
,点就在分配的内存之外。我想知道它是否会触发某种未定义的行为?我应该memcpy
用一个额外的包起来吗if
?
我知道任何合理的实现memcpy
都会很好地工作,但是这个问题更多地是从代码正确性的角度出发并避免未定义的行为。
nie*_*sen 22
正如 Stephen C 的回答所指出的,C17 规范对于这是否定义良好有点模糊。
然而,C23 规范在 7.1.4 部分的脚注中澄清了这一点,指出
如果函数参数被描述为数组,则传递给函数的指针应具有一个值,以便所有地址计算和对对象的访问(如果指针确实指向此类数组的第一个元素,则这将是有效的)有效的。
脚注(235)如下:
例如,这包括传递一个指向数组末尾一位且大小为 0 的有效指针,或者使用大小为 0 的任何有效指针。
句子的第一部分明确地将 OP 情况定义为明确定义的。
添加此声明可以被视为承认 C17 规范在这一点上不够明确,因此不能排除 C17 编译器的实现者可能会善意地解释该标准,从而使这种情况不是定义的行为。
然而,C23 应该消除这种不确定性。
该代码具有明确定义的行为。
\n函数在状态下排序的“字符串处理函数” memcpy
(C17 7.24.1):
\n\n如果参数声明为
\nsize_t n
指定函数的数组长度,n
则在调用该函数时可以将值设置为零。除非在本节中特定函数的描述中另有明确说明,否则此类调用中的指针参数仍应具有有效值,如 7.1.4 中所述。
C17 7.1.4 中有关传递给标准库函数的数组参数的部分有些相关:
\n\n\n如果函数参数被描述为数组,则实际传递给函数的指针应具有一个值,以便所有地址计算和对对象的访问(如果指针确实指向此类数组的第一个元素,则该值将有效)数组)实际上是有效的。
\n
(然而,参数memcpy
不一定是一个或多个数组。但在本例中它们都是。)
地址计算和对数组一项的以下访问是由指针算术规则定义的,特别是关于加法运算符的 C17 6.5.6 \xc2\xa78,相关部分是这个:
\n\n\n如果指针操作数和结果都指向同一个数组对象的元素,或者超过数组对象的最后一个元素,则求值不会产生溢出;否则,行为是未定义的。如果结果指向数组对象的最后一个元素,则不得将其用作所求值的一元 * 运算符的操作数。
\n
因此,只要我们不取消引用该位置,“将一项指向数组末尾”这一特殊规则buffer + buffer_length
就明确允许。在这种情况下不会发生这种情况。如果我们buffer + buffer_length + 1
这样写,这将是无效的地址计算和未定义的行为。
归档时间: |
|
查看次数: |
2255 次 |
最近记录: |