This section provides some examples for the use of const or volatile because const and volatile are very common for Embedded Programming.
Consider the following example:
int i; const int ci;
The above definitions are: a `normal' variable i and a constant variable ci. Each are placed into ROM. Note that for C++, the constant ci must be initialized.
int *ip; const int *cip;
ip is a pointer to an int, where cip is a pointer to a const int.
int *const icp; const int *const cicp;
icp is a const pointer to an int, where cicp is a const pointer to a const int.
It helps if you know that the qualifier for such pointers is always on the right side of the *. Another way is to read the source from right to left.
You can express this rule in the same way to volatile. Consider the following example of an `array of five constant pointers to volatile integers':
volatile int *const arr[5];
arr is an array of five constant pointers pointing to volatile integers. Because the array itself is constant, it is put into ROM. It does not matter if the array is constant or not regarding where the pointers point to. Consider the next example:
const char *const *buf[] = {&a, &b};
Because the array of pointers is initialized, the array is not constant. `buf' is a (non-constant) array of two pointers to constant pointers which points to constant characters. Thus `buf' cannot be placed into ROM by the Compiler or Linker.
Consider a constant array of five ordinary function pointers. Assuming that:
void (*fp)(void);
is a function pointer `fp' returning void and having void as parameter, you can define it with:
void (*fparr[5])(void);
It is also possible to use a typedef to separate the function pointer type and the array:
typedef void (*Func)(void);
Func fp;
Func fparr[5];
You can write a constant function pointer as:
void (*const cfp) (void);
Consider a constant function pointer having a constant int pointer as a parameter returning void:
void (*const cfp2) (int *const);
Or a const function pointer returning a pointer to a volatile double having two constant integers as parameter:
volatile double *(*const fp3) (const int, const int);
And an additional one:
void (*const fp[3])(void);
This is an array of three constant function pointers, having void as parameter and returning void. `fp' is allocated in ROM because the `fp' array is constant.
Consider an example using function pointers:
int (* (** func0(int (*f) (void))) (int (*) (void))) (int (*)
(void)) {
return 0;
}
It is actually a function called func. This func has one function pointer argument called f. The return value is more complicated in this example. It is actually a function pointer of a complex type. Here we do not explain where to put a const so that the destination of the returned pointer cannot be modified. Alternately, the same function is written more simply using typedefs:
typedef int (*funcType1) (void);
typedef int (* funcType2) (funcType1);
typedef funcType2 (* funcType3) (funcType1);
funcType3* func0(funcType1 f) {
return 0;
}
Now, the places of the const becomes obvious. Just behind the * in funcType3:
typedef funcType2 (* const constFuncType3) (funcType1);
constFuncType3* func1(funcType1 f) {
return 0;
}
By the way, also in the first version here is the place where to put the const:
int (* (*const * func1(int (*f) (void))) (int (*) (void)))
(int (*) (void)) {
return 0;
}