void some_function(ptr *some_object, size_t n);
// would definitely work...but including a header in a header is ugly practice
typedef long long unsigned size_t
// to account for win64 making `long` 32-bits...but it's possibly non-portable
typedef char * size_t
// I like this one...but am not 100% sure about how portable it is
size_tis undefined, it's said that the only portable way to get the definition for it is to include one of the headers that has it.
However, as I understand it, using an
#includein a header file is bad practice, because headers shouldn't include other headers, right?
You have been misinformed. There is no reason not to include headers in other headers. Multiple
#includes of the same header can cause problems if they don't have proper include guards, but the standard headers are guaranteed to have such guards (or equivalent) -- and non-standard headers should have such guards. (
<assert.h> is a special case.)
If you need
size_t, either in a source file or in a header file, then you should use
#include <stddef.h> to make the name
size_t visible (or you can include one of the other standard headers that also
<stddef.h> in a header will make its declarations visible in any other files that include your header, but that's not a problem.
As I understand it, the
size_ttype is the smallest type guaranteed to support the longest object your system can handle.
It's an unsigned integer type chosen by the implementation to hold size values. It should be able to hold the size in bytes of any object (though that's not explicitly guaranteed). There's no guarantee that it's the smallest such type, and no benefit in assuming that it is.
As for pointers, any given pointer represents an offset from the 0th byte in RAM, where dereferencing that offset will give you the value stored at that offset.
Thus, aren't size_t and any given pointer type guaranteed to be the same size?
No, there is no such guarantee. On most modern systems, pointers do represent a byte offset from virtual address 0, but that's not guaranteed. On a system with a segmented memory model, the maximum size of a single object might be much smaller than the size of memory; a pointer might contain a segment indicator and a byte offset, with an object not allowed to occupy more than one segment.
A value of type
size_t needs to represent the size of any single object. A pointer (say, of type
unsigned char* needs to represent the address of any object, or of any byte within it.
Don't make any unnecessary assumptions about the sizes of pointers or of
size_t. Let the compiler figure it out for you.
The possibilities seem to be:
#include <stddef.h> // would definitely work...but including a header in a header is ugly practice
No, it's not ugly practice. Just do this.
typedef long long unsigned size_t // to account for win64 making `long` 32-bits...but it's possibly non-portable
This is absolutely non-portable. It's even possible that
size_t could be wider than
unsigned long long. But defining your own type that's wider than the compiler's
size_t is neither useful nor necessary.
typedef char * size_t // I like this one...but am not 100% sure about how portable it is
size_t is required to be an unsigned integer type. If you've been told that integers and pointers are interchangeable, you've been badly misinformed.