Post-Incremented C Function Parameters No-Go on Intel?

Moderator
Posts: 3,570
Joined: 2003.06
Post: #1
I just started migrating some of my projects over to Universal Binary and it has not been uneventful. There have been a few problems that have popped up here and there. Usually they are no big deal, like i386 is less forgiving about uninitialized pointers (which I'm usually pretty good about anyway, but everybody makes mistakes from time to time ;-). But a weird one that I don't understand in my skeletal animation code is as follows:

(BTW, these are three different versions of the same thing that I tried while debugging)

This works:

// translate root joint
float x, y, z;

x = animation->animationBuffer[i++];
y = animation->animationBuffer[i++];
z = animation->animationBuffer[i++];
glTranslatef(x, y, z);

And this works:

// translate root joint
glTranslatef(animation->animationBuffer[i], animation->animationBuffer[i + 1], animation->animationBuffer[i + 2]);
i += 3;

But this does not work as I would expect, on i386:

// translate root joint
glTranslatef(animation->animationBuffer[i++], animation->animationBuffer[i++], animation->animationBuffer[i++]);

I am then calling a recursive function to compute all the rest of the joints in the skeletal heirarchy, which does work as expected (animationBuffer is not accessed with post-increments in the recursive function), except the root joint (and hence, the whole skeleton) is translated all funky with the last technique I showed above. I can't tell exactly what is happening, but it *appears* during playback that the root joint is getting something like this:

glTranslatef(x, x, x); [edit] this is actually z, y, x -- see edit below [/edit]

instead of

glTranslatef(x, y, z);

The post increments are still taking effect since the next function call is indeed passing the correct index value of i += 3, but during the call to glTranslatef they don't *appear* to be taking effect before the call as they ought to -- there might be something else going on here though. It works just fine on the PPC, and I've been doing things like that for years (maybe I shouldn't have been?). Does anyone know why this happens for i386?


[edit] After posting this I got an idea to try to see if I could simulate what data is actually getting sent, and here's what I found. This is what it's sending when I use the post-incremented technique:

glTranslatef(animation->animationBuffer[i + 2], animation->animationBuffer[i + 1], animation->animationBuffer[i + 0]);
i += 3;

It's an endian issue! Wow, I still don't get it, but I guess I'm on the trail now...
Quote this message in a reply
sh4ggy87
Unregistered
 
Post: #2
Quote this message in a reply
Moderator
Posts: 3,570
Joined: 2003.06
Post: #3
Interesting. So then it's up to the compiler to decide when it wants to modify the value. However, as I noted in my edit, they *are* being evaluated before being sent, just that they're being sent in reverse order to glTranslatef.

IOW, as I confusingly tried to point out, this:

glTranslatef(animation->animationBuffer[i++], animation->animationBuffer[i++], animation->animationBuffer[i++]);

is behaving the same as this:

glTranslatef(animation->animationBuffer[i + 2], animation->animationBuffer[i + 1], animation->animationBuffer[i + 0]);
i += 3;
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #4
Your example is an example of one of the nono's of C and C++. You never want to assume the order of a side-effect operator in a single expression. This is valid for function calls, mathematical expressions, etc. The compiler evaluates each peice of the expression in the order it feels is the best and fastest, and doesn't take into account the order of operators that have side effects. (pre and post ++ -- and all the operators with =) As a result, the order of the execution of those peices can differ from compiler to compiler, system to system, and even with the same compiler and system if you change expressions to have it decide to do the expressions in a different order and/or change the optimization level.
Quote this message in a reply
Oldtimer
Posts: 832
Joined: 2002.09
Post: #5
I was under the impression that GCC 4.0 does not allow you to write MyFunc (i++); at all? I had an Apple tech tell me, and the Universal Binary Guidelines also say this?
Quote this message in a reply
Moderator
Posts: 3,570
Joined: 2003.06
Post: #6
> I was under the impression that GCC 4.0 does not allow you to write MyFunc (i++); at all

Well, whatever version of GCC is under the hood of Xcode 2.2 certainly does, because I'm looking at it right now, and there were no warnings.

> Your example is an example of one of the nono's of C and C++.

Yeah, I guess I shouldn't have assumed that. I just read over that FAQ a few more times, which sh4ggy87 linked. I've seen this discussion come up a few times over the years in different places, but whenever I've seen the examples I just shrugged them off because the behavior has always been predictable for whatever environment on the Mac I've used -- until now that is.

Regardless of *why* it is behaving that way, I will not be using pre-increment/decrement in function calls ever (which I don't do anyway), and post-increment/decrement only if it is a single and don't care when it is evaluated. I don't even normally do things the way my example is, but it definitely bit me this time!

Like the FAQ mentioned, I learned this habit through one of the books I originally learned C through. Tsk, tsk... I've programmed many hundreds of thousands of lines of C code over the years, and this just goes to show that there are ALWAYS new lessons to learn.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #7
Heh, this is one of the things that was hammered into our heads in school, to make sure we don't try to rely on the order for those kinds of expressions. Java does guarantee left-to-right ordering of that, though. Still, I would normally try to not do such a thing anyways to be on the safe side, at least for readability.
Quote this message in a reply
Member
Posts: 469
Joined: 2002.10
Post: #8
how about this:
Code:
float* joint = animation->animationBuffer + i;
glTranslatef(joint[0], joint[1], joint[2]);
i += 3;

[edit:] better yet, you can just hold onto your joint pointer and forget about using i altogether?

---Kelvin--
15.4" MacBook Pro revA
1.83GHz/2GB/250GB
Quote this message in a reply
Moderator
Posts: 3,570
Joined: 2003.06
Post: #9
Thanks for the suggestion. It's true, I don't need to use i at all. In fact, my recursed funtion below it doesn't (don't ask why I mixed techniques ;-). I could just as well go joint->x, joint->y, joint->z. I debugged past the data access issue within minutes and got it fixed, but the thing that threw me for a spin was that I didn't understand why glTranslatef(data[i++], data[i++], data[i++]) didn't work. I still don't know why they were sent backwards by the compiler, but as the discussion has shown, that doesn't matter anyway because I shouldn't have done more than one i++ in the parameter list and expected it to predictable in the first place. And to drag the issue further, Fenris suggested that he heard you couldn't use even one post increment in a function call at all! Ahh! Now my confidence is shaken and my whole world is falling apart in front of me. LOL
Quote this message in a reply
Member
Posts: 198
Joined: 2005.01
Post: #10
AnotherJake Wrote:Regardless of *why* it is behaving that way, I will not be using pre-increment/decrement in function calls ever (which I don't do anyway), and post-increment/decrement only if it is a single and don't care when it is evaluated. I don't even normally do things the way my example is, but it definitely bit me this time!

I saw this topic and thought "oh boy! an arcane C question I can actually answer..." Smile Ah well.

There's nothing wrong with using those operators during a function call or in any other expression. It does exactly what the spec says: i++ increments i and takes the value beforehand; ++i increments i and takes the value afterwards. You just can never rely on the order in which these types of things will happen within one statement. e.g., this is fine:

int i, j, k;
foo(i++, j++, k++);

While things like this probably are not, even if they don't involve function calls Smile

int * i;
*i++ = *i++;

Cryptic Allusion Games / Cryptic Allusion, LLC
http://www.cagames.com/
Quote this message in a reply
Moderator
Posts: 3,570
Joined: 2003.06
Post: #11
I wouldn't be afraid of this:

int i, j, k;
foo(i++, j++, k++);

But this, is apparently a bad idea:

int i;
foo(i++, i++);

The reasoning, as I understand it, is that i is only guaranteed to be incremented (however many times) after the statement, foo(i++, i++), is considered finished. Whether "finished" is after the call to foo or not is up to the compiler as I currently understand it.
Quote this message in a reply
Member
Posts: 198
Joined: 2005.01
Post: #12
Yeah, I agree. I think I maybe misread one of your sentences. Smile

Cryptic Allusion Games / Cryptic Allusion, LLC
http://www.cagames.com/
Quote this message in a reply
omgomghilol
Unregistered
 
Post: #13
Using operators with side effects isn't the problem (it's well defined and allowed by the standard). The problem is using two operators with side effects on the same variable, or doing something like f(i, i++) since the order of evaluation of function arguments is not defined by the standard.
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  unescape, post and php AcidZombie24 3 3,713 Jul 31, 2008 01:03 PM
Last Post: ThemsAllTook