auto in C23
Tags: c
Published: 2024-01-18 16:42:54

---
title: auto in C23
published: true
description: A description of the auto keyword in C23.
tags: #c
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2024-01-18 02:01 +0000
---

## Introduction

The `auto` keyword in C actually predates C. It first appeared in [B](https://en.wikipedia.org/wiki/B_(programming_language)) for use when declaring _automatic_ (meaning “automatically created in the stack frame of the enclosing function”) variables.

B is a typeless language in that all variables are integers, so, for example, the following declares some variables in B:

```c
auto a, b, c;
```

C, of course, is a typed language; but to make migration of code from B to C easier, [Ritchie](https://en.wikipedia.org/wiki/Dennis_Ritchie):

1. Adopted `auto` into C.
2. Made `int` the default type (meaning if a type were omitted in a declaration, it was assumed to be `int`).

Hence, the above declaration means the same thing in C as it does in B. However, as a consequence, in C:

```c
void f() {
  auto int x;  // Explicit "auto" adds nothing ...
  int y;       // ... since "auto" is the default.
  // ...
```

the declaration of `y` is the same as `x` since `auto` is the default storage class (as opposed to either `static` or `extern`), so explicit use of `auto` adds nothing.

Since `int` requires typing one fewer character than typing `auto`, being explicit about `int` eventually became common practice. (In [C99](https://en.wikipedia.org/wiki/C99), `int` stopped being assumed.) Hence there was no reason to specify `auto` explicitly in new code and its use faded away.

> For those interested in a much more detailed history of C, see [_The Development of the C Language_](https://www.bell-labs.com/usr/dmr/www/chist.html), Dennis M. Ritchie, April, 1993.

## Resurrection

Eventually, [C++11](https://en.wikipedia.org/wiki/C%2B%2B11) repurposed `auto` to mean “automatically deduced type” where the type of the variable being declared is deduced based on the type of its initializer:

```cpp
double sqrt( double );

auto r = sqrt( x );  // deduces double
```

As of [C23](https://en.wikipedia.org/wiki/C23_(C_standard_revision)), the repurposed `auto` of C++11 was back-ported to C (more or less, but mostly less).

## `auto` in C23

There are a few differences between C++11’s and C23’s `auto`. In general, `auto` in C is a weaker version of that in C++, specifically:

1. In C++, `auto` can declare more than one variable in the same declaration; in C23, it can’t.
2. In C++, `auto` can be used with `*` to deduce a pointed-to type; in C23, it can’t.
3. In C++, `auto` can _not_ be used for its original purpose of being a storage class; in C23, it can.
4. In C++, `auto` can be used as the return type of an `inline` (or `constexpr`) function; in C23, it can’t.
5. In C++, `auto` can be used as a function parameter type as a shorthand for templates; in C23, it can’t.
6. In [C++23](https://en.wikipedia.org/wiki/C%2B%2B23), `auto` can be used to get an explicit copy of an object; in C23, it can’t.

C23’s `auto` is modeled on the existing `gcc` [`__auto_type`](https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Auto-Type.html) extension.

### No Multiple Declaration Support

An example of the first difference is:

```cpp
auto x = get_x(), y = get_y(); // C++: OK; C: error
```

Why didn’t the C committee allow `auto` to declare multiple variables in the same declaration? Because `__auto_type` doesn’t allow it and the committee simply wanted to standardize the existing `__auto_type`.

### No `*` Support

An example of the second difference is:

```cpp
auto *p = strchr( s, '.' );  // C++: 'char'; C: error
auto  q = strchr( s, '.' );  // C++ & C: 'char*'
```

Why didn’t the C committee allow `*`?  First, in C++, you _need_ to be able to do `auto&`, that is optionally use references with `auto`.  Since `&` is allowed, `*` is also allowed in C++ for symmetry.

C, of course, doesn’t have references so there’s no symmetry issue.  And `__auto_type` doesn’t allow `*`.

### Still a Storage Class

An example of the third difference is:

```c
auto int i;  // C++: error; C: 'int i'
```

That is `auto`, when paired with an explicit type, retains its original purpose as a storage class (like either `static` or `extern`).  Since the C committee is a conservative group, my guess is that they wanted to maintain backwards compatibility even with the likely trivially few C programs that use the original `auto` explicitly.

### No Function Support

Examples of the fourth and fifth differences are:

```cpp
inline auto max( auto x, auto y ) {  // C++: OK; C: error
  return x > y ? x : y;
}
```

In C++, being able to have the compiler deduce a return type helps a lot since it can vary depending on the type(s) of the arguments.  Also in C++, allowing `auto` for parameters is a light-weight version of templates since the above is equivalent to:

```cpp
template<typename T1, typename T2>
inline auto max( T1 x, T2 y ) {
  return x > y ? x : y;
}
```

Since C doesn’t have templates, there was no compelling reason to allow `auto` for functions.

> Allowing `auto` for functions would be a nice way to add a “lite” version of templates to C without all the boilerplate `template` syntax. But I wouldn’t hold my breath for the C committee to adopt it.

### No Copy Support

In C++23, you can use `auto(x)` to get a copy of `x` without having either to know its type or to name it. Again, since C doesn’t have templates (which is why you wouldn’t know its type), there was no reason to allow this in C.

> If you’re interested in the use-case for `auto(x)` in C++, you can read its original [proposal](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0849r8.html).

## Uses for `auto` in C

Since `auto` in C is a weaker version of that in C++, is `auto` in C really useful?  It’s useful in a few cases:

1. [Future-proofing](https://en.wikipedia.org/wiki/Future-proof) code.
2. Type-agnostic [macros](https://dev.to/pauljlucas/cc-preprocessor-macros-fh5).
3. Coordination with [`_Generic`](https://dev.to/pauljlucas/generic-in-c-i48).

### Future-Proofing

Consider a function to generate a unique ID number every time it’s called:

```c
unsigned get_new_id();

void f() {
  unsigned id = get_new_id();
  // ...
```

What if at some point you need to change the return type of `get_new_id()` to, say, `unsigned long` or [UUID](https://en.wikipedia.org/wiki/Universally_unique_identifier)? You’d have to change the types of all variables that stored an ID throughout your program.

While the traditional way to solve this is to use a `typedef` for an ID, you can now just use `auto` and it will always be the right type:

```c
auto id = get_new_id();  // future-proof
```

### Type-Agnostic Macros

Sometimes in a [macro](https://dev.to/pauljlucas/cc-preprocessor-macros-fh5), it’s convenient to be able to declare a variable that’s the same type as one of the arguments, for example:

```c
#define SWAP(X,Y)          \
  do {                     \
    auto const temp = (X); \
    (X) = (Y);             \
    (Y) = temp;            \
  } while (0)
```

will work for any type (even `struct`s).

### Coordination with `_Generic`

When a macro can return a value of a varying type via [`_Generic`]((https://dev.to/pauljlucas/generic-in-c-i48)), `auto` allows you to always get the matching type, for example:

```c
#define div(N,D)       \
  _Generic( (N) + (D), \
    int      : div,    \
    long     : ldiv,   \
    long long: lldiv   \
  )( (N), (D) )

auto result = div( 38484848448, 448484844 );
```

The type of `result` will be the same as the return type of whichever function the `div()` macro ends up expanding into.

> Remember that `_Generic` does _not_ evaluate expressions. The use of `(N) + (D)` causes the compiler to determine what the common type of `N` and `D` are via the [usual arithmetic conversions](https://en.cppreference.com/w/cpp/language/usual_arithmetic_conversions). For example, if one is `int` and the other is `long`, the `int` will be promoted to `long`, the result will be `long`, and that’s the type that’s selected.

## Conclusion

Despite C23’s `auto` being a weaker version of C++’s `auto`, it has its few niche uses. Perhaps some day, the C committee will adopt more of C++’s `auto`.

## Epilogue

You may be aware that C23 also added [`typeof`](https://en.cppreference.com/w/c/language/typeof) as another way to deduce types. So what about `typeof`? How does it differ from `auto`? Why did C23 add both? That’s a [story for another time](https://dev.to/pauljlucas/typeof-in-c23-55p2).