Skip to Content
Course content

243: Typeof Operators (typeof and typeof_unqual)

Click on the "Edit" button in the top corner of the screen to edit your slide content.

If you've been following along, you know that C is historically very strict about types. Usually, if you want a variable to be the same type as another, you have to explicitly name that type. But with C23, we finally got typeof and typeof_unqual. These are game-changers for writing macros and handling complex types without repeating yourself.

Why would I use typeof instead of just naming the type?

On the surface, it seems redundant. Why write typeof(x) when you can just write int? The real power kicks in when you're dealing with complex pointers, deeply nested structs, or types that are defined by the platform. I've spent way too much time in the past updating a type name in five different places because a uint32_t became a uint64_t.

Take this example. Imagine you're working with a complex buffer structure where the data type might change based on the hardware header:

struct Packet {
    uint32_t id;
    float payload[16];
};

struct Packet pkt;
// Instead of remembering it's a float, I just mirror the type of the element
typeof(pkt.payload[0]) temporary_value = pkt.payload[0];

If I later change payload to double, temporary_value updates automatically. It keeps your code "DRY" (Don't Repeat Yourself) and prevents those annoying precision-loss warnings that pop up when you forget to update a temporary variable's type.

What exactly does typeof_unqual do that typeof doesn't?

This is where things get interesting. typeof is a literal mirror; it copies everything, including qualifiers like const, volatile, and restrict. Most of the time, that's what you want. But sometimes, you want the base type without the restrictions.

I'll show you what I mean with a const variable. If you use typeof on a constant, the new variable is also constant, which means you can't modify it. typeof_unqual strips those qualifiers away.

const int max_limit = 100;

typeof(max_limit) a = max_limit; 
// 'a' is now 'const int'. Trying to do 'a = 10' will trigger a compiler error.

typeof_unqual(max_limit) b = max_limit; 
// 'b' is just a plain 'int'. I can modify it however I want.
b = 110; // Perfectly legal.

I find typeof_unqual incredibly useful when I'm writing a function that needs to take a value from a read-only configuration struct and modify it locally before using it in a calculation.

Can I use these to make my macros more flexible?

Absolutely. This is probably the primary reason these were added to the language. Before C23, if you wanted to write a "generic" macro that stored a value in a temporary variable (to avoid the double-evaluation bug), you were stuck. You either had to force the user to pass the type as an argument or rely on void* and casting, which is ugly and dangerous.

Now, you can write a macro that adapts to whatever the user throws at it:

#define SAFE_SQUARE(x) ({ \
    typeof(x) _temp = (x); \
    _temp * _temp; \
})

int i = 5;
float f = 2.5f;

int res1 = SAFE_SQUARE(i);    // _temp is int
float res2 = SAFE_SQUARE(f);  // _temp is float

Notice how _temp automatically assumes the correct type? This makes your macros behave much more like C++ templates or generic functions in other languages, while staying within the realm of standard C.




📋 Practical Task

Build a Type-Agnostic Local Maximum Tracker

You are building a system that tracks the "highest value seen so far" for various sensors. Some sensors report as int, others as double, and some as unsigned short.

Create a macro called UPDATE_MAX that takes two arguments: a variable representing the current maximum (current_max) and a new value (new_val). The macro should:

  • Use typeof to create a temporary variable that holds the value of new_val to ensure it is only evaluated once.
  • Compare the temporary variable to current_max.
  • Update current_max if the temporary value is larger.

Test your macro with at least two different types (e.g., an int pair and a float pair) to verify that the compiler doesn't throw type-mismatch warnings and that the values update correctly.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.