This session produces two versions of a small C program that reads a list of integers, prints their mean, and looks up one value by index. The first version is correct: give it valid input and it prints the right answer every time. It is also unreliable: give it input that is slightly off and it prints garbage, crashes, or quietly returns a wrong number. The second version does the same job but checks every input before using it. Session 11 was about code that reads badly; this session is about code that fails badly.
Objectives
Do not copy. Read for understanding and the viva- State the difference between correctness (right answer on valid input) and reliability (acceptable behaviour on all input, over time, on any machine).
- Identify the specific lines where a program trusts input it has not checked.
- Show, with real runs, how the same program behaves on valid and invalid input.
- Rewrite the program so that every failure becomes a clear message and a non-zero exit status.
Problem Statement
Write in lab recordSession 12: Write a Program that is correct but still not reliable. Justify your answer. Make necessary assumptions.
Concept
Do not copy. Read for understanding and the vivaCorrectness versus reliability
Correctness is a statement about the specification: for every input the specification allows, the output matches. Reliability is a statement about operation: the probability that the program runs without failure for a given period under stated conditions, including conditions the specification never mentioned. A program can be 100 percent correct on its test set and still crash the first day a real user types a letter instead of a number. The manual’s section 1.6 asks for “validation checks” and “error and exception handling” for exactly this reason.
Where unreliability hides in C
C does not check anything for you. Each of these is a place where a correct program stays silent and then fails:
scanfreturns how many items it converted; if you ignore it, a bad token leaves the variable unset and the program continues with whatever was in memory.- An array of size 10 accepts index 50 without complaint; reading gives garbage, writing corrupts the stack.
intwraps around on overflow; two billion plus two billion is a negative number.- Integer division by zero is undefined behaviour: it crashes on x86, and on ARM64 (Apple Silicon) the hardware returns 0, so the program prints a wrong answer and carries on. Undefined means “any of these”, which is the definition of unreliable.
What a reliable version does instead
It follows one rule: never use a value you have not checked. Check the count is a number and in range, check every value converted, use a wider type for the sum, and check the index against the count. Each failure prints what went wrong and exits with EXIT_FAILURE, so a calling script can detect it.
Assumptions
Write in lab record- The program reads a count N, then N integers into an array of capacity 10, prints the integer mean, then reads an index and prints the value at that index.
- Valid input is: 1 to 10 for N, each value an
int, and an index from 0 to N minus 1. On valid input both versions print identical output (checked withdiff). - “Unreliable” means the program does not detect invalid input and its behaviour is then undefined. The runs below were done on macOS (Apple Silicon, clang as
gcc); on another machine the garbage values and crash messages will differ, which is itself the point. - The reliable version treats every invalid input as an error, prints one line saying why, and exits with status 1.
Program A: Correct but Unreliable
Write in lab record/*
* unreliable.c
* Reads N integers, prints their mean, then prints the value at a
* requested index. Correct for well-formed input. Not reliable: it
* trusts every value the user types.
*/
#include <stdio.h>
#define CAPACITY 10
int main(void)
{
int values[CAPACITY];
int n;
int i;
int index;
int sum = 0;
printf("How many values: ");
scanf("%d", &n); /* return value ignored, n unchecked */
for (i = 0; i < n; i++) { /* writes past values[9] when n > 10 */
printf("Value %d: ", i + 1);
scanf("%d", &values[i]);
}
for (i = 0; i < n; i++) {
sum += values[i]; /* int overflow for large values */
}
printf("Mean: %d\n", sum / n); /* divides by zero when n is 0 */
printf("Index to look up (0 to %d): ", n - 1);
scanf("%d", &index);
printf("values[%d] = %d\n", index, values[index]); /* no range check */
return 0;
}Compiles with gcc -Wall unreliable.c with no warnings. The comments mark the five places where it trusts the user.
Program B: Reliable
Write in lab record/*
* reliable.c
* Same task as unreliable.c: read N integers, print their mean, print the
* value at a requested index. Every input is checked before it is used,
* so the program either prints a correct answer or a clear error message.
*/
#include <stdio.h>
#include <stdlib.h>
#define CAPACITY 10
/* Reads one int from stdin into *out. Returns 1 on success, 0 on failure. */
static int read_int(const char *prompt, int *out)
{
printf("%s", prompt);
return scanf("%d", out) == 1;
}
int main(void)
{
int values[CAPACITY];
int n;
int i;
int index;
long long sum = 0; /* cannot overflow for 10 int values */
char prompt[40];
if (!read_int("How many values: ", &n)) {
printf("Error: count is not a number\n");
return EXIT_FAILURE;
}
if (n < 1 || n > CAPACITY) {
printf("Error: count must be between 1 and %d\n", CAPACITY);
return EXIT_FAILURE;
}
for (i = 0; i < n; i++) {
snprintf(prompt, sizeof prompt, "Value %d: ", i + 1);
if (!read_int(prompt, &values[i])) {
printf("Error: value %d is not a number\n", i + 1);
return EXIT_FAILURE;
}
}
for (i = 0; i < n; i++) {
sum += values[i];
}
printf("Mean: %lld\n", sum / n); /* n >= 1 here, never divides by zero */
snprintf(prompt, sizeof prompt, "Index to look up (0 to %d): ", n - 1);
if (!read_int(prompt, &index)) {
printf("Error: index is not a number\n");
return EXIT_FAILURE;
}
if (index < 0 || index >= n) {
printf("Error: index %d is out of range 0 to %d\n", index, n - 1);
return EXIT_FAILURE;
}
printf("values[%d] = %d\n", index, values[index]);
return EXIT_SUCCESS;
}Reliability Failures
Write in lab recordEach row is one condition, what unreliable.c actually did when run, and what reliable.c does instead.
| Condition | Line in unreliable.c | What the unreliable version does | What the reliable version does |
|---|---|---|---|
| N larger than capacity (N = 12) | 22 to 25 | Writes values 11 and 12 past the end of the array, corrupting the stack; prints a mean, then aborts on return from main with Abort trap: 6 | Prints Error: count must be between 1 and 10 and exits with status 1 before reading any value |
| Index out of range (index = 50 with N = 3) | 33 to 34 | Prints values[50] = 1809555360; a second run printed 1868292000; the number is whatever is on the stack | Prints Error: index 50 is out of range 0 to 2 and exits with status 1 |
| N = 0 | 30 | Integer division by zero; on this ARM64 machine it printed Mean: 0 and continued; on x86 it would crash with Floating point exception | Rejected by the same range check as above |
| Sum overflow (values 2000000000 and 2000000000) | 28 to 30 | Prints Mean: -147483648, a wrong answer with no warning | Sum is long long; prints Mean: 2000000000 |
Non-numeric count (abc) | 20 | scanf converts nothing; n holds a leftover stack value; printed Index to look up (0 to -316699233) and continued | Prints Error: count is not a number and exits with status 1 |
| Non-numeric value in the list | 24 | Same as above for that element; the mean is computed on garbage | Prints Error: value k is not a number and exits |
Correctness and Reliability
Write in lab record| Aspect | Correctness | Reliability |
|---|---|---|
| Question it answers | Does the output match the specification for valid input? | Does the program keep behaving acceptably when conditions are not ideal? |
| How it is measured | Test cases with expected outputs (Session 8 style) | Failure rate over time or over inputs; mean time between failures |
| Where it fails | Wrong algorithm, wrong formula, off-by-one | Unchecked input, overflow, resource exhaustion, environment differences |
| Program A | Correct: all valid-input tests pass | Unreliable: five ways to make it crash or lie |
| Program B | Correct: same output as A on valid input | Reliable within its stated limits: every bad input becomes a message and a status code |
Program A is correct because for valid input it is indistinguishable from Program B. It is unreliable because the word “valid” is doing all the work, and nothing in the program enforces it.
Sample Runs
Write in lab recordRun 1: valid input, both versions agree
$ gcc -Wall -o unreliable unreliable.c
$ gcc -Wall -o reliable reliable.c
$ printf '4\n10\n20\n30\n40\n2\n' | ./unreliable > a.out
$ printf '4\n10\n20\n30\n40\n2\n' | ./reliable > b.out
$ diff a.out b.out && echo IDENTICAL
IDENTICALAt the terminal, either program shows:
How many values: 4
Value 1: 10
Value 2: 20
Value 3: 30
Value 4: 40
Mean: 25
Index to look up (0 to 3): 2
values[2] = 30Run 2: invalid input, N larger than capacity and index out of range
Unreliable version, N = 12 into an array of 10, then index 50:
$ ./unreliable
How many values: 12
Value 1: 1
Value 2: 2
...
Value 10: 10
Value 11: 11
Value 12: 12
Mean: 6
Index to look up (0 to 11): 50
values[50] = 1829724128
Abort trap: 6
$ echo $?
134What happened: values 11 and 12 were written past the end of values[] onto the stack. The program did not notice; it printed a mean and a garbage lookup, and only when main returned did the stack-protector detect the corruption and abort the process (signal 6, exit status 134). Nothing in the program’s own output says anything went wrong. When the output is redirected to a file instead of a terminal, even the prompts are lost, because the process was killed before flushing its buffer.
Reliable version, same input:
$ ./reliable
How many values: 12
Error: count must be between 1 and 10
$ echo $?
1Same index error with a valid count:
$ printf '3\n5\n6\n7\n50\n' | ./unreliable
How many values: Value 1: Value 2: Value 3: Mean: 6
Index to look up (0 to 2): values[50] = 1809555360
$ printf '3\n5\n6\n7\n50\n' | ./reliable
How many values: Value 1: Value 2: Value 3: Mean: 6
Index to look up (0 to 2): Error: index 50 is out of range 0 to 2Viva Questions
Do not copy. Read for understanding and the viva- Q: Give a one-line definition of reliability. A: The probability that software operates without failure for a stated time under stated conditions.
- Q: Program A passes every test in Session 8 style. Is it reliable? A: No; tests cover valid input, reliability is about what happens outside that set.
- Q: Why did the N = 12 run print a mean before crashing? A: The out-of-bounds writes corrupted the stack silently; the abort came only when
mainreturned and the stack canary was checked. - Q: Why did the N = 0 run not crash on this machine? A: Integer division by zero is undefined behaviour; ARM64 hardware returns 0 instead of trapping. Different machine, different failure.
- Q: What does
scanfreturn, and why does it matter? A: The number of items converted; if you do not check it, a non-numeric token leaves the variable unset. - Q: Why
long longfor the sum? A: Tenintvalues can exceed theintrange;long longholds at least 63 bits, so the sum cannot wrap. - Q: Is Program B reliable in every situation? A: No; it is reliable within its stated limits (10 values,
intinput). Reliability is always relative to stated conditions. - Q: Which is cheaper, adding the checks now or later? A: Now; every check in Program B is two or three lines, and the crash in Program A would be a bug report with no useful message.
Common Mistakes
Do not copy. Read for understanding and the viva- Submitting a program that is simply wrong and calling it unreliable. It must give correct output on valid input first.
- Describing the failure in theory only. Run the invalid case and paste what actually appeared, including the exit status.
- Claiming “it crashes” when it did not. On this machine N = 0 printed a wrong mean; say what you observed and why it may differ elsewhere.
- Fixing reliability by adding one check and stopping. Each of the five failure rows is a separate defect and needs its own check.
- Making the reliable version change its output on valid input. Check with
diff.
Session Summary
Write in lab record- Assumptions, including the machine the runs were done on.
- Program A (
unreliable.c) and Program B (reliable.c). - The reliability failures table with line references and observed behaviour.
- The correctness versus reliability comparison.
- Run 1 (valid, identical) and Run 2 (invalid, unreliable misbehaves, reliable reports) with real output.