csrobe@ICASE.EDU (Charles S. [Chip] Roberson) (03/21/89)
Greetings fellow psycho-programmers! Well, I just learned a lesson today
and I thought I pass it on to any others who haven't learned this, yet.
It only blew a day and a half of my time.
Below is a pretty slick fprintf() statement from some of the UUPC/ST code
munged enough to simplify this example. Looking at it, I was sure that it
should work. I mean, why not? It's pretty straight forward -- pass
fprintf() 3 arguments: stderr, the appropriate format string, and finally
the desired value to print. Then once the value is passed, increment.
Right? Wrong!
--------------------------------------------------------------------------
#include <stdio.h>
#include <ctype.h>
unsigned char charlie = 'I';
void oops() {
unsigned char *cp;
cp = charlie;
fprintf( stderr, isprint(*cp)? "[%c]":"[%02x]", *cp++ );
}
--------------------------------------------------------------------------
MWC passes parameters in reverse order. That means that the "*cp++"
gets evaluated BEFORE the "isprint(*cp)..." stuff. So, everytime
fprintf will get passed the approprate character, but isprint is going
to look at whatever is in the next data location. [In my case that was
usually garbage in an unused portion of a character buffer!]
For those of you who need to see the nasty stuff, here is a segment of the
MWC assembly code. It should explain everything. Especially, note the
lines that I marked with an '*'. [Thanks to MWC's db pgm!]
--------------------------------------------------------------------------
movea.l -8(a6), a0 ;; move *cp into a0 for passing to fprintf()
* addq.l $1, -8(a6) ;; increment cp as specified in fprintf arg
clr.w d0 ;; clean out cruft in d0
move.b (a0), d0 ;; load value destined for passing to fprintf()
move d0, -(a7) ;; push it on the stack
* movea.l -8(a6), a0 ;; move *cp into a0 for isprint() check
clr.w d0 ;; clean out some more cruft
move.b (a0), d0 ;; stash *cp in d0 for testing
swap.w d0 ;; toss it into the upper word
clr.w d0 ;; clean out the lower word
swap.w d0 ;; move *cp value back into lower word
movea.l d0, a0 ;; prep for isprint()
adda.l $_ctype_+1, a0 ;; mung it a little
move.b (a0), d0 ;; mung it some more
ext.w d0 ;; ok, it's munged
andi $171, d0 ;; does it map to printable set?
beq.s L10006 ;; nope, go print it in hex
pea L37 ;; yes, use character format in fprintf
bra.s L10007 ;; go finish pushing args
L10006:
pea L38 ;; use, hexidecimal format in fprintf
L10007:
pea _stderr_ ;; finally, pass 1st arg to fprintf
jsr fprintf_ ;; go do it!
--------------------------------------------------------------------------
The moral of this story? Well, as my Theory of Prog. Lang prof would
say, "That's just another insecurity of C!"; to which he would add
"Ada is a much better language." Well, I guess I would say
"Hey! Be careful out there."
Seriously, this is one side-effect that I hadn't thought about. How
do other ST compiler's pass their parameters? So much for portability,
eh? Does anybody know how the dpANS standard would affect this, if at
all? I can't seem to remember anything in it that would.
oh well.
cheers,
-c
rob@kaa.eng.ohio-state.edu (Rob Carriere) (03/21/89)
In article <8903202254.AA00303@icase.edu> csrobe@ICASE.EDU (Charles S. [Chip] Roberson) writes: [...] > fprintf( stderr, isprint(*cp)? "[%c]":"[%02x]", *cp++ ); [doesn't work] >The moral of this story? Well, as my Theory of Prog. Lang prof would >say, "That's just another insecurity of C!"; to which he would add >"Ada is a much better language." Well, I guess I would say >"Hey! Be careful out there." He may say that, but it'd be false. Order of parameter evaluation is *undefined* in both C and ADA. In other words, can do in ADA, no prob! The *real* moral of the story is: Thou shalst not rely on the order in which side-effects in the parameters thou art passing to thy function shall take place, for that order hath not been standardized. >Seriously, this is one side-effect that I hadn't thought about. How >do other ST compiler's pass their parameters? So much for portability, >eh? Does anybody know how the dpANS standard would affect this, if at >all? I can't seem to remember anything in it that would. No, twon't. No problem though, just say no to side-effects in function arguments. SR
bam@rudedog.SGI.COM (Brian McClendon) (03/21/89)
In article <8903202254.AA00303@icase.edu>, csrobe@ICASE.EDU (Charles S. [Chip] Roberson) writes: [stuff deleted] > fprintf( stderr, isprint(*cp)? "[%c]":"[%02x]", *cp++ ); [stuff deleted] > The moral of this story? Well, as my Theory of Prog. Lang prof would > say, "That's just another insecurity of C!"; to which he would add > "Ada is a much better language." Well, I guess I would say > "Hey! Be careful out there." > > Seriously, this is one side-effect that I hadn't thought about. How > do other ST compiler's pass their parameters? So much for portability, > eh? Does anybody know how the dpANS standard would affect this, if at > all? I can't seem to remember anything in it that would. > This isn't a problem with MWC. There is no definition of what order arguments are "executed" in the C bible (K&R) that I could find. While the fprintf(...) is slick code, it is also machine-dependent and VERY un-portable (I think the Clipper (RISC) compiler also executes parameters backwards). -- - brian -------------------------------------------------------------------------- Brian McClendon bam@rudedog.SGI.COM ...!uunet!sgi!rudedog!bam 415-335-1110 --------------------------------------------------------------------------
root@yale.UUCP (Celray Stalk) (03/22/89)
In article <8903202254.AA00303@icase.edu> csrobe@ICASE.EDU (Charles S. [Chip] Roberson) writes: >Greetings fellow psycho-programmers! Well, I just learned a lesson today >and I thought I pass it on to any others who haven't learned this, yet. >It only blew a day and a half of my time. > >Below is a pretty slick fprintf() statement from some of the UUPC/ST code >munged enough to simplify this example. Looking at it, I was sure that it >should work. I mean, why not? It's pretty straight forward -- pass >fprintf() 3 arguments: stderr, the appropriate format string, and finally >the desired value to print. Then once the value is passed, increment. >Right? Wrong! > >-------------------------------------------------------------------------- > fprintf( stderr, isprint(*cp)? "[%c]":"[%02x]", *cp++ ); >-------------------------------------------------------------------------- > >The moral of this story? Well, as my Theory of Prog. Lang prof would >say, "That's just another insecurity of C!"; to which he would add >"Ada is a much better language." Well, I guess I would say >"Hey! Be careful out there." > >Seriously, this is one side-effect that I hadn't thought about. How >do other ST compiler's pass their parameters? So much for portability, >eh? Does anybody know how the dpANS standard would affect this, if at >all? I can't seem to remember anything in it that would. This is a valuable lesson for people to learn: don't write code that depends on undefined features of a language. Order of evaluation of operands in expressions is not defined in many languages, Ada and C included (excecpt for logical operators and the "," operator in C). This means that code such as the above that depends on order of evaluation is not correct and certainly not likely to be portable. One might want to know out of curiousity in what order other ST comiplers pass their parameters, but you should never write code that depends on this knowledge since it will not be correct according to the language definition and will not be portable. Operators to be careful of in C include ++, --, =, etc., but also function calls. For example, z = f(x) + y; can have unpredictable results if f happens to change y. Even with assignment statements, you can't assume the right hand side will be completely evaluated before the left, e.g. { x=1; a[x++] = x + 3; } might cause either 4 or 5 to be stored into a[1], depending on when your compiler chooses to evaluate the subscript. ================================================== | Michael Fischer | | Arpanet: <fischer-michael@cs.yale.edu> | | Bitnet: <fischer-michael@yalecs.bitnet> | | UUCP: <fischer-michael@yale.UUCP> | ==================================================
scott@applix.UUCP (Scott Evernden) (03/22/89)
In article <8903202254.AA00303@icase.edu> csrobe@ICASE.EDU (Charles S. [Chip] Roberson) writes: >Greetings fellow psycho-programmers! Well, I just learned a lesson today > fprintf( stderr, isprint(*cp)? "[%c]":"[%02x]", *cp++ ); >} >-------------------------------------------------------------------------- > >MWC passes parameters in reverse order. That means that the "*cp++" >gets evaluated BEFORE the "isprint(*cp)..." stuff. Reverse compared to to what? There is no defined "order" for arguments to be evaluated or pushed onto the stack (assuming you're using a stack, which you shouldn't assume!). >The moral of this story? Well, as my Theory of Prog. Lang prof would >say, "That's just another insecurity of C!"; >... So much for portability, C is quite portable if you don't try to do cute things like the above which are defined to be non-portable. This isn't a "gotcha", but something you simply need to learn. What would you expect from, say: *ptr++ = ptr[5] + 2; Well, you'd be screwing yourself if you wrote this code, for the same reasons. There are plenty of similar examples. -scott
greg@bilbo (Greg Wageman) (03/24/89)
In article <8903202254.AA00303@icase.edu> csrobe@ICASE.EDU (Charles S. [Chip] Roberson) writes: >Greetings fellow psycho-programmers! Well, I just learned a lesson today >and I thought I pass it on to any others who haven't learned this, yet. >It only blew a day and a half of my time. > > fprintf( stderr, isprint(*cp)? "[%c]":"[%02x]", *cp++ ); > >MWC passes parameters in reverse order. That means that the "*cp++" >gets evaluated BEFORE the "isprint(*cp)..." stuff. So, everytime >fprintf will get passed the approprate character, but isprint is going >to look at whatever is in the next data location. [In my case that was >usually garbage in an unused portion of a character buffer!] > >Seriously, this is one side-effect that I hadn't thought about. How >do other ST compiler's pass their parameters? So much for portability, >eh? Does anybody know how the dpANS standard would affect this, if at >all? I can't seem to remember anything in it that would. "The C Programming Language", Kernighan & Ritchie, Second Edition, page 53, reproduced verbatim: "Similarly, the order in which function arguments are evaluated is not specified, so the statement printf("%d %d\n", ++n, power(2, n)); /* WRONG */ can produce different results with different compilers, depending on whether n is incremented before power is called. The solution, of course, is to write ++n; printf("%d %d\n", n, power(2, n));" It's a shame you wasted so much time on a well-documented aspect of the C language. I guess you didn't RTFM. :-) What gets *me* ticked off is when I *do* RTFM, and TFM is *wrong*. Longish .signature follows. Skip now, or don't complain! Greg Wageman DOMAIN: greg@sj.ate.slb.com Schlumberger Technologies UUCP: ...!uunet!sjsca4!greg 1601 Technology Drive BIX: gwage San Jose, CA 95110-1397 CIS: 74016,352 (408) 437-5198 GEnie: G.WAGEMAN ------------------ Opinions expressed herein are solely the responsibility of the author. (And the author wouldn't have it any other way.)
csrobe@ICASE.EDU (Charles S. [Chip] Roberson) (03/28/89)
In Info-Atari16 Digest Friday, March 24, 1989 Volume 89 : Issue 108 Scott Evernden (mailrus!ulowell!m2c!applix!scott@tut.cis.ohio-state.edu) writes: >In article <8903202254.AA00303@icase.edu> csrobe@ICASE.EDU (Charles S. [Chip] Roberson) writes: >>Greetings fellow psycho-programmers! Well, I just learned a lesson today > >> fprintf( stderr, isprint(*cp)? "[%c]":"[%02x]", *cp++ ); >>} >>-------------------------------------------------------------------------- >> >>MWC passes parameters in reverse order. That means that the "*cp++" >>gets evaluated BEFORE the "isprint(*cp)..." stuff. > > Reverse compared to to what? There is no defined "order" for arguments > to be evaluated or pushed onto the stack (assuming you're using a stack, > which you shouldn't assume!). > Apparently, I stepped on a few people's toes with the wording I used in my message. First, I have to say my choice of wording above was incorrect. The phrase "reverse order" was just a quickly (and apparently poorly) chosen one. Let me state, that I am well aware that order of parameter evaluation IS undefined in C. What I wanted to emphasize was that after programming in C for over 5 years (with bouncing back to Pascal, Ada, LISP, and Prolog) I still got caught by interaction. I guess since I would have never thought of doing that trick, I didn't see the consequences. Second, The IMPORTANT thing to remember is, when the same variable appears twice on the command line (or ever in a MACRO) be careful with operators such as '--' and '++'. Ok, here is what the MWC manual says and what I should have said: "Programs that depend upon specific details of these calling conventions may not be portable to other processors and other C compilers." "In general, Mark Williams C pushes arguments from RIGHT to LEFT." >>The moral of this story? Well, as my Theory of Prog. Lang prof would >>say, "That's just another insecurity of C!"; > >>... So much for portability, > > C is quite portable if you don't try to do cute things like the above > which are defined to be non-portable. This isn't a "gotcha", but > something you simply need to learn. What would you expect from, say: > > *ptr++ = ptr[5] + 2; > > Well, you'd be screwing yourself if you wrote this code, for the > same reasons. There are plenty of similar examples. Again, I had several knights rise to the defense of C, so I must assume that my use of the word "insecurity" offended more people. This is a standard term used when talking about languages. EVERY LANGUAGE HAS INSECURITIES -- areas of vulnerability. It does not make the language bad or worthless -- it just means that there are ways of doing things that produce results that aren't obvious to the programmer and are not readily apparent from the syntax and semantics. This is from Thomas Plum's _Notes_on_the_Draft_C_Standard_: "The <stdarg.h> header provides a portable method for accessing variable-length argument lists. A function that uses this method can access its named parameters directly, just by using their names as usual. But it can also access further, un-named argument values which were passed by the calling function." This does not avert the above insecurity, but does put in place a mechanism for providing portable access to var-args -- which RIGHT->LEFT parameter passing facilitates. Anyway, it was not my intention to offend C but to point out something that I felt could catch anyone. [I had even made a sample three line program to test the code fragment in isolation, and left off the '++' because I felt it wasn't important. A temporary lapse of reason, I suppose.] For anyone who cares, C is my favorite language. >-scott >-c |Chip Roberson ARPANET: csrobe@cs.wm.edu | |Dept of Comp. Sci. csrobe@icase.edu | |College of William and Mary BITNET: #csrobe@wmmvs.bitnet | |Williamsburg, VA 23185 UUCP: ...!uunet!pyrdc!gmu90x!wmcs!csrobe| [ The Animal-Rights mailing list is now on the air. ] [ Send mail to Animal-Rights-Request@cs.odu.edu to subscribe. -c ]
andrew@uxm.sm.ucl.ac.UK (Andrew Dawson) (03/30/89)
In issue 106, Charles S. [Chip] Roberson <csrobe@icase.edu>, writes: (extracts only quoted below) > Below is a pretty slick fprintf() statement from some of the UUPC/ST code > I was sure that it should work. It's pretty straight forward -- pass > fprintf() 3 arguments: stderr, the appropriate format string, and finally > the desired value to print. Then once the value is passed, increment. > fprintf( stderr, isprint(*cp)? "[%c]":"[%02x]", *cp++ ); > MWC passes parameters in reverse order. That means that the "*cp++" > gets evaluated BEFORE the "isprint(*cp)..." stuff ..... > .... How do other ST compiler's pass their parameters? So much for > portability... Does anybody know how the dpANS standard would affect this, > if at all? I can't seem to remember anything in it that would. K&R 2nd edition states: "The order in which function arguments are evaluated is not specified, so the statement printf("%d %d\n", ++n, power(2,n)); /* WRONG */ can produce different results with different compilers, depending on whether n is incremented before power is called. The solution is to write ++n; printf("%d %d\n", n, power(2,n)); " Most compilers I've used on the ST seem to evaluate right to left. Note in particular, that the system interfaces gemdos/bios/xbios expect to receive parameters this way, so any compiler that works the other way would have to incorporate argument reversing code for these functions. Andrew Dawson School of Medicine Computer Unit University College London