^Prog' Lang's^ ^C^


These following standards are equivalent (Harbison & Steele 1995):


char, int, I/O and EOF

The following is very dangerous:

   char ch;                        /* don't do this and ... */
   while( (ch=fgetc(...)) != EOF ) /* ... also this. */
    { ... }

because int fgetc(FILE *stream) returns an int, which will be truncated to the char ch and may or may not be sign-extended for comparison with the EOF.
Such code behaves differently on `silas', an `alpha', (OK) and on an `indy' (not OK), both using `gcc', L.A. 10/1999!

Better is to make ch an int, and/or separate the assignment to ch from the test on EOF, particularly by using int feof(FILE *stream) to test for end-of-file.

NB. "1. Type char may be a signed integral type, equivalent to signed char.
2. Type char may be a signed integral type, equivalent to unsigned char.
3. Type char may be a pseudo-unsigned integral type - that is, it can contain only non-negative values, but it is treated as if it were a signed type when performing the usual unary conversions." (Harbison & Steele 1995, p115).

NB. "The value EOF is conventionally used as a value that signals end-of-file - that is the exhaustion of input data. It has the value -1 in most traditional implementations, but ISO C requires only that it be a negative integral constant expression." (Harbison & Steel 1995, p345),

Go to the [top of the page].






Large programs are usually split into more than one file for easy management. This also allows parts of the program to be (re)compiled separately and therefore more quickly.

The command make is used to compile only those parts of a program that have changed since the last compilations. An attendant makefile indicates dependencies between the files that make up the complete program. Try the command man make to find out more about make.

For a modest program made up of a few .c and .h files in one directory, the following shortcut is quick and is usually sufficient:
   gcc *.c  
where gcc is the gnu C compiler.

Go to the [top of the page].

Function Formal Parameters

int f() { .... }          /* f is some function */

void rtn1( int g(void) )  /* g is a (function) formal parameter */
 { int i;
   ....; i=g(); ....      /* call actual parameter bound to g */

void rtn2(int (*h)(void)) /* param h, "classic" C style */
 { int i;
   ....; i=h(); ....      /* call actual parameter bound to h */


rtn1(f);                  /* call rtn1 with f bound to g */
			  /* NB. f is not called at this point */

rtn2(f);                  /* call rtn2 with f bound to h */

The first form, as in the definition of rtn1 is generally preferred because the syntax is much simpler. The 2nd (classic C) form, as in the definition of rtn2, does illustrate how a function formal parameter is implemented in either case, i.e. as a pointer to the code for the function. Both forms are valid. e.g. See Harbison and Steele (1995) p270.

Go to the [top of the page].


Go to the [top of the page].

Lloyd Allison 1999