Do not trust random websites about C
Do not trust random websites about C
Almost every web tutorial/book/blog on C programming is riddled with subtle and not so subtle errors. If you are a beginner then you are, by definition, not qualified to determine the quality of the content. If you were qualified, you wouldn’t need to be reading it.
Yes, that includes this website. However, I try to cite the relevant sections in the C standard, and I welcome corrections. Readers should read the relevant parts of the standard and verify what I’m saying is correct. If it’s not, corrections would be greatly appreciated.
Below are some incorrect examples found on the web. If the errors have been since corrected, it happened after this post was written.
Even large companies that produce compilers can’t get it right
#include <stdio.h>
void swapnum(int *i, int *j) {
int temp = i;
i = j;
j = temp;
}
int main(void) {
int a = 10;
int b = 20;
swapnum(&a, &b);
printf("A is %d and B is %d\n", a, b);
return 0;
}
A description then follows it:
When the function swapnum() is called, the actual values of the variables a and b are exchanged because they are passed by reference. The output is:
A is 20 and B is 10
In fact, it won’t swap the variables at all. It even has constraint violations. The first and third lines of swapnum try to implicitly convert pointers and integers, and the seconds line simply assigns the copies of the pointers local to that function. Finally, the description claims they’re passed by reference, when they’re passed by value. C doesn’t have pass by reference. This probably stems from the fact that they’re trying to teach both C and C++ on the same page.
It should read:
void swapnum(int *i, int *j) {
int temp = *i;
*i = *j;
*j = temp;
}
You would think I’m picking on Joe Random Blogger tutorials, but the above comes from IBM’s compiler documentation. It is probably a careless oversight from the author, rather than a lack of knowledge of something fundamental to C. However, it is a clear indication that the author has not bothered to test the code.
I left feedback about this error via IBM’s website on 26 Feb 2014, more than 2 months before this post, no response or fix yet.
Learn C The Hard Way (a far more ironically accurate title than is first apparent)
Learn C The Hard Way is a free and popular online book written in the last few years. Unfortunately, in my opinion, it is carelessly written and full of mistakes. I could write a multipart series of posts just on the errors in this book, but it may not be constructive. I have yet to read all the book, but here are some glaring problems I quickly found.
One chapter is titled “Exercise 17: Heap And Stack Memory Allocation”. If you’ve read my previous post called “There is no stack”, you’d quickly realise why I just knew that this was a red flag. The author writes:
The easiest way to keep this straight is with this mantra: If you didn’t get it from
malloc
or a function that got it frommalloc
, then it’s on the stack.
Putting aside the fact that C doesn’t prescribe stacks, the above statement still has several problems. Common C implementations may use a stack to store variables of automatic storage duration, so let’s pretend that “it’s on the stack” means “has automatic storage duration”. Except:
- What if it was declared at file scope? Then it has static storage duration.
- What if it was declared inside a block with the static keyword? Then it has static storage duration.
- What if the C implementation decided to put the variable in a register instead for optimization purposes?
Recall from “There is no stack”, objects with static storage duration exist for the lifetime of the program, why would they be put on a stack?
It would have been far more useful for the author to have just spoken about storage duration instead of pretending that certain things always go on a stack, they don’t.
The author goes on to state:
If you take a pointer to something on the stack, and then pass that or return it from your function, then the function receiving it will “segmentation fault” (segfault) because the actual data will get popped off and disappear. You’ll be pointing at dead space.
This is wrong and misleading in several ways. For beginners, a segmentation fault occurs on some platforms when invalid access to storage occurs. A C implementation needn’t provide this facility1. Let’s set aside this and talk about implementations that do.
A segmentation fault is a useful thing to have, as it is an immediate cue to a developer or even end user that something is wrong with the program. A user could, at the very least, inform the developer than the program is crashing. With a segmentation fault, one can typically then examine a dump of the program’s current state with a debugger, and try to work out what went wrong. Great! Let’s try this on an implementation that’s using a stack:
#include <stdio.h>
#include <string.h>
char *int2str(int x) {
char a[100];
snprintf(a, sizeof a, "%d", x);
return a;
}
int main(void) {
puts(int2str(42)); // undefined behavior, using pointer
// to object whose lifetime has expired
return 0;
}
Now let’s compile and run this program:
% clang bad.c -o bad
x.c:7:12: warning: address of stack memory associated with local variable 'a' returned [-Wreturn-stack-address]
return a;
^
1 warning generated.
The clang compiler helpfully told me what was wrong. Now I’m going to cast caution to the wind and run the program with undefined behavior:
% ./bad
d[s��Zs�S�
The output was garbage, but no segmentation fault. Furthermore, if I then compile the same program using the -O2 flag (enabled some optimization), the program actually outputs 42. As we can see here, undefined behavior is entirely unpredictable, and it is misleading to beginners to claim otherwise. The author of Learn C The Hard Way claims it points at “dead space”, but in one instance it’s pointing at garbage (and didn’t segfault) and in another instance it’s still apparently pointing at the “42” string.
In the same quote, the author seems to think it’s wrong to pass pointers to these variables. The same quote:
If you take a pointer to something on the stack, and then pass that … then the function receiving it will “segmentation fault” … because the actual data will get popped off and disappear.
What, so the following code is bad and will segfault? No, it’s fine.
#include <stdio.h>
int main(void) {
char msg[] = "hello"; // stack variable according to author
puts(msg); // passing a pointer to "something on the stack"
return 0;
}
The author of Learn C The Hard Way has demonstrated an unwillingness to acknowledge or correct errors2, so I have given up trying to report them to him.
Conclusion
Please don’t try to learn C from random websites. I’ve listed some good books in the resource page that are widely regarded as explaining C properly and correctly. You will save yourself a lot of time and stress in the long run.
There might well be other good C books, but in my experience the majority aren’t.