Skip to Content
Course content

246: auto for Type Inference in C23

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

For decades, the auto keyword in C was essentially a ghost. It technically meant "automatic storage duration," but since variables declared inside functions have automatic storage by default, it was completely redundant. You'd almost never see it in a professional codebase.

C23 changes that. Now, auto behaves like it does in C++: it tells the compiler to look at the value you're assigning to a variable and figure out the type for you. It sounds like a dream for those of us tired of typing out long struct names, but there is a catch that trips up almost everyone the first time they try it.

The "Undefined Type" Compiler Error

// Imagine we have a complex type for a system state
typedef struct {
    uint32_t clock_speed;
    uint8_t power_mode;
    bool is_initialized;
} SystemState;

SystemState get_current_state() {
    return (SystemState){16000000, 1, true};
}

int main() {
    auto state; 
    state = get_current_state();
    return 0;
}

If you try to compile this with a C23 compiler, it's going to scream at you. You might expect it to work like a dynamically typed language where you declare the name and assign the type later. But C is still a statically typed language. When the compiler hits auto state;, it looks at the line and asks, "What is state?" Since there is no assignment on that same line, the compiler has no way to infer the type, and the build fails.

Binding Type to Initialization

The fix is simple: you must initialize the variable on the same line you declare it. The compiler needs the expression on the right-hand side to determine the type for the left-hand side.

int main() {
    // The compiler sees get_current_state() returns SystemState, 
    // so it makes 'state' a SystemState.
    auto state = get_current_state(); 
    return 0;
}

I'll be honest: for simple int or float variables, using auto is usually a waste of keystrokes and makes the code harder to read. I don't recommend it there. But when you're dealing with complex return types from a library or deeply nested structs, it's a massive quality-of-life improvement. It also makes refactoring easier; if you change the return type of get_current_state from SystemState to ExtendedSystemState, you don't have to hunt down every single variable declaration in your project to update the type.

Static Typing Isn't Dynamic Typing

One thing you need to keep in mind is that once the compiler decides what auto is, that type is locked in for the life of the variable. You can't do this:

auto value = 10;      // 'value' is now an int
value = "Hello World";  // Error: cannot assign a string to an int

It's easy to confuse type inference with dynamic typing because the syntax looks similar, but the "magic" happens entirely at compile time. The resulting binary is exactly the same as if you had typed the full type name manually.




📋 Practical Task

Refactoring the Hardware Register Interface

You are working on a driver for a specialized sensor. The library provides a very verbose type for register handles. Currently, the code is cluttered with repetitive type declarations. Your task is to refactor the read_sensor_data function to use auto for the handles, making the code cleaner without changing its behavior.

Original Code:

typedef struct {
    uintptr_t address;
    uint16_t offset;
    uint8_t permissions;
} SensorRegisterHandle;

SensorRegisterHandle get_data_register() {
    return (SensorRegisterHandle){0x4000, 0x04, 0x01};
}

SensorRegisterHandle get_status_register() {
    return (SensorRegisterHandle){0x4000, 0x00, 0x01};
}

void read_sensor_data() {
    SensorRegisterHandle data_reg = get_data_register();
    SensorRegisterHandle status_reg = get_status_register();
    
    // Imagine complex logic using data_reg and status_reg here...
}

Instructions:

  1. Modify the read_sensor_data function.
  2. Replace the explicit SensorRegisterHandle type declarations with auto.
  3. Ensure the variables are correctly initialized on the same line to avoid compilation errors.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.