stddef.h
tags: stddef.h
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 */