Can we modify the value of a const variable?

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


Can we modify the value of a const variable?

Yes, You can modify a const variable through various means: Pointer hackery, casts etc...
Do Read next Q!!

Is it valid code to modify the value of a const variable?

No! What that gives you is Undefined Behavior.

Technically, your code example has an Undefined Behavior.
The program is not adhering to c standard once you modify the const and hence may give any result.

Note that an Undefined Behavior does not mean that the compiler needs to report the violation as an diagnostic. In this case your code uses pointer hackery to modify a const and the compiler is not needed to provide a diagnostic for it.

The C99 standard 3.4.3 says:

Undefined behavior: behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this International Standard imposes no requirements.

NOTE Possible undefined behavior ranges from ignoring the situation completely with unpredictable results, to behaving during translation or program execution in a documented manner characteristic of the environment (with or without the issuance of a diagnostic message), to terminating a translation or execution (with the issuance of a diagnostic message).


Your code compiles, but it has undefined behavior.

The author's point is to use const and register so that the code no longer compiles:

const int a = 81; 
int *p = (int *)&a; /* no compile error */
*p = 42; /* UB */ 

register const int b = 81; 
int *q = (int *)&b; /* does not compile */