We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
I am using simultaneously in the 8bit C51 and 16bit compilers for two related projects.
Am I correct that for 8bit integers, 8-bit compiler uses %bu in printf() 16bit compiler uses %hu ?
Read & compare the Manuals for the two compilers.
C166 manual pg 299: only mentions the L modifier, and does not indicate what size integer it pertains to. For a 16bit mcu: Long should be 32, regular should be 16 short should be 8 C51 manual pg 288: mentions L and B modifiers, but does not explicitly indicate size. I'm just trying to keep it straight, and ideally use an explicit syntax that might work in both. ANSI C recognizes the H modifier, not the B. The C166 appears to recognize the H as well. I would think this would be a common topic to have been sorted out by many others... can anyone clarify, or direct me to the right place? Thank you.
For a 16bit mcu: Long should be 32, regular should be 16 short should be 8 No. short is *never* 8 bits. It's actually forbidden to make that any smaller than effectively 16 bits, by the definition of the C programming language. ANSI C recognizes the H modifier [...] But only for scanf() and friends, not for printf(), mind you.
16bit compiler uses %hu I would rather hope it doesn't. As per the language definition, actually neither of these should need any prefix at all, for printf()ing an 8-bit number. The %b prefix in C51 is there only because C51 doesn't do ANSI default argument promotions. If it did, the prefix wouldn't be needed because, by the time printf() is called, the char would have been silently casted into a 16-bit value.
"C51 manual pg 288: mentions L and B modifiers, but does not explicitly indicate size." What's the date on your manual? Mine's 09.2001, and p291 says: "The optional characters b or B and l or L may immediately precede the type character to respectively specify char or long versions of the integer types d, i, u, o, x, and X." Although not explicitly stated in that very paragraph, the Manual clearly tells you that char is 8 bits, int is 16 bits, and long int is 32 bits. Online manual: "The optional characters b or B and l or L may immediately precede the type character to respectively specify char or long versions of the integer types d, i, u, o, x, and X." http://www.keil.com/support/man/docs/c51/c51_printf.htm Seems clear enough to me?
"The %b prefix in C51 is there only because C51 doesn't do ANSI default argument promotions. If it did, the prefix wouldn't be needed because, by the time printf() is called, the char would have been silently casted into a 16-bit value" For further discussion of default promotions (with particular reference to printf) see: http://www.keil.com/forum/docs/thread4014.asp
Hans-Bernhard Broeker says, (Quoting a previous post saying "ANSI C recognizes the H modifier"), "But only for scanf() and friends, not for printf(), mind you." I'm the person responsible for adding the "%hu" form to the C166 codebase the original poster and I are working on. The first time I added it, it caused no obvious errors and appeared to do what I expected, so I kept doing it. I've used it since Borland version 3 and haven't thought about it much since then, as every compiler I've used has accepted it as I expected. (I was going to say since the first edition of K&R, but I see they only defined 'l' and not 'h'.) When I added it to a C51 program, we discovered it didn't do what we expected, and that we had to use 'b' for the size qualifier. Hence the current exploration. My first reference is always "Standard C", by Plauger and Brodie in 1989, which (on a well-used page with a turned-up corner) clearly defines h, l, and L size qualifiers for _both_ printf and scanf. They claim to be describing the completed ANSI standard, but before it was accepted by ISO. My secondary reference for such questions is Plauger's "The Standard C Library" from 1992, which also describes and shows library code for h, l, and L qualifiers for printf (P. 307-308) and scanf (P. 315, 324). I'm certainly open to hearing that the world has moved on from 1989, but some time with Google produced far more pages with 'h' for printf than without. And a pretty clear message that nobody is giving away the definitive standard. <http://www.keil.com/support/man/docs/c166/c166_sscanf.asp> shows: printf ("Reading a signed byte, int,and long\n"); argsread = sscanf ("1 -234 567890", "%bd %d %ld", &a, &b, &c); printf ("%d arguments read\n", argsread); printf ("Reading an unsigned byte, int, and long\n"); argsread = sscanf ("2 44 98765432", "%bu %u %lu", &x, &y, &z); printf ("%d arguments read\n", argsread); So apparently we could use 'b' in both C51 and C166, at least for scanf. This however would appear to be inconsistent with the standard, even the version Mr. Broeker is quoting. I've never seen 'b' used as a size qualifier until these Keil compilers. <http://www.keil.com/support/man/docs/c166/c166_printf.htm> says: Format specifications have the following format: % [flags] [width] [.precision] [{l|L}] type and: The optional characters l or L may immediately precede the type character to respectively specify long versions of the integer types d, i, u, o, x, and X. Which indicates that 'h' is not intended to be used. But using it does not produce compiler errors, and so far as we have noticed does not break the expected function of the code, so they must expect people to use it and explicitly tolerate it. Of course the C51 compiler does not complain about 'h', either, it just doesn't print what you expect unless you use 'b'. So just to mess with my mind, I found out today that Java (JDK5.0) now has printf... Thankfully it does not involve any size qualifiers, but it certainly has enough other alphabet soup to be confused with C. Loren
Has anyone else noticed that this forum requires Microsoft Internet Explorer in order to actually post a reply? I was able to do everything up until pressing the "Post" button using Opera 7.5, but the reply to the post was always "Connection closed by server". Even if Opera was identifying as IE6... Loren (Trying to defend standards again... But keeping IE6 handy for exceptions to them.)
Ah, sorry, now I actually looked it up, I see I was making an incorrect transfer of knowledge from floating-point to integer printing formats. In floats, %hf actually doesn't exist, but in integers, it does --- sorry for having caused any uncertainty or confusion. There can be a difference in results between %hu and %u for printf() only if unsigned int and unsigned short have different ranges. Which is not the case in either of the compilers discussed here (C51, C166). I.e. in these compilers, you don't actually need %hu instead of %u --- it's better to put it in, though, for the sake of maximum portability of the code. Keil's '51 and '166 implementations of printf() could simply ignore the 'h' length modifier, and your experiment suggests that's exactly what they do. But IMHO they really should document this fact. Looking at the documentation, one would actually expect to get an error message from C51 for use of %hu, although that's perfectly valid standard C code. The 'b' length modifier is, indeed, a Keil-ism. It's needed in C51 because that compiler explicitly violates the ANSI/ISO C89 standard in one important aspect: arguments to var-args functions that fit into 8 bits aren't automatically expanded to 16 bits, contrary to the language definition. IMHO, this behaviour should be changed by the [NO]INTPROMOTE switch, but apparently it's not. So printf() will actually receive 8-bit values as arguments, which in ANSI C is supposed never to happen, and therefore it needs a special length specifier to tell it about this.
Has anyone else noticed that this forum requires Microsoft Internet Explorer in order to actually post a reply? They wouldn't, because it doesn't. Both Netscape 4 and current Mozilla-based browsers (incl. Firefox) work quite well, too. The only feature that is somewhat flaky seems to be the handling for the read "New posting" marker in the thread view --- that requires IE or a very recent Firefox, in my experience.
"In floats, %hf actually doesn't exist, but in integers, it does" You're thinking of the %lf conversion in scanf() for a double which doesn't exist in printf(). I incorrectly used %lf in printf() for many years with many compilers and every single one of them ignored it - which is presumably why I was able to go on doing the wrong thing for so long without noticing. "Looking at the documentation, one would actually expect to get an error message from C51 for use of %hu, although that's perfectly valid standard C code." Compilers are not required to, and generally do not, parse the format string. It would be impossible for them to do so in all situations. You can stick any old rubbish you like in there without getting a diagnostic. "The 'b' length modifier is, indeed, a Keil-ism. It's needed in C51 because that compiler explicitly violates the ANSI/ISO C89 standard in one important aspect: arguments to var-args functions that fit into 8 bits aren't automatically expanded to 16 bits, contrary to the language definition. IMHO, this behaviour should be changed by the [NO]INTPROMOTE switch, but apparently it's not." The point here is that the default *integer* promotions and the default *argument* promotions are quite different things, so it would make little sense to have the INTPROMOTE switch affect argument promotions. "So printf() will actually receive 8-bit values as arguments, which in ANSI C is supposed never to happen, and therefore it needs a special length specifier to tell it about this." Another related gotcha is the %c specifier - the standard states that %c expects an int argument. In an ANSI implementation this means that a char argument will also work as the char will be promoted to int, however Keil's printf() expects (and will only work with) a char. An interesting side point: As I understand it Keil is what the standard calls a 'freestanding' implementation (no OS present on the target). It is therefore not required to implement the 'C' library functions at all but can still be a conforming implementation. I cannot determine whether a freestanding implementation is required to ensure that if it does implement a library function that it implements it in strict accordance with the standard or not. Or, for that matter, whether it is required to document any differences from ANSI behaviour in this situation.
My simple question seems indeed to have a non-trivial and non-obvious answer wrt various C implementations. Thank you for the answers. (Andrew, Hans: pardon, my ref to short should have indeed been char. In my mind, when working with 8bit i/o registers and small loop counters, I don't think of them as characters, but integers--which I typecast to int8u or int8s.) Stefan: regarding the %c, will it correctly handle signed 8bit integers as well? In general, I wonder what a good strategy is for handling these instances... for example, is it advisable to cast printf() arguments explicitly? Perhaps there is a big efficiency penalty vs. simply using the appropriate modifier.
You're thinking of the %lf conversion in scanf() for a double which doesn't exist in printf(). No. I was thinking of exactly what I wrote: that the 'h' modifier doesn't exist at all, for floating-point, neither in printf() nor in scanf(). Compilers are not required to, and generally do not, parse the format string. Quite a number of compilers do parse it, if they can see it. GCC, e.g., does. And IMHO such checks are even more important to be done by a compiler for embedded systems, like C51, where the runtime library has essentially no sane way of signalling a runtime error.