What does a type followed by _t (underscore-t) represent?
Asked Answered
G

11

351

This seems like a simple question, but I can't find it with the Stack Overflow search or Google. What does a type followed by a _t mean? Such as

int_t anInt;

I see it a lot in C code meant to deal closely with hardware—I can't help but think that they're related.

Gosser answered 23/10, 2008 at 22:18 Comment(1)
Where is int_t defined? If it's always defined as int, it's not useful; it's much clearer to use int directly. If it's not always defined as int (say, if it could be long int or short int), then it's a poorly chosen and confusing name.Isidoro
G
277

As Douglas Mayle noted, it basically denotes a type name. Consequently, you would be ill-advised to end variable or function names with '_t' since it could cause some confusion. As well as size_t, the C89 standard defines wchar_t, off_t, ptrdiff_t, and probably some others I've forgotten. The C99 standard defines a lot of extra types, such as uintptr_t, intmax_t, int8_t, uint_least16_t, uint_fast32_t, and so on. These new types are formally defined in <stdint.h> but most often you will use <inttypes.h> which (unusually for standard C headers) includes <stdint.h>. It (<inttypes.h>) also defines macros for use with the printf() and scanf().

As Matt Curtis noted, there is no significance to the compiler in the suffix; it is a human-oriented convention.

However, you should also note that POSIX defines a lot of extra type names ending in '_t', and reserves the suffix for the implementation. That means that if you are working on POSIX-related systems, defining your own type names with the convention is ill-advised. The system I work on has done it (for more than 20 years); we regularly get tripped up by systems defining types with the same name as we define.

Genome answered 23/10, 2008 at 22:29 Comment(9)
it seems reasonable that OS and common runtime libraries define types with genericish names; but shouldn't your company's types also be prepended with a prefix or something?Extraordinary
Yes, they should. Unfortunately, I wasn't in charge of the naming convention at the time when it was used. And global search and replace isn't something that's sanctioned - though it could be used to fix a lot of problems, even in a monstrous code base.Genome
I use _type instead of _t on my typedefs precisely to avoid that.Flail
@Jonathan Leffler -- What naming convention would you use for user-defined types?Larousse
@Andrew: if you have a convenient abbreviation to use as a prefix, then you may be safe to use abbr_xxxxx_t type names. Without such a prefix, you may get caught any time. Generally, the standardized _t types use all lower-case (FILE and DIR are two exceptions, twice - all caps, and no _t), so you could use CamelCase_t with moderate safety, with or without the leading caps. The system I mainly work on tends to live dangerously and use _t anyway, but it has bitten us on occasions. I tend to use CamelCase without a suffix for my own work; my functions are usually all lower-case.Genome
@JonathanLeffler, I've started using that convention, CamelCase for types, lower_case for functions. I searched this question hoping I wasn't the only one. Thanks for the validation!Mispronounce
@AustinMullins that's PascalCase, not camelCase. For reference: snake_case, kebab-case, SHOUT_CASE, Title Case, Sentence caseMesa
No version of the C standard up to (and including) C11 have any mention of off_t, it is a POSIX extensionLalapalooza
Convention-wise, after running into this problem due to redefining timer_t over top of the POSIX definition accidentally, I'm switching from this convention: typedef struct timer_s { /* definition here */ } timer_t; and typedef enum timer_error_e { /* definition here */ } timer_error_t to this, respectively: typedef struct Timer_s { /* definition here */ } Timer; and typedef enum Timer_error_e { /* definition here */ } Timer_error. This gets rid of all of my _t endings, which are reserved by POSIX.Vatican
M
72

The _t usually wraps an opaque type definition.

GCC merely add names that end with _t to the reserved namespace you may not use, to avoid conflicts with future versions of Standard C and POSIX (GNU C library manual). After some research, I finally found the correct reference inside the POSIX Standard 1003.1: B.2.12 Data Types (Volume: Rationale, Appendix: B. Rationale for System Interfaces, Chapter: B.2 General Information):

B.2.12 Data Types
Defined Types
The requirement that additional types defined in this section end in "_t" was prompted by the problem of name space pollution. It is difficult to define a type (where that type is not one defined by POSIX.1-2017) in one header file and use it in another without adding symbols to the name space of the program. To allow implementors to provide their own types, all conforming applications are required to avoid symbols ending in "_t", which permits the implementor to provide additional types. Because a major use of types is in the definition of structure members, which can (and in many cases must) be added to the structures defined in POSIX.1-2017, the need for additional types is compelling.

In a nutshell, the Standard says that there are good chances of extending the Standard types' list, therefore the Standard restricts the _t namespace for its own use.

For instance, your program matches POSIX 1003.1 Issue 7 and you defined a type foo_t. POSIX 1003.1 Issue 8 is eventually released with a newly defined type foo_t. Your program does not match the new version, which might be a problem. Restricting the _t usage prevents from refactoring the code. Thus, if you aim to a POSIX compliancy, you should definitely avoid the _t as the Standard states it.

Side note: personally, I try to stick to POSIX because I think it gives good basics for clean programming. Moreover, I am pretty fond of Linux Coding Style (chapter 5) guidelines. There are some good reasons why not using typedef. Hope this help!

Measure answered 4/10, 2012 at 12:16 Comment(0)
I
64

It's a convention used for naming data types, e.g with typedef:


typedef struct {
  char* model;
  int year;
...
} car_t;

Impoverish answered 23/10, 2008 at 22:26 Comment(3)
Do not use the _t suffix for your own types, since they are reserved in some standards, better use _T. Also, never typedef an anonymous struct, forward declaring this struct will become impossible. Give the struct a name or do not use typedef at all.Raffle
To add to what 12431... (the guy above me) said, you should in general NEVER typedef pointers and structs/unions. Doing so is confusing and only serves to obfuscate type information. struct foo f; and union userinput *ui_ptr; are much clearer and easier to understand than foo f; and userinput ui;. I know it's tempting to have a short type name, but don't. Function pointers are the one exception where IMO it's okay, e.g. typedef void (*foobarfn)(struct foobar *); ... foobarfn my_callback = find_response(some_input);Threedecker
@BradenBest it's always been crazy to me just how many books, guides, and blogs by "experts" always recommend you do typedef structs, union, pointers, etc. For opaque types: yes; for everything else: no!Endocarp
R
19

It is a standard naming convention for data types, usually defined by typedefs. A lot of C code that deals with hardware registers uses C99-defined standard names for signed and unsigned fixed-size data types. As a convention, these names are in a standard header file (stdint.h), and end with _t.

Racklin answered 23/10, 2008 at 22:31 Comment(0)
E
16

The _t does not inherently have any special meaning. But it has fallen into common use to add the _t suffix to typedef's.

You may be more familiar with common C practices for variable naming... This is similar to how it's common to stick a p at the front for a pointer, and to use an underscore in front of global variables (this is a bit less common), and to use the variable names i, j, and k for temporary loop variables.

In code where word-size and ordering is important, it's very common to use custom defined types that are explicit, such as BYTE WORD (normally 16-bit) DWORD (32-bits).

int_t is not so good, because the definition of int varies between platforms -- so whose int are you conforming to? (Although, these days, most PC-centric development treats it as 32 bits, much stuff for non-PC development still treat int's as 16 bits).

Extraordinary answered 23/10, 2008 at 22:32 Comment(1)
To me, this is the best answer provided. Great, simple, and to the point explanation. Thank you @Toybuilder.Coated
A
13

It means type. size_t is the size type.

Assignee answered 23/10, 2008 at 22:20 Comment(0)
U
13

It's just a convention which means "type". It means nothing special to the compiler.

Underglaze answered 23/10, 2008 at 22:23 Comment(0)
M
10

There were a few good explanations about the subject. Just to add another reason for re-defining the types:

In many embedded projects, all types are redefined to correctly state the given sizing to the types and to improve portability across different platforms (i.e hardware types compilers).

Another reason will be to make your code portable across different OSs and to avoid collisions with existing types in the OS that you are integrating in your code. For this, usually a unique (as possible) prefix is added.

Example:

typedef unsigned long dc_uint32_t;
Merras answered 24/10, 2008 at 11:9 Comment(0)
C
8

If you're dealing with hardware interface code, the author of the code you're looking at might have defined int_t to be a specific size integer. The C standard doesn't assign a specific size to the int type (it depends on your compiler and target platform, potentially), and using a specific int_t type would avoid that portability problem.

This is a particularly important consideration for hardware interface code, which may be why you've first noticed the convention there.

Cotton answered 23/10, 2008 at 22:26 Comment(2)
this will be not very good practice, i would expect one to define [u]int_[32 16 8]_t to make clear what is the size you defining.Merras
You're quite right, "int_t" by itself tells the programmer that it's a user defined type but not what it really is!Cotton
L
4

For example in C99, /usr/include/stdint.h:

typedef unsigned char           uint8_t;
typedef unsigned short int      uint16_t;
#ifndef __uint32_t_defined
typedef unsigned int            uint32_t;
# define __uint32_t_defined
#endif
#if __WORDSIZE == 64
typedef unsigned long int       uint64_t;
#else
__extension__
typedef unsigned long long int  uint64_t;
#endif

_t always means defined by typedef.

Lacerated answered 11/7, 2018 at 7:43 Comment(0)
V
1

_t means "type". It's a common convention to use it with typedefs.

I've been using it for years, for instance, in professional code bases, like this: notice my usage of timer_t and timer_error_t:

typedef struct timer_s 
{ 
    // When the timer was started
    uint32_t start_ticks;
    // How long until the timer expires
    uint32_t timeout_ticks;
    // Whether or not the timer has previously expired without being restarted
    bool expired;
} timer_t;

typedef enum timer_error_e
{
    // No error
    TIMER_ERROR_NONE = 0,
    // You passed in a NULL pointer
    TIMER_ERROR_NULL_POINTER,
    // The timeout value invalid (ex: too large)
    TIMER_ERROR_INVALID_TIMEOUT,
} timer_error_t;

However, I recently learned from @Jonathan Leffler's answer that POSIX reserves the _t suffix for its own types, and I ran into the problem where timer_t is already defined by POSIX, preventing the above from compiling!

G++ compile-time error:

/usr/include/x86_64-linux-gnu/bits/types/timer_t.h:7:19: note: previous declaration as ‘typedef void* timer_t’
    7 | typedef __timer_t timer_t;
      |                   ^~~~~~~

So, I'm switching to this new convention to avoid using the _t suffix which is reserved by POSIX. I've made the following changes, thereby removing my usage of _t:

timer_s --> Timer_s
timer_t --> Timer

timer_error_e --> Timer_error_e
timer_error_t --> Timer_error

Here's the final result:

Recommended naming convention for structs and enums to avoid redefining _t types which are reserved by POSIX

typedef struct Timer_s 
{ 
    // When the timer was started
    uint32_t start_ticks;
    // How long until the timer expires
    uint32_t timeout_ticks;
    // Whether or not the timer has previously expired without being restarted
    bool expired;
} Timer;

typedef enum Timer_error_e
{
    // No error
    TIMER_ERROR_NONE = 0,
    // You passed in a NULL pointer
    TIMER_ERROR_NULL_POINTER,
    // The timeout value invalid (ex: too large)
    TIMER_ERROR_INVALID_TIMEOUT,
} Timer_error;

This new convention avoids the risk of having overlapping usages of _t.

Vatican answered 1/2 at 20:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.