Re: C programming on ARM



Wilco Dijkstra wrote:
"David Brown" <david@xxxxxxxxxxxxxxxxxxxxxxxxxxxxx> wrote in message news:47a1a46d$0$14996$8404b019@xxxxxxxxxxxxxxxxxx
Wilco Dijkstra wrote:
"CBFalconer" <cbfalconer@xxxxxxxxx> wrote in message news:47A11649.3D617492@xxxxxxxxxxxx
Wilco Dijkstra wrote:
... snip ...
My point was that the bitpatterns of all pointer types are the
same for all addresses in the address space.
Not necessarily so. void* carries all the information, apart from
the object type. void* bitpattern is identical with the char*
bitpattern, but after that there are no limits.
You're talking about what the C language standards guarantee.
I'm talking about how compilers implement the language. On a
byte addressable architecture you can rely on the one I mentioned
above. The fact is most programs rely on this whether the standard
permits it or not...

It's a fair point that there is a difference what you can rely on for real, practical C and what the standards say. You can happily rely on things like 2's complement arithmetic, and data sizes being 8, 16, 32, 64 bits - if you are programming a dinosaur mainframe, you are probably aware of the fact!

Most programs also rely on signed arithmetic not trapping, signed
right shifts working as expected, division rounding as expected, char
being 8-bit and ASCII...


All of this is reasonable enough in practice. Of course, some programs also rely on "char" being signed or unsigned, or "int" being 32-bit, and other things that may be true on one commonly used compiler/target but not others.

And often when programming, you know exactly what compiler and target you are using, and can program accordingly rather than writing generic code.

However, you *can't* rely on pointers working like you describe in general - not in embedded programming. Aside from alignment problems (which may mean that using the pointer does not "just work", even if conversions back and forth do),

As I said derefencing is another issue altogether. Casting is not always
done in order to dereference, I often use pointer casts to allow comparisons
between pointers (eg. range check), to align a pointer (to avoid alignment
issues!) or add an integer to a pointer etc.

there are many architectures with different pointer types and different memory spaces. These don't fit well with C (which expects a single memory space), but they are the reality we live with. On an AVR Mega256, a general flash pointer will 24 bits, while a ram pointer will be 16 bits. On some architectures you get "far" and "near" pointers. There are some that have a section of bit-addressable memory, with a different pointer type. You can't rely on these things having a consistent pointer size - you should not even rely on the C requirement of a "void *" supporting them all (embedded compilers don't always follow the standards if they conflict with generating good object code for real-world source code).

But as you say C doesn't map onto these architectures, so we're not
talking about standard C. Most programs won't compile for these
architectures (assuming the code would fit), and if they did still wouldn't
run correctly - precisely because these architectures do not support the
defacto assumptions that most C programs make.


C doesn't map directly onto these architectures - but that does not stop people using C for them on a regular basis! Look at the AVR, for example - it has different address spaces for flash and ram data, so the pointers are incompatible. On the larger AVRs, a full flash pointer must be 24 bits, while ram pointers are only 16 bits. Yet somehow there are vast numbers of these devices in use, almost all of them programmed in C. You certainly can't take a random C program off the internet and expect it to compile and work on an AVR, but there is no problem writing portable code that works on the 8-bit AVR, 16-bit msp430, 32-bit big-endian ColdFire, 32-bit little-endian ARM, and even some of these horrible DSP architectures with their 16-bit chars. You just have to stick to assumptions that are appropriate for embedded programming, rather than thinking "big" C.

The reverse is true as well, programs written for these C dialects do not
work on standard C compilers. One case that comes up often is:
unsigned char c = 255; if (~c == 0) ... This is always false in standard C,
but there are compilers which think it is true...


If code like that comes up "often", you have bigger problems than just bad assumptions - that code snippet is horrible. But it is certainly true that code written for small embedded C systems often has compiler/target specific features that don't work on larger systems - code for a compiler with an extra "flash" keyword for flash-based data will clearly not compile on anything else without some changes.
.



Relevant Pages

  • Re: C programming on ARM
    ... I'm talking about how compilers implement the language. ... It's a fair point that there is a difference what you can rely on for real, practical C and what the standards say. ... alignment problems (which may mean that using the pointer does not "just work", even if conversions back and forth ... But as you say C doesn't map onto these architectures, ...
    (comp.arch.embedded)
  • Re: Banks and economy
    ... These are both Declarations of pointers to char. ... will be assigned some valid pointer, so pointer space in the data ... that's what these vintage compilers did. ... implementors to promote an array to a first class object, ...
    (alt.lang.asm)
  • Re: Request
    ... char *p=calloc; ... in several systems with several compilers. ... HAS THE BUG ... the same result as if I had a NULL pointer. ...
    (comp.lang.c)
  • Unicode: ugh!
    ... a string is defined as a pointer to char in the ... C language, and is conventionally terminated with a NULL ... You'd think folks writing standards would bother to properly read ...
    (comp.lang.c)
  • Re: pointer alignment
    ... >> A char has no alignment restrictions. ... A pointer to char may. ... >> (and at least some compilers don't have) alignment requirements for ...
    (comp.lang.c)