Board index » cppbuilder » Re: Any help on why?

Re: Any help on why?

2004-07-09 06:09:00 AM
Thomas Maeder [TeamB] wrote:
Bob Gonder writes:

>>In contrary to your code, mine is at least correct.
>Not quite, yours is also wrong-endian on Win/Intel.

Mine is endianness agnostic.
Don't see how that can be.
>>>>unsigned char const *const begin(reinterpret_cast<unsigned char *>(&d));
This returns the lowest address (the LSB) of d.
(MSB in BigEndian, right?)
>>>>unsigned char const *const end(begin + sizeof d);
This returns the address 1 past d (1 past MSB)
(Again, LSB in BigEndian, right?)
>>>>for (unsigned char const *c = begin; c!=end; ++c)
This outputs data LSB-to-MSB, which is backwards for a hex value.
In BigEndian, it would output MSB-to-LSB, which is the normal way to
display a hex value.
For long d = 0x12345678
Your output for LittleEndian would be
For Big Endian it would be
Hardly agnostic (or correct)
We weren't talking hex dumps here (for which your code is fine), we
were talking %x replacements for displaying hex values. For that, your
code is broken.
>>>>double d(3.11415);
>>>>unsigned char const *const begin(reinterpret_cast<unsigned char *>(&d));
>>>>unsigned char const *const end(begin + sizeof d);
>>>>for (unsigned char const *c = begin; c!=end; ++c)
>Should be c=end-1, c>= begin; --c

That would have undefined behavior when c is moved to begin-1.
Could be, but I'd do it completely different anyway.
probably closer to c=end;do{--c;use c;}while(c != begin)
And, being a library subroutine, I'd switch it, along with all my file
and screen io routines, when changing platforms. I have no
expectations that my libraries will survive intact when platforms
collide. But, I'm trying to improve my applications so they rely on
library code instead of platform code.
>Go along with that, but maybe better would be to fire those inept
>programmers (or retrain them).

After all these things you posted in this thread, I doubt that you are in
a position to ask somebody else to get retrained, I'm afraid.
But then, I'm not the code snob that abhors "inappropriate" usage.
_If_ I were, I wouldn't look to ISO to "fix" the language, I'd "fix"
the programmers. The language was just fine, thankyouverymuch.
I do try to look ahead to 64, perhaps 128 bit x86 platforms, but I
don't see worrying about Motorola or other cross-platform issues.
It just isn't important to me, nor I'd think to most non-academics.
Maybe large enterprises worry, but then they worry about lots of
things that don't affect normal people.

Re:Re: Any help on why?

Wayne A. King wrote:
On Thu, 08 Jul 2004 10:35:00 -0700, Bob Gonder wrote:

Fine. Right specifier for the output desired.
Wrong input argument type for the specifier used.
I suppose you better get onto the committee then, and get a proper
format specifier defined for
"hex output of double"
And, while you're at it,
"hex output of unknown type of size (prefix)"

Re:Re: Any help on why?

Wayne A. King wrote:
On Thu, 08 Jul 2004 10:29:37 -0700, Bob Gonder < XXXX@XXXXX.COM >

>The OP reports that LX works for his 5.02 app.

Again a misuse of specifiers. "L" is intended for use with long doubles,
Only when applied to float formats. for integer formats including X,
it is long long (or int64)
The side effects are unknown and not always
immediately apparent. (e.g. - possible corruption of adjacent memory or
the stack,
Totally bogus <expletive deleted>. The caller is responsible for the
stack, and the caller knows full well what it put on the stack, and
how to remove it. No corruption CAN occur as the stack data is const.
If it DOES occur, then the RTL is broken.
I really, really, expect better of you Wayne.
>you can only use the
>_sizes_ that are implimented. But what you pack into that _size_
>agument on the stack can be Anything (that fits).

See above. You're recommending using a wrong size modifier.
See yourself. L is 64 bits for int formats, not 80.
I clearly alluded to the parsing of the source code. The parser can tell
from the source that a type mismatch is present, and freely choose to
ignore it, alter it, pass it unchanged, etc. The effects of mismatching are
Wrongo. Again. Wayne. (At least, in this world) The *compiler* knows
*nothing* (see code at end). It passes a String. The RTL Parses the
String. The Data is already on the Stack!
The RTL Knows Nothing about What got put on the Stack. It can only
Assume that the programmer did a good job of it. C ain't type safe. I
hope it never becomes type safe.
>I'd like to know _how_ a printf runtime implimentor, who's only input
>is a string and an unknown bunch of binary data on the stack, can
>distinguish between an apple and an orange.

The parser can distinguish an integer from a float type. It's looking
at the source code, not the stack.
The Compiler Parser? I've yet to see a compiler do anything other than
pass the string to the RTL.
X is specifically intended for use with ints. If you pass a float to it the
parser can detect that. (As shown in the warnings from CLint++.)
If the parser chooses to process it anyway as raw bits, you got lucky.
How is it "lucky" to rely on the RTL to Do It's Job?
But the parser may also choose to silently ignore it, or assume an
int of a different size, etc.
Tell me Wayne, How Can It Assume a Different Size from what the
Published Size is??????? You've NEVER Answered That!
The parser may be written to determine
correctly the sizes of integer arguments when processing X, and may
assume a default size if passed an illegal type. The effects are
unpredictable, undefined and unreliable.
Un-believable. If the compiler doesn't do what I tell it to do, then
it is broken. It can't decide on it's own that it knows what's good
for the program(mer). You can't have compilers going around saying,
Oh, he said it was a DWORD, but I know he meant Long Unicode.
>printf ain't type-safe C++,

That's no excuse for abusing it.
It ain't abuse if it was designed that way.
>Which is also why you shouldn't lie to him
"lie"?? That implies deliberate deception and impugns my integrity.
Personal insults are not welcome in these newsgroups.
Yeah, I hate doing that. I know you mean well, but it gets under my
skin when you (of all people) say "can't" like it doesn't work, when
you "can", and it does work. Your below is better. But still should
be "shouldn't".
Fine. If you need it qualified: He can't use %X on non-ints and:
(1) expect reliable, consistent, predictable results
(2) expect to do so with impunity
(3) expect any degree of portability in his code
(4) be considered a competent C programmer

>So, _I_ think that this usage _was_ intended.

I suspect you're alone in this world on that assumption.
I doubt that, but I'll let it drop.
Sure. Give him enough rope and he'll shoot himself in the foot.
That's what I like about C. Almost as good for foot shooting as ASM.
{*word*209}ing the language may have its appeal for quick and
dirty programs (programmers?), but it is not a practice to be
encouraged widely. Neophyte C programmers should be
taught and encouraged to use the language as designed
and in a safe and reliable manner. After they've become
competent in the "correct" use of the language, they can
then safely explore aberrations from a solid base to which
they can (and should) return as soon as possible.
Yep. Just gotta know when and how to bend those rules, and what
"works". Reminding those neophytes that what they are trying is
non-portable or non-standard should be enough though.
It's like telling a {*word*249}ager he "can't" take his motorcylcle apart.
Maybe he "shouldn't", but he's going to learn quite a lot by doing it.
Are you suggesting that compiler writers don't have brains?
I sometimes wonder when I look as the asm output.....Other times, I go
WOW! I'd never come up with that! Like a lot of the small multiplies
being done by addressing functions and additions instead of the slower
Passing a non-int to a function such as printf when only an
int is expected presumes that the implementation has been
coded to determine correctly the size of *all* types.
No, it presumes that the implementation has been coded to follow the
rules as published in the acompanying docs about format specifiers.
To wit: %LX prints the hex of an int64 (the next 64 bits on the
stack). HOW those 64bits Got on the stack is none of the RTL's
It may
not have been designed to do that. It may use a default size
if the arg passed is not an integer type
Again, How does the RTL Know what was passed? It Doesn't.
It just processes the format string, and applies it to the binary data
on the stack.
This may or may not
happily coincide with the incorrect type of arg being passed.
If the programmer is any good, it Will coincide.
For efficiency, C library functions are "bare bones" implementations.
They place the burden of responsibility for correct usage squarely
on the programmer.
>"Warning" because the programmer _might_ know what he's doing.

Most unlikely.
But not _always_, which is why it _is_ allowed, _is_ only a warning,
and _not_ an Error.
>Otherwise, it'd be an "Error".

Right. Passing incorrect specifiers to printf won't cause a compile
to abort. It *will* usually cause the program to malfunction when
"Might" cause incorrect output, but not if the programmer knows what
he's doing. And no malfunction at all unless using sprintf type and
the output string is decoded or otherwise programically relied upon
elsewhere.Well, I can think one _one exception..if the printf also has
a float conversion and the values are misalligned.
run. Passing the incorrect number of arguments or specifiers will
also pass undetected in most compilers. That doesn't mean it may
All compilers by definition of the function. See code below.
be due to "cleverness" on the part of the programmer.
Doesn't rule it out either, which is the real point.
- - - -
You keep going on how the "parser" is going to see the parameter types
and magically change the format specifier. (I assume you are talking
about the compiler parser, because the RTL parser Never sees the
source!) Well, it can't. It's not allowed.
char format[1024];
ReadFile( f, format, whatever
printf( format, intval1,dblval2);
So tell me how it's going to figure that out?
How's it going to "know" that dblval2 is "supposed" to be an int?
Can't do anything but be a good little compiler, and push the values
and the string. The RTL can't do anything other than parse the string
and apply the formats to what is now just a block of binary on the
stack. End of story. End of thread.
(This message alone took 3 hours, and I need to get back to work as
another bug crept in. A feature went missing.)


Re:Re: Any help on why?

I did verify that %LX will correctly display a "double" float. 1.0 =
0x3FF0,0000,0000,0000. Follow thread on down. I used this code in another
#include <stdio>
float x;
float *ptr_x;
x = 1.0;
ptr_x = &x;
printf("%f = %LX\n %p, %p, %d\n", x, x, ptr_x, &x, sizeof x);
return 0;
result :
doesn't seem to care if it's double or float. It always returns
0x3FF0,0000,0000,0000 = 1.0
I changed values to 2, 3, 4, 4.1 etc and it follows correctly except the
bits seem to always be 64 bit result.
"Bob Gonder" < XXXX@XXXXX.COM >wrote in message
Wayne A. King wrote:

>On Wed, 07 Jul 2004 07:36:44 -0700, Bob Gonder
>>What if you want to see how a float works?
>Then you dump it as raw bytes (chars),

Yeah, you can do that too. But then you'd have endian issues as you
pointed out. The %LX seems to correct endianness.

>not by using a type
>specifier in a lib function which is intended for a different type.

I don't really think it was inteded for any "type". It was intended to
convert binary data (of the proper size) into hex.

>The compiler implementors are allowed to assume that the
>correct argument type will be passed.

They are allowed to assume that the correct size data will be passed.
What's in that data, they don't care about.

>Since doubles and ints
>are different sizes, and different internal layouts, passing doubles
>where ints are expected can only lead to unpredictable behavior.

I think it would be predictable. Perhaps surprising to the unwary, but

>>%X takes BYTES off the stack and displays them. It doesn't care what
>>type they were before they got put on the stack.
>Where did you get that notion from? Show me where it says that in
>the ISO/ANSI specs for the C/C++ Standard Libraries.

Where else is it going to get it? From Space?
It reads the Byte, Word, Dword, Qword (whatever the prefix calls for),
from the stack and formats it in hex. What's so difficult to
understand about that?

>You're making assumptions about implementation details,
>which may vary from compiler to compiler.

I very much doubt %X is going to vary much. It's pretty cut-n-dried.

>You're ignoring
>the fact that C and C++ are languages which have extensive
>formal specifications. The language specifies what is valid
>or invalid.

I don't recall seeing anything that said that you had to pass an INT
and *nothing but* an INT to a %X. Says it takes an int, but it
doesn't say you can't use something else the size of an int.

>You can't arbitrarily use standard library functions
>in any way you want (and expect it to work consistently across

Right, not when sizes are changing all the time. But that's a bug in
the standard (some would call it a feature).

>>Poster could have used %lX for float or double as %X is int, whatever
>>size that is (target dependant)..
>When l (lower case L) is used with any of these:
>d i o u x X
>then arg is interpreted as a long int.
>On the platform in question, a long int is 32-bits and a double is 64

Right, should have been %LX

So the OP's printf("%x %x", double1,double2) would have ignored the
double2, printing the first 32bits (LSB) of double1 (which was zero),
then the MSB 32 bits. (Are you still reading, Stan?)

>Further, since floats/doubles are stored with certain bits used for the
>mantissa and certain others for the exponent,

There's the words I was looking for! (Got them backwards too...)

>whereas ints are stored
>according to the endian architecture of the platform, interpreting a hex
>dump of a float (mis)interpreted as an int (or vice versa) would be
>challenging to say the least.

I don't see why. Perhaps I'm wrong (again) but when a value is stored,
be it short or long or float, isn't it stored in endian fashion? So a
float or double will be pushed in endian fashion, and %X will read it
in endian fashion, and display it MSB first.

%X seems to take endian into account when converting. Needs to,
doesn't it? I mean, if you take int i=0x1234 you don't want to see
3412 come out of %X.


Re:Re: Any help on why?

This is a much better way of doing it for sure. No uncontrolled alignment
or size problems. I'll have to move on in the book because Union are coming
Thx guys
It's been entertaining to say the least!
As far as "equate". It's been awhile. Just know I did it in Fortran with
great results about 20 yrs ago. In fact I don't even have the books or
software anymore.
The %LX deal works, but I can see that size and alignment are issues which
may be hard to nail down and control from the programmer side of the
The comment about the float being passed as a double seems to be upheld in
one of my experiments. Where 1.0 = 0x3FF0,0000,0000,0000 in float or double
variables. And when using different values the number did track correctly
(as doubles).
"Ed Mulroy [TeamB]" < XXXX@XXXXX.COM >wrote in message
>Question: In fortran I can map an array of integers on
>top of an array of floating point. This allows me to gain
>access to the data in multiplt formats. This is done
>by using the "equate" operand.
>Is there a similar operation within C ???

If by "equate" you meant "equivalence" then look at unions. A union
is a way in C for allowing you to use the same block of memory as any
of two or more types.

. Ed

>Stan DeGroff wrote in message
>news: XXXX@XXXXX.COM ...


Re:Re: Any help on why?

Wayne A. King wrote:
As I previously stated: only in compilers which support it. The OP wants to
target 16-bit embedded systems. No 16-bit compiler I know of supports
long long or __int64.
BC5.02 isn't a 16bit compiler?
>See yourself. L is 64 bits for int formats, not 80.

Not in any 16-bit compiler which the OP is likely to use.
Including BC5.02 which the OP uses, and for which Lx is 64 bits?
Even in BC++3, %L(float) is 80bit, %L(int) is 64 bit. %X is an int
type. %LX isn't supported by 3.0, but it defines %L as 64 for int
To claim that %LX is 80 is to claim that X is a float type.
Wrong again. The C compiler's parser can detect anything
which a Lint program can, and some do. According to your
Detecting and acting on that detection are two different things.
A float is 32 bits.
A bit specious as you've already posted that Boland always promotes
floats to doubles as printf arguments. One would presume that that
might not be "standard" behaviour.
long argument required (warning) <<-- line 13, 3rd arg
long argument required (warning) <<-- line 16, 2nd arg
Note that in both cases the message from the compiler says
"required", not "recommended". Note that the compiler implementors
"cared" enough to include the check(s) in the parser. Note that the
compiler "knew" that a mismatch was being attempted.
Note also that they didn't care so much as to make it an "error"
instead of a "warning". Big difference.
Note the effect on subsequent (left to right) arguments when an
incorrect type specifier is encountered. Are the displayed values
those which you expected?
Note that those effects are always to be expected when the sizes are
incorrect. The key to success is for the programmer to get the sizes
correct *for*his*compiler* as shifting platforms is pretty much
guaranteed to shift stack alignments. (16 bit progs push 16 bit
values, 32 bit progs push 32, probably 64 bit progs will push 64)
And different compilers might push floats instead of promoting to
double. And who knows what else some compiler writter might come up
with promotion wise. They might decide to promote all integer types to
128 bits so the RTL has it easy.
This compiler also flags %lf when used with printf, giving the message:
Meaning one presumes (since I don't have the docs for that compiler),
that that "feature" isn't supported by that compiler. Is there some
big lesson here? Compilers that don't support the features of your
"home" compiler, won't compile your code (properly). Does that mean
that we shouldn't use the new features? That seems to be your
argument. Won't be long now before we have int128 available natively.
Should we then limit ourselves to 64 bit because 128 isn't available
on all platforms?
error in format string (warning)
Note the phrases "not syntactically correct" and "this will cause
unexpected behaviour at run time."
Also notice it is still a "Warning".
too many arguments for format string (warning)

Again note that it is the *source code* being parsed by the *compiler*,
something which you claim is never done. Note also the consequences
for ignoring some of these "warnings", as shown by the last example.
You are free to ignore syntax if you choose. You are also free to ignore
traffic lights and railway crossing barriers.
And, presumably, since the compiler only issued "Warning"s, it went
ahead and *compiled* the code "as written".
If %LX is used (suggesting a long long integer type) as in:
printf("%f\t%f\t%LX\n", 1.5, 1.5, 1.5); /* line 13 */
the compiler issues this message:

reptest.c(13) Warning: long long unsigned int format, double arg (arg 4)

It seems these compiler implementors also felt it worthwhile to
detect such mismatches when *parsing* the *source* code.
Yep, they "detected" it, but went ahead and *compiled* it as written,
>If the compiler doesn't do what I tell it to do, then it is broken.

Not if what you tell it to do violates the rules of the language.
These don't violate any "rules" reguarding printf.
The rules of printf are very simple.
1) A string is passed.
2) Other variables my be passed if desired.
That's it.
The rest of the rules are on the compiler's side.
1) pass the string.
2) push any arguments on the stack.(How that is accomplished is
compiler dependant)
3) call the RTL
4) remove those arguments from the stack.
If it doesn't do those 4 steps, it's non conforming.
The RTL has it even simpler.
1) parse the string
2) Decode formats as the compiler docs contracted.
3) assume any parameters called for by the string are on the stack
>It can't decide on it's own that it knows what's good
>for the program(mer).

It does it all the time by optimizing code. e.g. - Omitting "dead'
code, using prefix operators when the programmer specified
post-fix (e.g. ++x instead of x++), merging strings, inlining or not
inlining contrary to the programmer's source code, unrolling
loops, using or not using registers when it decides to supply what
the programmer omitted or omit what he/she supplied, etc.
There you go, off topic again. Nothing to do with printf.
It can decide (quite rightly) to enforce the rules of the language,
You haven't shown it "enforcing" anything.
You have shown that some compilers complain more than others.
But all of those complaints not withstanding, it continued to compile
and run without dire results.
and to penalize programming anarchists who can't adapt. You
don't seem to have accepted the realities of programming with
And you don't seem to be able to accept the realities of real-world
programming, where you use what you've got, and cross-platform
portablitiy is just a pipe dream.
In an endevour where (what's the figure?) 80%? of all projects are
never completed or delivered. Where those that _are_ delivered are
never migrated (still plenty of people running mission critical DOS
apps) Where MS has a virtual monopoly on programs through their APIs
(can _any_ native Windows app be portable?) And where companies latch
onto a compiler vendor (hopefully Borland) and stick with them to the
bitter end ..... I think wasting effort making all those apps that
will never be "ported", "portable" from the beginning .. would be a
bad move to management's bottom line.
I still have clients on my DOS versions even though I ported to Win32
4 or 5 years ago and the update is free (16 to 32bit ASM wasn't all
that difficult a port, but the API shift was something else.) Platform
and retraining costs are factors for some users. (If it ain't broke,
keep what you've got)
anything other than a 1st-generation language.
Disregarding the rules of syntax and grammar in HLLs can have catastrophic effects.
No syntax or grammer rules broken or bent here. Syntax is way too
simple for that:
printf( char*, ... )
Granted C is more like a 2nd-GL than a 3rd, it nonetheless *does*
have requirements which are imposed on the programmer.
Yep, _most_ of the time. Just not here. With elipsis args, there are
no arg constraints. You can put whatever you want in there.
As you've shown, the compiler is also free to complain. Heck, there's
probably nothing to say it can't complain about perfectly legal and
standard and portable code too should the compiler be in such a mood.
As long as it goes ahead and *compiles* the code, it can complain all
it wants (about anything).
You're free to disregard them if you please, and accept the consequences.
No "language police" will come knocking at midnight. (Unless I find
their phone number.) ;-)
As I've said before, I have no problem with you reminding us that
this or that is non-portable. That's fine. But insisting that a
person "can't" do something that they "can" just doesn't fly.
That a compiler generates a "warning" doesn't signify "can't".
When a compiler means "can't", it says "Error" and refuses to compile.
If it compiles, then it "can". (Might not do what you think, but
that's what programming on the edge is all about.) ;->

Re:Re: Any help on why?

Wayne A. King wrote:
On Thu, 08 Jul 2004 18:58:29 -0700, Bob Gonder wrote:

Now that I have demonstrated that some compilers *do* actually parse
the format string being passed to the printf function
Umm, yes, they do seem to do that, but they don't *act* on that
information (other than to complain).
it's time to consider
the possible effects of mismatches. The compiler writers are under *no*
obligation to pass on code to be compiled which it has detected violates
some syntactical rule.
I would beg to differ on that. The rule is
printf( const char*, ... )
There's no syntax rules there that _any_ type argument can break.
They may handle it in *any* way they deem most
appropriate. Some possible scenarios, given a mismatch between format
specifiers and arguments being passed:

(1) Pass on both the format string and the variable arguments exactly as
written in the source. This *may* be the most attractive option, as it entails
the least amount of programming for the compiler writers.
And has the positive aspect of being conforming.
It has the down side of resulting in potentially confusing and unpredictable consequences
when the program is run.
That is the programmer's responsability.
I understand that it is currently non-PC to insist on anyone accepting
responsability for anything, but....
(2) Silently alter the format string before passing it on to the RTL function,
How is it going to alter a const string and still be conforming?
And, if the format is "hidden" to the compiler, then what?
(3) Silently cast the mismatched variable argument so that it matches the type
specifier in the format string. e.g. - If %f is encountered with a correspond-
ing integer variable, supply an implicit typecast for the variable:
(float) intvar
This approach would be limited by any language constraints on casting
between different types.
If the format is "hidden" to the compiler, then what?
And, if it doesn't "convert" variables for hidden formats, how can it
for visible formats, and remain conforming?
e.g. - Given this:

int myint = 44;
float myflt = 1.5;
printf("%f\t%d\n", myint, myflt); // vars don't match type specifiers

issue warnings and then dynamically change to this:

printf("%f\t%d\n", (float)myint, (int)myflt); // now they do

This would give much more predictable and easily interpreted results
when run, albeit the order of appearance of the output fields may seem
switched from what the programmer expected or intended.
I would still dispute the legality of that.
Consider that in this code
char format[]="%f%f%Lx";
extern char format[];
printf( format, float1,float2,double1);
The compiler can not see "format".
It must assume it is correct.
If it "changes" the second printf, creating differing output, then how
can you say it's conforming?

While *most* (even all) compilers currently available *may* adopt
approach (1) above, there is *no* guarantee that they do or that
future compilers or future versions of current compilers will use the
same method.
There's also no guarantee that the sun will continue to shine...
Considering those "other" methods are non-conforming, I'd hope the
"guarantee" is solid.
All bets are off when you have source code which
transgresses the specified syntax or grammar of the language or
compiler-specific language extensions.
Correct. But again, there is nothing wrong with either syntax nor
grammar in these examples.
Proceed with (great) caution
when making assumptions about compiler behavior in such cases,
and don't generalize from empirical results obtained in specific
Sizes of variables,
Placement of variables on the stack (packing)
Supported conversions
All of these are compiler/target dependant.
Any of them may change when you change compiler or target.
You use the combinations that work for your compiler/target.
It is a hack.
It is allowed. (by C if not by managment)

Re:Re: Any help on why?

Wayne A. King wrote:
While Borland and
Microsoft C/C++ apparently both follow the IEEE spec, Microsoft BASIC
uses a different format (Microsoft Binary - MSB).
Not too surprising, as the modern guys use the ubiquitous FPU while
MSBasic was wriiten before there were any FPUs in existance (MSBasic
began on the 8080 in the late '70s). No, wait a sec, there might have
been something from MosTech, or some such, but pricey, and only in
highest-end number-crunching workstations.