the loop control

K

Keith Thompson

James Kuyper said:
A compiler that warns whenever a loop condition can never be false would
annoy those people who rely upon the while(1) idiom.

Yes, such compilers exist, and yes, they're annoying.
 
P

Phil Carmody

compare

int fc(int len, struct foo *fob, struct bar *bab)
{
...

for (j = 0; j < len; j++)
{
fob[j].a = bab[j].x;
fob[j].b = bab[j].y + 1;
fob[j].c = bab[j].d;
}

...
}

versus

int fd(int len, struct foo *fob, struct bar *bab)
{
...

for (j = 0; j < len; j++)
{
fop = &fob[j];
bap = &bab[j];
fop->a = bap->x;
fop->b = bap->y + 1;
fop->c = bap->d;
}

...
}

The first one looks like Pascal or Fortran. The second one looks more
like C.

An absurd assertion.
It says 'i don't need the array base and index, just give me
the pointer'.

Since when has C been about ditching indices and arrays? Not in any version
of the language I've ever used. Then again, my use only dates back to 1989.

Phil
 
P

Phil Carmody

James Kuyper said:
"true" => "false".

A compiler that warns whenever a loop condition can never be false would
annoy those people who rely upon the while(1) idiom.

I see a clear subset relationship separating constant expressions and
expressions that always have the same value. The compiler _must_ be
able to distinguish the two, so it seems a shame if it's unable to
make use of that distinction in situations like these.

Phil
 
J

Jorgen Grahn

I don't like that "feature" of C.

It's harder to debug, it's harder to maintain.

I found your example upthread hard to understand /because/ it didn't
take advantage -- too many (non-const) variables with similar-looking
names.

If you don't intend to modify your parameters, at least make them const.

/Jorgen
 
I

Ike Naar

Needing to use something more than once, where use modifies that
thing, clearly requires you to maintain an unmodified copy of the
original.

If a local variable is needed to maintain an unmodified copy of the original,
one might as well leave the original parameter unmodified,
and use a local variable as the entity-to-be-modified.
This is (arguably) cleaner, because the meaning of the parameter
does not change during the process.
Needing to use something once, where use modifies that thing, clearly
does not require you to maintain an unmodified copy of the original.

If you are unable to see the difference between these cases, then you
will clearly be hampered when it comes to chosing appropriate coding
techniques. That, however, is your problem, and not the language's
problem.

If somebody has a different view, it's not necessarily because they
are "unable" to see things your way.
 
J

James Kuyper

If a local variable is needed to maintain an unmodified copy of the original,
one might as well leave the original parameter unmodified,
and use a local variable as the entity-to-be-modified.
This is (arguably) cleaner, because the meaning of the parameter
does not change during the process.

That depends entirely upon how you interpret the meaning of the
parameter. Like any variable, if it's value varies, it's meaning must be
something that can be said to remain unchanged despite the fact that the
value has changed.

The meaning of 'count' and 'point' do not change anywhere within the
following code:

void zero_array(
int *point,
int count
){
while(count-- > 0)
*point = 0;
}

At the start of the function, and at the end of each pass through the
loop, 'count' contains the number of elements that still need to be
zeroed, and if that count is non-zero, then 'point' points at the first
of the elements that still need to be zeroed.
If somebody has a different view, it's not necessarily because they
are "unable" to see things your way.

He's not addressing your failure to agree with him, but rather your
claim that the alternative approach leads to confusion. He's saying, in
essence, that any person who would be confused for that reason still has
a lot to learn about programming.
 
K

Kaz Kylheku

That depends entirely upon how you interpret the meaning of the
parameter. Like any variable, if it's value varies, it's meaning must be
something that can be said to remain unchanged despite the fact that the
value has changed.

The meaning of 'count' and 'point' do not change anywhere within the
following code:

void zero_array(
int *point,
int count
){
while(count-- > 0)
*point = 0;

*point++ = 0;
}

At the start of the function, and at the end of each pass through the
loop, 'count' contains the number of elements that still need to be
zeroed, and if that count is non-zero, then 'point' points at the first
of the elements that still need to be zeroed.

I.e. it's an iterative rewrite of the tail recursion:

void zero_array(int *point, int count)
{
if (count > 0) {
*point = 0;
zero_array(point + 1, count - 1);
}
}

This local modification of arguments lets us write certain cases
of obvious recursion iteratively. Just assign the different values to the
parameters and loop.
 
I

Ike Naar

That depends entirely upon how you interpret the meaning of the
parameter. Like any variable, if it's value varies, it's meaning must be
something that can be said to remain unchanged despite the fact that the
value has changed.

The meaning of 'count' and 'point' do not change anywhere within the
following code:

void zero_array(
int *point,
int count
){
while(count-- > 0)
*point = 0;
}

At the start of the function, and at the end of each pass through the
loop, 'count' contains the number of elements that still need to be
zeroed, and if that count is non-zero, then 'point' points at the first
of the elements that still need to be zeroed.

This is not an example where one needs a local variable to maintain
an unmodified copy of the original. And actually the meanings of
count and point *do* change, initially they describe the entire
set of points, and later on they describe the set of points
yet-to-be-processed. In this simple example the difference is
futile, because there is no reason to keep the entire set for
later reference. Looking at a slightly more complicated example
that *does* need local variables:

void froboz_array(int *point, int count)
{
int *all_point = point;
int all_count = count;
while (count-- > 0)
fro(*point++);

point = all_point;
count = all_count;
while (count-- > 0)
boz(*point++);
}

some might prefer a variant that is almost identical,
but has the advantage that it does not modify the parameters:

void froboz_array(int *all_point, int all_count)
{
int *point = all_point;
int count = all_count;
while (count-- > 0)
fro(*point++);

point = all_point;
count = all_count;
while (count-- > 0)
boz(*point++);
}

In the simpler example that you showed (only one sweep
across the point array), one can make a tradeoff between two
desirable objectives:
a) keeping the parameters constant
b) minimizing the number of local variables

I think a) vs. b) is a honest tradeoff, and not a dumb vs. smart question.
He's not addressing your failure to agree with him, but rather your
claim that the alternative approach leads to confusion. He's saying, in
essence, that any person who would be confused for that reason still has
a lot to learn about programming.

Phil was talking to Joe; I am not Joe.
 
J

James Kuyper

Correction:
*point++ = 0;
This is not an example where one needs a local variable to maintain
an unmodified copy of the original.

Well, true - that was precisely my point. As a result:
In the simpler example that you showed (only one sweep
across the point array), one can make a tradeoff between two
desirable objectives:
a) keeping the parameters constant
b) minimizing the number of local variables

I think a) vs. b) is a honest tradeoff, and not a dumb vs. smart question.

I'm in perfect agreement with that.

....
Phil was talking to Joe; I am not Joe.

Sorry - I sometimes loose track of who's saying what, paying attention
only to the points that they are arguing. Your points seemed similar to
Joe's. Replace "your" with "Joe's" in the preceding paragraph.
 
J

James Kuyper

....
... And actually the meanings of
count and point *do* change, initially they describe the entire
set of points, and later on they describe the set of points
yet-to-be-processed.

No, those variables always describe the set of points to be processed;
the fact that this is the entire set at the beginning of the routine is
just a special case. It's not necessarily true even then:

int array[20];

zero_array(array+5, 10);
 
N

Nick Keighley

It doesn't matter.  It's there.  If you write code which acts like it's
not, it sends up big red flags for any future maintainer of your code.

not if I'm the maintainer
 
S

Seebs

Yes, such compilers exist, and yes, they're annoying.

A sufficiently clever compiler (hah) can distinguish between "a test
which is always true" and "a literal number which is always true".

-s
 
S

Seebs

not if I'm the maintainer

I have ended up maintaining a lot of code that was written by someone
who did not anticipate the possibility of other people touching the code.
I have, in my own code, gradually learned to write with the intent that
it should not require you to be me to maintain it or have a good sense for
what it's doing and why.

-s
 
J

Joe keane

Joe writes this code:

...
val = *frp++;
HAIRY_MACRO(val, qux);
...

It works fine and everyone's happy.

Bob comes along and says 'maybe we don't need "val", maybe it is used
only once', and he finds that it is.

He makes this change:

...
HAIRY_MACRO(*frp++, qux);
...

The change works fine, so no one notices.

Bob's like 'i'm a genius!'.

....

A year later, Jim is working on a big new project.

He changes:

#define HAIRY_MACRO(X, Y) \
(((X) & 0x8000) == 0 ? BIGMAC1(Y) : BIGMAC2(Y))

to:

#define HAIRY_MACRO(X, Y) \
(((X) & 0x8000) == 0 ? BIGMAC1(Y) : \
((X) & 0x4000) == 0 ? BIGMAC2(Y) : BIGMAC3(Y))

We know the second bit is never set in the old code, so the only
possible problem is a bug in BIGMAC3.

The new code works, mostly... Worst yet, the old code works, mostly...
There is some glitch that no one can track down.

Jim invites his friends to look over the macros, and related functions,
but they find nothing wrong.

Jim spends a few hours debugging his favorite test program, trying to
pinpoint the problem. Then he gets it.

Jim's like 'oh! some a---hole put a postincrement in the macro
argument! who does that?!'.

[OBTW Bob is no longer with the company]
 
I

Ian Collins

Joe writes this code:

...
val = *frp++;
HAIRY_MACRO(val, qux);
...

It works fine and everyone's happy.

Bob comes along and says 'maybe we don't need "val", maybe it is used
only once', and he finds that it is.

He makes this change:

...
HAIRY_MACRO(*frp++, qux);
...

The change works fine, so no one notices.

Bob's like 'i'm a genius!'.

....

A year later, Jim is working on a big new project.

He changes:

#define HAIRY_MACRO(X, Y) \
(((X)& 0x8000) == 0 ? BIGMAC1(Y) : BIGMAC2(Y))

to:

#define HAIRY_MACRO(X, Y) \
(((X)& 0x8000) == 0 ? BIGMAC1(Y) : \
((X)& 0x4000) == 0 ? BIGMAC2(Y) : BIGMAC3(Y))

Reason number 248 for not using function-like macros.
 
J

Joe keane

Do you like the follwing example better?

int f3(int len, struct foo *fob)
{
...

#ifdef DEBUG
printf("f3 before\n");
printf("[ ");
ct = len;
fop = fob;
while (--ct >= 0)
{
printf("<%d,%d> ", fop->e, fop->f);
fop++;
}
printf("]\n");
#endif

ct = len;
fop = fob;
while (--ct >= 0)
{
fop->alg |= FOO_ALG_ZYX;
gromlyana(fop);
fop->erg &= ~FOO_ERG_BAZ;
fop++;
}

#ifdef DEBUG
printf("f3 after\n");
printf("[ ");
ct = len;
fop = fob;
while (--ct >= 0)
{
printf("<%d,%d> ", fop->e, fop->f);
fop++;
}
printf("]\n");
#endif

...
}
 
N

Nick Keighley

I have ended up maintaining a lot of code that was written by someone
who did not anticipate the possibility of other people touching the code.
I have, in my own code, gradually learned to write with the intent that
it should not require you to be me to maintain it or have a good sense for
what it's doing and why.

oh I don't write obfuscated code (not intentionally anyway) but I
expect coding style to vary and don't get all bent out of shape over
minor style issues. And I think this is one of them. We don't pay a
penny for each variable usd so i don't think the occaisional,
technically, redundant one is automatically a "red flag" or a "code
smell".
 
8

88888 Dihedral

Joe keaneæ–¼ 2011å¹´10月30日星期日UTC+8上åˆ5時38分28秒寫é“:
Which is better?

int fa(int fool, struct foo *foob)
{
int ct;
struct foo *foop;

...

ct = fool;
foop = foob;

while (--ct >= 0)
{
...(foop)
foop++;
}
This is not useful for slow jump with JZ but no JNZ in ct.
if ct==1, --ct>=0 is TRUE.
 

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

No members online now.

Forum statistics

Threads
474,085
Messages
2,570,597
Members
47,218
Latest member
GracieDebo

Latest Threads

Top