[英]Pointer arithmetic on pointers to variably-modified types
以下是有效的 C 代码吗? (天马行空)
#include <stddef.h>
ptrdiff_t f(size_t n, void *x, void *y)
{
if (!n) return 0;
typedef unsigned char element[n];
element *a = x, *b = y;
return a - b;
}
随着-Werror=pointer-arith
clang 大声抱怨
<source>:8:14: error: subtraction of pointers to type 'element' (aka 'unsigned char [n]') of zero size has undefined behavior [-Werror,-Wpointer-arith]
return a - b;
~ ^ ~
而 gcc 编译代码却毫无怨言。
clang 认为正在发生的未定义行为是什么? 减法的可能性为零,因此不是指向数组元素或其他元素的有效指针? 没有执行数组访问,对吗? 所以应该不是这样的……
如果代码确实表现出未定义的行为,是否有一种简单的方法可以修改代码以使其完全符合要求,同时仍然使用指向 VM 类型的指针?
如果您的编译器支持 VLA,则发布的代码是有效的 C 代码。 请注意,C99 中引入的 VLA 在最新版本的 C 标准中已成为可选。
gcc和clang都能正确编译代码,可以使用Godbolt 的编译器资源管理器进行验证。
然而,如果n
参数的值恰好为空, clang 会发出关于潜在未定义行为的警告,未能识别出这种情况已通过显式测试处理。 问题不是减法的值,但类型的大小,这将是0
,如果n == 0
。 此警告并不是真正的错误,而是实施质量问题。
同样有争议的是,只有当a
和b
指向同一个数组或刚好经过它的最后一个元素时才定义a - b
。 因此x
和y
必须验证此约束并具有unsigned char (*)[n]
或兼容类型。 此规则有一个例外,用于将任何类型作为字符类型的数组进行访问,因此将指针传递给相同的int
数组是可以的,但以这种方式调用f
将是不正确的(尽管可能无害):
int x, y;
ptrdiff_t dist = f(sizeof(int), &x, &y);
编译器可以自由地发布诊断消息来吸引程序员对潜在问题的关注,事实上,在许多情况下,对于初学者和高级程序员来说,这样的警告都是救命稻草。 诸如-Wall
、 -Werror
和-Weverything
类的编译器选项非常有用,但在这种特殊情况下,如果-Werror
也处于活动状态,则需要添加-Wno-pointer-arith
以允许clang编译此函数。
另请注意,使用 C89 函数可以获得相同的结果:
ptrdiff_t f89(size_t n, void *x, void *y)
{
if (!n) return 0;
unsigned char *a = x, *b = y;
return (a - b) / n;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.