Program Characteristics, and Which Ones Are Desirable
Chapter Six
Syllabus topic 1, "Introduction: Algorithms, History of C, Structure of C Program. Program Characteristics, Compiler, Linker and preprocessor, pseudo code statements and flowchart symbols, Desirable program characteristics. Program structure. Compilation and Execution of a Program, C Character Set, identifiers and keywords, data types and sizes, constants and its types, variables, Character and character strings, typedef, typecasting"
Pages 25 to 29 of 222
In one line
Every program has characteristics you can judge it by, and the ones worth aiming at are integrity, clarity, simplicity, efficiency, modularity and generality, in that order of importance, because a fast program that gives the wrong answer is worth nothing.
Why this is on the syllabus at all
Two programs can produce identical output and be very different things. One can be read by a stranger in a minute, changed safely, and reused; the other can only be understood by the person who wrote it, and only for about a week. In a first semester it feels like fussiness. It stops feeling like fussiness the first time you have to change a program you wrote a month ago.
The list is also an examination topic in its own right, and it is asked as bookwork, so learn the six words. But the six words are worth very little without an example each, and an answer that gives one line of reasoning per word is worth roughly twice one that gives the words alone.
The two programs
Both of these read nothing, compute the same thing, and print the same numbers. The task is the one from the lab's first practical, extended: print a small table of simple interest for three deposits.
Program A. It works.
#include <stdio.h>
int main(void)
{
int i;
double a[3] = {15000.0, 20000.0, 32000.0};
double b[3] = {8.5, 7.25, 9.0};
double c[3] = {2.0, 3.0, 1.5};
for (i = 0; i < 3; i++) {
printf("%.2f %.2f %.2f %.2f\n", a[i], b[i], c[i],
a[i] * b[i] * c[i] / 100.0);
}
return 0;
}15000.00 8.50 2.00 2550.00
20000.00 7.25 3.00 4350.00
32000.00 9.00 1.50 4320.00Program B. It works too, and produces the same four numbers per row.
/*
* interest-table.c
* Prints the simple interest earned on each of several deposits.
*/
#include <stdio.h>
#define PERCENT 100.0
#define DEPOSITS 3
/* Simple interest = P * R * T / 100, with R a percentage per year. */
double simple_interest(double principal, double rate_percent, double years)
{
return principal * rate_percent * years / PERCENT;
}
int main(void)
{
double principal[DEPOSITS] = {15000.0, 20000.0, 32000.0};
double rate_percent[DEPOSITS] = {8.5, 7.25, 9.0};
double years[DEPOSITS] = {2.0, 3.0, 1.5};
printf("%12s %8s %7s %12s\n", "Principal", "Rate", "Years", "Interest");
for (int i = 0; i < DEPOSITS; i++) {
printf("%12.2f %8.2f %7.2f %12.2f\n",
principal[i], rate_percent[i], years[i],
simple_interest(principal[i], rate_percent[i], years[i]));
}
return 0;
} Principal Rate Years Interest
15000.00 8.50 2.00 2550.00
20000.00 7.25 3.00 4350.00
32000.00 9.00 1.50 4320.00Now the six characteristics, each read off the difference between those two files.
1. Integrity
Integrity is the accuracy of the result. It is first because nothing else can compensate for its absence.
Program Characteristics, and Which Ones Are Desirable
Both programs have it here, and it is worth seeing how easily one could lose it. Write the formula as principal rate_percent years / 100 with an integer 100 and the answer is still right, because one operand is already a double. Write it as principal (rate_percent years / 100) and it is still right. Write it as (int) principal rate_percent years / PERCENT and it is right only while the principal happens to be a whole number. Integrity is not preserved by good intentions; it is preserved by knowing the rules, which is why chapter 14 on conversions exists.
Integrity also covers what happens on bad input. A program that divides by a number the user typed has no integrity until it checks whether that number is zero.
2. Clarity
Clarity is how easily a reader who did not write it can tell what the program does.
Program A calls its arrays a, b and c. Nothing in the file says which is the rate and which is the number of years, so the expression a[i] b[i] c[i] / 100.0 cannot be checked by reading: you have to go back to the initialisers and count. Program B calls them principal, rate_percent and years, and the same expression becomes a formula you can compare against the one in your textbook.
Clarity in C comes from four cheap things, all visible above:
- Names that say what the thing is.
rate_percentrather thanb, andrate_percentrather thanrate, because the unit was the thing a reader would have had to guess. - A comment saying why, not what.
/ Simple interest = P R T / 100 /is worth writing.i++; / add one to i /is not. - Consistent indentation. Everything inside a block moves right by the same amount. The compiler does not care, and every reader does.
- One idea to a line. Program B's
printfis split across lines so each argument can be seen.
3. Simplicity
Simplicity is doing the job without more machinery than the job needs.
This is the one that cuts both ways, and Program B is longer than Program A. Length is not complexity. Program B has one idea per construct: a function that computes one interest figure, a loop that walks the deposits, a header row. Program A has fewer lines and puts the formula inside the printf call, where it is mixed up with the formatting.
Simplicity is also what stops you writing a[i] b[i] c[i] / 100.0 inside a printf in the first place. A reader now has to hold the formatting and the arithmetic in mind at once, and the two have nothing to do with each other.
Program Characteristics, and Which Ones Are Desirable
4. Efficiency
Efficiency is the time and the memory the program uses.
Here the two programs are effectively identical, and that is the honest answer: at three deposits, nothing you do to this program can be measured. Efficiency matters when the size of the input grows, and the thing that decides it is almost always the algorithm rather than the code. A sort that compares every element with every other takes a hundred times as long on a hundred times as much data, whatever style it is written in.
Do not write an unclear program in the belief that it is a fast one. Program B calls a function three times, which Program A avoids, and on any modern compiler at any optimisation level that call disappears. Choosing bad names to save typing buys nothing at all.
5. Modularity
Modularity is dividing the program into pieces that each do one job and can be understood, tested and changed on their own.
Program A has one piece. Program B has two, and simple_interest is the useful one: it takes three numbers and returns one, it does not print anything, it does not read anything, and it can be tested by calling it with numbers whose answer you already know. That is what a module is for. When the next practical asks for compound interest, Program B gets a second function beside the first and main barely changes.
Modularity is also what makes a program more than one person can work on, and what MU's lab is preparing you for when practicals 4, 7 and 8 all say "using a function".
6. Generality
Generality is how much the program can do without being rewritten.
Neither program is very general: both have the deposits typed into them. Program B is closer, because the count is DEPOSITS in one place rather than the literal 3 in four places, so adding a fourth deposit is two edits rather than five. The genuinely general version reads its input, and that is the version the lab asks for in Practical 1.
Generality has a limit. A program that tries to handle every case nobody asked about becomes large and unclear, which costs you two characteristics to buy one. Make it general where the requirement is likely to change, which for these programs means the data, not the formula.
The ordering, and why it is not negotiable
| Characteristic | What it means | Beaten by |
|---|---|---|
| Integrity | The answer is right | Nothing |
| Clarity | A stranger can read it | Integrity |
| Simplicity | No more machinery than needed | Integrity, clarity |
| Efficiency | Time and memory used | The three above, until the program is too slow to use |
| Modularity | Split into pieces with one job each | Nothing much; it usually helps the others |
| Generality | Handles more cases unchanged | Simplicity, when it is speculative |
Program Characteristics, and Which Ones Are Desirable
The single sentence to remember, because it answers the exam question and it is true: make it right, then make it clear, then make it fast, and only if it is actually too slow.
What this does NOT mean
Fewer lines is not simpler. Program A is shorter and harder to read. Compressing two statements onto one line with a comma operator or a nested assignment makes a file shorter and a program more difficult.
Comments do not create clarity. A badly named variable with a comment explaining it is worse than a well named one with no comment, because the comment can go stale and the name cannot. Rename first, comment second.
Efficiency is not about operators. Writing i++ instead of i = i + 1 does not make a program faster; they compile to the same instructions. Efficiency is decided by how much work the algorithm does, which is chapter 35's territory and the reason recursion has to be understood before it is used.
Modularity is not just "use functions". A function of eighty lines that does four unrelated things is not a module. A module has one job, a name that says what that job is, and inputs and outputs you can describe in a sentence.
Portability is a separate property, and it is the one this book's -std=c17 -Wall -Wextra is for. A program that relies on int being four bytes is not portable even if it is clear, simple and correct on your machine. Chapter 9 gives the sizes the standard actually guarantees.
Quick revision
- Six characteristics: integrity, clarity, simplicity, efficiency, modularity, generality.
- Integrity first: a wrong answer produced quickly is still wrong.
- Clarity comes from names, indentation, comments that say why, and one idea per line.
- Simplicity is machinery, not line count. A longer program can be simpler.
- Efficiency is decided by the algorithm, not by the choice of operator.
- Modularity means one job per function, testable on its own.
- Generality means the data is input rather than typed in, and constants are named in one place.
- Make it right, then clear, then fast, and only if it is genuinely too slow.
Test yourself
1. Name the six desirable characteristics of a program.
Integrity, clarity, simplicity, efficiency, modularity, generality.
2. A program gives the right answer and nobody, including its author, can follow it. Which characteristic does it lack, and does it matter?
Clarity. It matters, because every future change to it is a guess, and a guess is how a program loses its integrity.
Program Characteristics, and Which Ones Are Desirable
3. Which two characteristics is #define DEPOSITS 3 serving?
Clarity, because DEPOSITS says what the 3 counts, and generality, because the count is now in one place and can be changed there.
4. Your program takes two seconds on the data you were given and will never be given more. Should you make it faster?
No. Two seconds is fast enough, and any change you make for speed costs clarity and risks integrity. Efficiency is a requirement, not a virtue in itself.
5. Give one change to Program A that improves clarity and costs nothing else.
Rename a, b and c to principal, rate_percent and years. No line is added and the arithmetic becomes checkable by reading.
What can be asked on this, and how to answer it
"What are the desirable characteristics of a program? Explain." Name all six and give one sentence each, and give an example for at least three. Put integrity first and say why it is first. This is bookwork and the marks are in the explanations, not the list.
"What is meant by program clarity? How is it achieved?" Clarity is how easily a reader who did not write the program can tell what it does. Achieved by meaningful names, consistent indentation, comments that give the reason rather than restate the code, small functions with one job each, and avoiding expressions that do several things at once.
"Distinguish between simplicity and efficiency." Simplicity is about the program as a text: no more machinery than the job needs, so that a reader can follow it. Efficiency is about the program as it runs: time taken and memory used. They usually agree, and where they conflict, correctness and clarity come first and efficiency is improved only when the program is measurably too slow.
"Why is modularity important?" Because a program divided into functions that each do one job can be understood, tested and changed one piece at a time, can be worked on by more than one person, and lets a piece be reused instead of rewritten.
The rest of this subject
These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.