Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I look forward to the content. As a beginning C++ instructor I find there is something lacking between the truth of the language and the conventions for presenting it. Nobody, AFAIK save for the truly hardcore student, has nailed it.


Got any feedback on the things students get wrong? My experience is they fail to grasp memory management, pointers, functions as pointers, linkers or just how a program actually runs.

If you've got others I'd love to hear them.


One of the first programs I tried to write in C++ (my first language) was a chess game. I eventually hacked it together, but it wasn't pretty.

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.


I've watched and spoken to a large number of beginning C and C++ programmers (and beginning programmers in general) during their first terms of programming in university. I've noticed a few common problems, and I think they all stem from not understanding the details of how a program runs on a real machine:

- 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.


As a teaching assistant for a class (http://www.cs.cmu.edu/~410/) where students write a whole lot of code in C, please introduce the address-of (&) operator and explain how to obtain pointers without using malloc(). Do this before the concept of the heap is ever introduced at all.

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++)).


I find it is much more helpful, when teaching C, to avoid the word 'address', and call (&) the pointer-to operator. Thinking of pointers as numbers (which the address-analogy does) tends to be harmful for newer C programmers because they want to treat them like numbers.


They are numbers (either 32-bit or 64-bit). Nothing more, nothing less. I don't understand why you wouldn't want to think of them that way.

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.)


No, they aren't numbers. The standard is quite clear on this. Consider old DOS architecture where you had a section and offset. Your code is also not well-defined C. C only lets you perform subtraction between two pointers in the same block, this code is at best implementation defined and worst undefined (I can't remember).


You'd still need to explain pointer arithmetic somehow.


That is easy since pointer arithmetic is only valid inside a contiguous block. You don't need to talk about it in terms of a pointer being a number at all.


can you get around the second without typeof(a gnu extension)?


at least if you stick to macros


You can't, unfortunately. The point is to be cognizant of the fact that expressions will be evaluated for their effects more than once (maybe). So, don't put effectful things inside a macro expansion.


When learning a new language, the syntax and exercises which explore this come to me fairly quickly. The difficulties are (a) learning the best way of structuring large blocks of code using the new syntax and tools, and (b) for new programmers: understanding how to convert conceptual rules in my head into algorithms.

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.


My experience as an interviewer showed that in addition to the things you listed, people often fail to grasp binary representation of numbers in a computer at all.

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.


Does C require that you are running on a binary computer?

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.


> "v" is now 9, and to really understand it one must grasp how numbers are represented in binary under the hood.

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.


C allows differences in how negative integers are represented (at least, C89 did---I'm not sure about C99 as I don't have the standard in front of me). There are three different ways to represent negative numbers in binary, sign magnitude, one's complement, and two's complement (these days, most computers are two's complement). The upshot is that you might end up with two type of zeros (sign magnitude, one's complement) or an unequal range of negative numbers (two's complement has one additional negative number) and taking the absolute value of an integer may not be possible for some values (given a 16-bit integer on a two's complement system, you cannot get the absolute value for -32,768, which is a valid integer on such a system).


If 19 is represented differently in binary on your platform than (leading zeros)10011, you are already quite screwed.

You might be thinking of character representation for the later example.


No, I was thinking binary 19. Does C say the implementation is going to be 1s complements, 2s complements or whatever?

Is there anything that says, for example, it can't be BCD?


Yes even though this is exactly why people want to use C now, to get exact binary behaviour.


The arrow operator and identifiers with leading underscores seem to be glossed over or skipped fairly often in education. It's intimidating to look at code that uses them if you don't know what they are.


Identifiers with leading underscores? I use those for member variables... (this makes more sense in C++ without the arrow notation.)


Maybe he's referring to how identifiers with leading underscores followed by a capital letter are reserved in C++ (I'm not sure if they are in C)...


Oho. Followed by a capital letter? Interesting.

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".


In fact, here's a reference: http://msdn.microsoft.com/en-us/library/e7f8y25b(v=vs.80).as... (It's in a "Microsoft Specific" block but says it's part of the ANSI C standard.) It applies to C, and it also applies to identifiers staring with two underscores.


The C Standard reserves identifiers starting with a leading underscore for the system implementation (C standard library, Posix libraries, etc). They aren't meant for use by user code (or rather, you can use them, but they might conflict with system defined identifiers).


The first problem is the notion of absolute precision and detail required to do even the simplest task. The next is how syntax relates to such notions. Then comes mapping the syntax to the task. If they don't grok variables at this point, they're not going anywhere. A lot don't. Above all is the imperative of completion: if they don't make it work somehow, it doesn't work period. I'll try to salvage their work, tell them what to fix, and accept resubmissions - but if it doesn't work, it inherently won't pass the low bar for success.


I'm not sure if this is going to be pure C or a mix of C/C++, but if you're sticking to pure-C, here's a chapter idea: using containers in C.

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.


Too often we think of C in terms of commands. C is a matter of _operators_. Consider "=", say in the statement

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.


In my opinion the thing that many new C developers have trouble with is how large the gap between what their implementation does can be from what the standard says. I think newer C programmers are used to languages that specify a lot more than C does. Try It And See is generally not a useful response for a C question.


I was taught C at Uni, but this was after having done a semester of x86 assembly language so we already had a good grounding in pointers, how addresses work etc. This was our textbook http://oreilly.com/catalog/9780937175231


dedicate some later chapters to ramping the reader onto assembler, the stack (and smashing it) and instructions

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


While this is important, it probably shouldn't be in beginner C book.

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.


imho, please do have a look at "expert c programming" it is quite nice. the conversational style of the book makes it very enjoyable to read.


using gdb?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: