这是一段似乎被接受而没有错误的代码:
#include <stdio.h>
#include <string.h>
int main() {
if (strcmp(1, 2))
printf(3);
}
使用clang -std=c11 -Weverything
进行编译会产生4个警告:
badstrcmp.c:5:16: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'const char *' [-Wint-conversion]
if (strcmp(1, 2))
^
/usr/include/string.h:77:25: note: passing argument to parameter '__s1' here
int strcmp(const char *__s1, const char *__s2);
^
badstrcmp.c:5:19: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'const char *' [-Wint-conversion]
if (strcmp(1, 2))
^
/usr/include/string.h:77:43: note: passing argument to parameter '__s2' here
int strcmp(const char *__s1, const char *__s2);
^
badstrcmp.c:6:16: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'const char *' [-Wint-conversion]
printf(3);
^
/usr/include/stdio.h:259:36: note: passing argument to parameter here
int printf(const char * __restrict, ...) __printflike(1, 2);
^
badstrcmp.c:6:16: warning: format string is not a string literal (potentially insecure) [-Wformat-security]
printf(3);
^
badstrcmp.c:6:16: note: treat the string as an argument to avoid this
printf(3);
^
"%s",
4 warnings generated.
我的问题是为什么C标准允许它编译?。这类问题应该被诊断为错误,应该拒绝代码。为什么C标准允许翻译程序?
答案 0 :(得分:6)
你的问题似乎是基于一个无效的前提,C标准以某种方式#34;允许这个代码编译&#34;。实际上,C标准没有“#34;允许或不允许代码编译等概念”。
如果代码无效,标准要求编译器通过诊断消息告诉您。标准不要求编译器拒绝编译您的代码。他们仍然可以继续以一些实现定义的方式编译它。
根据C标准,您的代码明显无效。标准C语言不允许隐式整数转换为指针。您的编译器通过诊断消息清楚地告诉您这一点。这足以使编译器满足标准的要求。
之后所有的赌注都被取消了。您的编译器可能会将其编译为&#34;某些&#34;,但这不是一个符合C的程序。它的行为不是由语言定义的。
至于您收到的诊断消息的格式(以及它们是&#34;警告&#34;或&#34;错误&#34;) - 这是您的编译器的问题。在C中,它是实施质量问题。 C标准与它无关。
您可以要求clang
报告C语言约束违规行为&#34;错误&#34;提供-pedantic-errors
标志。它不是完美的目的,但它会让编译器拒绝编译你的代码(如果你想要的话)。
答案 1 :(得分:5)
并且应该拒绝代码。
这是一个很大的假设。
实际引用标准:
只要仍然正确翻译了有效的程序,实现就可以自由地生成任意数量的诊断。它也可能成功翻译无效的程序。
标准对遇到错误时编译器必须做的事情没有很大的限制,正是因为在许多情况下和许多平台上,&#34;错误&#34;实际上可能是(通过实现)良好定义的扩展行为。从向开发人员提供有用信息的角度来看,在程序的以下部分中继续翻译和获取其他错误或信息也可能更好,而不是放弃第一个非常小的,仍然在句法上有效的难点。
它也不愿做出太多关于究竟是什么&#34;翻译&#34;涉及(翻译阶段7基本上只是&#34;这里发生的事情&#34;)。没有&#34;机器代码&#34;的概念。 (例如,在语言的严格标准版本中不包括J.5.7之类的扩展名),因此标准不能禁止编译器发出它。
答案 2 :(得分:4)
一方面,标准显式允许整数到指针类型的转换(参见C99 standard):
6.3.2.3指针 ... (5)整数可以转换为任何指针类型。除了以前 指定,结果是实现定义的,可能不是 正确对齐,可能不指向引用的实体 类型,可能是一个陷阱表示.56)
另一方面,标准要求在这种情况下进行明确演员:
6.5.4强制运算符...(3)除了6.5.16.1的约束允许之外,涉及指针的转换应通过显式转换来指定
所以printf(3)
显然不正确,但它是否会产生错误或警告似乎是 - 在这种情况下 - 受制于编译器。
答案 3 :(得分:3)
如果您不想编译此类代码,请使用-Werror
作为附加标志,将每个警告转换为编译错误,并且不会编译您的代码。
答案 4 :(得分:3)
#include <stdio.h>
#include <string.h>
int main() {
if (strcmp(1, 2))
printf(3);
}
对strcmp
和printf
的调用,因为它们传递了不正确类型的参数(并且没有可用的隐式转换),约束违规。
对于违反约束或语法规则的任何程序,C标准至少需要一条诊断消息。就标准而言,警告是完全有效的诊断信息。
以下是标准所说的内容(引用C11标准的N1570草案,第5.1.1.3节):
符合要求的实施应至少产生一种诊断 消息(以实现定义的方式标识)如果a 预处理翻译单元或翻译单元包含一个 违反任何语法规则或约束,即使行为是 也明确指定为未定义或实现定义。 在其他情况下不需要生成诊断消息。
标准实际上要求源文件被拒绝的唯一情况是它包含#error
指令。第4节第4段:
实施不能成功转换预处理 翻译单元包含
#error
预处理指令,除非它 是条件包含跳过的组的一部分。
我个人更喜欢gcc和clang拒绝违反语法规则或约束的程序,但正如我所说,标准允许非致命警告。如果您希望拒绝此类程序,可以使用各种选项,例如-std=c11 -pedantic-errors
。请注意,默认情况下gcc和clang并不完全符合C标准,但这些非致命警告不是该不符合的示例。