Yes, fully agreed with SiliconWizard above.
Perhaps some more waffling about the
sizeof operator would be useful?

If you are interested, the C99 standard with all corrigenda included is available as a PDF
here.
The
sizeof operator has two forms:
sizeof (type) sizeof expressionThe first form evaluates to the size of the type
type. It must not be an incomplete type, except that a structure with a flexible array member as the last member is allowed; then it evaluates to the size of the structure without that member (but including any padding before that member).
If the type is a variable-length array, then the expression specifying the array length is evaluated (C99 6.5.3.4p2 like SiliconWizard already explained above), and any side effects of that array length evaluation are visible outside the expression.
The second form evaluates to the size of the value the expression yields, but it cannot be a bit field or a function. (Function pointer is fine.)
The expression itself is only evaluated for its
type (unless it involves a variable-length array, in which case that length sub-expression is fully evaluated). This means that if you want to know the size of the type that pointer
p points to, use
sizeof *p as it is always safe, even when
p is NULL or undefined.
Even if you have something as odd as say
struct foo ***p, you can use
sizeof *p == sizeof (struct foo **),
sizeof **p == sizeof (struct foo *), and
sizeof *p = sizeof (struct foo). Only the type matters. No memory is ever examined, and the
value of
p is irrelevant.
As I already mentioned, I consider any increment, decrement, or assignment in the operand (right side) of a
sizeof expression to be extremely suspicious: a sure sign of foul play.
There are three common patterns, that can confuse unaware C programmers.
- Number of array elements
When you have an array type, say
sometype my_array[MY_ARRAY_SIZE];
you can use
sizeof my_array / sizeof my_array[0]
to obtain the number of elements in my_array. The dividend is the number of bytes in the entire array, and the right side is the number of bytes in the first element in the array.
This only works for array types with specific lengths. It does not work for pointers, because the C compiler only knows the length of arrays at compile time; pointers do not have any such "array length" information associated with them, and there is no such information at run time by default (which is the reason you need to keep track of the length of many things yourself).
- Robust/correct-size dynamic allocation
When your code declares a pointer, say struct foo *p;, and you need to allocate memory for one such structure later on, it is useful to use
p = malloc(sizeof *p);
instead of p = malloc(sizeof (struct foo));, because we humans sometimes end up changing the type the p points to, but forget to update the type later in the sizeof expression later in the code. The former form is more robust, and if you read it as "malloc the size of the thing p points to", it makes more sense, too.
- Allocation of structures with flexible array members
Let's say you create a string or byte array type,
typedef struct {
size_t size;
size_t used;
unsigned char data[];
} mystr;
A function that creates a new one duplicating existing data can be written as
mystr *mystr_create(const void *src, size_t len, size_t space)
{
mystr *result;
result = malloc((sizeof *result) + len + space + 1);
if (!result) {
errno = ENOMEM;
return NULL;
}
if (len > 0) {
memcpy(result->data, src, len);
}
result->data[len] = '\0'; /* A convenience! */
result->used = len;
result->size = len + space + 1;
return result;
}
Note that you often also see the equivalent result = malloc(sizeof (mystr) + len + space + 1);. In the above snippet, the extra parentheses around sizeof *mystr is intended to clarify the expression for us humans; they are not strictly necessary. (For the compiler, the operand to sizeof is an unary expression, which means that sizeof a + b == (sizeof a) + b.)
Finally, when reading pointer types, split the definition at each
*, read the type from rightmost part left, eplacing each
* with
"is a pointer to", to get the correct English human-readable definition. For example, if you see something nasty like
volatile struct bar *const *p;you read it as
"p is a pointer to a const pointer to a volatile struct bar". A
const pointer is a pointer that the code won't try to modify to point elsewhere, and
volatile means the value can be changed at any time so the compiler must not generate code that caches/remembers the value. So,
p points to a pointer that the code won't try to modify, and that pointer points to a
struct bar whose value can change unexpectedly. Both the members in that struct, and
p itself, can be modified.