这是一段似乎被接受而没有错误的代码:
#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.
我的问题是 Why does the C Standard allow it to compile? . 这类问题应该被诊断为错误,应该拒绝代码 . 为什么C标准允许翻译程序?
5 回答
对
strcmp
和printf
的调用,因为它们传递了不正确类型的参数(并且没有可用的隐式转换),所以是约束违规 .对于违反约束或语法规则的任何程序,C标准至少需要一条诊断消息 . 就标准而言,警告是完全有效的诊断消息 .
这是标准所说的内容(引用C11标准的N1570草案,第5.1.1.3节):
标准实际上要求拒绝源文件的唯一情况是它包含
#error
指令 . 第4节第4段:我个人更喜欢gcc和clang拒绝违反语法规则或约束的程序,但正如我所说,标准允许非致命警告 . 如果您希望拒绝此类程序,可以使用各种选项,例如
-std=c11 -pedantic-errors
. 请注意,默认情况下gcc和clang并不完全符合C标准,但这些非致命警告不是该不一致的示例 .您的问题似乎是基于C Standard以某种方式“允许此代码编译”的无效前提 . 实际上,C Standard没有“允许或不允许代码编译”这样的概念 .
如果代码无效,则标准要求编译器通过诊断消息告诉您 . 标准不要求编译器拒绝编译您的代码 . 他们仍然可以继续以一些实现定义的方式编译它 .
根据C标准,您的代码明显无效 . 标准C语言不允许隐式整数到指针转换 . 您的编译器通过诊断消息清楚地告诉您这一点 . 这足以使编译器满足标准的要求 .
之后所有的赌注都没有了 . 您的编译器可能会将其编译为“某些东西”,但这不是一个符合C的程序 . 它的行为不是由语言定义的 .
至于您收到的诊断消息的格式(以及它们是"warnings"还是"errors") - 这是您的编译器的问题 . 在C中,这是一个实施质量问题 . C标准与它无关 .
您可以通过提供
-pedantic-errors
标志,要求clang
将C语言约束违规报告为"errors" . 它不是完美的目的,但它会让编译器拒绝编译你的代码(如果这是你想要的) .那是一个很大的假设 .
要实际引用标准:
(5.1.1.3 note 9)
当遇到错误时,标准对编译器必须做的事情没有很大的限制,正是由于在许多情况下和许多平台上,“错误”实际上可以很好地定义(通过实现)扩展行为 . 从向开发人员提供有用信息的角度来看,在程序的以下部分中继续翻译和获取其他错误或信息也可能更好,而不是放弃第一个非常小的,仍然在句法上有效的难点 .
它还倾向于不对"translation"究竟涉及的内容做出太多假设(翻译阶段7基本上只是"something happens here") . 没有"machine code"的概念(不包括像J.5.7这样的扩展名)例如,在严格标准版本的语言中,标准不能禁止编译器发出它 .
一方面,标准明确允许整数到指针类型转换(参见C99 standard):
另一方面,标准要求在这种情况下进行明确的演员:
所以
printf(3)
显然不正确,但它是否会产生错误或警告似乎是 - 在这种情况下 - 受制于编译器 .如果您不想编译此类代码,请使用
-Werror
作为附加标志,将每个警告转换为编译错误,并且不会编译您的代码 .