You are on page 1of 3

RETURN

PROGRAMMING POINTERS

Dan Saks

Placing Data into ROM


with Standard C
Several months ago I described sions. Although you may have little or against any particular architecture.
the problem of using C or C++ to choice but to use such extensions, you Thus, the C standard describes pro-
place data into read-only memory should use them only as needed. You gram behaviors in terms of a hypo-
(ROM). I explained that declaring an should stick to the standard dialect as thetical abstract machine rather than
object const is necessary but not suffi- much as possible because, all other any real one.
cient to get your compiler to place things being equal, portable code is A real machine that executes a C
that object into ROM. (See “Placing better than non-portable code. program need not behave exactly like
Data into ROM,” May 1998, p 11.)
In the following months, I elabo-
rated on the const qualifier. (See When it comes to placing data into ROM, both the
“Placing const in Declarations,” June
1998, p. 19, and “What const Really C and C++ standards say surprisingly little. But
Means,” August 1998, p. 11.) I also
introduced the volatile qualifier. looking at what they do say is still worthwhile.
(See “Volatile Objects,” September
1998, p. 101.) With that as back-
ground, we can take a closer look at Unfortunately, when it comes to the abstract machine in every respect.
what it takes to place data into ROM. placing data into ROM, both the C The standard requires only that pro-
and C++ standards say surprisingly lit- grams executing on a real machine
Hewing to the standard tle. (Appallingly little, some might produce the same results that they
say.) Nonetheless, it’s still worth look- would when executing on the
C and C++ implementations vary ing at what they do say. Since the C++ abstract machine. This requirement
across platforms, sometimes signifi- standard builds on groundwork laid by is commonly known as the “as if” rule
cantly. The purpose of a language the C standard, we’ll start by looking because a C language implementa-
standard is, among other things, to at what the C standard has to say. tion is free to do as it pleases as long
provide programmers with a common as compiled programs produce the
dialect for writing programs that can C’s abstract machine same observable behavior as if they
execute on a wide variety of platforms. were executing on the abstract
Unfortunately, embedded systems Often, the clearest and most direct machine.
often deal with physical resources way to explain the behavior of a pro- The abstract machine for standard
such as device registers or ROM, gram construct is to describe how it C has separate address spaces for code
which, by their nature, strain the executes on some representative com- and data. Although on many real
capabilities of a platform-indepen- puter. This is what the C standard tries machines, pointers to functions (in
dent dialect. to do. the code space) have the same repre-
In practice, it’s nearly impossible to Although real computers have sentation as pointers to objects (in the
write embedded systems entirely in many similarities, they also have differ- data space), the standard has no such
standard C or C++. Most standard-con- ences. To be commercially successful, requirement. Thus, a program that
forming C and C++ implementations a programming language standard converts a function pointer into an
provide some non-standard exten- should avoid unnecessary bias toward object pointer, or vice versa, is not a

Embedded Systems Programming NOVEMBER 1998 111


PROGRAMMING POINTERS

strictly conforming program. That is, the those conversion rules in “What const
program won’t execute properly on Really Means,” August 1998, p. 11.)
some standard C platforms. For example:

Providing for read-only storage int *pi = (int *)&ci; // ok

All data in the abstract machine Thus, a pointer to non-const T can


resides in one conceptual address point to a const T object. This seems
space. Neither the stack nor the heap to suggest that a program can use a
gets a separate data space. Thus, a pointer to a non-const type to store
pointer to an object of type T can into a const object, which in turn sug-
point to any T object, whether that gests that const objects really aren’t
object has automatic storage (on the read-only after all. If that’s the case,
stack), dynamic storage (in the heap), then how can a standard C imple-
or static storage. mentation ever place objects into
The single data space encompasses ROM?
objects with const-qualified and Permission to place objects into
volatile-qualified types, as well as ROM comes from the following rule:
unqualified types. Specifically, the
standard says that: • If a program attempts to modify an
object defined with a const-quali-
• Qualified and unqualified versions fied type via an expression with a
of a type have the same representa- non-const-qualified type, the
tion and alignment requirements behavior is undefined
• Pointers to qualified or unqualified
versions of a type have the same For example, the previous declara-
representation and alignment tion initializes pi to point to ci. The
requirements expression *pi has type int, which is a
non-const-qualified type. However,
These rules say that for any type T, *pi designates ci, which is defined as
a program can have a “pointer to a a const-qualified int. A program can
qualified T” pointing to an “unquali- use *pi to inspect the value of ci, as
fied T.” Here’s an example where T is in:
int:
if (*pi > 0) // ok
int i;
... However, if it tries to use *pi to store
int const *pci = &i; into ci, the behavior is undefined:

This declares pci as a “pointer to *pi = 7; // undefined


const int” pointing to an int.
By themselves, the rules also say This assignment is an error, but it’s
that a program can have a “pointer to the kind of error that is difficult, if
an unqualified T” pointing to a “quali- not impossible, to detect at compile
fied T,” as in: time. Acknowledging the difficulty of
diagnosing this error, the standard
int const ci; relieves the C implementation of all
... responsibility. Once a program lapses
int *pi = &ci; // not quite into undefined behavior, all bets are
off. The program has violated its
However, pointer conversion rules obligations under the standard, so
elsewhere in the standard prohibit the C implementation is under no
conversions that strip away qualifiers, obligation to get anything right
unless you use a cast. (I discussed anymore.

112 NOVEMBER 1998 Embedded Systems Programming


PROGRAMMING POINTERS

Often, the clearest and most direct way to explain the behavior of a program
construct is to describe how it executes on some representative computer. declare const objects as auto variables
This is what the C standard tries to do. and write-protect them at run time.
I’m not aware of any architecture that
actually does this, but if you know of
one, please let me know.
A footnote in the standard summa- • The implementation may place a
rizes the freedom that C implementa- const object with static storage that is Errata
tions have to place data into ROM: not volatile in a read-only region of
storage My previous column (“Volatile
• The implementation may place a Objects,” September 1998, p. 101)
const object that is not volatile in a An object can’t very well reside in contained numerous program frag-
read-only region of storage ROM unless it has a fixed address ments built around a loop of the form:
determined at translation time. Maybe
A C compiler cannot place an omitting the phrase “with static stor- while (*pc & READY == 0)
object into ROM if it’s volatile as well age” was an oversight, but maybe it /* do nothing until ready */;
as const. The value of a volatile object wasn’t.
might be changed by events outside I suppose this wording allows for an Reader Don Starr pointed out that
the program’s control. An object can’t architecture that can write-protect the condition in the loop does not do
exhibit volatile behavior if it resides in RAM at run time. Write-protected as I intended. Whereas && has a higher
ROM. RAM is not ROM in the sense that I precedence than ==, & has a lower
I’m a little intrigued by the exact think most embedded programmers precedence. As written, the loop is
wording of the footnote. I expected it speak of ROM, but it’s ROM nonethe- equivalent to:
to say something like: less. In theory, a program could
while (*pc & (READY == 0))
/* do nothing until ready */;

I should have written:

while ((*pc & READY) == 0)


/* do nothing until ready */;

I’ve never been one to insert


redundant parentheses. However,
when I showed this one to my brother
Joel, he said that his philosophy has
been for some time:
“When in doubt about precedence,
parenthesize. When certain about
precedence, parenthesize. In either
case, if you’re wrong, your program
will still work and no one will be the
wiser.”
Score one for his side. esp

Dan Saks is the president of Saks &


Associates, a C/C++ training and consult-
ing company. He is also a contributing edi-
tor for the C/C++ Users Journal. He
served for many years as secretary of the
C++ standards committee and remains an
active member. With Thomas Plum, he
wrote C++ Programming Guidelines.
You can write to him at dsaks@witten-
berg.edu.

114 NOVEMBER 1998 Embedded Systems Programming

You might also like