The author's point is that declaring a variable with register
storage class prevents you from taking its address, so it can not be passed to a function that might change its value by casting away const
.
void bad_func(const int *p) {
int *q = (int *) p; // casting away const
*q = 42; // potential undefined behaviour
}
void my_func() {
int i = 4;
const int j = 5;
register const int k = 6;
bad_func(&i); // ugly but allowed
bad_func(&j); // oops - undefined behaviour invoked
bad_func(&k); // constraint violation; diagnostic required
}
By changing potential UB into a constraint violation, a diagnostic becomes required and the error is (required to be) diagnosed at compile time:
c11
5.1.1.3 Diagnostics
1 - A conforming implementation shall produce at least one diagnostic message [...] if a preprocessing translation unit or translation unit
contains a violation of any syntax rule or constraint, even if the behavior is also explicitly
specified as undefined or implementation-defined.
6.5.3.2 Address and indirection operators
Constraints
1 - The operand of the unary &
operator shall be [...] an lvalue that designates an object that [...] is
not declared with the register
storage-class specifier.
Note that array-to-pointer decay on a register
array object is undefined behaviour that is not required to be diagnosed (6.3.2.1:3).
Note also that taking the address of a register
lvalue is allowed in C++, where register
is just an optimiser hint (and a deprecated one at that).
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…