Why was the register keyword created? Why was the register keyword created? c c

Why was the register keyword created?


register

In C, the register storage class was used as a hint to the compiler, to express that a variable should be preferentially stored in a register. Note that the hint to store a register variable in an actual register may or may not be honored, but in either case the relevant restrictions still apply. See C11, 6.7.1p6 (emphasis mine):

A declaration of an identifier for an object with storage-class specifier register suggests that access to the object be as fast as possible. The extent to which such suggestions are effective is implementation-defined.[footnote 121]

[footnote 121] The implementation may treat any register declaration simply as an auto declaration. However, whether or not addressable storage is actually used, the address of any part of an object declared with storage-class specifier register cannot be computed, either explicitly (by use of the unary & operator as discussed in 6.5.3.2) or implicitly (by converting an array name to a pointer as discussed in 6.3.2.1). Thus, the only operators that can be applied to an array declared with storage-class specifier register are sizeof and _Alignof.

In C++ it is simply an unused reserved keyword, but it's reasonable to assume that it was kept for syntactical compatibility with C code.

auto

In C, the auto storage class defines a variable of automatic storage, but it's not usually used since function-local variables are auto by default.

Similarly, it's reasonable to assume that it was initially carried over to C++ for syntactical compatibility only, although later it got its own meaning (type inference).


register in C served two purposes:

  • Hint to the compiler that the variable should be stored in a register for performance. This use is largely obsolete now.
  • Prevent the programmer from using the variable in ways that would prevent it from being stored in a register. This use is only somewhat obsolete IMO.

This is similar to const, which

  • Hints to the compiler that a variable may be stored in read-only memory.
  • Prevents the programmer from writing to the variable

As an example, consider this simplistic function:

int sum(const int *values, size_t length) {    register int acc = 0;    for (size_t i = 0; i < length; ++i) {        acc += values[i];    }    return acc;}

The programmer has written register to keep the accumulator off the stack, avoiding a memory write every time it's updated. If the implementation gets changed to something like this:

// Defined in some other translation unitvoid add(int *dest, int src);int sum(const int *values, size_t length) {    register int acc = 0;    for (size_t i = 0; i < length; ++i) {        add(&acc, values[i]);    }    return acc;}

The acc variable can no longer be stored in a register when its address is taken for the add() call, because registers have no address. The compiler will thus flag &acc as an error, letting you know that you've probably destroyed the performance of your code by preventing acc from living in a register.

This used to be a lot more important in the early days when compilers were dumber and variables would live in a single place for an entire function. Nowadays a variable can spend most of its life in a register, being moved onto the stack only temporarily when its address is taken. That is, this code:

/* Passed by reference for some reason. */void debug(const int *value);int sum(const int *values, size_t length) {    int acc = 0;    for (size_t i = 0; i < length; ++i) {        acc += values[i];    }    debug(&acc);    return acc;}

would have caused acc to live on the stack for the whole function in older compilers. Modern compilers will keep acc in a register until just before the debug() call.

Modern C code does not generally use the register keyword.


C99 Rationale provides some more context of keyword register:

Rationale for International Standard — Programming Languages — C

§6.7.1 Storage-class specifiers

Because the address of a register variable cannot be taken, objects of storage class register effectively exist in a space distinct from other objects. (Functions occupy yet a third address space.) This makes them candidates for optimal placement, the usual reason for declaring registers; but it also makes them candidates for more aggressive optimization.