这篇文章的内容主要是介绍 C 标准。
当一个 C 语言程序在两个不同的编译器下得到两个不同的结果时,以哪个为准呢?
例如,下面这个简单的 C 语言程序:
void main() { }上面的程序当主函数的返回类型是 void 时,在 gcc 下编译失败,但是在 Turbo C 编译下没问题。那我们应该如何决定一个 C 语言程序是否合法呢?
我们再看下面这个示例,它在不同的编译器下产生不同的结果。
#include<stdio.h> int main() { int i =1; printf("%d %d %d\n", i++, i++, i); return 0; } 2 1 3 - using g++ 4.2.1 on Linux.i686 1 2 3 - using SunStudio C++ 5.9 on Linux.i686 2 1 3 - using g++ 4.2.1 on SunOS.x86pc 1 2 3 - using SunStudio C++ 5.9 on SunOS.x86pc 1 2 3 - using g++ 4.2.1 on SunOS.sun4u 1 2 3 - using SunStudio C++ 5.9 on SunOS.sun4u 1 2 3 - my mac: Apple clang version 11.0.0 (clang-1100.0.33.8)本示例来自于: Stackoverflow
那么上面那个编译器是正确的呢?
上面所有问题的答案均要靠 C 语言编程标准来决定,因此我们需要看看上面的问题在 C 标准下是怎么说的。
什么是 C 语言编程标准?
最新的 C 语言编程标准是 ISO/IEC 9899:2011,也就是我们熟知的 C11 ,因为它的终稿发布于 2011 年,在此之前,采用的是 C99。 C11 标准的最终版本可以在这里获取到。在这里可以查看 C 语言编程标准的历史。
我们是否可以从 C 语言编程标准中知道所有的语法行为结果?
C 语言编程标准中留出了一下行为,对于一些 C 语法是未定义的,还有一些是未指明的。这可以简化规范并允许实现中的某些灵活性。例如,在 C 语言中,任何自动变量在初始化前的使用都会产生未定义的行为,其子表达式的求值顺序也未指定。这可以使编译器在提交这样的程序时可以做任何最简单或最有效的事情。
所以,对于上面两个示例,其结论是什么?
我们先来看第一个示例,关于主函数,我们看看 C 标准中是什么说的:
程序入口调用的函数被命名为 main。 The implementation declares no prototype for this function. 它应该被定义为有一个 int 类型的返回值并且无参: int main(void) { /* ... */ } 或者有两个参数(一般是 argc 和 argv,这里的参数名字其实可以随意): int main(int argc, char *argv[]) { /* ... */ } or equivalent;10) or in some other implementation-defined manner.所以根据 C 标准,主函数的返回类型是不允许返回空类型的,但是在一些 C 编译器下允许的(不推荐),我们下面来看看第二个例子,实际上第二个例子中的语句在 C 标准中是未指定的行为。
The order in which the function designator, arguments, and subexpressions within the arguments are evaluated in a function call (6.5.2.2).在 C 语言编程过程中,如果遇到未定义或未指定的行为语法时,应该怎么办?
作为一个程序开发人员,如果使用的编程语法是未定义或未指定的,那将不是一个 good idea,这样的程序应该被避免存在,因为它在不同的编译环境下可能会产生不同的行为。