Introduction
Among other things, C23 added the typeof and typeof_unqual keywords that return the type of an expression:
int x;        // int (obviously)
typeof(x) y;  // also int
Note: for this article, I’ll simply use
typeofto mean eithertypeofortypeof_unqualrather than always repeating “ortypeof_unqual” unless otherwise stated.
The expression is not evaluated.  Like sizeof, typeof is a compile-time operator.  typeof standardizes the long-existing gcc extension.
typeofis sort-of likedecltypein C++. Why isn’ttypeofcalleddecltypein C also? (Or why isn’tdecltypein C++ calledtypeof?) The short answer is that C++ has references and C doesn’t — and that affects the type deduced. There’s also the long answer.
But wait! Since C23 also added auto, why is typeof needed?
- Variables declared with typeofdon’t need initializers.
- 
typeofcan clarify complicated declarations.
Declarations without Initializers
Using auto always requires an initializer since the compiler deduces the type from the type of the initializer expression; using typeof does not:
double f(void);
auto x = f();   // double
typeof(f()) y;  // double, but without initializer
Depending on what you’re doing, you may not want to initialize upon declaration, for example if a variable is initialized only conditionally or you need to defer initialization.
Of course you still can use an initializer with typeof if you want to guarantee a type based on something other than the initializer:
typeof(f()) y = g();  // type is what f() returns, not g()
Clarifying Complicated Declarations
C in infamous for complicated declarations. Normally, complicated declarations can be simplified via typedef. However, typeof can also be used since it can also take a type instead of an expression:
int *p1, *q1;                   // both pointer to int
typeof(int*) p2, q2;            // same
// array 4 of pointer to function (int) returning double
double (*a1[4])(int);
typeof(double(int)) *a2[4];     // same
// function (void) returning ...
// ... pointer to function (int) returning double
double (*f1(void))(int);
typeof(double(int))* f2(void);  // same
While the typeof declarations are longer, they’re much clearer.
The ability of
typeofto take either an expression or a type parallels the ability ofsizeofto do the same.
You can even declare macros to create an entirely alternate declaration syntax:
#define ARRAY_OF(T, N)  typeof(T[N])
#define POINTER_TO(T)   typeof(T*)
ARRAY_OF(POINTER_TO(char const), 4) pc;  // char const *pc[4]
  
  
  typeof_unqual
The difference between typeof and typeof_unqual is that the latter removes all top-level qualifiers (_Atomic, const, restrict, and volatile):
extern int i;
extern int const ci;
extern int *pi;
extern int const *pci;
extern int *const cpi;
extern int const *const cpci;
typeof       (i)    i2;      // int
typeof_unqual(i)    i2u;     // int
typeof       (ci)   ci2;     // int const
typeof_unqual(ci)   ci2u;    // int
typeof       (pi)   pi2;     // int *
typeof_unqual(pci)  pci2u;   // int const*
typeof       (cpi)  cpi2;    // int *const
typeof_unqual(cpi)  cpi2u;   // int *
typeof       (cpci) cpci2;   // int const *const
typeof_unqual(cpci) cpci2u;  // int const *
Conclusion
typeof is complementary to auto as an addition to C for writing type-agnostic code or simplifying complicated declarations.
 

 
    
Top comments (0)