While reading Keywords That Aren't (or, Comments by Another Name) by Herb Sutter I came across these lines:
That's right, some keywords are semantically equivalent to whitespace, a glorified comment.
And
We've seen why the C++ language treats keywords as reserved words, and we've seen two keywords —auto and register — that make no semantic difference whatsoever to a C++ program. Don't use them; they're just whitespace anyway, and there are faster ways to type whitespace.
If the keywords like auto
(maybe not in C++11) and register
are of no value, then why were they created and used?
If it doesn't make any difference to include the register
before a variable
#include<stdio.h>
int main(){
register int a = 15;
printf("%d\n%d\n",&a,a);
return 0;
}
Why does the above program give an error?
test_register.c: In function ‘main’:
test_register.c:4:2: error: address of register variable ‘a’ requested
printf("%d\n%d\n",&a,a);
The following program works in C++.
#include<iostream>
int main(){
register int a = 15;
std::cout<<&a<<'\n'<<a;
return 0;
}
Register variables tell the compiler to store the variable in CPU register instead of memory. Frequently used variables are kept in registers and they have faster accessibility. We can never get the addresses of these variables.
In the C programming language, register is a reserved word (or keyword), type modifier, storage class, and hint.
It tells the compiler to try to use a CPU register, instead of RAM, to store the variable. Registers are in the CPU and much faster to access than RAM.
When should the register modifier be used? Does it really help? The register modifier hints to the compiler that the variable will be heavily used and should be kept in the CPU's registers, if possible, so that it can be accessed faster.
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 anauto
declaration. However, whether or not addressable storage is actually used, the address of any part of an object declared with storage-class specifierregister
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 specifierregister
aresizeof
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.
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:
This is similar to const
, which
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 unit
void 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 classregister
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With