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
registersuggests 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
registerdeclaration simply as anautodeclaration. However, whether or not addressable storage is actually used, the address of any part of an object declared with storage-class specifierregistercannot 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 specifierregisteraresizeofand_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
registervariable cannot be taken, objects of storage classregistereffectively 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