One makes sure that all calls to the fopen function are followed by a test to make sure that the pointer returned is non-null.
FILE *infile; infile = fopen("input", "r"); if (infile == NULL) { fprintf(stderr, "cannot open input\n"); exit(1); }
The FILE * type is used to define variables that will point to what is sometimes called a "file control block" - a structure containing information about a file and its implementation, for example on a disk. I do not have first hand information on what these look like for the libraries that you are using. But it might be fun to look at the analagous information that I needed to implement a Modula-2 library. Even if you don't know Modula-2, this code should be fairly understandable.
File = POINTER TO FileRec; Buftype = ARRAY [0 .. BufSize] OF CHAR; FileRec = RECORD name : ARRAY [0 .. 100] OF CHAR; ref : FIO.File; (* pointer to more operating system stuff *) self : File; (* self reference used for checking that a file pointer really points to a file *) dosHandle, (* MS-DOS assigns internal numbers to files *) handle : CARDINAL; savedCh : CHAR; (* to allow ReadAgain ("ungetc") to work *) noData, (* set to record error - past end of file *) eof, eol, (* flags to record textfile state *) inError, (* TRUE whenever data conversion fails *) noOutput, (* TRUE for read-only files *) noInput, (* TRUE for write-only files *) isDevice, (* TRUE for non-disk files *) haveCh : BOOLEAN; (* TRUE after ReadAgain *) buffer : Buftype; (* large buffer for efficiency *) END;
It will depend on the quality of the underlying operating system. If you are lucky, calls to routines such as
fscanf(infile, "%s", str);for which infile = NULL will be trapped and the program aborted from within the library code. If you are unlucky - if for example infile is non-NULL but points to some garbage file control block - then almost anything could happen. "Almost anything" has a habit of sometimes being disastrous.
Again, it may depend on the quality of the I/O implementation. In practice most disk files are accessed by reading or writing full "buffers" of data, rather than by being read from or written to in tiny chunks. Hence, if a file is not explicitly closed, there is the danger that unfilled buffers will not be copied to disk when the program terminates. Some filing systems take precautions to make sure that all buffers are "flushed", and all files closed before control reverts to the operating system when a program is complete, but it may be unwise to rely on this.
Develop stack machine code equivalents of the following simple programs:
(a) PROGRAM One; VAR I, J; BEGIN Read(I, J); Write(I + J) END. DSP 2 ADR -1 INN ADR -2 INN ADR -1 VAL ADR -2 VAL ADD PRN NLN HLT (b) PROGRAM Two; VAR I, J; BEGIN Read(I, J); J := (2 * I + J) / 2 END. DSP 2 ADR -1 INN ADR -2 INN ADR -2 LIT 2 ADR -1 VAL MUL ADR -2 VAL ADD LIT 2 DVD STO HLT (c) PROGRAM Three; VAR I, J; BEGIN Read(I, J); IF I > J THEN Write(I, ' is larger than ', J); Write('Difference is', I - J) END. DSP 2 ADR -1 INN ADR -2 INN ADR -1 VAL ADR -2 VAL GTR BZE 27 ADR -1 VAL PRN PRS ' is larger than ' ADR -2 VAL PRN NLN PRS 'Difference is' ADR -1 VAL ADR -2 VAL SUB PRN NLN HLT (d) void main () { for (int i = 0; i <= 10; i++) printf("%d\n", i) } DSP 1 ADR -1 LIT 0 STO ADR -1 VAL LIT 10 LEQ BZE 30 ADR -1 VAL PRN NLN ADR -1 ADR -1 VAL LIT 1 ADD STO BRN 7 HLT (e) void main () { int i, list[11]; i = 0; while (i <= 10) { list[i] = 2 * i; i++; } } DSP 12 ADR -1 LIT 0 STO ADR -1 VAL LIT 10 LEQ BZE 41 ADR -2 ADR -1 VAL LIT 11 IND LIT 2 ADR -1 VAL MUL STO ADR -1 ADR -1 VAL LIT 1 ADD STO BRN 7 HLT (f) PROGRAM Six; VAR List[10], I, J; BEGIN Read(I, J, List[I+J]); List[List[J]] := List[J + 2 * I] END. DSP 13 ADR -12 INN ADR -13 INN ADR -1 ADR -12 VAL ADR -13 VAL ADD LIT 11 IND INN ADR -1 ADR -1 ADR -13 VAL LIT 11 IND VAL LIT 11 IND ADR -1 ADR -13 VAL LIT 2 ADR -12 VAL MUL ADD LIT 11 IND VAL STO HLT (g) void main () { // You might like to predict the output of this program first (in other // words - do you really understand its dynamic semantics?) int i, list[20]; for (i = 0; i <= 10; list[++i] = i); for (i = 0; i <= 10; printf("%d %d ", i, list[i++]); }Using the Borland or G++ compilers, the program produces the following output:
1 ??? 2 1 3 2 4 3 5 4 6 5 7 6 8 7 9 8 10 9 11 10This may not immediately be apparent. The first for loop is equivalent to
for (i = 0; i <= 10; i++) list[i+1] = i+1;and the second one is equivalent to
for (i = 0; i <= 10; i++) printf("%d %d ", i+1, list[i]);and equivalent Clang and stack machine code versions are as below.
You would be well advised not to try to write "clever" code using the ++ and -- operators. The reason that we output i+1 and list[i] rather than i and list[i] is tied up with the way in whch the arguments are evaluated before the printf function is called.
PROGRAM Seven; VAR I, List[19]; BEGIN I := 0; WHILE I <= 10 DO BEGIN List[I+1] := I + 1; I := I + 1 END; I := 0; WHILE I <= 10 DO BEGIN Write(I+1, List[I]); I := I + 1 END; END. DSP 21 ADR -1 LIT 0 STO ADR -1 VAL LIT 10 LEQ BZE 44 ADR -2 ADR -1 VAL LIT 1 ADD LIT 20 IND ADR -1 VAL LIT 1 ADD STO ADR -1 ADR -1 VAL LIT 1 ADD STO BRN 7 ADR -1 LIT 0 STO ADR -1 VAL LIT 10 LEQ BZE 86 ADR -1 VAL LIT 1 ADD PRN ADR -2 ADR -1 VAL LIT 20 IND VAL PRN NLN ADR -1 ADR -1 VAL LIT 1 ADD STO BRN 49 HLT