It might sound naive, but it is a very common interview question, and I actually think the notation can be deceiving. And furthermore, it is essential. A few months off and you will be asking yourself “how is that again?”. Unless you get this rule: start reading from right to left.
Question: Please read the following declarations and explain their properties regarding read and write.
#1 int v;
Reads as v is a variable of integer type
int v; //declaration
int same_scope_var = 0x10; //declaration and definition
v = 0; //definition
v = same_scope_var; //redefinition v=0x10
After the scope (declaration) and initial value of v is defined, you can read and write it in run-time.
#2 int const v;
Reads as v is a constant integer, i.e., read-only
After the scope and initial value of v is defined, you can only read it in run-time.
#3 int *v;
Reads as v is variable pointer to an integer.
int *v; //declaration
int same_scope_var = 0xFFFFFFFF; //declaration and defitinion
int same_scope_var2 = 0xF0FFFFFF; //declaration and defitinion
v = &same_scope_var ; //definition
v = &same_scope_var2 ; //definition
After the scope and initial value (that is a memory address that holds an integer) of v is defined, you can change it to whatever other integer address you want in run-time.
#4 int const *v;
Reads as v is variable pointer to a constant integer; that is, the address v holds can change, although it is expected to point to a constant value.
int const same_scope_k = 0xFFFFFFFF; //declaration and defitinion
int const same_scope_k2 = 0xF0FFFFFF;
int same_scope_var = 0xF0FBFFFF;
int const *v; //declaration
v = &same_scope_k; //definition
v = &same_scope_k2; //redefinition
*v = same_scope_k; //error wont compile
v = &same_scope_var; //probably compile with a warning
After the scope and initial value of v is defined, you can write and read it whenever you want in run-time, but you cannot change the value it points to by dereferencing.
#5 int * const v;
Reads as v is a constant pointer to a variable integer, i.e., v is ready-only, but the value that it points to (dereference) can change.
int same_scope_variable = 0xFFFFFFFF; //declaration and definition
int same_scope_variable2 = 0xF0FFFFFF;
int * const v; //declaration
v = &same_scope_variable ; //definition
*v = same_scope_variable2; // redefinition for same_scope_variable = same_scope_variable2
After the scope and initial value of v is defined, you can only read the address it points to in run-time, but you can change the value the pointed address holds, by dereferencing.
#6 int const * const v;
Reads as v is a constant pointer to a constant integer. That is, the value v holds is a read-only address that points to a read-only integer. Its more usual to see that as a function argument when you want to be sure the method cannot change neither the address v holds nor the value it dereferences.
int const same_scope_k = 0xFFFFFFFF;
int const same_scope_k2 = 0xF0FFFFFF;
int const * const v; //declaration
v = &same_scope_k; //defined, read-only
v = &same_scope_k2; //error wont compile
*v = same_scope_k2; //error wont compile
The code below should compile with no warnings. It is on a PC, therefore memory addresses have 8 bytes
#include <stdio.h>
int main()
{
int my_variable = 0x30;
int const my_constant = 0x60;
//#1 defined and declared v
int v = 0x05;
//#2 defined and declared read-only const
int const v_k = 0x10;
int const v_k2 = 0x11;
//#3 defined and declared v_ptr as the address of v
int *v_ptr = &v;
//#4 declared and defined this as a pointer to a constant
int const *v_ptr_to_k = &v_k;
//#5 declared and defined this as a constant pointer to an variable integer
int * const v_k_ptr = &my_variable;
//#6
//declared and defined this as a constant pointer to a constant integer
int const * const v_k_ptr_k = &my_constant;
// operations
// change v
printf("v has the value %x, and v_ptr is %p\n\r", *v_ptr, v_ptr);
v = 0x80;
printf("v has now the value %x, and v_ptr is %p\n\r", *v_ptr, v_ptr);
// changing v_ptr
v_ptr = &my_variable;
printf("v_ptr dereferences to value %x, and v_ptr is %p\n\r", *v_ptr, v_ptr);
//change pointer to a constant
printf("v_ptr_to_k dereferences to value %x, and v_ptr_to_k is %p\n\r", *v_ptr_to_k, (unsigned long int *)v_ptr_to_k);
v_ptr_to_k = &v_k2;
printf("v_ptr_to_k dereferences to value %x, and v_ptr_to_k is %p\n\r", *v_ptr_to_k, (unsigned long int *)v_ptr_to_k);
//*v_ptr_to_k = 0x08; //<== not allowed!
//operating with a constant pointer
printf("v_k_ptr dereferences to the value %x, and v_k_ptr is %p\n\r", *v_k_ptr, (unsigned long int *)v_k_ptr);
v = 0x35;
*v_k_ptr = v;
printf("v_k_ptr dereferences to the value %x, and v_k_ptr is %p\n\r", *v_k_ptr, (unsigned long int *)v_k_ptr);
//operating with a constant pointer to a constant integer
printf("v_k_ptr_k dereferences to the value %x, and v_k_ptr_k is %p\n\r", *v_k_ptr_k, (unsigned long int *)v_k_ptr_k);
//*v_k_ptr_k = 0x92; <== not allowed!
//v_k_ptr_k = &my_constant; <== not allowed!
return 0;
}
Output:

(Erratum: in the printf above v_ptr_to_k was mentioned as v_ptr_k but the variable declared and defined on the code is v_ptr_to_k.)