++i vs i++

R

Richard Bos

Derrick Coetzee said:
Keep in mind that not every programmer who maintains your code may be an
expert, or be an expert who is particular lucid at the time (under high
stress, low on sleep, hangover, etc.)

If this "expert" is so very low on sleep that he isn't awake enough any
more to understand ++ and --, that expert has no business shoving his
grubby paws into _my_ code. Or anyone else's, for that matter. Let him
get a decent night's sleep and come back when he's capable of doing a
decent job again.
This is effectively a local transformation, unlike ++, which can potentially be
transmitted across an arbitrarily long string of tokens, as in this example:

_Transmitted_ across a string of tokens? Whatever do you mean by that?
b = a++ + /* one-million token expression */;

a gets increased. b gets the old value of a, plus that of the
million-token expression. What is there not to understand? What on
_earth_ is "transmitted" here?

Richard
 
R

Richard Bos

Derrick Coetzee said:
If you prefer, here are some alternate translations:
Original:
while(*++p) { ... }
With comma:
while(p++, *p) { ... }
Duplicating increment:
p++;
while(*p) { ...; p++; }

Congratulations. You've just turned a perfectly good, completely
understandable while loop into a rather more complicated and somewhat
less maintainable one, which may under unusual circumstances not even be
completely equivalent.

Richard
 
M

Michael Wojcik

The other baddy is the conditon ? x : y operator. It doesn't mean anything
to someone who doesn't know C (but is maybe a skilled programmer in another
language, and is forced to maintain your C program by circumstance),

Sure. And sometimes we have our plumber do carpentry work, so all of
our wooden framing members are soldered together.

A skilled programmer who is called on to maintain a program in an
unfamiliar language learns that language first. That's a necessary
condition of "skilled programmer".

Conversely, anyone who tries to maintain C code without understanding
the ternary operator is a fool. Trying to write non-trivial C code
that's proof against being maintained by fools is itself a fool's
errand.
++i doesn't have such redeeming features. The form should be banned, because
where it is used for the increment alone then it is completely exchangeable
with the postfix form. Where it is used in a compound expression with the
value taken, it leads to confusion.

Confusion for whom? You seem to be a member of a rather small
minority - just you, perhaps - which finds it confusing. Or do you
have some empirical data to demonstrate otherwise?

--
Michael Wojcik (e-mail address removed)

This is a "rubbering action game," a 2D platformer where you control a
girl equipped with an elastic rope with a fishing hook at the end.
-- review of _Umihara Kawase Shun_ for the Sony Playstation
 
M

Michael Wojcik

Your example shows UB when sp equals STACK_SIZE -1. As I said, use of
pre-increment can be confusing.

Non sequitur. The code displays the same UB without use of the
pre-increment operator (that is, if another increment expression,
such as the one John suggested in the comments, were substituted
for it), and nothing you've posted shows that the pre-increment
operator disguised that fact in any way.

Also, your argument hinges on your contention that stack be declared:
int stack[STACK_SIZE];

while it might just as easily have been declared:
int stack[STACK_SIZE+1];

The latter form - where a manifest constant for "array size" actually
refers to the largest valid index - is not uncommon in C source.


--
Michael Wojcik (e-mail address removed)

Thanatos, thanatos! The labourer, dropping his lever,
Hides a black letter close to his heart and goes,
Thanatos, thanatos, home for the day and for ever. -- George Barker
 
M

Michael Wojcik

As compared to how many post-increment / decrements?

An irrelevant query, since my claim was that the pre-increment and
pre-decrement operators are not, as you claimed, non-idiomatic; and
that they are in fact commonly used. I'd consider 1068 instances
pretty common.

But since you asked: 5028 instances of post-increment and -decrement.
That means the pre- forms represent about 18%, which certainly seems
to me sufficient to label them "common" and "idiomatic".
 
R

Richard Tobin

Michael Wojcik said:
Yet a quick scan through the 4062 C source files on this machine found
1068 instances of pre-increment and pre-decrement operators.

For stack-like applications, post-increment goes naturally with
pre-decrement and vice versa. Having used PDP-11 assembler a few
decades ago, I generally write i++ and --i when there is no other
reason to prefer one to the other.

-- Richard
 
J

Jarno A Wuolijoki

Derrick said:
If you prefer, here are some alternate translations:
Original:
while(*++p) { ... }
[...]
Duplicating increment:
p++;
while(*p) { ...; p++; }

Note that these two forms are *not* necessarily
equivalent. For extra credit: Why not?

I'm not sure why these nits are allowed to continue; it all depends
on what ... is supposed to represent.

"float p=0"
 
S

S.Tobias

Eric Sosman said:
Derrick said:
If you prefer, here are some alternate translations:
Original:
while(*++p) { ... }
[...]
Duplicating increment:
p++;
while(*p) { ...; p++; }
Note that these two forms are *not* necessarily
equivalent. For extra credit: Why not?

Because "..." might contain "continue"?
 
E

Eric Sosman

S.Tobias said:
Eric Sosman said:
Derrick said:
If you prefer, here are some alternate translations:
Original:
while(*++p) { ... }
[...]
Duplicating increment:
p++;
while(*p) { ...; p++; }

Note that these two forms are *not* necessarily
equivalent. For extra credit: Why not?


Because "..." might contain "continue"?

Right you are! In the first form, `continue'
would transfer control to the test of `*++p', while
in the second it would go straight to testing `*p',
without incrementation.
 
D

Derrick Coetzee

Richard said:
_Transmitted_ across a string of tokens? Whatever do you mean by that?

I refer to the distance the token would have to be moved in order for
the lexical order to reflect the execution order. The analogy wasn't
very good, since the same can happen with ->, but my point is that ++
and -- seriously screw with the correspondence between lexical order and
execution order.
 
D

Derrick Coetzee

Richard said:
Congratulations. You've just turned a perfectly good, completely
understandable while loop into a rather more complicated and somewhat
less maintainable one, which may under unusual circumstances not even be
completely equivalent.

I certainly wouldn't prefer that particular translation, because it is
less maintainable (code duplication) and separates the increment from
the condition. I was simply listing alternatives. I actually like the
one using array indexing the most, because it makes it easier to reason
about out-of-bounds conditions and resembles your average linear scan
with a for loop through an array.
 
R

Richard Bos

Derrick Coetzee said:
I refer to the distance the token would have to be moved in order for
the lexical order to reflect the execution order.

Hum. Does the term "sequence point" mean anything to you? What makes you
think that there even _is_ a fixed execution order which would make this
move necessary?

Richard
 
J

John Bode

Derrick Coetzee said:
Keep in mind that not every programmer who maintains your code may be an
expert, or be an expert who is particular lucid at the time (under high
stress, low on sleep, hangover, etc.) To experienced programmers,
assembly language is second nature, but that doesn't mean we prefer it.

Anyone programming in C or C++ for a living learns how ++ and -- work,
and rather quickly. Statements like 'while (*p++) {...}' are
perfectly idiomatic C, which any sufficiently
brain-dam^H^H^H^H^H^H^H^H^H competent code monkey should be able to
understand.

Seriously, this isn't *that* difficult a concept to grasp.
The important thing is that lexical order reflects execution order.

Since when? Given the expression

a() + b() * c()

is there any guarantee that a() is executed before b() or c()? Should
there be?
In this case you can read "p->" as a single conceptual operation with
chooses a member from the object p refers to. The choice of * as a
prefix rather than a postfix operator is arbitrary (although in this
case needed to automatically parse things correctly). This is
effectively a local transformation, unlike ++, which can potentially be
transmitted across an arbitrarily long string of tokens, as in this example:

b = a++ + /* one-million token expression */;

'a++' evaluates to the value of 'a'; that value is applied immediately
as part of the expression (i.e., lexical order reflects execution
order). The *side effect* may be applied immediately, or it may be
deferred to the next sequence point. As far as assigning to b is
concerned, it simply doesn't matter. You just have to make sure that
the token 'a' doesn't appear again before the next sequence point.
 
M

Malcolm

Michael Wojcik said:
Non sequitur. The code displays the same UB without use of the
pre-increment operator (that is, if another increment expression,
such as the one John suggested in the comments, were substituted
for it), and nothing you've posted shows that the pre-increment
operator disguised that fact in any way.
Evidence isn't proof. The fact that a bug appeared in a small fragment
showing use of the pre-increment operator is evidence that using such an
operator caused the problem. However programmers make errors all of the
time. Maybe John Bode had drunk one too many beers before posting, and that
was the reason for the mistake. My own view is that use of pre-increment was
a contributing factor
Also, your argument hinges on your contention that stack be declared:
int stack[STACK_SIZE];

while it might just as easily have been declared:
int stack[STACK_SIZE+1];

The latter form - where a manifest constant for "array size" actually
refers to the largest valid index - is not uncommon in C source.
This is error masking. Some function deep in the gubbins of the program
overflows the stack by one. So instead of getting to the root of the problem
we increment the stack size by one and hope the bug goes away. Often it
works. Other times it piles bug on bug.
STACK_SIZE should be the size of the stack - i.e. the maximum number of
items it can hold. Anything else is sowing confusion.
 
M

Malcolm

Michael Wojcik said:
Confusion for whom? You seem to be a member of a rather small
minority - just you, perhaps - which finds it confusing. Or do you
have some empirical data to demonstrate otherwise?
See elsethread for an example of a bug introduced into example code
supposedly showing how to use the prefix increment operator.
 
R

Richard Bos

'a++' evaluates to the value of 'a'; that value is applied immediately
as part of the expression (i.e., lexical order reflects execution
order). The *side effect* may be applied immediately, or it may be
deferred to the next sequence point. As far as assigning to b is
concerned, it simply doesn't matter. You just have to make sure that
the token 'a' doesn't appear again before the next sequence point.

Not quite. You have to make sure that the _object_ a doesn't appear
again before the next sequence point. If there's a pointer to a, you
can't dereference it in the one million tokens.

Richard
 
J

John Bode

Malcolm said:
Evidence isn't proof. The fact that a bug appeared in a small fragment
showing use of the pre-increment operator is evidence that using such an
operator caused the problem.

Didn't you just say that evidence isn't proof?

This is priceless. The bug had dick all to do with the preincrement
operator; like Michael pointed out, the same UB would have shown up if
I hadn't used the preincrement. The root cause of the bug is that C
arrays are 0-origin (and that I wasn't paying close enough attention).
However programmers make errors all of the time.

Hence why we have QA and maintenance programmers.
Maybe John Bode had drunk one too many beers before posting, and that
was the reason for the mistake.

Oh, would that were true...mmmm, beer...dammit, now I'm thirsty...
My own view is that use of pre-increment was a contributing factor

My view is that a 0-origin array was the primary factor.
Also, your argument hinges on your contention that stack be declared:
int stack[STACK_SIZE];

while it might just as easily have been declared:
int stack[STACK_SIZE+1];

The latter form - where a manifest constant for "array size" actually
refers to the largest valid index - is not uncommon in C source.
This is error masking.

No, it isn't merely "error masking".
Some function deep in the gubbins of the program
overflows the stack by one.

Not if you design your API correctly.
So instead of getting to the root of the problem
we increment the stack size by one and hope the bug goes away. Often it
works. Other times it piles bug on bug.
STACK_SIZE should be the size of the stack - i.e. the maximum number of
items it can hold. Anything else is sowing confusion.

The maximum stack size is a *logical* entity; there's no reason why
the physical implementation has to be the same size. In fact, the way
I wrote it, the array *has* to be STACK_SIZE+1 elements to hold
STACK_SIZE items, because element 0 is never written to.
 
M

Malcolm

John Bode said:
Didn't you just say that evidence isn't proof?

This is priceless. The bug had dick all to do with the preincrement
operator; like Michael pointed out, the same UB would have shown up if
I hadn't used the preincrement. The root cause of the bug is that C
arrays are 0-origin (and that I wasn't paying close enough attention).
Here's the code

added by Malcolm

int stack[STACK_SIZE];
int sp;
if (sp < STACK_SIZE)
{
/*
** The following statement replaces these two statements
**
** sp = sp + 1;
** stack[sp] = value;
*/
stack[++sp] = value;
}
else
{
/* handle overflow */
}
Seems to me quite clearly that the pre-increment is contributing to the
problem. On entry, sp < STACK_SIZE, so writing "stack[sp++] = value;" would
give you the right behaviour, at the price of making sp no longer pointing
to stack top but to the empty space above it.However we all make mistakes, so to conclude that pre-increment is confusing
on the basis of one bug is a bit premature. To conclude that it is not
confusing (evidence isn't proof, therefore evidence that falls short of
proof is evidence against) is a fallacy.
The maximum stack size is a *logical* entity; there's no reason why
the physical implementation has to be the same size. In fact, the way
I wrote it, the array *has* to be STACK_SIZE+1 elements to hold
STACK_SIZE items, because element 0 is never written to.
You're in good company here. No less than the authors of "Numerical Recipies
in C" endorse 1-based arrays. Mathematicians count 1, 2, 3 ..., computers
count 0, 1, 2 ... . However the consensus is that 1-based arrays in C are a
bad idea.
 
J

John Bode

Malcolm said:
John Bode said:
Didn't you just say that evidence isn't proof?

This is priceless. The bug had dick all to do with the preincrement
operator; like Michael pointed out, the same UB would have shown up if
I hadn't used the preincrement. The root cause of the bug is that C
arrays are 0-origin (and that I wasn't paying close enough attention).
Here's the code

added by Malcolm

int stack[STACK_SIZE];
int sp;
if (sp < STACK_SIZE)
{
/*
** The following statement replaces these two statements
**
** sp = sp + 1;
** stack[sp] = value;
*/
stack[++sp] = value;
}
else
{
/* handle overflow */
}
Seems to me quite clearly that the pre-increment is contributing to the
problem.

What if I had written

if (sp < STACK_SIZE)
{
sp = sp + 1;
stack[sp] = value;
}

The pre-increment operator is nowhere in sight, yet the bug is still
there. It seems to me that the problem is in the test (actually, the
problem's in the overall logic; more on that below).
On entry, sp < STACK_SIZE, so writing "stack[sp++] = value;" would
give you the right behaviour, at the price of making sp no longer pointing
to stack top but to the empty space above it.

Which is not what I want sp to do. I want sp to point to the last
thing pushed onto the stack, not the next available slot.

Actually, now that I've thought about it, the *right* solution is to
grow from the top down:

static int sp = STACK_SIZE;

void push (int value)
{
if (sp > 0)
stack[--sp] = value;
else
/* overflow */
}

int pop (void)
{
if (sp < STACK_SIZE)
return stack[sp++];
else
/* underflow */
}

A little cleaner, and all array elements actually get used. Like I
said, I tossed off the original example while waiting for something
else to finish and didn't verify it properly. Bad me. No cookie.
However we all make mistakes, so to conclude that pre-increment is confusing
on the basis of one bug is a bit premature. To conclude that it is not
confusing (evidence isn't proof, therefore evidence that falls short of
proof is evidence against) is a fallacy.
You're in good company here. No less than the authors of "Numerical Recipies
in C" endorse 1-based arrays. Mathematicians count 1, 2, 3 ..., computers
count 0, 1, 2 ... . However the consensus is that 1-based arrays in C are a
bad idea.

I don't *endorse* the idea of 1-based arrays in C. I was merely
pointing out that the bug in my code was due to not taking 0-origin
arrays into account, not because I used a preincrement operator.
Since the bug still exists if I replace the preincrement with a
separate increment statement, I'd say my point is valid.

And my *actual* point was that the physical and logical
representations of a stack didn't have to map exactly.
 
D

Dave Thompson

On 27 Sep 2004 20:17:24 GMT, (e-mail address removed) (Richard Tobin)
wrote:
For stack-like applications, post-increment goes naturally with
pre-decrement and vice versa. Having used PDP-11 assembler a few
decades ago, I generally write i++ and --i when there is no other
reason to prefer one to the other.
For downward-growing stacks as introduced on the -11 and now seemingly
universal. Some other machines, (most?) notably PDP-10, had upward
stacks which of course prefer preinc and postdec.
- David.Thompson1 at worldnet.att.net
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,202
Latest member
MikoOslo

Latest Threads

Top