Address of Register Variable

address of register variable in C and C++

C and C++ are different languages.

  • In C, you cannot take the address of a variable with register storage. Cf. C11 6.7.1/6:

    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: The implementation may treat any register declaration simply as an auto declaration. [...]

  • In C++, register is a deprecated, meaningless keyword that has no effect (except perhaps serve as a compiler hint), and variables declared as register still just have automatic storage. In particular, C++ doesn't have a "register" storage class. (It just has the storage class specifier, inherited from C.) Cf. C++11, 7.1.1/3:

    A register specifier is a hint to the implementation that the variable so declared will be heavily used. [ Note: The hint can be ignored and in most implementations it will be ignored if the address of the variable is taken. This use is deprecated [...]

Even in C nothing is actually guaranteed about how register storage is implemented (implementations are free to treat register as auto), but the language rules apply regardless.

Address of register variable

Here's an excerpt from Section 6.7.1 (footnote 101) of the C99 standard (pdf):

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 operator that can be applied to an array declared with storage-class specifier register is sizeof.

And from Section 7.1.1, Paragraph 3 of the C++ standard (pdf):

A register specifier has the same semantics as an auto specifier together with a hint to the implementation that the object so declared will be heavily used. [Note: the hint can be ignored and in most implementations it will be ignored if the address of the object is taken. —end note]

Fun tidbits about register

The C++ group (WG21) wants to deprecate register:

The register keyword serves very little function, offering no more than a hint that a note says is typically ignored. It should be deprecated in this version of the standard, freeing the reserved name up for use in a future standard, much like auto has been re-used this time around for being similarly useless.

Notes from the March, 2009 meeting:

The consensus of the CWG was in favor of deprecating register.

Look what the C99 group (WG14) said about register (pdf) at a meeting:

General agreement to deprecate the “auto” keyword. Should we ask WG21 to go back to
the previous use of “register” (no address)? No, this will not fly with WG21.

Address of a variable stored in a register

First, the C Standard prohibits taking the address of a variable that is declared register, just as it does for bit fields in structs.

For non-register ("auto") variables, the short answer is yes. The simplest strategy of an optimizer is to immediately spill variables whose addresses are taken.

"Spill" is just a term from the literature of register allocation meaning "decide to place in memory rather than a register."

A sophisticated optimizer can do an alias analysis and still hold a value in a register, even though its address has been taken. This is possible wherever it can be proven that the resulting pointer can't possibly be used to change the value.

Another relevant optimization is live range splitting. This allows a variable to be stored in a register for part of the range of instructions where it's holding a useful value (its "live range") and to be spilled in other parts. In this case the spilled parts would correspond to places where the pointer might be used to change the variable's value. For example:

x = 3;
... lots of computations involving x
if T {
// SPILL HERE, so store register holding x to memory
int *p = &x;
... lots of computations, perhaps using p to change x
*p = 2;
// DONE SPILL HERE, so reload register
... more code here not using p to change x.
}
else {
... lots of computations involving x.
}

An aggressive optimizer of this code might allocate a stack position for x, but load it into a register at the top of the code, maintaining it there except for the region marked as a SPILL. This region would be surrounded by a store of the register to memory and a matching register load.

C: Address on register or memory?

How does the address in a pointer look when it points on a register?

Pointers are a concept of memory, registers do not have addresses. Every processor has a limited, fixed amount of registers (8 - 16 probably).

As others have mentioned, the register is not really useful anymore and even sometimes ignored by compilers.

To understand what registers really are, consider this example:

int a = k / 53;  // k is an int defined somewhere else...
int b = a * 9;
// a is not used after the above line

Now, we have not declared a with register but any reasonable compiler will still keep a in a register.

The reason for this is that to perform any operation the operands have to be in certain registers. The the result of the operation will then e.g. be stored in the register of the first operand.

In order to compute a from the above example, the compiler will write code to load k (which presumably is in memory) into a certain register (let's call it register A) and 53 into another. After the computation is done, register A will contain the result of the operation. Since we are going to multiple that result by nine in the next line anyway, we can just keep it were it is, load 9 into the other register and multiply. Storing the value into memory and then loading it back into a register would just waste a lot of time.

Note that declaring a with volatile would prevent optimizations like this and force the compiler to actually store and load a. (Although volatile does not make any sense with this example here at all, and is hardly ever useful if you do not, for instance, interface special kind of hardware.)

Address of register variable when it is taken as auto variable

No. You can't. The standard is quite clear on this what operators are allowed to be applied on a register storaged-classed variable regardless of whether an actual register is used or not:

C11, 6.7.1 Storage-class specifiers, p6

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. 121

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
.

(emphasis mine)

Address of register variable when it is taken as auto variable

No. You can't. The standard is quite clear on this what operators are allowed to be applied on a register storaged-classed variable regardless of whether an actual register is used or not:

C11, 6.7.1 Storage-class specifiers, p6

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. 121

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
.

(emphasis mine)

Register Variable Address

The keyword register is only a hint to the compiler. In fact, most compilers today ignore it as they contain advanced code to pick the best register variable candidates anyway.

Whenever you take the address of a variable, it is typically placed on the stack, despite the fact that you have used the register keyword.

Why address-of operator ('&') can be used with objects that are declared with the register storage class specifier in C++?

The restriction on taking the address was deliberately removed in C++ - there was no benefit to it, and it made the language more complicated. (E.g. what would happen if you bound a reference to a register variable?)

The register keyword hasn't been much use for many years - compilers are very good at figuring out what to put in registers by themselves. Indeed in C++ the keyword is currently deprecated and will eventually be removed.

register keyword in C?

It's a hint to the compiler that the variable will be heavily used and that you recommend it be kept in a processor register if possible.

Most modern compilers do that automatically, and are better at picking them than us humans.



Related Topics



Leave a reply



Submit