Hacker News new | ask | show | jobs
by saagarjha 2119 days ago
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 <stdint.h> please

> The char constant 'A' is really just a synonym for the ordinary integer value 65 which is the ASCII value

Not 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 heap

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

4 comments

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

Actually, I believe _Bool is guaranteed to use 0 for false, and any non-0 value is stored as 1 for true. Arithmetic on _Bool is also guaranteed, based on those values.

For example, I believe the standard guarantees:

    _Bool x = 255;
    assert(x == 1);

    size_t y = 10 + x;
    assert(y == 11);
This is exactly why I included earlier bit where they claimed there was no boolean type, to show that their conclusion is logically inconsistent rather than just incomplete ;)
>> I generally stick the * on the left with the type.

> Not a problem, but :(

In a declaration, * is a type modifier. E.g. `int a;` declares a variable of type "int" named "a". `int* a;` declares a variable of type "pointer to int" named "a".

The only time this doesn't work is if you stick multiple declarations on the same line. That's annoying to me, because it means you're breaking the "declare variables as close to their first use as possible" practice. It's not K&R C any more, you don't need to declare everything all at once at the top of the scope.

Also, to prove that `` in a declaration is part of the type, note that K&R style function declarations (no argument names, just types) are still valid C (though I'd strongly discourage their use). So `void func(int a, int b);` is identical to `void func(int, int);`. It's very different from `void func(int, int);` that you'd get if you assume the `` goes with the `a`.

It took Microsoft 16 years to support most of C99 in MSVC, and they are still not completely done after 21 years. I think for a document last updated in 2003 it's ok not being based on C99 ;)
> Under the assumption that there are no boolean types from earlier, this is not true

Can you elaborate on this one? I thought && and || expressions always evaluated to 0 or 1.

C99 added type _Bool (also called bool if you #include <stdbool.h>) -- but it doesn't actually use it much. Operators that yield logically "boolean" values (<, <=, >, >=, ==, !=, &&, ||, !) yield values of type int, not _Bool. The value is always 0 or 1 -- in contrast with isdigit(), for example, which is specified to return 0 for false and some unspecified non-zero value for true.

Converting any scalar value (that includes pointers) to _Bool yields 0 or 1 (false or true).

Now that I think about it, I think that depends on what you mean by "use". I was commenting from a perspective that you can pass in something that is not 0 or 1, and in general it is not advised to assume that a "boolean" is 0 or 1 especially given that this document doesn't mention the boolean type (which is guaranteed to have those values). This is true even under ANSI C, because as it mentions later, programmers depend on any nonzero value being "truthy".