If you've got others I'd love to hear them.
If you've got others I'd love to hear them.
The hardest part for me was learning the patterns necessary to do anything, and specifically to do it remotely well.
For example maybe one of the exercises could be some kind of board game, where you demonstrate how you translate "the ideas" into "the C code"? For example just because you know that a Knight can move two-plus-one spaces doesn't mean you have any idea where that code should be inserted into the program's structure, or how to write it in such a way where you avoid overrunning an array. (The naive solution would crash when the Knight tries to move off the board.)
Translating ideas -> C code was easily my hardest task when I was first starting out.
- The concept of a variable, and variables changing over time, seems quite difficult for people to grasp even when explained a few different ways. "x = 5" followed by "x = 12" proves quite mystifying, and "x = x + 1" even more so. People seem to have the most success with the idea that declaring a variable "int x" creates a location x which can hold an int, and you can put things in that location.
- Pointers actually don't seem to trip that many people up initially, once they get to that point. However, I don't think people actually understand exactly what they do, so much as memorize the rules for dealing with them. The same idea of a location to put something applies here too.
- Any case where the same function gets called more than once often ends up tripping people up; this applies particularly to recursion, but it can happen even when just calling the same function several times. In particular, this often interacts badly with people's understandings of variables. People need some understanding of scope.
- Combining several of the above, it would help to have clear explanations of the interactions between pointers, locations, and functions. Bonus for explaining what goes horribly wrong if a pointer refers to something that goes out of scope. That concept requires understanding several different pieces of C and putting them together.
Be careful when explaining the compiler and how a program actually runs. I've found that a lot of student problems come from "the compiler is magic" when it really isn't (maybe related to my other surprised comment below, about how people "don't get C"--they attribute too much magic to the compiler.) Maybe even emphasize that every piece of C code can be translated in a fairly easy fashion to a pretty small amount of assembly.
For the preprocessor, emphasize that it is a solely textual replacement, with no symbolic evaluation. Explain why:
#define FOO BAR + BAZ
or #define MAX(x,y) (((x) < (y)) ? (y) : (x))
will go horribly wrong (the first in 5*FOO, the second in MAX(x++, y++)).For example...
const char* current = "ohai thar";
const char* end = current + strlen( current );
assert( end >= current );
size_t bytecount = (end - current);
(size_t is an unsigned type, so if 'end' is less than 'current', it will overflow. If you want to allow for that, use ptrdiff_t.)If you set out to only teach the language syntax and paradigms you are leaving a beginner with a lot of extra work before they can start or contribute to meaningful projects. This is the reason that people learn so much from reading other people's code in open-source projects: most writers skim over trying to teach the most fundamental skill of programming.
I do believe it is possible to teach practicalities in addition to theory and if you attempt to do this, you will be doing a lot more than most writers have done in the past.
This most often comes up when people are asked to do some binary manipulation of numbers, i.e.:
unsigned u = 19;
unsigned v = u >> 1;
"v" is now 9, and to really understand it one must grasp how numbers are represented in binary under the hood.People also fail to understand strings:
char* s = "string literal";
To some, it's absolutely opaque that the first byte "s" points to contains 0x73, and that represents "s" in ASCII.The C99 standard defines the >> operator in terms of division by powers of 2, so one can determine the result of 19 >> 1 without needing to know anything about how 19 is actually represented by the machine.
Correct me if I am wrong, but I don't think C guarantees anything about the binary representation. Depending on the architecture, `v` can have different value.
You might be thinking of character representation for the later example.
Is there anything that says, for example, it can't be BCD?
I always thought that "all identifiers with a leading underscore were reserved". I just consciously ignored it, and have never had a problem in years. But I was also always using member variable names like "_children", "_childCount", etc, not "_Children".
This is the big selling point for C++ : the convenience of STL. In all the projects that I worked, it was one of the important reasons to choose C++ over C.
Yes, this focuses less on the language itself and more on the ecosystem around it, but I figured if you have an entire chapter dedicated to "make", then this makes sense as well.
y=1+x=3;
Why and how this works is foreign to most beginners. Likewise (ok, this is C++ but the point remains)
cout<<1+x;
Isn't a command to display 1+x. The output is the result of inserting the computed value into couture. There is a semantic difference.
Groking operators early is key to understanding C well.
there are a lot of C devs who don't understand what it is their code is producing, and how an application and memory are managed
I understand all of it now, but had no idea about the stack or the x86 instructions, etc for years. I was still productive, and wasn't hindered.
The details can come later. Even big details like "how it works at the low level".
The most important part is to keep things fun; LPTHW was fun. For me, mucking about in assembly wasn't, and that sentiment seems like it might be common among new C programmers. Assembly has a way of slowly steamrolling your motivation.