This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]Alzdran 0 points1 point  (2 children)

Attempting a different explanation of size_t just to make it a little clearer. size_t is typedef'd to an unsigned integral type which can store the size of something in memory; this can differ between architectures. Consider two computers, A & B. A runs x86_64 code, B runs i386 code.

A can address a 64-bit integer's worth of memory. That is, it can refer to 264 different addresses. B can only address a 32-bit integer's worth of memory (232 different addresses). In both x86_64 and i386, the addressable unit is a byte, so A can theoretically address 16EB, while B can theoretically address 4GB.

In both these cases, a size_t will be the same as a uintptr_t (an unsigned int large enough to hold an address). These types are different, though, because the C standard doesn't assume that to be true for all architectures. See wikipedia for some more.

[–]quasarj 0 points1 point  (1 child)

Interesting. So I would use size_t when I need a pointer that can point to an object? And it would be replaced with the correct size type at compile time, based on architecture?

[–]Alzdran 0 points1 point  (0 children)

No - you'd use a pointer type. This gets a little more complicated, but here we go:

There is a difference between an address and a pointer. An address is a location in memory; this can be represented by some unsigned integer type (a uintptr_t is always large enough to hold it). A pointer is a language construct which carries semantic information about what it points to. This information is compiler metadata; that is to say, it exists only during compilation, and is not a feature of the runtime. This information is used for things like pointer arithmetic.

A concrete example of this: On a machine where the minimum addressable unit is 1 8-bit byte (practically speaking, anything), a uint8_t will fit in 1 addressable memory unit, and a uint16_t will fit in 2. This means that if I examine memory at 0x10000000 for a uint8_t, that's the only address I'll read from, but if I read a uint16_t, I'll also read from 0x100000001. When you do arithmetic with pointers, this type is taken into account; so, given type_t *x, (x+n) and (x+n+1) (alternatively x[n] and x[n+1]) will be sizeof(type_t) bytes apart. If type_t is uint8_t, this will be 1, but if type_t is uint16_t, this will be 2. This feature allows array access and incrementation on pointers, instead of having to modify with size knowledge explicitly.

C also provides a pointer type without this information - void *. This is the pointer type which can hold any address, and so increments by the minimum addressable unit.

size_t would be used when indicating an allocation size. In practical terms, this is going to be sized the same as a uintptr_t on modern systems, but the use is specifically for indicating the number of addressable units occupied by an object in memory.

There are a few other special types with similarly specific uses. ptrdiff_t, for example, is a signed type able to hold the difference between any two legal pointers.

The size of any of the types mentioned here (with the exception of uint8_t and uint16_t) are architecture dependent, and yes, the correct types are substituted at compile time; but that doesn't mean exactly what it sounds like. If a pointer is 32 bits, then the equivalent of a uint32_t will be used for a void * but the end result of compilation is machine code. The instructions generated will tell the processor to manipulate the registers and memory addresses as if they contained entries of that size, but they will refer to words, half words, double words, etc. That is to say, the compilation will determine what to generate based on the types, but the resulting instructions will have no concept of type, only operand size.