【发布时间】:2019-12-20 23:29:07
【问题描述】:
在尝试利用 C99 函数原型语法为函数参数指定非空指针时,我遇到了 clang 和 gcc 之间的一些不一致的行为:
可以声明和定义一个函数来接收一个指向最小大小数组的非空指针。例如:
char *mystrcpy(char dest[restrict static 1], char const src[restrict static 1]);
声明函数mystrcpy 将非空受限指针指向char 数组。
这个定义比strcpy的标准定义更严格,使用了更经典的形式:
char *strcpy(char restrict *dest, const char restrict *src);
没有为 C 编译器提供关于非空参数的约束的信息。
我编写了一个测试程序来验证这两个原型的兼容性,并惊讶地发现它们确实兼容,尽管第一个比第二个包含更多信息。这些事实更令人惊讶:
- 启用所有警告后,clang 不会抱怨
strcpy接收空参数。 - gcc 确实抱怨
strcpy接收空参数,而不是抱怨mystrcpy,尽管它的定义明确。 - 将
strcpy或mystrcpy分配给使用这两种语法定义的函数指针不会导致任何警告。 - 通过函数指针将空指针传递给间接调用并不会在 clang 中触发直接调用的警告。
我的问题是:这些观察结果是否符合 C 标准,或者 gcc 和/或 clang 在函数参数的
[]内实现 C99 的static关键字时是否不正确?
代码如下:
#include <stdio.h>
#include <string.h>
static char *mystrcpy(char dest[restrict static 1], char const src[restrict static 1]) {
char *p = dest;
while ((*p++ = *src++) != '\0')
continue;
return dest;
}
static char *(*f1)(char *dest, const char *src) = strcpy;
static char *(*f2)(char *dest, const char *src) = mystrcpy;
static char *(*f3)(char dest[restrict static 1], char const src[restrict static 1]) = strcpy;
static char *(*f4)(char dest[restrict static 1], char const src[restrict static 1]) = mystrcpy;
int main() {
char a[100];
strcpy(a, "a");
strcpy(a, "");
strcpy(a, NULL);
strcpy(a, a);
strcpy(NULL, a);
strcpy(NULL, NULL);
mystrcpy(a, "a");
mystrcpy(a, "");
mystrcpy(a, NULL);
mystrcpy(a, a);
mystrcpy(NULL, a);
mystrcpy(NULL, NULL);
f1(a, "a");
f1(a, "");
f1(a, NULL);
f1(a, a);
f1(NULL, a);
f1(NULL, NULL);
f2(a, "a");
f2(a, "");
f2(a, NULL);
f2(a, a);
f2(NULL, a);
f2(NULL, NULL);
f3(a, "a");
f3(a, "");
f3(a, NULL);
f3(a, a);
f3(NULL, a);
f3(NULL, NULL);
f4(a, "a");
f4(a, "");
f4(a, NULL);
f4(a, a);
f4(NULL, a);
f4(NULL, NULL);
return 0;
}
gcc 输出:它只抱怨使用NULL 参数直接调用strcpy。
clang 的输出:只抱怨使用 NULL 参数直接调用 mystrcpy。
较新版本的 gcc 也抱怨为 2 个限制限定参数传递相同的指针,但仍然不支持 static 最小长度说明符(请参阅 Godbolt session):
【问题讨论】:
-
restrict的目的是允许编译器优化,而不是强制违反约束的错误。如果程序员编写的代码不符合使用restrict的别名断言 - 例如通过将 NULL 指针传递给您的mystrcpy()- 那么行为是未定义的。这意味着不需要诊断。换句话说,标准根本不需要您期望的行为。因此,两个编译器都是正确的——从某种意义上说,未定义的行为意味着它们被允许做任何事情, -
我不知道 clang,但我很确定 gcc 只是忽略了最小数组长度限定符。不过,您使用的是旧版本,而我最近没有对当前版本进行测试。
-
@Shawn: gcc 的
nonnull属性是导致strcpy衰减的原因。我想使用具有相同语义的标准功能,但与 clang 不同,恐怕 gcc 不支持它。 -
我想这可能会回答你的问题:stackoverflow.com/a/3430353/5218277
-
对
static的使用也有相同的评论 - 例如,请参阅stackoverflow.com/questions/3430315/…
标签: c language-lawyer c99 c11 null-pointer