Post by David BrownThe standards make it clear that program lifetime objects without
explicit initialisation are zero'ed before the start of the program. A
freestanding C implementation can use whatever it likes as a start
function - it can be "void main(void)", or "int reset(int)", or whatever
it wants. That is for the implementation to decide. And if the
programmer uses a form that is not specified by the implementation, then
/that/ is undefined behaviour.
The notion of "the start of a program" can be somewhat murky in some free-
standing scenarios. Many C-language tools generate code for independent
functions; once a collection of functions is loaded and RAM is set up for
them, any of them could be executed directly without having previously run
any other code produced by the C-language tools. Responsibility for any
zero-initialization would lie with the external loading process.
TI does bundle some assembly-language files which can be adjusted to
accommodate a variety of loading scenarios (code runs from flash; code gets
loaded into RAM from an external storage device and then run, etc.), and
it would probably have been a good idea to have them default to zero-
initializing storage. I'm not sure whether such files should be viewed as
really being part of the "C implementation", however, since one could do C
programming without them if e.g. one had a boot loader that could process
ELF files directly and set up RAM according to that spec (including zero-
initialization).
Post by David BrownClearly it is possible to write code that can be run by the second
implementation. And clearly it is possible to write code that /cannot/
be run by the first implementation (just use enough constant data to
overflow the ram in the microcontroller, avoiding the
implementation-specific features needed to keep that constant data in
flash). But with the second implementation, standard programs that
should be able to run (i.e., they are within the limitations of ram,
rom, etc.), will compile cleanly but have different and incorrect
behaviour compared to other conforming C implementations. And that is
the vital difference.
I've not had problems with the const-pointer treatment breaking code; I'm
not sure how much otherwise-usable code would be broken by it. On many
parts, the difference in RAM and ROM sizes is sufficiently large (30:1 or
more) that a lot of programs would be nowhere close to being able to store
all their tables in RAM, but would still be nowhere near filling their code
space.
Post by David BrownThe problem is non-const data accessed through const qualified pointers.
extern int average(int noOfSamples, const int * pSamples);
int x[10];
int av = average(10, x);
On the PIC compilers, that will work without difficulty. The act of reading
a const pointer causes the compiler to check a bit in the pointer and either
access RAM or ROM. The situations the PIC has trouble with are using non-
const pointers to read const data, or round-tripping const pointers through
non-const pointers, but of which are much rarer than using const pointers to
read both const and non-const data.
I don't know what the Atmel compiler does, but if it can't use const pointers
to read non-const data I would agree that is an unjustifiable abuse of the
"const" keyword which doesn't accommodate any useful scenarios that would not
be served just as well using a different keyword. The reason I regard the
PIC's behavior as somewhat reasonable [albeit obviously non-conforming] is
that it allows many programs to run efficiently without modification in a
way which would not be possible had another keyword been chosen. If the
Atmel compiler's implementation doesn't offer that advantage, I would see
no justification for re-purposing the keyword.
Post by David BrownA C compiler should not be made to work only with a particular
specialised variety of programs! A compiler sold as a "C Compiler"
should be made to correctly compile as many valid C programs as possible
given the limitations of the target.
Since only a particular specialized variety of programs could possibly
do anything useful with 36 bytes of RAM and 1Kword of code space, it would
be impossible to write a compiler for such a platform that *wasn't* limited
to a particular specialized variety of programs.
More generally, most embedded compilers are intended largely for use on
systems which don't have any defined concept of console or file I/O, and
could thus only process usefully the particular specialized variety of
programs that are designed to operate with whatever hardware is built into
the target system.
The advantage of using C on many such systems isn't that one can simply grab
arbitrary bits of code from here and there and run it, but rather that a
programmer who wants to turn on a light need only worry about:
1. Which I/O port is the light hooked up to
2. Which bits of which addresses need to be written to activate that
port
Rather than also having to worry about the sequence of instructions that
might be needed to perform such an access, any restrictions that might exist
regarding the placement of such code and any data it requires, and also the
question of what one must do to ensure that code gets put into whatever kind
of code section is required on the target. The placement of the light and
the sequence of accesses needed to activate it will vary depending upon the
hardware platform, but there's no reason the syntax for performing such
accesses should need to.