A common misconception: the register keyword

Since probably its beginnings C has the register keyword and I recently read several times that it has no effect and would be ignored by the compiler. This is a misconception of what this keyword does and what it is meant to do. Unfortunately, this is just due to the misnoming of the feature as register. One of the misnomers that we encounter in C, others being static, inline and const.

The only purpose that register nowadays (and since long) has is that inhibits the taking of the address of a variable. I makes e.g the following code invalid:

register double a;
double *ap = &a;

The principal use of this is to serve as an optimization hint to the compiler:

I know, I wouldn’t need an address of that variable, do all you can to optimize the access to it

In that sense it is of the same importance as the restrict keyword.

This can be particularly useful for array variables. An array variable is easily confounded with a pointer variable. Unless it is followed by a [expr] or with a sizeof it evaluates to the address of the first element. If you declare the array register all these uses are forbidden; we only access individual elements or ask for the total size. Such an register-array then may be much easier used as if it just were a set of variable by the optimizer. No aliasing (accessing the same variable through different pointers) may occur.

Another use for declaring a variable as register and const is to inhibit any non-local change of that variable, even trough taking its address and then casting the pointer. Even if you think that you yourself would never do this, once you pass a pointer (even with a const attribute) to some other function, you can never be sure that this might be malicious and change the variable under your feet. So in a setting with exposure to risks, you could declare all your variables as being register and then carefully inspect all the remaining places where you take addresses and pass stack pointers to other functions.

As said, unfortunately this purpose is not so easily deducible from its name `register`. Holding the variable in a register of the CPU is just one possible optimization that the compiler might find for such a variable, another one would be to just realize it as an immediate assembler operator. Maybe one day a compiler could enforce to hold such a variable in cache, and not to spill it out to higher levels of the memory hierarchy. Or maybe in many cases it just can’t do anything for you, perhaps it was worth to try?

Edit: register variables are also the technical base of a proposal for generic named constants that I recently elaborated.

10 thoughts on “A common misconception: the register keyword”

  1. It is like inline, the compiler may inline it, it may not, it may even inline it on a case by case basis. Anyway, IMHO they should be renamed to something like register_hint and inline_hint.

  2. Arrays declared with the `register` storage class are useless. The standard says (note particularly the last sentence):

    > Except when it is the operand of the sizeof operator
    > or the unary & operator, or is a string literal used
    > to initialize an array, an expression that has type
    > ‘‘array of type’’ is converted to an expression with
    > type ‘‘pointer to type’’ that points to the initial
    > element of the array object and is not an lvalue. If
    > the array object has register storage class, the
    > behavior is undefined.

    Note that this includes the use of the [] operator.

    There’s also a (non-normative) footnote 103 that says “Thus, the only operator that can be applied to an array declared with storage-class specifier register is sizeof.”

    1. No, I first thought so, too, but after digging deeper I don’t agree with that view. You have e.g a phrase like the following in 6.5.32:

      The operand of the unary & operator shall be either a function designator, the result of a
      [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is
      not declared with the register storage-class specifier.

      Which for me implicitly permits the application of the [] operator to arrays that are declared with register. Also if you lookup the paragraph on array subscripting itself, it contains no hint at all that subscripting of arrays with register attribute should not be permitted. I read the footnote that you cite as

      “Thus, the only operator that can be applied to an array object declared with storage-class specifier register is sizeof.”

      (I will not be able to discuss this further the next days since I will be traveling.)

      1. I don’t see how you read that paragraph you cite as saying anything about applying [] to arrays – all it is doing is putting restrictions on the operands of unary &.

        If you apply [] to an array, the array is neither the operand of the sizeof operator nor the operand of the unary & operator, so if it has register storage class then the behaviour is explicitly undefined. GCC agrees with me – if you specify -pedantic, then you get:

        x.c:5: warning: ISO C forbids subscripting ‘register’ array

      2. I read it like that:

        The operand of the unary & operator shall be either a (function designator, the result of a
        [] or unary * operator, or an lvalue) that designates an object that is not a bit-field and is
        not declared with the register storage-class specifier.

        That is, for me, reduced to the case of the [] operator this reads

        The operand of the unary & operator shall be the result of a
        [] operator that designates an object that is
        not declared with the register storage-class specifier.

        which for me makes sense: if you just take as intention of the register keyword to inhibit taking the address of an object and to relax the requirements of a specific stack layout (here to have all elements consecutively) this is just how you would formulate the requirement.

        And to come back to the specification of the [] operator itself, nowhere it even shows the intention to forbid it for arrays qualified with register. The section is named array subscripting (or so don’t have it under my nose at the moment), and not pointer subscripting. The intention as I read the main part of it do describe it the other way round, how the [] operator applied to a pointer still makes sense, although the expression to which the subscripting operator is applied is not an array.

        It says that the result of it is identical to the result of *(A + i). But it doesn’t say that the result is obtained by doing *(A + i), which indeed would not be allowed for a register-qualified variable.

        In any case, our discussion shows that this issue merits a defect report.

      3. I agree with the claim that C forbids applying [] to an array with register storage class. The reason it’s called “array subscripting” and not “pointer subscripting” has nothing to do with whether the name of the array is decaying to a pointer in this context (which it does); it has to do with the fact that arithmetic on arbitrary pointers is invalid/undefined unless they point into an array. Thus the only time [] is useful to apply to a pointer is when that pointer points to an element of an array. Note that, technically, all objects are a member of an array (the array of exactly one element of the object’s type) anyway.

        1. Yeah, R.., probably you’re right. It is just a pity, since it is not necessary to restrict the use of arrays like this. Since C99, C also has designators ([] with an integer constant expression) that would do a perfect job for that, but which are only used in initializers. Have a look at this

          register struct hiding { double a[2] } arr = { .a = { [0] = 0.0, [1] = M_PI } };
          #define GET_HIDING(ARR, X) ((struct hiding){ 0 } = (ARR)).a[X])
          #define SET_HIDING(ARR, X, VAL)  \
           ARR = (const struct hiding){    \
             .a = {                        \
               [0] = GET_HIDING(ARR, 0),   \
               [1] = GET_HIDING(ARR, 1),   \
               [X] = (VAL)                 \
             }                             \
          }
          
          printf("element %d is %f", i, GET_HIDING(arr, i));
          SET_HIDING(arr, 0, 23.0);
          

          Any modern smart enough compiler will optimize all the compound literals out of this. So technically it would not be big deal to just allow for designators to access the compounds of any composite type, unless it is a VLA.

  3. This actually has to do with how the code is executed in hardware.

    Using the word *register* means the compiler *never allocates space in memory for it,* but rather holds the variable in a register. It can’t have a pointer because there *is no physical memory location it exists in.*

    If it weren’t for the fact that modern compilers are basically omniscient, then use of the *register* keyword would save time by skipping the memory allocation. But because compilers are so good, any time you can get away with having a variable be stuck exclusively in registers (i.e. never referenced anywhere else, no pointers to it, etc.) then it will be done because it’s faster, letting you skip the memory allocation and memory access steps on instructions with that variable.

    1. No, you can’t say it does hold such a variable in a register, it is free to realize the variable by any means that please, e.g assembler immediates, do clever case analysis without having the value somewhere … there are a multitude of possibilities.

      And no, memory allocation for an automatic variable doesn’t take time and never has. This is not a question of clever compilers. Usually automatic variables are reserved on the “stack” by just adding or subtracting the right value to the stack pointer, once for the whole function. So there is not much point in doing that for one variable more or less.

Comments are closed.