0

The C Programming Language by Brian Kernighan and Dennis Ritchie contains a lot of examples such as this famous one (K&R 2nd edition 1.1):

#include <stdio.h>

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

Here I note the following issues:

  • No return type.

    Writing functions with no return type was allowed in C90 which the second edition of the book claims to follow. These will default to int in C90. It is invalid C in later versions of the language.

  • No return statement.

    A function with a return type and no return statement was not well-defined in C90. Writing main() with no return being equivalent to return 0; was a feature added in C99.

  • Empty parameter list of main().

    This is valid C still (as of C17) but has always been an obsolescent feature even in C90. (Upcoming C23 talks of finally getting rid of K&R style functions.)


My question:

Was any code in K&R 2nd edition ever a conforming program, in any version of the standard?

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Bookmarked. Will be used to close many dupes. :) – Sourav Ghosh May 17 '22 at 14:12
  • @SouravGhosh Rather, I got fed up with having this discussion over and over with K&R fans. For questions regarding valid forms of main(), I think we should still use [What should main() return in C and C++?](https://stackoverflow.com/questions/204476/what-should-main-return-in-c-and-c) But then I'm partial since I wrote an answer there. – Lundin May 17 '22 at 14:15
  • This question seems to overlook the distinction between *conforming* and *strictly conforming*. – Steve Summit May 17 '22 at 16:49
  • @SteveSummit Not at all, I even carefully read the definitions before posting. The term _conforming_ means that there may be implementation-defined, non-portable aspects present in the program, unlike _strictly conforming_ which allows no poorly-defined behavior at all. But a conforming program should not contain undefined behavior, or the term would hold no meaning. – Lundin May 18 '22 at 06:20
  • @Lundin I am not a first-tier language lawyer, but I would say that the simple fact that the programs are accepted by conforming compilers means that they'e conforming (and that the answer to the posted question is, unhesitatingly but perhaps uninterestingly, "yes"). I don't think you get to make up your own rules, like "a conforming program should not contain undefined behavior". And `main()` with nothing between the parentheses isn't undefined behavior anyway. – Steve Summit May 18 '22 at 21:14
  • @SteveSummit I have added quotes regarding the definition of a conforming implementation in the answer, for your convenience. Notably, compilers like gcc and clang do not claim to be conforming compilers unless you add the `-pedantic` option. – Lundin May 19 '22 at 06:08

4 Answers4

2

By definition, any source text or collection thereof which is "accepted" by a Conforming C Implementation is a "Conforming C Program". Because implementations are given broad latitude to extend the language in any way which does not affect the behavior of any Strictly Conforming C Programs, any source text T which would not otherwise be a Conforming C Program could be turned into a Conforming C Program by modifying a Conforming C Implementation so that if it were given program that doesn't match T, it would process it normally, and if fed a copy of T it would behave as though it were fed some other program that it would accept.

While this may seem an absurdly broad definition, it satisfies one of the major goals of the C Standards Committee, which was to ensure that if any existing programs could accomplish a task, the task could be accomplished by a Conforming C Program.

As for whether the programs were Strictly Conforming under C89, that's a bit harder to answer. The Standard says that if execution falls through the end of main() it will return an Undefined Value to the host environment, and imposes no requirements about the consequence of doing so, which would suggest that such an action would invoke Undefined Behavior. On the other hand, the Standard also imposes no requirements upon what happens if a program returns EXIT_SUCCESS, nor what happens if it returns EXIT_FAILURE, nor if it returns some other value. Thus, all such actions could be viewed as invoking Undefined Behavior. On the other hand, viewing things in such fashion would make it impossible for any program which terminates to be Strictly Conforming.

I think the most reasonable way of interpreting the Standard would be to say that a program whose execution falls through the end of main() waives any control it might have had to affect what the execution environment does once it terminates. If all courses of action the host environment could perform after a program exits would be equally acceptable, a program's failure to do anything to influence which course of action is taken would not be a defect.

In considering whether a program that fails to specify a return value, or any program for that matter, is "Strictly Conforming", one cannot merely examine the source text, but must also consider the application requirements. If one needs a program to output the characters x and y once each, in some order, the following would be a strictly conforming program that accomplishes that:

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

If, on the other hand, one need a program to output "xy", the above would not be a strictly conforming program for that purpose. Thus, I would say that if the application requirements for some task specify that a program must use its return value to influence the host environment, a program that falls through the end of main would not be a Strictly Conforming C Program to accomplish that task. If, however, such influence over the host environment is not part of the application requirements for a task, then a Strictly Conforming C Program could waive such control.

Citations below:

From N1570 section 4 pararaph 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.

Undefined Behavior is defined in 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

From the C99 Rationale, talking about the definition of conformance [emphasis original]:

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.

The fact that a program exits without setting a return value may make it non-portable, but the Standard deliberately avoids "demeaning" non-portable programs by calling them non-conforming.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • "By definition, any source text or collection thereof which is "accepted" by a Conforming C Implementation is a "Conforming C Program"" Quotation needed as this is clearly wrong, see chapter 4 of any version of the C standard. Invoking undefined behavior is clearly not allowed by a conforming implementation, since undefined behavior would affect the behavior of a strictly conforming program. It's really that simple, period. – Lundin May 20 '22 at 06:10
  • Overall this is a very low quality answer since you are just pulling things out of a hat instead of using valid sources and references. – Lundin May 20 '22 at 06:10
  • @Lundin: Citations added. I know it's popular for people to pretend that only portable programs are "Conforming", but that ignores the words and intentions of the authors of the Standard. – supercat May 20 '22 at 15:18
  • @Lundin This novel construction of yours that "invoking undefined behavior is clearly not allowed" is something that strikes me as being pulled out of a hat. – Steve Summit May 20 '22 at 15:25
  • @SteveSummit: Where am I saying that it's clearly not allowed? To the contrary, I'm saying that it has always been common for correct-but-non-portable programs to rely upon implementations behaving "in a documented manner characteristic of the environment" in situations where the Standard imposes no requirements. What historically made C useful is that tasks *not imagined by the Standard* can be accomplished through the use of such constructs. The notion that UB implies "erroneous" emerged around 2005, and contradicts the wording of Standard and the intentions documented in the Rationale. – supercat May 20 '22 at 15:40
  • @supercat I was replying to Lundin there! – Steve Summit May 20 '22 at 15:41
  • @SteveSummit: Ooops. `:*3` His philosophy is unfortunately not his alone. It is also shared by the maintainers of clang and gcc, both of which interpret the Standard as an invitation to turn into a Weird Machine at the drop of a hat. In gcc, that can be triggered by multiplying two values of type unsigned short, even if the result is ignored, and in clang it can be triggered by a function receiving arguments that would cause an endless loop, even if code as written never makes use of any values computed therein. – supercat May 20 '22 at 15:43
  • @SteveSummit: Unfortunately, a religion has formed around the C Standard, oblivious to the fact that it was never intended to fully describe nor deprecate the dialects that most people were using, but merely describe *areas of commonality among them*. Too many compiler maintainers today fail to realize that the gaps in the Standard aren't defects in the language, but rather *what made it so useful* by allowing implementations targeting different platforms and purpose *to fill in the gaps in whatever way would best suit those platforms and purposes*. – supercat May 20 '22 at 15:57
  • If undefined behavior is taken as not altering the behavior of a strictly conforming program, then anything goes and then the term strictly conforming does not exist. And in case it does not exist, neither does standardization. – Lundin May 21 '22 at 13:38
  • @Lundin: The purpose of the C Standard was to recognize a core set of features that should be common to all C implementations. It was intended that this set of features be sufficient to give programmers a "fighting chance" to write portable programs *to perform tasks which could be accomplished by portable programs*, but since many of the tasks for which C was and continues to be used involved concepts that could only be meaningful to a small portion of C implementations, the Standard relied upon implementations extending the semantics of the language by e.g. processing constructs... – supercat May 21 '22 at 17:05
  • ... "in a documented manner characteristic of the environment". Since the authors of the Standard stated in the published Rationale document that they did not wish to preclude the use of C as a "high-level assembler", those who insist that those who would use the language in that fashion are "abusing it" could only be telling the truth if the authors of the Standard were lying about their intentions. Were they? – supercat May 21 '22 at 17:08
  • I had hoped for a more definitive-seeming consensus, but I guess it is not to be. Awarding to the bounty to the (narrowly) highest-voted answer, as promised. – Steve Summit Jun 06 '22 at 23:23
  • @Lundin Iin C11 "conforming program" is defined through "conforming implementation" and "conforming implementation" is defined through "strictly conforming program". Consequence: a program P may be conforming when using conforming implementation X, but non-conforming when using conforming implementation Y. – pmor Aug 02 '22 at 00:50
  • @pmor: The existence of any conforming C implementation somewhere in the universe that accepts a source text would be sufficient to make that source text a conforming C program. On the flip side, thanks to the "one program rule" there are almost no non-contribed situations where anything an otherwise-conforming implementation could do in response to any particular conforming C program would render that implementation non-conforming. – supercat Aug 02 '22 at 05:50
0

I compiled the program

#include <stdio.h>

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

under two well-regarded compilers, gcc and clang. Just for fun I added the --pedantic option also. As far as I know both of these compilers would be considered "conforming", and I believe that's one of the things their authors certainly strive for.

Both compilers produced an executable which printed hello, world. Under the definition that

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

, I conclude that the program is conforming.

I pass no judgement on the question of whether the program would have been conforming under C89.

Although I have not studied the code examples in K&R2 in some years, I believe that most/all of the rest of them are similarly conforming, despite various pedagogical or other shortcuts which might render them not strictly conforming.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103
  • You have misunderstood what these compilers claim. You can study the claims of gcc here: https://gcc.gnu.org/onlinedocs/gcc/Standards.html. Specifically "to obtain all the diagnostics required by the standard, you should also specify -pedantic". In which case gcc correctly reports "warning: return type defaults to 'int' [-Wimplicit-int]". Also in order for a program to be conforming, it may not alter the behavior of a strictly conforming program, meaning it cannot contain undefined behavior (but it may otherwise contain impelmentation-defined aspects). The compiler will not report UB. – Lundin May 19 '22 at 06:22
  • @Lundin But diagnostics — such as those enabled by `--pedantic` — have no bearing on conformance. And, once again, there is no undefined behavior in the program under discussion, so that issue is irrelevant also. – Steve Summit May 19 '22 at 09:48
  • Diagnostics have _everything_ to do with conformance. Specifically, 5.1.1.3 Diagnostics "A conforming implementation shall produce at least one diagnostic message (identified in an implementation-defined manner) if a preprocessing translation unit or translation unit contains a violation of any syntax rule or constraint" As for undefined behavior, it is explicitly there as per C90 6.6.6.4, read my posted answer. – Lundin May 19 '22 at 09:52
  • Related: [What must a C compiler do when it finds an error?](https://software.codidact.com/posts/277340) – Lundin May 19 '22 at 09:53
  • I believe that's what's known as [affirming the consequent](https://en.wikipedia.org/wiki/Affirming_the_consequent). – Steve Summit May 19 '22 at 10:08
  • How else would you know if the compiler is working as intended, if not from the diagnostic messages? – Lundin May 19 '22 at 10:58
  • @Lundin I should have said "diagnostics don't necessarily have any bearing on conformance". – Steve Summit May 19 '22 at 13:51
  • 1
    @Lundin: An implementation whose `--pedantic` option would unconditionally output "Warning: This compiler does not output any diagnostics the authors thinks are stupid" would have been just as conforming as the actual behavior of `--pedantic`, since feeding in any program for which the Standard would require at least one diagnostic would result in at least one diagnostic being issued. The "one diagnostic" provision was a compromise between those who thought certain constructs should be allowed and those who thought they should be forbidden. Programmer uses construct, ... – supercat May 20 '22 at 15:47
  • ...compiler issues message, programmer ignores message, and everybody gets on with their day. I think the authors of the Standard may have intended that every implementation be usable to evaluate strict conformance, but it would have been better to have a standardized set of validation tools for that. Indeed, the Standard could have published such tools in source-code form and recognized a category of conformance for hosted implementations that could run them. – supercat May 20 '22 at 15:50
0

Some quotes:

"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."

(source Wikipedia)

In other words, K&R edition 1 predates any official C standards. At the time the only specification was "The C Reference Manual" by Dennis M. Ritchie.


"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."

(source Wikipedia)

In other words, K&R edition 2 was "aligned with" the first official ANSI C standard otherwise known as C89.


However, at the time K&R edition 2 was published, C89 was not yet complete. According the the Wikipedia page on 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."

(source Wikipedia)

Thus, they may be differences between what K&R says and the ANSI C standard.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • To quote direct sources instead of Wikipedia, K&R 2nd edition Preface: "This Second Edition of The C Programming Language describes C as defined by the ANSI standard. Although we have noted the places where the language has evolved, we have chosen to write exclusively in the new form." – Lundin May 19 '22 at 12:33
-1

No, the programs in the K&R book were never conforming programs 1) (C17 4/7) under any verison of the standard.

  • In C90 (ISO 9899:1990), the code invoked undefined behavior because of the missing return statement.
  • In C99 (ISO 9899:1999) and beyond, the code won't compile because of the implicit int.

Sources below.


Regarding implicit int, one major difference in function return types between C90 and latter versions can be found here:

C90 6.7.1 Function definitions

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 Function definitions

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.

The main difference being the "complete object type" wording, the definition of complete object type being one of the basic types or a pointer to one (C17 6.2.5). We can conclude that implicit int was allowed in C90 both as the return type or as part of a (non-prototype) parameter list.


Regarding no return statement, this text was always there for general functions:

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.

However, main() is a special case and an exception was added in 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.

Whereas in C90, the equivalent text says:

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.


Regarding empty parameter lists, it has been marked as obsolescent from C90 to C17. See future language directions, for example C17 6.11 (or C90 6.9, identical text):

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.

This does however not mean that code using the feature isn't conforming, at least up to ISO 9899:2018. It's simply not recommended practice, and was not recommended practice at the point where K&R 2nd edition was released either.


1) C17 from chp 4:

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.

This means that a conforming program may use features of a conforming implementation that are non-portable, but it may not alter the behavior of a strictly conforming program by for example invoking undefined behavior explicitly listed as such in the standard.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/245405/discussion-on-answer-by-lundin-have-the-code-examples-from-kr-ever-been-conform). – Ryan M Jun 08 '22 at 06:38