11. ANSI/ISO Standard C

comp.lang.c FAQ list · Question 11.1

Q: What is the ``ANSI C Standard?''

A: In 1983, the American National Standards Institute (ANSI) commissioned a committee, X3J11, to standardize the C language. After a long, arduous process, including several widespread public reviews, the committee's work was finally ratified as ANS X3.159-1989 on December 14, 1989, and published in the spring of 1990. For the most part, ANSI C standardized existing practice, with a few additions from C++ (most notably function prototypes) and support for multinational character sets (including the controversial trigraph sequences). The ANSI C standard also formalized the C run-time library support routines.

A year or so later, the Standard was adopted as an international standard, ISO/IEC 9899:1990, and this ISO Standard replaced the earlier X3.159 even within the United States (where it was known as ANSI/ISO 9899-1990 [1992]). As an ISO Standard, it is subject to ongoing revision through the release of Technical Corrigenda and Normative Addenda.

In 1994, Technical Corrigendum 1 (TC1) amended the Standard in about 40 places, most of them minor corrections or clarifications, and Normative Addendum 1 (NA1) added about 50 pages of new material, mostly specifying new library functions for internationalization. In 1995, TC2 added a few more minor corrections.

Most recently, a major revision of the Standard, ``C99'', has been completed and adopted.

Several versions of the Standard, including C99 and the original ANSI Standard, have included a ``Rationale,'' explaining many of its decisions, and discussing a number of subtle points, including several of those covered here.

comp.lang.c FAQ list · Question 11.2

Q: How can I get a copy of the Standard?

A: An electronic (PDF) copy is available on-line, for US$18, from www.ansi.org. Paper copies are available in the United States from

American National Standards Institute
11 W. 42nd St., 13th floor
New York, NY  10036  USA
(+1) 212 642 4900
Global Engineering Documents
15 Inverness Way E
Englewood, CO  80112  USA
(+1) 303 397 2715
(800) 854 7179  (U.S. & Canada)
In other countries, contact the appropriate national standards body, or ISO in Geneva at:
ISO Sales
Case Postale 56
CH-1211 Geneve 20

(or see URL http://www.iso.ch or check the comp.std.internat FAQ list, Standards.Faq).

The mistitled Annotated ANSI C Standard, with annotations by Herbert Schildt, contains most of the text of ISO 9899; it is published by Osborne/McGraw-Hill, ISBN 0-07-881952-0, and sells in the U.S. for approximately $40. It has been suggested that the price differential between this work and the official standard reflects the value of the annotations: they are plagued by numerous errors and omissions, and a few pages of the Standard itself are missing. Many people on the net recommend ignoring the annotations entirely. A review of the annotations (``annotated annotations'') by Clive Feather can be found on the web at http://www.lysator.liu.se/c/schildt.html .

The text of the original ANSI Rationale can be obtained by anonymous ftp from ftp.uu.net (see question 18.16) in directory doc/standards/ansi/X3.159-1989, and is also available on the web at http://www.lysator.liu.se/c/rat/title.html . That Rationale has also been printed by Silicon Press, ISBN 0-929306-07-4.

Public review drafts of C9X were available from ISO/IEC JTC1/SC22/WG14's web site.

See also question 11.2b.

comp.lang.c FAQ list · Question 11.2b

Q: Where can I get information about updates to the Standard?

A: You can find information (including C9X drafts) at the web sites http://www.lysator.liu.se/c/index.html, http://www.dkuug.dk/JTC1/SC22/WG14/, and http://www.dmk.com/ .

comp.lang.c FAQ list · Question 11.3

Q: My ANSI compiler complains about a mismatch when it sees

	extern int func(float);

	int func(x)
	float x;
	{ ...

A: You have mixed the new-style prototype declaration ``extern int func(float);'' with the old-style definition ``int func(x) float x;''. It is usually possible to mix the two styles (see question 11.4), but not in this case.

Old C (and ANSI C, in the absence of prototypes, and in variable-length argument lists; see question 15.2) ``widens'' certain arguments when they are passed to functions. floats are promoted to double, and characters and short integers are promoted to int. (For old-style function definitions, the values are automatically converted back to the corresponding narrower types within the body of the called function, if they are declared that way there.) Therefore, the old-style definition above actually says that func takes a double (which will be converted to float inside the function).

This problem can be fixed either by using new-style syntax consistently in the definition:

	int func(float x) { ... }

or by changing the new-style prototype declaration to match the old-style definition:

	extern int func(double);
(In this case, it would be clearest to change the old-style definition to use double as well. [footnote] )

It is arguably much safer to avoid ``narrow'' (char, short int, and float) function arguments and return types altogether.

See also question 1.25.

References: K&R1 Sec. A7.1 p. 186
K&R2 Sec. A7.3.2 p. 202
ISO Sec., Sec.
Rationale Sec., Sec.
H&S Sec. 9.2 pp. 265-7, Sec. 9.4 pp. 272-3

comp.lang.c FAQ list · Question 11.4

Q: Can you mix old-style and new-style function syntax?

A: Doing so is legal (and can be useful for backwards compatibility), but requires a certain amount of care (see especially question 11.3). Modern practice, however, is to use the prototyped form in both declarations and definitions. (The old-style syntax is marked as obsolescent, so official support for it may be removed some day.)

References: ISO Sec. 6.7.1, Sec. 6.9.5
H&S Sec. 9.2.2 pp. 265-7, Sec. 9.2.5 pp. 269-70

comp.lang.c FAQ list · Question 11.5

Q: Why does the declaration

extern int f(struct x *p);
give me an obscure warning message about ``struct x declared inside parameter list''?

A: In a quirk of C's normal block scoping rules, a structure declared (or even mentioned) for the first time within a prototype cannot be compatible with other structures declared in the same source file. (The problem is that the structure and the tag go out of scope at the end of the prototype; see question 1.29.)

To resolve the problem, you should probably rearrange things so that the actual declaration of the structure precedes the function prototype(s) using it. (Usually, both the prototype and the structure declaration will end up in the same header file, so that the one can reference the other.) If you must mention a hitherto-unseen structure in a prototype, precede the prototype with the vacuous-looking declaration

	struct x;
which places an (incomplete) declaration of struct x at file scope, so that all following declarations involving struct x can at least be sure they're referring to the same struct x.

References: ISO Sec., Sec., Sec.

comp.lang.c FAQ list · Question 11.6

Q: I had a frustrating problem which turned out to be caused by the line

	printf("%d", n);
where n was actually a long int. I thought that ANSI function prototypes were supposed to guard against argument type mismatches like this.

A: See question 15.3.

comp.lang.c FAQ list · Question 11.7

Q: I heard that you have to #include <stdio.h> before calling printf. Why?

A: See question 15.1.

comp.lang.c FAQ list · Question 11.8

Q: I don't understand why I can't use const values in initializers and array dimensions, as in

	const int n = 5;
	int a[n];

A: The const qualifier really means ``read-only''; an object so qualified is a run-time object which cannot (normally) be assigned to. The value of a const-qualified object is therefore not a constant expression in the full sense of the term, and cannot be used for array dimensions, case labels, and the like. (C is unlike C++ in this regard.) When you need a true compile-time constant, use a preprocessor #define (or perhaps an enum).

References: ISO Sec. 6.4
H&S Secs. 7.11.2,7.11.3 pp. 226-7

comp.lang.c FAQ list · Question 11.8b

Q: If you can't modify string literals, why aren't they defined as being arrays of const characters?

A: One reason is that so very much code contains lines like

	char *p = "Hello, world!";
which are not necessarily incorrect. These lines would suffer the diagnostic messages, but it's really any later attempt to modify what p points to which would be problems.

See also question 1.32.

comp.lang.c FAQ list · Question 11.9

Q: What's the difference between const char *p, char const *p, and char * const p?

A: The first two are interchangeable; they declare a pointer to a constant character (you can't change any pointed-to characters). char * const p declares a constant pointer to a (variable) character (i.e. you can't change the pointer).

Read these declarations ``inside out'' to understand them; see question 1.21.

References: ISO Sec.
Rationale Sec.
H&S Sec. 4.4.4 p. 81

comp.lang.c FAQ list · Question 11.10

Q: Why can't I pass a char ** to a function which expects a const char **?

A: You can use a pointer-to-T (for any type T) where a pointer-to-const-T is expected. However, the rule (an explicit exception) which permits slight mismatches in qualified pointer types is not applied recursively, but only at the top level. (const char ** is pointer-to-pointer-to-const-char, and the exception therefore does not apply.)

The reason that you cannot assign a char ** value to a const char ** pointer is somewhat obscure. Given that the const qualifier exists at all, the compiler would like to help you keep your promises not to modify const values. That's why you can assign a char * to a const char *, but not the other way around: it's clearly safe to ``add'' const-ness to a simple pointer, but it would be dangerous to take it away. However, suppose you performed the following more complicated series of assignments:

	const char c = 'x';		/* 1 */
	char *p1;			/* 2 */
	const char **p2 = &p1;		/* 3 */
	*p2 = &c;			/* 4 */
	*p1 = 'X';			/* 5 */
In line 3, we assign a char ** to a const char **. (The compiler should complain.) In line 4, we assign a const char * to a const char *; this is clearly legal. In line 5, we modify what a char * points to--this is supposed to be legal. However, p1 ends up pointing to c, which is const. This came about in line 4, because *p2 was really p1. This was set up in line 3, which is an assignment of a form that is disallowed, and this is exactly why line 3 is disallowed.

Assigning a char ** to a const char ** (as in line 3, and in the original question) is not immediately dangerous. But it sets up a situation in which p2's promise--that the ultimately-pointed-to value won't be modified--cannot be kept.

(C++ has more complicated rules for assigning const-qualified pointers which let you make more kinds of assignments without incurring warnings, but still protect against inadvertent attempts to modify const values. C++ would still not allow assigning a char ** to a const char **, but it would let you get away with assigning a char ** to a const char * const *.)

In C, if you must assign or pass pointers which have qualifier mismatches at other than the first level of indirection, you must use explicit casts (e.g. (const char **) in this case), although as always, the need for such a cast may indicate a deeper problem which the cast doesn't really fix.

References: ISO Sec., Sec., Sec. 6.5.3
H&S Sec. 7.9.1 pp. 221-2

comp.lang.c FAQ list · Question 11.11

Q: I've got the declarations

	typedef char *charp;
	const charp p;
Why is p turning out const, instead of the characters pointed to?

A: typedef substitutions are not purely textual. (This is one of the advantages of typedefs; see question 1.13.) In the declaration

	const charp p;
p is const for the same reason that const int i declares i as const. The typedef'ed declaration of p does not ``look inside'' the typedef to see that there is a pointer involved.

Additional links: further reading

References: H&S Sec. 4.4.4 pp. 81-2

comp.lang.c FAQ list · Question 11.11b

Q: What's the difference between

	const MAXSIZE = 100;
	#define MAXSIZE 100

A: See question 10.5b.

comp.lang.c FAQ list · Question 11.12a

Q: What's the correct declaration of main()?

A: There are two valid declarations:

	int main(void)
	int main(int argc, char **argv)
although they can be written in a variety of ways. The second parameter may be declared char *argv[] (see question 6.4), you can use any names for the two parameters, and you can use old-style syntax:
	int main()

	int main(argc, argv)
	int argc; char **argv;

See also questions 11.12b to 11.15.

References: ISO Sec., Sec. G.5.1
H&S Sec. 20.1 p. 416
CT&P Sec. 3.10 pp. 50-51

comp.lang.c FAQ list · Question 11.12b

Q: Can I declare main as void, to shut off these annoying ``main returns no value'' messages?

A: No. main must be declared as returning an int, and as taking either zero or two arguments, of the appropriate types. If you're calling exit() but still getting warnings, you may have to insert a redundant return statement (or use some kind of ``not reached'' directive, if available).

Declaring a function as void does not merely shut off or rearrange warnings: it may also result in a different function call/return sequence, incompatible with what the caller (in main's case, the C run-time startup code) expects. That is, if the calling sequences for void- and int-valued functions differ, the startup code is going to be calling main using specifically the int-valued conventions, and if main has been improperly declared as void, it may not work. (See also question 2.18.)

(Note that this discussion of main pertains only to ``hosted'' implementations; none of it applies to ``freestanding'' implementations, which may not even have main. However, freestanding implementations are comparatively rare, and if you're using one, you probably know it. If you've never heard of the distinction, you're probably using a hosted implementation, and the above rules apply.)

Additional links: further reading

References: ISO Sec., Sec. G.5.1
H&S Sec. 20.1 p. 416
CT&P Sec. 3.10 pp. 50-51

comp.lang.c FAQ list · Question 11.13

Q: But what about main's third argument, envp?

A: It's a non-standard (though common) extension. If you really need to access the environment in ways beyond what the standard getenv function provides, though, the global variable environ is probably a better avenue (though it's equally non-standard).

References: ISO Sec. G.5.1
H&S Sec. 20.1 pp. 416-7

comp.lang.c FAQ list · Question 11.14a

Q: I believe that declaring void main() can't fail, since I'm calling exit instead of returning, and anyway my operating system ignores a program's exit/return status.

A: It doesn't matter whether main returns or not, or whether anyone looks at the status; the problem is that when main is misdeclared, its caller (the runtime startup code) may not even be able to call it correctly (due to the potential clash of calling conventions; see question 11.12b).

Your operating system may ignore the exit status, and void main() may work for you, but it is not portable and not correct.

Additional links: further reading

comp.lang.c FAQ list · Question 11.14b

Q: So what could go wrong? Are there really any systems where void main() doesn't work?

A: It has been reported that programs using void main() and compiled using BC++ 4.5 can crash. Some compilers (including DEC C V4.1 and gcc with certain warnings enabled) will complain about void main().

Additional links: further reading examples

comp.lang.c FAQ list · Question 11.15

Q: The book I've been using, C Programing for the Compleat Idiot, always uses void main().

A: Perhaps its author counts himself among the target audience. Many books unaccountably use void main() in examples, and assert that it's correct. They're wrong, or they're assuming that everyone writes code for systems where it happens to work.

comp.lang.c FAQ list · Question 11.16

Q: Is exit(status) truly equivalent to returning the same status from main?

A: Yes and no. The Standard says that a return from the initial call to main is equivalent to calling exit. However, a return from main cannot be expected to work if data local to main might be needed during cleanup; see also question 16.4. A few very old, nonconforming systems may once have had problems with one or the other form. (Finally, the two forms are obviously not equivalent in a recursive call to main.)

References: K&R2 Sec. 7.6 pp. 163-4
ISO Sec.

comp.lang.c FAQ list · Question 11.17

Q: I'm trying to use the ANSI ``stringizing'' preprocessing operator `#' to insert the value of a symbolic constant into a message, but it keeps stringizing the macro's name rather than its value.

A: It turns out that the definition of # says that it's supposed to stringize a macro argument immediately, without further expanding it (if the argument happens to be the name of another macro). You can use something like the following two-step procedure to force a macro to be expanded as well as stringized:

#define Str(x) #x
#define Xstr(x) Str(x)
#define OP plus
char *opname = Xstr(OP);
This code sets opname to "plus" rather than "OP". (It works because the Xstr() macro expands its argument, and then Str() stringizes it.) [footnote]

An equivalent circumlocution is necessary with the token-pasting operator ## when the values (rather than the names) of two macros are to be concatenated.

Note that both # and ## operate only during preprocessor macro expansion. You cannot use them in normal source code, but only in macro definitions.

References: ISO Sec., Sec.

comp.lang.c FAQ list · Question 11.18

Q: What does the message ``warning: macro replacement within a string literal'' mean?

A: Some pre-ANSI compilers/preprocessors interpreted macro definitions like

	#define TRACE(var, fmt) printf("TRACE: var = fmt\n", var)
such that invocations like
	TRACE(i, %d);
were expanded as
	printf("TRACE: i = %d\n", i);
In other words, macro parameters were expanded even inside string literals and character constants. (This interpretation may even have been an accident of early implementations, but it can prove useful for macros like this.)

Macro expansion is not defined in this way by K&R or by Standard C. (It can be dangerous and confusing: see question 10.22.) When you do want to turn macro arguments into strings, you can use the new # preprocessing operator, along with string literal concatenation (another new ANSI feature):

	#define TRACE(var, fmt) \
		printf("TRACE: " #var " = " #fmt "\n", var)
See also question 11.17.

References: H&S Sec. 3.3.8 p. 51

comp.lang.c FAQ list · Question 11.19

Q: I'm getting strange syntax errors inside lines I've #ifdeffed out.

A: Under ANSI C, the text inside a ``turned off'' #if, #ifdef, or #ifndef must still consist of ``valid preprocessing tokens.'' This means that the characters " and ' must each be paired just as in real C code, and the pairs mustn't cross line boundaries. (Note particularly that an apostrophe within a contracted word looks like the beginning of a character constant.) Therefore, natural-language comments and pseudocode should always be written between the ``official'' comment delimiters /* and */. (But see question 20.20, and also 10.25.)

References: ISO Sec., Sec. 6.1
H&S Sec. 3.2 p. 40

comp.lang.c FAQ list · Question 11.20

Q: What are #pragmas and what are they good for?

A: The #pragma directive provides a single, well-defined ``escape hatch'' which can be used for all sorts of (nonportable) implementation-specific controls and extensions: source listing control, structure packing, warning suppression (like lint's old /* NOTREACHED */ comments), etc.

References: ISO Sec. 6.8.6
H&S Sec. 3.7 p. 61

comp.lang.c FAQ list · Question 11.21

Q: What does ``#pragma once'' mean? I found it in some header files.

A: It is an extension implemented by some preprocessors to help make header files idempotent; that is, to make sure that their contents are processed exactly once even if they are #included multiple times. It is equivalent to the #ifndef trick mentioned in question 10.7, though less portable. Some people claim that #pragma once can be implemented ``more efficiently'' (of course only compilation efficiency is a factor here), but in fact a preprocessor that is serious about compilation efficiency can arrange for the portable #ifndef trick to be handled just as efficiently.

comp.lang.c FAQ list · Question 11.22

Q: Is char a[3] = "abc"; legal? What does it mean?

A: It is legal in ANSI C (and perhaps in a few pre-ANSI systems), though useful only in rare circumstances. It declares an array of size three, initialized with the three characters 'a', 'b', and 'c', without the usual terminating '\0' character. The array is therefore not a true C string and cannot be used with strcpy, printf's %s format, etc.

Most of the time, you should let the compiler count the initializers when initializing arrays (in the case of the initializer "abc", of course, the computed size will be 4).

References: ISO Sec. 6.5.7
H&S Sec. 4.6.4 p. 98

comp.lang.c FAQ list · Question 11.23

Q: Since array references decay into pointers, if arr is an array, what's the difference between arr and &arr?

A: See question 6.12.

comp.lang.c FAQ list · Question 11.24

Q: Why can't I perform arithmetic on a void * pointer?

A: The compiler doesn't know the size of the pointed-to objects. (Remember that pointer arithmetic is always in terms of the pointed-to size; see also question 4.4.) Therefore, arithmetic on void *'s is disallowed (though some compilers allow it as an extension). Before performing arithmetic, convert the pointer either to char * or to the pointer type you're trying to manipulate (but see also questions 4.5 and 16.7).

References: ISO Sec., Sec. 6.3.6
H&S Sec. 7.6.2 p. 204

comp.lang.c FAQ list · Question 11.25

Q: What's the difference between memcpy and memmove?

A: memmove offers guaranteed behavior if the memory regions pointed to by the source and destination arguments overlap. memcpy makes no such guarantee, and may therefore be more efficiently implementable. When in doubt, it's safer to use memmove.

It seems simple enough to implement memmove; the overlap guarantee apparently requires only an additional test:

void *memmove(void *dest, void const *src, size_t n)
	register char *dp = dest;
	register char const *sp = src;
	if(dp < sp) {
		while(n-- > 0)
			*dp++ = *sp++;
	} else {
		dp += n;
		sp += n;
		while(n-- > 0)
			*--dp = *--sp;

	return dest;
The problem with this code is in that additional test: the comparison (dp < sp) is not quite portable (it compares two pointers which do not necessarily point within the same object) and may not be as cheap as it looks. On some machines (particularly segmented architectures), it may be tricky and significantly less efficient [footnote] to implement.

References: K&R2 Sec. B3 p. 250
ISO Sec., Sec.
Rationale Sec. 4.11.2
H&S Sec. 14.3 pp. 341-2
PCS Sec. 11 pp. 165-6

comp.lang.c FAQ list · Question 11.26

Q: What should malloc(0) do? Return a null pointer or a pointer to 0 bytes?

A: The ANSI/ISO Standard says that it may do either; the behavior is implementation-defined (see question 11.33). Portable code must either take care not to call malloc(0), or be prepared for the possibility of a null return.

References: ISO Sec. 7.10.3
PCS Sec. 16.1 p. 386

comp.lang.c FAQ list · Question 11.27

Q: Why does the ANSI Standard place limits on the length and case-significance of external identifiers?

A: The problem is linkers which are under control of neither the ANSI/ISO Standard nor the C compiler developers on the systems which have them. The limitation is only that identifiers be significant in some initial sequence of characters, not that they be restricted to that many characters in total length. (The limitation was to six characters in the original ANSI Standard, but has been relaxed to 31 in C99.)

References: ISO Sec. 6.1.2, Sec. 6.9.1
Rationale Sec. 3.1.2
C9X Sec. 6.1.2
H&S Sec. 2.5 pp. 22-3

comp.lang.c FAQ list · Question 11.28

Q: What was noalias and what ever happened to it?

A: noalias was another type qualifier, in the same syntactic class as const and volatile, which was intended to assert that an object was not pointed to (``aliased'') by other pointers. The primary application, which is an important one, would have been for the formal parameters of functions designed to perform computations on large arrays. A compiler cannot usually take advantage of vectorization or other parallelization hardware (on supercomputers which have it) unless it can ensure that the source and destination arrays do not overlap.

The noalias keyword was not backed up by any ``prior art,'' and it was introduced late in the review and approval process. It was surprisingly difficult to define precisely and explain coherently, and sparked widespread, acrimonious debate, including a scathing pan by Dennis Ritchie. It had far-ranging implications, particularly for several standard library interfaces, for which easy fixes were not readily apparent.

Because of the criticism and the difficulty of defining noalias well, the Committee declined to adopt it, in spite of its superficial attractions. (When writing a standard, features cannot be introduced halfway; their full integration, and all implications, must be understood.) The need for an explicit mechanism to support parallel implementation of non-overlapping operations remains unfilled (although some work is being done on the problem).

References: ISO Sec. 6.9.6

comp.lang.c FAQ list · Question 11.29a

Q: My compiler is rejecting the simplest possible test programs, with all kinds of syntax errors. It's complaining about the first line of

	main(int argc, char **argv)
		return 0;

A: Perhaps it is a pre-ANSI compiler, unable to accept function prototypes and the like.

See also questions 1.31, 10.9, 11.30, and 16.1b.

If you don't have access to an ANSI compiler, and you need to convert some newer code (such as that in this list) so that you can compile it, perform these steps:

  1. Remove the argument type information from function prototype declarations, and convert prototype-style function definitions to old style. The new-style declarations
    	extern int f1(void);
    	extern int f2(int);
    	int main(int argc, char **argv) { ... }
    	int f3(void) { ... }
    would be rewritten as
    	extern int f1();
    	extern int f2();
    	int main(argc, argv) int argc; char **argv; { ... }
    	int f3() { ... }
    (Beware of parameters with ``narrow'' types; see question 11.3.)
  2. Replace void * with char * .
  3. Perhaps insert explicit casts where converting between ``generic'' pointers (void *, which you've just replaced with char *) and other pointer types (for instance in calls to malloc and free, and in qsort comparison functions; see questions 7.7 and 13.9).
  4. Insert casts when passing the ``wrong'' numeric types as function arguments, e.g. sqrt((double)i);.
  5. Rework calls to realloc that use NULL or 0 as first or second arguments (see question 7.30).
  6. Remove const and volatile qualifiers.
  7. Modify any initialized automatic aggregates (see question 1.31).
  8. Use older library functions (see question 13.24).
  9. Re-work any preprocessor macros involving # or ## (see questions 10.20, 10.21, and 11.18).
  10. Rework conditional compilation involving #elif.
  11. Convert from the facilities of <stdarg.h> to <varargs.h> (see question 15.7).
  12. Cross your fingers. (In other words, the steps listed here are not always sufficient; more complicated changes may be required which aren't covered by any cookbook conversions.)

It is possible to make many of these changes with the preprocessor rather than by editing source code.

See also the Rationale's list of ``quiet changes'' (see question 11.2).

See also question 11.31.

comp.lang.c FAQ list · Question 11.29b

Q: What does the message ``Automatic aggregate intialization is an ANSI feature'' mean? My compiler is complaining about valid ANSI code.

A: Messages like these are typically emitted by pre-ANSI compilers which have been upgraded just enough to detect (but not properly translate) new C features which were introduced with the ANSI Standard. The implication of the message is that you should pay your vendor more money for a copy of their real ANSI C compiler.

comp.lang.c FAQ list · Question 11.30

Q: Why are some ANSI/ISO Standard library functions showing up as undefined, even though I've got an ANSI compiler?

A: It's possible to have a compiler available which accepts ANSI syntax, but not to have ANSI-compatible header files or run-time libraries installed. (In fact, this situation is rather common when using a non-vendor-supplied compiler such as gcc.) See also questions 11.29a, 13.25, and 13.26.

comp.lang.c FAQ list · Question 11.31

Q: Does anyone have a tool for converting old-style C programs to ANSI C, or vice versa, or for automatically generating prototypes?

A: Two programs, protoize and unprotoize, convert back and forth between prototyped and ``old style'' function definitions and declarations. (These programs do not handle full-blown translation between ``Classic'' C and ANSI C.) These programs are part of the FSF's GNU C compiler distribution; see question 18.3.

The unproto program (/pub/unix/unproto5.shar.Z on ftp.win.tue.nl) is a filter which sits between the preprocessor and the next compiler pass, converting most of ANSI C to traditional C on-the-fly.

The GNU GhostScript package comes with a little program called ansi2knr.

Before converting ANSI C back to old-style, beware that such a conversion cannot always be made both safely and automatically. ANSI C introduces new features and complexities not found in K&R C. You'll especially need to be careful of prototyped function calls; you'll probably need to insert explicit casts. See also questions 11.3 and 11.29a.

Several prototype generators exist, many as modifications to lint. A program called CPROTO was posted to comp.sources.misc in March, 1992. There is another program called ``cextract.'' Many vendors supply simple utilities like these with their compilers. See also question 18.16. (But be careful when generating prototypes for old functions with ``narrow'' parameters; see question 11.3.)

comp.lang.c FAQ list · Question 11.32

Q: Why won't the Frobozz Magic C Compiler, which claims to be ANSI compliant, accept this code? I know that the code is ANSI, because gcc accepts it.

A: Many compilers support a few non-Standard extensions, gcc more so than most. Are you sure that the code being rejected doesn't rely on such an extension? The compiler may have an option to disable extensions; it may be wise to use such an option if you're not certain your code is ANSI-compatible. (gcc, to its credit, includes a -pedantic option which turns off extensions and attempts to enforce strict ANSI compliance.)

It is usually a bad idea to perform experiments with a particular compiler to determine properties of a language; the applicable standard may permit variations, or the compiler may be wrong. See also question 11.35.

comp.lang.c FAQ list · Question 11.33

Q: People seem to make a point of distinguishing between implementation-defined, unspecified, and undefined behavior. What do these mean?

A: First of all, all three of these represent areas in which the C Standard does not specify exactly what a particular construct, or a program which uses it, must do. This looseness in C's definition is traditional and deliberate: it permits compiler writers to (a) make choices which allow efficient code to be generated by arranging that various constructs are implemented as ``however the hardware does them'' (see also question 14.4a), and (b) ignore (that is, avoid worrying about generating correct code for) certain marginal constructs which are too difficult to define precisely and which probably aren't useful to well-written programs anyway (see for example the code fragments in questions 3.1, 3.2, and 3.3).

These three variations on ``not precisely defined by the standard'' are defined as:

implementation-defined: The implementation must pick some behavior; it may not fail to compile the program. (The program using the construct is not incorrect.) The choice must be documented. The Standard may specify a set of allowable behaviors from which to choose, or it may impose no particular requirements.

unspecified: Like implementation-defined, except that the choice need not be documented.

undefined: Anything at all can happen; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended.

Note, too, that since the Standard imposes absolutely no requirements on the behavior of a compiler faced with an instance of undefined behavior, the compiler (more importantly, any generated code) can do absolutely anything. In particular, there is no guarantee that at most the undefined bit of the program will behave badly, and that the rest of the program will perform normally. It's perilous to think that you can tolerate undefined behavior in a program, imagining that its undefinedness can't hurt; the undefined behavior can be more undefined than you think it can. (See question 3.2 for a relatively simple example.)

Since many people seem to have trouble comprehending the depths to which undefined behavor can descend, it is traditional to come up with eye-catching, outrageous examples. Undefined means that, notwithstanding question 9.2, printf("%d", j++ <= j); can print 42, or ``forty-two.''

If you're interested in writing portable code, you can ignore the distinctions, as you'll usually want to avoid code that depends on any of the three behaviors.

See also questions 3.9, and 11.34.

(A fourth defined class of not-quite-precisely-defined behavior, without the same stigma attached to it, is locale-specific.)

References: ISO Sec. 3.10, Sec. 3.16, Sec. 3.17
Rationale Sec. 1.6

comp.lang.c FAQ list · Question 11.33b

Q: What does it really mean for a program to be ``legal'' or ``valid'' or ``conforming''?

A: Simply stated, the Standard talks about three kinds of conformance: conforming programs, strictly conforming programs, and conforming implementations.

A conforming program is one that is accepted by a conforming implementation.

A strictly conforming program is one that does not depend on any implementation-defined, unspecified, or undefined behavior, that does not exceed any implementation limits, and that otherwise uses only the features of the language and library as specified in the Standard.

A conforming implementation is one that does everything the Standard says it's supposed to. (The way the Standard says this is that a conforming implementation ``shall accept any strictly conforming program''.) There are two kinds of conforming implementation: hosted and freestanding. A hosted implementation is intended for use with conventional application programs; a freestanding implementation is intended for use with embedded systems and the like, and is not required to supply all of the standard library functions.

Unfortunately, neither of the definitions relating to conforming programs are as practically useful as one might wish. There are very few realistic, useful, strictly conforming programs. On the other hand, a merely conforming program can make use of any compiler-specific extension it wants to.

Other words you may hear are ``compliant'' and ``conformant'' which are basically just synonyms for ``conforming''.

References: ISO Sec.
Rationale Sec. 1.7

comp.lang.c FAQ list · Question 11.34

Q: I'm appalled that the ANSI Standard leaves so many issues undefined. Isn't a Standard's whole job to standardize these things?

A: It has always been a characteristic of C that certain constructs behaved in whatever way a particular compiler or a particular piece of hardware chose to implement them. This deliberate imprecision often allows compilers to generate more efficient code for common cases, without having to burden all programs with extra code to assure well-defined behavior of cases deemed to be less reasonable. Therefore, the Standard is simply codifying existing practice.

A programming language standard can be thought of as a treaty between the language user and the compiler implementor. Parts of that treaty consist of features which the compiler implementor agrees to provide, and which the user may assume will be available. Other parts, however, consist of rules which the user agrees to follow and which the implementor may assume will be followed. As long as both sides uphold their guarantees, programs have a fighting chance of working correctly. If either side reneges on any of its commitments, nothing is guaranteed to work.

See also questions 11.35 and 19.42.

References: Rationale Sec. 1.1

comp.lang.c FAQ list · Question 11.35

Q: People keep saying that the behavior of i = i++ is undefined, but I just tried it on an ANSI-conforming compiler, and got the results I expected.

A: A compiler may do anything it likes when faced with undefined behavior (and, within limits, with implementation-defined and unspecified behavior), including doing what you expect. It's unwise to depend on it, though.

Here is another way of looking at it, due to Roger Miller:

``Somebody told me that in basketball you can't hold the ball and run. I got a basketball and tried it and it worked just fine. He obviously didn't understand basketball.''

See also questions 7.3b, 11.32, 11.33, and 11.34.

Read sequentially: prev next up

about this FAQ list   about eskimo   search   feedback   copyright

Hosted by Eskimo North