In addition to the integral and floating types provided by C, there are some extra types that will be commonly used. The standard C library groups these common types (and some macros) into a single header, stddef.h.

Types

To define new types in C you used a typedef. A typedef does not actually introduce a new type, it just creates an alias to an existing type. They take the form of:

typedef existing-type new-type;

For example, if you wanted to make it more convenient to use the unsigned int type by only having to type uint, then you would use the following typedef:

typedef unsigned int uint;

Now, any occurrence of the uint identifier will be treated as if it were unsigned int. This may sound similar to a #define, but a typedef does not perform a text replacement.

ptrdiff_t

This type "is the signed integral type of the result of subtracting two pointers". So, we need this type to be of the same type as an address for the specific architecture. The x86_64 ABI tells us that a pointer to any type is an "unsigned eightbyte". Since this type is signed then we can focus on the other signed eightbyte types designated in the ABI: signed long, and signed long long. We'll use the signed long type:

typedef signed long ptrdiff_t;

size_t

This type "is the unsigned integral type of the result of the sizeof operator". The sizeof operator yields the size (in bytes) of its operand. It can be used to find the number of bytes in a type, an array, or a structure:

struct myStruct
{
    char a;
    int b;
    unsigned long c;
};

int a = 0;
char myArray[] = "hello, world";
struct myStruct test;

sizeof char;
sizeof a;
sizeof myArray;
sizeof test;

Since an array or a struct can be arbitrarily long we should choose a type which can represent a large range of values. The largest unsigned integral type available on the x86_64 architecture is the unsigned long type, which is what we'll use.

typedef unsigned long size_t;

wchar_t

This type "is the integral type whose range of values can represent distinct codes for all members of the largest extended character set specified among the supported locales". In other words, if your extended character set used a maximum of 3 bytes per character then wchar_t would need to be at least 3 bytes in length. Since we are using UTF-8 which uses a maximum of 4 bytes per character then we only need to consider types which are at least 4 bytes in length. There is no sense in using a type which is more than 4 bytes as that would just waste space, so we can choose between signed int and unsigned int. A character doesn't really have a sign so we will choose the unsigned int type:

typedef unsigned int wchar_t;

Macros

If you're not already familiar with macros, then see the errno.h post for more information.

NULL

This macro "expands to an implementation-defined null pointer constant". The x86_64 ABI states that "a null pointer (for all types) has the value zero." We can simply cast the value 0 as a void pointer to accomplish this:

#define NULL    ((void *) 0)

offsetof(type, member-designator)

This macro "expands to an integral constant expression that has type size_t, the value of which is the offset in bytes, to the structure member (designated by member-designator), from the beginning of its structure (designated by type). The member-designator shall be such that given static type t; then the expression &(t.member-designator) evaluates to an address constant. (If the specified member is a bit-field, the behavior is undefined.)"

So, the number of bytes between the beginning of a structure and the defined structure member. To get this we can subtract the address of the structure from the address of the member:

#define offsetof(type, member)  ((size_t) &(type.member) - &type)

Conclusion

That's everything for stddef.h. It's not a very involved header but we will see these types and macros pop up frequently throughout the rest of the library.

#ifndef _STDDEF_H
#define _STDDEF_H

typedef signed long ptrdiff_t;

typedef unsigned long size_t;

typedef unsigned int wchar_t;

#define NULL    ((void *) 0)

#define offsetof(type, member) ((size_t) &(type.member) - &type)

#endif /* _STDDEF_H */
comments powered by Disqus