K&R 的代码示例是否合规?

Have the code examples from K&R ever been conforming?

The C Programming Language 由 Brian Kernighan 和 Dennis Ritchie 编写,包含很多示例,例如这个著名的示例(K&R 第二版 1.1):

#include <stdio.h>

main()
{
  printf("hello, world\n");
}

这里我注意以下问题:


我的问题:

在任何版本的标准中,K&R 第 2 版中的任何代码是否曾经是符合程序

不,K&R 书中的程序从未 符合程序 1) (C17 4/7)标准。

  • 在 C90 (ISO 9899:1990) 中,由于缺少 return 语句,代码调用了未定义的行为。
  • 在 C99 (ISO 9899:1999) 及更高版本中,由于隐式 int.
  • ,代码无法编译

来源如下。


关于隐式 int,C90 和更高版本之间函数 return 类型的一个主要区别可以在这里找到:

C90 6.7.1 函数定义

The return type of a function shall be void or an object type other than array.
/--/
If the declarator includes an idenfifier list, the types of the parameters may be declared in a following declaration list. Any parameter that is not declared has type int.

C17 6.9.1 函数定义

The return type of a function shall be void or a complete object type other than array type.
/--/
If the declarator includes an identifier list, the types of the parameters shall be declared in a following declaration list. In either case, the type of each parameter is adjusted as described in 6.7.6.3 for a parameter type list; the resulting type shall be a complete object type.

主要区别在于“完整对象类型”的措辞,完整对象类型的定义是基本类型之一或指向基本类型的指针 (C17 6.2.5)。我们可以得出结论,隐式 int 在 C90 中被允许作为 return 类型或作为 (non-prototype) 参数列表的一部分。


关于没有 return 声明,此文本始终用于一般功能:

C90 6.6.6.4

If a return statement without an expression is executed and the value of the function call is used by the caller, the behavior is undefined. Reaching the } that terminates a function is equivalent to executing a return statement without an expression.

C17 6.9.1/12

If the } that terminates a function is reached, and the value of the function call is used by the caller, the behavior is undefined.

不过,main()是特例,在C99中加入了例外:

C99 5.1.2.2.3

If the return type of the main function is a type compatible with int, a return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument; reaching the } that terminates the main function returns a value of 0.

而在 C90 中,等效文本表示:

C90 5.1.2.2.3

A return from the initial call to the main function is equivalent to calling the exit function with the value returned by the main function as its argument. If the main function executes a return that specifies no value, the termination status returned to the host environment is undefined.


关于空参数列表,它已从 C90 到 C17 标记为过时。查看未来的语言方向,例如 C17 6.11(或 C90 6.9,相同的文本):

6.11.6 Function declarators
The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.

6.11.7 Function definitions The use of function definitions with separate parameter identifier and declaration lists (not prototypeformat parameter type and identifier declarators) is an obsolescent feature.

但这并不意味着使用该功能的代码不符合标准,至少不符合 ISO 9899:2018。根本不推荐这种做法,在 K&R 第二版发布时也不推荐这种做法。


1) 第 4 章的 C17:

A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any strictly conforming program.

A conforming program is one that is acceptable to a conforming implementation.

A strictly conforming program shall use only those features of the language and library specified in this International Standard. It shall not produce output dependent on any unspecified, undefined, or implementation-defined behavior, and shall not exceed any minimum implementation limit.

这意味着符合规范的程序可以使用non-portable的符合规范实现的特性,但它不能改变严格符合规范的程序的行为,例如调用在标准。

我编译了程序

#include <stdio.h>

main()
{
    printf("hello, world\n");
}

在两个 well-regarded 编译器下,gccclang。只是为了好玩,我还添加了 --pedantic 选项。据我所知,这两种编译器都被认为是“符合标准”的,我相信这是他们的作者所追求的目标之一。

两个编译器都生成了一个打印 hello, world 的可执行文件。根据定义

A conforming program is one that is acceptable to a conforming implementation

,我的结论是程序符合要求。

对于程序是否符合 C89 的问题,我不作任何判断。

虽然我已经有几年没有研究 K&R2 中的代码示例了,但我相信 most/all 其余的代码示例同样符合规范,尽管有各种教学或其他捷径可能会使它们不严格符合规范。

一些引述:

"The first edition, published February 22, 1978, was the first widely available book on the C programming language. Its version of C is sometimes termed K&R C (after the book's authors), often to distinguish this early version from the later version of C standardized as ANSI C."

(来源Wikipedia

换句话说,K&R 第 1 版早于任何官方 C 标准。当时唯一的规范是 Dennis M. Ritchie 的“The C Reference Manual”。


"In April 1988, the second edition of the book was published, updated to cover the changes to the language resulting from the then-new ANSI C standard, particularly with the inclusion of reference material on standard libraries."

(来源Wikipedia

换句话说,K&R 第 2 版与第一个正式的 ANSI C 标准(也称为 C89)“对齐”。


然而,在 K&R 第 2 版发布时,C89 尚未完成。根据 ANSI C.

上的维基百科页面

"In 1983, the American National Standards Institute formed a committee, X3J11, to establish a standard specification of C. In 1985, the first Standard Draft was released, sometimes referred to as C85. In 1986, another Draft Standard was released, sometimes referred to as C86. The prerelease Standard C was published in 1988, and sometimes referred to as C88."

(来源Wikipedia

因此,它们可能是 K&R 所说的与 ANSI C 标准之间的差异。

根据定义,任何被一致 C 实现“接受”的源文本或其集合都是“一致 C 程序”。因为实现被赋予以任何不影响任何严格符合 C 程序行为的方式扩展语言的广泛自由度,所以任何源文本 T 否则不会成为符合 C 程序可以通过修改符合 C 实现,以便如果给它的程序与 T 不匹配,它会正常处理它,如果提供 T 的副本,它的行为就好像它被提供一些它会接受的其他程序一样。

虽然这似乎是一个荒谬的宽泛定义,但它满足了 C 标准委员会的主要目标之一,即确保如果任何现有程序可以完成一项任务,则该任务可以由符合 C 标准的程序完成程序。

至于这些程序是否严格符合C89,这有点难以回答。该标准表示,如果执行失败到 main() 结束,它将 return 主机环境的未定义值,并且对这样做的后果没有任何要求,这表明这样的行为会调用未定义的行为。另一方面,标准 also 对程序 returns EXIT_SUCCESS 时发生的情况没有强加任何要求,也没有对 returns 时发生的情况强加任何要求EXIT_FAILURE,也不是 return 其他值。因此,所有此类操作都可以被视为调用未定义行为。另一方面,以这种方式看待事物会使任何终止的程序都不可能严格符合。

我认为解释该标准的最合理方式是说,一个程序的执行落在 main() 的末尾,它放弃了它可能不得不影响执行环境在它终止后所做的事情的任何控制。如果宿主环境在程序退出后可以执行的所有操作过程都同样可以接受,那么程序没有做任何事情来影响采取哪种操作过程就不是缺陷。

在考虑未指定 return 值的程序 或与此相关的任何程序 是否“严格符合”时,不能仅仅检查源文本,但还必须考虑应用要求。如果需要一个程序以某种顺序分别输出字符 x 和 y 一次,那么下面将是一个严格符合要求的程序来实现:

#include <stdio.h>
int outputx(void) { return printf("x"); }
int outputy(void) { return printf("y"); }
int main(void)
{
  return outputx() + outputy() && printf("\n") && 0;
}

另一方面,如果需要一个程序来输出“xy”,那么上面的程序就不是一个严格符合该目的的程序。因此,我会说,如果某些任务的应用程序要求指定程序必须使用其 return 值来影响主机环境,则落在 main 末尾的程序将不是严格符合 C 程序完成那个任务。但是,如果这种对主机环境的影响不是任务应用程序要求的一部分,那么严格符合 C 程序可以放弃这种控制。

以下引用:

来自 N1570 第 4 节第 7 段:

A conforming program is one that is acceptable to a conforming implementation. (*) 5) Strictly conforming programs are intended to be maximally portable among conforming implementations. Conforming programs may depend upon nonportable features of a conforming implementation.

未定义的行为在 3.4.3 中定义:

behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements

来自C99 Rationale,谈一致性的定义[强调原文]:

A strictly conforming program is another term for a maximally portable program. The goal is to give the programmer a fighting chance to make powerful C programs that are also highly portable, without seeming to demean perfectly useful C programs that happen not to be portable, thus the adverb strictly.

程序在未设置 return 值的情况下退出的事实可能使其成为 non-portable,但标准通过调用 non-conforming 故意避免“贬低”non-portable 程序].