Essential C (2003) [pdf]
This guide is short (which is always nice) but not has a couple of flaws in places in the brief skim I gave it. For example:> In particular, if you are designing a function that will be implemented on several different machines, it is a good idea to use typedefs to set up types like Int32 for 32 bit int and Int16 for 16 bit int.Use  please> The char constant 'A' is really just a synonym for the ordinary integer value 65 which is the ASCII valueNot always, especially right after you came off a paragraph explaining how different machines have implementation-specific behaviors> The compiler can do whatever it wants in overflow situations -- typically the high order bits just vanish.This is a good time to explain what undefined behavior actually means> The // comment form is so handy that many C compilers now also support it, although it is not technically part of the C language.Part of the language since C99> C does not have a distinct boolean type_Bool since C99> Relying on the difference between the pre and post variations of these operators is a classic area of C programmer ego showmanship.I'm fine with you mentioning that this can be tricky, but this is more opinion than I am comfortable with in an introductory text> The value 0 is false, anything else is true. The operators evaluate left to right and stop as soon as the truth or falsity of the expression can be deduced. (Such operators are called "short circuiting") In ANSI C, these are furthermore guaranteed to use 1 to represent true, and not just some random non-zero bit pattern.Under the assumption that there are no boolean types from earlier, this is not true> The do-while is an unpopular area of the language, most everyone tries to use the straight while if at all possible.I would argue that people use do-while more than they need to> I generally stick the * on the left with the type.Not a problem, but :(> The & operator is one of the ways that pointers are set to point to things. The & operator computes a pointer to the argument to its right. The argument can be any variable which takes up space in the stack or heapAnd constants/globals> To avoid buffer overflow attacks, production code should check the size of the data first, to make sure it fits in the destination string. See the strlcpy() function in Appendix A.strlcpy is non-standard and probably not what you want> The programmer is allowed to cast any pointer type to any other pointer type like this to change the code the compiler generates.> p = (int * ) ( ((char * )p) + 12); // [Some spaces added by me to prevent Hacker News from eating the formatting]Only in some very specific cases…> Because the block pointer returned by malloc() is a void* (i.e. it makes no claim about the type of its pointee), a cast will probably be required when storing the void* pointer into a regular typed pointer.Casting malloc is never required (and I would say usually not a good thing to do)