为什么C标准允许编译没有错误?

时间:2017-02-11 18:18:45

标签: c language-lawyer

这是一段似乎被接受而没有错误的代码:

#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标准允许翻译程序?

5 个答案:

答案 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)

  

并且应该拒绝代码。

这是一个很大的假设。

实际引用标准:

  

只要仍然正确翻译了有效的程序,实现就可以自由地生成任意数量的诊断。它也可能成功翻译无效的程序。

5.1.1.3 note 9

标准对遇到错误时编译器必须做的事情没有很大的限制,正是因为在许多情况下和许多平台上,&#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);
}

strcmpprintf的调用,因为它们传递了不正确类型的参数(并且没有可用的隐式转换),约束违规

对于违反约束或语法规则的任何程序,C标准至少需要一条诊断消息。就标准而言,警告是完全有效的诊断信息。

以下是标准所说的内容(引用C11标准的N1570草案,第5.1.1.3节):

  

符合要求的实施应至少产生一种诊断   消息(以实现定义的方式标识)如果a   预处理翻译单元或翻译单元包含一个   违反任何语法规则或约束,即使行为是   也明确指定为未定义或实现定义。   在其他情况下不需要生成诊断消息。

标准实际上要求源文件被拒绝的唯一情况是它包含#error指令。第4节第4段:

  

实施不能成功转换预处理   翻译单元包含 #error 预处理指令,除非它   是条件包含跳过的组的一部分。

我个人更喜欢gcc和clang拒绝违反语法规则或约束的程序,但正如我所说,标准允许非致命警告。如果您希望拒绝此类程序,可以使用各种选项,例如-std=c11 -pedantic-errors。请注意,默认情况下gcc和clang并不完全符合C标准,但这些非致命警告不是该不符合的示例。