All Those _t Data Types

You see them often if you code in C: time_t, size_t, and other _t data types, usually specific to some function or library. The C gurus have a method to their madness when it comes to naming these variables. The _t stands for something. It’s very consistent on purpose.

The _t implies a typedef, a defined data type. The typedef is based on an existing type. Here are the basic C language data types:

char
int
float
double

Qualifiers applied to the basic types include short, long, signed, unsigned.

Also included as data types are void and _Bool, though for the _t typedefs, the four basic ones plus a few of the qualifiers are used.

The question is: Why bother?

The reason is compatibility and consistency between architectures.

Not matter how a library or operating system measures the time, in C you use the time_t data type to declare a time value. Years ago, this value may have been a 16-bit integer. Then it became a 32-bit integer to deal with the Y2K problem. For Y36/Y38 problem, it might be a 64-bit integer on some systems. Still, no matter what the underlying mechanics, you code compiles providing you use the time_t data type when dealing with time values.

The question of the underlying type does play a role when using a placeholder.

For example, the sizeof operator returns a size_t value – essentially a quantity of bytes. As I do so often in my code, I use the %d placeholder to output a size_t value. That is, until the compiler reminds me:

warning: format specifies type 'int' but the argument has type 'unsigned long' [-Wformat]
        printf("The array is %d bytes in size\n",sizeof(array));
                             ~~                  ^~~~~~~~~~~~~
                             %lu
1 warning generated.

Years ago, the %d placeholder worked! But the underlying format is unsigned long, and clang is nice enough to show me where the mistake took place, which argument is referenced, and which conversion characters to use. It’s all too often that I rely upon compiler warnings like this to discover which placeholder to use.

Oh, by the way, the %zu placeholder can always be used to output a size_t value, whether it’s changed in the future or not.

When you don’t know the underlying data type for a _t typedef, you can go spelunking in the header files to locate the specific definition. Sometimes you might be lucky and find the typedef immediately in the header file. With modern compilers, however, the typedef is most likely included in another header file, often in a subdirectory.

For example, time_t is found in the /usr/include/bits/types/time_t.h file in one of my Linux distros. But in this file, it’s included in the /usr/include/bits/types.h header file, where its definition is related to defined constants in even other header files. Ugh.

No, it’s best to just use the typedef variable as intended. And, unfortunately, if the man page doesn’t explain which placeholder to use, best-guess it and wait for the warning to appear. This approach is disappointing, but it works.

Leave a Reply