5 Sneaky C Bugs Every Programmer Should Avoid
This article lists five common C programming mistakes—uninitialized variables, array out‑of‑bounds access, string overflow, double free, and invalid file pointers—explains why they occur, shows concrete code examples with typical output, and offers practical ways to prevent each bug.
1. Uninitialized Variables
When a variable is defined without an explicit initializer, its value is indeterminate. The content may appear as zero on some systems, but portable C code must initialize all automatic variables to avoid random values that can cause logic errors or crashes.
#include <stdio.h>
#include <stdlib.h>
int main() {
int i, j, k;
int numbers[5];
int *array;
puts("These variables are not initialized:");
printf(" i = %d
", i);
printf(" j = %d
", j);
printf(" k = %d
", k);
puts("This array is not initialized:");
for (i = 0; i < 5; i++) {
printf(" numbers[%d] = %d
", i, numbers[i]);
}
puts("malloc an array ...");
array = malloc(sizeof(int) * 5);
if (array) {
puts("This malloc'ed array is not initialized:");
for (i = 0; i < 5; i++) {
printf(" array[%d] = %d
", i, array[i]);
}
free(array);
}
puts("Ok");
return 0;
}Running the program on different platforms yields different garbage values, demonstrating why every automatic variable, array element, and dynamically allocated memory should be explicitly initialized (e.g., using memset or assigning zero).
2. Array Out‑of‑Bounds Access
In C, arrays are zero‑based. Accessing an index equal to or greater than the array length reads or writes memory outside the allocated region, which leads to undefined behavior, possible data corruption, or crashes.
#include <stdio.h>
#include <stdlib.h>
int main() {
int i;
int numbers[5];
int *array;
puts("This array has five elements (0 to 4)");
for (i = 0; i < 5; i++) {
numbers[i] = i;
}
for (i = 0; i < 10; i++) { // out‑of‑bounds read
printf(" numbers[%d] = %d
", i, numbers[i]);
}
puts("malloc an array ...");
array = malloc(sizeof(int) * 5);
if (array) {
puts("This malloc'ed array also has five elements (0 to 4)");
for (i = 0; i < 5; i++) {
array[i] = i;
}
for (i = 0; i < 10; i++) { // out‑of‑bounds read
printf(" array[%d] = %d
", i, array[i]);
}
free(array);
}
puts("Ok");
return 0;
}The first five reads are valid; the remaining indices produce unpredictable values. Writing beyond the bounds would typically cause an immediate segmentation fault.
3. String Overflow
In C, a string is an array of char. Using unsafe input functions such as gets allows the user to write more characters than the buffer can hold, overwriting adjacent variables and potentially crashing the program.
#include <stdio.h>
#include <string.h>
int main() {
char name[10]; /* buffer for up to 9 characters + '\0' */
int var1 = 1, var2 = 2;
printf("var1 = %d; var2 = %d
", var1, var2);
puts("Where do you live?");
gets(name); /* unsafe – does not limit input length */
printf("<%s> is length %zu
", name, strlen(name));
printf("var1 = %d; var2 = %d
", var1, var2);
puts("Ok");
return 0;
}Providing a short input (e.g., "Beijing") works, but a long input such as the 58‑character town name "Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch" overwrites var1 and var2, changes their values, and may trigger a segmentation fault. Safer alternatives include fgets with a size limit or getline, which allocates sufficient memory automatically.
4. Double Free (Repeated Memory Release)
Memory allocated with malloc must be released exactly once with free. Calling free on the same pointer a second time results in undefined behavior, often detected as a "double free" error and causing program termination.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *array;
puts("malloc an array ...");
array = malloc(sizeof(int) * 5);
if (array) {
puts("malloc succeeded");
free(array); /* first free */
}
puts("Free the array again ...");
free(array); /* second free – error */
puts("Ok");
return 0;
}Typical mitigation strategies are:
Set the pointer to NULL immediately after freeing, so a subsequent free becomes a no‑op.
Encapsulate allocation and deallocation in the same function or module to keep ownership clear.
Use tools such as Valgrind or AddressSanitizer to detect double‑free bugs during development.
5. Using an Invalid File Pointer
The fopen function returns NULL when the requested file cannot be opened (e.g., it does not exist or permission is denied). Dereferencing a null file pointer leads to a segmentation fault.
#include <stdio.h>
int main() {
FILE *pfile;
int ch;
puts("Open the FILE.TXT file ...");
pfile = fopen("FILE.TXT", "r");
if (pfile == NULL) {
perror("fopen failed");
return 1;
}
puts("Now display the contents of FILE.TXT ...");
while ((ch = fgetc(pfile)) != EOF) {
printf("<%c>", ch);
}
fclose(pfile);
puts("Ok");
return 0;
}Always check the return value of fopen before using the pointer. If the check fails, handle the error gracefully (e.g., print a diagnostic message and exit or fall back to a default configuration).
Conclusion
These five classic C pitfalls—uninitialized variables, out‑of‑bounds array access, string overflow, double free, and invalid file pointers—are common sources of undefined behavior and crashes. By habitually initializing data, respecting array bounds, using safe input functions, freeing memory exactly once, and validating file pointers, developers can write more robust and portable C programs.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Liangxu Linux
Liangxu, a self‑taught IT professional now working as a Linux development engineer at a Fortune 500 multinational, shares extensive Linux knowledge—fundamentals, applications, tools, plus Git, databases, Raspberry Pi, etc. (Reply “Linux” to receive essential resources.)
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
