Re: Delay Routine: Fully-portable C89 if possible



John Devereux wrote:
David Brown <david@xxxxxxxxxxxxxxxxxxxxxxxxxxxxx> writes:

John Devereux wrote:
David Brown <david@xxxxxxxxxxxxxxxxxxxxxxxxxxxxx> writes:


[...]


There are several situations where a 33-bit integer would not suit
when I want a 32-bit integer (or more realistically, reasons why I
might particularly want a 16-bit integer and not a 32-bit integer).
Here's a few of them:

1) RAM space - on a small micro, you don't want to waste space.
I would use "short" for this.

But a "short int" may be bigger than 16-bit. Some 8-bit compilers may
may also implement "short int" as 8-bit - I know it's non-conforming,
but it's far more likely that an embedded programmer will meet a
non-standard compiler with 8-bit shorts than a standard compiler with
36-bit ints.

If a "short" is bigger that 16 bit, surely the int_least16_t will be
too? They both have the same rationale AFAIK.


Yes, I think that's true. But if the target/compiler does not support a true 16-bit int, then I the type "int16_t" would not exist (even though int_least16_t and int_fast16_t would).

Note that it is perfectly possible, though somewhat unlikely in this contrived example, for an embedded compiler to support 16-bit integers while having short ints bigger than 16 bits. Embedded compilers sometimes support different sized types as extensions, beyond what is available through char, short int, int, long int and long long int. It's more realistic to expect sizes such as 24-bit or 40-bit integers to be available in this way, but some compilers have 32-bit short ints (perhaps having 64-bit ints, or perhaps just because 32-bit data are much faster on the architecture in question), and may provide 16-bit integers through an extension.


[...]

5) Bit manipulation - sometimes it's important to know the exact
number of bits when doing logic operations.
Maybe - but I can't think of a real-life example.

extern void sendChar(char c);
void sendHexNibble(uint8_t n) {
static const char hexChars[] = "0123456789abcdef";
sendChar(hexChars[n]);
}
void sendHexByte(uint8_t n) {
sendHexNibble(n >> 4);
sendHexNibble(n & 0xf);
}

If the uint8_t used by sendHexByte had extra non-zero bits, you would
end up reading outside the hexChars[] array.

I confess, I would probably just go ahead with the exact same code but
using unsigned char as a synonym for uint8_t. They are always the same
on the machines I am familiar with. (And if they were not, there would
probably not be a uint8_t at all!)


I hope you don't have to try to get code working on one of these horrible 16-bit DSPs that can't access 8-bit data directly - an unsigned char is 16 bits on such architectures. I'd rather have the compiler reject the code because there is no uint8_t implemented, than generate incorrect code because I'd assumed a char is 8 bits.


6) Array efficiency - it is often more efficient to access arrays of
structs if the element size is a power of two for easy calculation of
addresses, so it's important to know the exact size of the elements.
You mean padding the struct with extra elements, or adjusting the type
of existing ones, to bring the total struct size to a power-of-two
number of bytes? I have never thought of doing this, but it sounds
very unportable. I suppose it could avoid doing a multiply on lookup.

It's perfectly portable - as long as you use size-specific types. And
yes, it avoids multiplies on lookups - multiplies can be costly on
small cpus. Even on big cpus, avoiding multiplies can be beneficial - sometimes multiplies have longer latencies than shifts, and many have
addressing modes that can include shifts in a single instruction. The
other advantage is that you can be sure that accesses are aligned.
The compiler should pad the struct to ensure that accesses are
possible, but not necessarily optimal - if you know the exact sizes,
you can add padding yourself to choose a balance between space and
speed.

Aren't compilers free to pad structures how they like?


Yes, and they are allowed to generate all sorts of code as long as the visible results are the same. The padding will (normally!) follow specific rules according to alignment rules on the target, but it's certainly advisable to check that the padding is as you expect. I generally have the "-Wpadding" flag on my gcc compiles, so that I get a warning if my structs are unexpectedly padded.
.



Relevant Pages

  • Re: It Pays to Enrich Your C Skills
    ... Check if you can score a perfect 10 (without using a compiler). ... int main{ ... struct bitfield { ... out if it is a negative integer constant or a constant expression ...
    (comp.lang.c.moderated)
  • Re: Delay Routine: Fully-portable C89 if possible
    ... non-standard compiler with 8-bit shorts than a standard compiler with ... so it's important to know the exact size of the elements. ... You mean padding the struct with extra elements, ... it avoids multiplies on lookups - multiplies can be costly on ...
    (comp.arch.embedded)
  • Re: Passing an already declarted array of structures!
    ... void LCD_PAINTSCREEN ... in above code by "int", ... If you guys see nothing wrong with the code, then I suppose its a compiler ... struct S { ...
    (microsoft.public.vc.language)
  • [MC++] Internal Compiler Error with /Ox
    ... ("NullReferenceException with value struct") ... __value struct Contours { ... int num_contours; ... Microsoft C/C++ Optimizing Compiler Version 13.10.3077 for .NET Framework ...
    (microsoft.public.dotnet.languages.vc)
  • Re: void * vs char *
    ... cast p to a (struct s*) to avoid a compile-time error. ... struct s {int a;}; ... function call consists solely of an identifier, and if no declaration ... And see what your compiler has to say. ...
    (comp.lang.c)