Karnataka KSEAB SSLC JTS Programming in ANSI C common mistakes
Karnataka KSEAB SSLC JTS Programming in ANSI C · Understand the traps and how to avoid them.
Writing `if (a = 5)` when the intention is to compare `a` with 5, so the condition is always true and `a` is overwritten.
The single = is the assignment operator, and beginners often treat it as equality because that is how it reads in mathematics. The compiler accepts it silently in most cases, so the error is not caught at compile time.
What to do instead
Use == for comparison and compile with warnings enabled (-Wall -Wextra), which flags 'suggest parentheses around assignment used as truth value'. Adopt the habit of writing the constant on the left (if (5 == a)) when a comparison against a literal is intended, so a typo becomes a compile error.
Declaring an array of size n and then reading or writing `arr[n]` in a loop written as `for (i = 0; i <= n; i++)`, and similarly forgetting the terminating `'\0'` when copying a string into a fixed-size char array.
C arrays are zero-indexed, so the valid indices are 0 through n-1. Loop bounds written from mathematical habit (<=) and string buffers sized to the visible characters rather than characters plus the null terminator are the two most frequent sources of off-by-one errors.
What to do instead
Use the canonical loop for (i = 0; i < n; i++). When declaring a string buffer, reserve one extra byte: char name[6] = "Ramesh"; needs 7 bytes including '\0'. Trace the first and last iteration by hand on paper before running the program.
Calling `scanf("%d", n)` instead of `scanf("%d", &n)`, or calling `scanf("%s", &name)` for a char array, and then being surprised by a crash or garbage output.
scanf must receive the address of the variable so it can store the converted value. Beginners memorise the format specifier but omit the address-of operator, and the compiler often only warns rather than errors. The reverse mistake, adding & to an array name, shows confusion about arrays already decaying to a pointer.
What to do instead
Remember the rule: for a scalar variable pass &var; for an array pass the array name without &. Check the return value of scanf (it equals the number of items successfully assigned) and compile with warnings on to catch the format mismatch.
Assuming that `float` arithmetic is exact, so `if (0.1 + 0.2 == 0.3)` is expected to be true, or printing a float with `%d`.
Binary floating point cannot represent most decimal fractions exactly, so the sum is a value very close to but not equal to 0.3. The format specifier mismatch is a separate habit error: %d expects an int, and passing a float through it gives undefined behaviour.
What to do instead
Compare floating-point values with a tolerance: if (fabs(a - b) < 1e-6). Use %f for float and double in printf (and %lf in scanf for double). Prefer double for general computation unless memory is constrained.
Writing a `switch` case without a `break`, so control falls through into the next case, and using a non-constant expression or a `float` as the switch controlling expression.
Fall-through is defined C behaviour, not a compiler bug, but beginners expect switch to behave like a chain of exclusive if-else. The restriction that the controlling expression must have integer type and case labels must be integer constant expressions is easy to overlook.
What to do instead
End every case with break unless fall-through is deliberate, and add a comment when it is. Always include a default label. If the selection depends on a range or a floating-point value, use if-else instead of switch.
Using the value of an uninitialised local variable, for example `int sum; sum = sum + marks;` inside a function, or assuming that a local array is zero-filled.
Automatic (local) variables have indeterminate values; only objects with static storage duration are zero-initialised. In a written exam the program may appear to work on one machine and produce garbage on another, so the mistake is not consistently punished during practice.
What to do instead
Initialise at the point of declaration: int sum = 0;, int marks[5] = {0};. When tracing a program on paper, write '?' beside any variable that has not yet been assigned and treat any read of it as an error.
Declaring a function prototype as `void swap(int a, int b)` and expecting the caller's variables to change, or omitting the prototype entirely and calling the function before its definition.
C passes all arguments by value, so the function receives copies. Beginners transfer the intuition of reference semantics from other languages. Calling a function without a visible prototype in older C dialects leads to implicit declaration and default argument promotions, which produces wrong results for double and pointer arguments.
What to do instead
To modify the caller's variable, pass its address and dereference inside the function: void swap(int *a, int *b). Place prototypes above main and definitions below it, and compile with -Wall so implicit declarations are reported.
Confusing `*p++`, `(*p)++` and `++*p`, or assuming that adding 1 to an `int *` advances the address by one byte.
Postfix ++ binds tighter than unary *, so *p++ increments the pointer and yields the old pointee, while (*p)++ increments the pointee. Pointer arithmetic is scaled by the size of the pointed-to type, which is a rule candidates often memorise without applying.
What to do instead
Parenthesise explicitly whenever the intent involves both dereference and increment. For pointer arithmetic, state the scaled step: on a machine with 4-byte int, p + 1 advances the address by 4 bytes. Draw a small memory diagram with addresses for each expression before answering.
Calling `malloc` and then dereferencing the result without checking for `NULL`, casting the return value unnecessarily, or freeing the same block twice.
Dynamic allocation is often taught late in the course and practised only in small programs where allocation never fails. The habit of casting malloc comes from C++ material, and double free arises from losing track of ownership across functions.
What to do instead
Use the idiom p = malloc(n * sizeof *p); if (p == NULL) { /* handle */ }, do not cast the result, and pair every successful allocation with exactly one free(p) followed by p = NULL. Sketch the ownership of each block on paper before writing the code.
Using a pointer to a local variable after the function that declared it has returned, or returning the address of a local array from a function.
The lifetime of an automatic variable ends when its block exits, but the pointer value remains and looks valid. Because the program may still print plausible output, the bug is intermittent and easily dismissed as a compiler problem.
What to do instead
Return values, not addresses of locals. If a function must return an array or a string, either have the caller supply the buffer (void fill(int *out, int n)), use static storage when a single shared result is acceptable, or allocate with malloc and document that the caller frees it.