What kinds of C syntax does the C standard specify for Defined Behavior and Undefined Behavior when using const
pointers to global data areas in order to communicate both to the C compiler and to a programmer that in some functions the global data areas may be modified and in the rest of the source the global data areas should be read only?
If one functionfunc2()
, accesses a global memory area through a pointer T const * const pObj;
there fore seen as read only yet the data area may be changed by another function, func()
, which accesses the same global memory area through a pointer T * const pObj;
and the function func()
is invoked either directly by func2()
or by a callee of func2()
will any local copies of data from the global memory area, for instance temporary variables or values in registers, be refreshed after any function call? Is a function call a kind of reset point that triggers all cached, non-local data (whether marked as const
or not) stored in registers or local temporary variables to be flushed and refreshed?
Working with older C source that has several memory resident data areas used to maintain state I would like for most of the source to see these areas as read only. However some functions will modify the data areas depending on various events.
What I thought to do was to have two types of pointers to these memory resident data areas both of which point to the same location but which are defined so that the memory area is treated as either read only (T const * const pObj;
) or as read/writable (T * const pObj;
).
What does the C standard have to say about this approach and how safe is it?
For instance in the following C source example across several files, I expect that func2()
will work correctly since the state change happens with a call to func()
before func2()
is called.
The include file contains the following source lines defining some types as well as declaring the global variables which are defined elsewhere.
typedef struct { int state; /* other stuff */ } ET; // Event type
typedef struct { ET state; /* other stuff */ } T; // memory resident data type
extern T const * const pObjImmutable; // readonly pointer to readonly memory
extern T * const pObjMutable; // readonly pointer to read/writable memory
extern int IsState (const ET state, const ET event); // check for equivalence of state and event
Next there is a source file where the pointers declared in the include file are actually defined along with an API for manipulation and changes.
static T objThing; // define the object but make static for file visibility only
T const * const pObjImmutable = &objThing; // define global readonly pointer to readonly memory
T * const pObjMutable = &objThing; // define global readonly pointer to read/writable memory
int IsState (ET state, ET event)
{
// check for equivalence of state and event ....
}
Finally there is a source file where the global objects are actually being used.
void func (ET event)
{
// make changes to objThing based on event
pObjMutable->state = event;
}
void func2 (void)
{
extern ET const stdStateOne, const stdStateTwo;
// use current state of objThing to make decisions
if (IsState (pObjImmutable->state, stdStateOne)) {
// do things for State One due to an event
} else if (IsState (pObjImmutable->state, stdStateTwo)) {
// do things for State Two due to an event
}
}
int main ()
{
func (eventOne); // initialize the state
func2 (); // do something based on current state
func (eventTwo); // change the state
func2(); // do something based on current state using new state
}
However what does the C standard say if the function func2()
calls a function which somewhere in the call tree a function is called which modifies the memory resident area? For example if the function func2()
calls the function func()
directly with a new event or if the function func2()
calls a function which in turn calls func()
with a new event?