3.7. Null Statements: Tricky Behavior Examined

Time: 00:01:56 | Download: Large Small | Streaming | Slides (PDF)
Review

Many Algol-derived languages have a puzzling "feature" called null statements. When they creep into a program, they go largely unnoticed like a bit of defunct DNA in a cell; if we notice one or it causes an error, we wonder why the compiler didn't detect and report it as a syntax error. Rarely, when used appropriately, a null statement can create compact and efficient code. When used inadvertently, the result ranges from a benign, extraneous semicolon to a hard-to-find logical error. Whether a null statement is benign (2) or malignant (2) is a product of where it appears in a program.

Although they are often confusing and their utility is questionable, they are nevertheless a valid part of many languages that we must necessarily understand. It's crucial to begin training yourself to spot null statements and to distinguish when they are helpful, benign, or malignant.

Benign Null Statements

A C++ program consists of a sequence of statements. Each statement is a computer instruction terminated with a semicolon. A null statement is an empty statement marked by an unneeded semicolon. They are legal, if unnecessary, anywhere in a program where a statement is valid. That is, stray semicolons create null statements, which can happen anywhere. Sometimes, null statements are benign and just look "out of place" without causing any problems.

	...;
	;
	...;
count = index + length;;
for (int i = 0; i < 10; i++)
	cout << i << endl;;
(a)(b)
Benign null statements. The red semicolons are not needed but don't cause problems.
  1. Although null statements can appear anywhere in a program, they are generally not "out in the open," as this example illustrates.
  2. Examples of common null statements "hidden" at the end of valid statements. The red semicolons terminate empty or null statements. The unneeded semicolons are benign and cause no harm - they only "look bad."

Malignant Null Statements

It only takes a small movement to make a benign null statement malignant, introducing a difficult-to-locate logical error into a program. Sometimes, a misplaced semicolon does cause a syntax error. The compiler component typically detects and reports syntax errors, including misplaced semicolons. Although not always accurate, the report includes the syntax error's location, serving as a starting point for localizing the error. Alternatively, The compiler system cannot detect logical errors, leaving their detection, identification, and correction to programmers.

for (int i = 0; i < 10; i++);
	cout << i << endl;

int i;
for (i = 0; i < 10; i++);
	cout << i << endl;
for (i = 0; i < 10; i++);
	cout << "Hello World" << endl;

(a)(b)(c)
Malignant null statements. Unlike Python, indentation in C++ programs clarifies the logic without affecting it. The red semicolons introduce null statements into a program, but these statements do cause problems.
  1. The red semicolon terminates or ends the for-loop. The indentation notwithstanding, the output statement is not part of the loop, and variable i is out of scope. The programmer is fortunate in this example because the compiler identifies the scoping error and stops compiling the program.
  2. Similar to (a), but the loop control variable is defined outside and above the for-loop. The red semicolon forms a null statement, ending the loop. So, the for-loop runs ten times but does nothing. The output statement is not part of the loop, but i is in scope, and the program compiles, runs, and produces one line of output.
  3. Similar to (b), but scope is not an issue because the output statement does not use the loop control variable. The loop runs ten times but does nothing. The output statement is not part of the loop, so it runs once after the loop ends, producing one output line.

If the null statements illustrated in these examples represent program errors, why doesn't the compiler detect and report the null statements? Part of the answer is that null statements are sometimes, albeit rarely, useful.

Beneficial Null Statements

Developing an authentic example of a helpful null statement is challenging; developing one that doesn't use features the text introduces later is more difficult. Nevertheless, the following example is from a program I wrote while working as a software engineer. My responsibilities included conducting the annual FORTRAN compiler ANSI-compliance validation required by the Federal Government. The validator arrived on site with a 9-track magnetic tape containing the validation programs formatted as a sequence of 80-column punch cards. My extraction program read the tape and formatted the validation programs for our operating system.

char	line[80];

// read data into line

int	end;
for (end = 79; end >= 0 && line[end] != ' '; end--)
	;
A beneficial null statement. A fragment of a program that reads a tape 80 characters at a time into a character array named line. The program searches the array backward (right to left), locates the first non-space character, trims the unneeded spaces, and writes the line to a file. The searching operation occurs entirely inside the parentheses of the for-loop, leaving nothing to do in the loop's body, making a null statement appropriate. When a null statement is appropriate, the programmer should make it clear and easy to see, which I did by placing the semicolon below the for-loop in the same indented location where the loop body would typically appear.

Although null statements can appear at any location in a program where a "regular" statement is legal, they are rarely useful outside of for and while loops. In contrast, they often introduce subtle bugs.

if (counter < 10);

for (int i = 0; i < 10; i++);

for (int = 0; i > 10; i++);
	cout << i * i << endl;
Common null statement errors. Begin training yourself to see these problems, especially the problem on the right.