the loop control

J

Joe keane

Which is better?

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

...

ct = fool;
foop = foob;

while (--ct >= 0)
{
...(foop)
foop++;
}

...
}

int fb(int fool, struct foo *foob)
{
struct foo *foop;
struct foo *fooe;

...

foop = foob;
fooe = foob + fool;

while (foop < fooe)
{
...(foop)
foop++;
}

...
}
 
K

Kaz Kylheku

Which is better?

Neither. Firstly, you're neglecting to take advantage of parameters being
local variables. Do you need fool and foob to retain the values
they had on entry into the function?

int fa(int fool, struct foo *foob)
{
while (fool-- > 0) {
/* process object referenced by foob */
foob++;
}
}
while (--ct >= 0)

Getting into the habit of coding a descending loop this way will bite
you if the loop counter is ever an unsigned type. (Hopefully a compiler will
warn you that the comparison is always true because of the range of the type.)

If you're going to introduce an extra integer index variable anyway, consider
array indexing.

for (ct = 0; ct < fool; ct++) {
... foob ...
}

This way you avoid introducing an extra pointer just to keep your
foob pointer stable.

If this array of struct foo is used frequently in the program,
you might want to encapsulate it better, so you don't have to keep
passing two parameters everywhere just to represent one object.

A null-terminated array is a another possibility. This is like
a null-terminated string, but of course the null element is a pointer.
The argument array passed into main is this way.

int fa(struct foo *foob)
{
for (; *foob != NULL; foob++) {
/* process foob */
}
}
 
J

Joe keane

Firstly, you're neglecting to take advantage of parameters being
local variables.

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

It's harder to debug, it's harder to maintain.
Do you need fool and foob to retain the values they had on entry into
the function?

It helps to retain sanity.

e.g.

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

for (j = 0; j < len; j++)
{
fob->a = fq(...);
fob++;
}

...

for (j = 0; j < len; j++)
{
fob->b = fr(...);
fob++;
}

...
}

Oops!
 
J

James Kuyper

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

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


It helps to retain sanity.

e.g.

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

for (j = 0; j < len; j++)
{
fob->a = fq(...);
fob++;
}

...

for (j = 0; j < len; j++)
{
fob->b = fr(...);
fob++;
}

...
}

Oops!

I agree; that's a context where you need to keep the original value of
fob. To get compiler enforcement of the idea that it should not be done,
declare fob to as "struct foo * const fob".

However, I don't see anything wrong with

int f2(int len, struct foo *fob)
{
while(len--)
{
fob->a = fq(...);
fob++;
}
}

I don't see that as being any harder to debug, any harder to maintain,
and I don't see it as being necessary to retain my sanity (though some
would undoubtedly argue that it's too late for me to worry about that :) ).
 
J

Joe keane

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. It says 'i don't need the array base and index, just give me
the pointer'.

All style, you should get the same machine code. But i've seen too many
cases where the code that looks 'faster' actually is! I mean it's nice
to say 'the optimizer will take care of this', but then it doesn't hurt
to help it some, by not making code more complicated than it should be.

Anyway readability is key; to me code like

ct = len;
fop = fob;
while (--ct >= 0)
{
...(fop)
fop++;
}

is so idiomatic that it's easiest.
 
J

jgharston

James said:
I don't like that "feature" of C.

It's not a feature of C, it's a feature of subroutines.
I can't think of any programming language where the
passed parameters are not local to the subroutine.

I'm happy for somebody to enlighten me, though.

JGH
 
S

Seebs

It's not a feature of C, it's a feature of subroutines.
I can't think of any programming language where the
passed parameters are not local to the subroutine.

At least some Fortrans. Everything was passed by reference, and yes,
this meant that if you called a routine that modified a parameter with
a constant, the constant might get changed.

-s
 
S

Sjouke Burry

It's not a feature of C, it's a feature of subroutines.
I can't think of any programming language where the
passed parameters are not local to the subroutine.

I'm happy for somebody to enlighten me, though.

JGH

Ever heard of FORTRAN?
 
J

James Kuyper

I didn't write that last sentence; it was written by Joe Keane. You need
to fix up your quoting system.
It's not a feature of C, it's a feature of subroutines.

It's a feature of C subroutines; there are other languages that do not
share that feature.
I can't think of any programming language where the
passed parameters are not local to the subroutine.

See <http://en.wikipedia.org/wiki/Evaluation_strategy>; C's
call-by-reference approach is far from being universal. With most of the
other strategies, it would be a bad idea to treat a function parameter
as a local variable.
 
K

Kaz Kylheku

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

This feature is found in other languages.

(defun lisp-function (arg)
(incf arg))
It's harder to debug, it's harder to maintain.

This sort of statement is impossible to justify without a lot of
qualifications. Undoubtedly whenever you modify *any* variable, be it
a by-value argument or not, you can create bugs. (Just ask any proponent of
functional languages.)

The trick of destructively manipulating by-value arguemnts keeps
small and simple functions small and simple, where extra variables
might just create clutter.

Everything you add to the function, some maintainer will have to suspect was
done for a significant reason beyond mere style.
It helps to retain sanity.
N
e.g.

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

for (j = 0; j < len; j++)
{
fob->a = fq(...);
fob++;
}

...

for (j = 0; j < len; j++)
{
fob->b = fr(...);
fob++;
}

...
}

Oops!

This function suggests to me that it is two functions combined into one.

Or, possibly, that the separate loop bodies can be combined into a single pass.

If there is no need for two passes, then the above code wastes my time
trying to confirming a suspicion that there have to be two passes,
because I respect the intelligence of the prior coder and assume he or
she did things for a good reason.

Thus if the following is a correct rewrite, then it is better:

for (j = 0; j < len; j++)
{
fob->a = fq(...);
fob->b = fr(...);
fob++;
}
 
K

Kaz Kylheku

Anyway readability is key; to me code like

ct = len;
fop = fob;
while (--ct >= 0)
{
...(fop)
fop++;
}

is so idiomatic that it's easiest.

As pointed out upthread, it's an infinite loop should ct ever happen
to be unsigned. Learning disability?

C programmers who know what they are donig rarely use predecrement like this in
descending loops, because the counter decrements even in the case that the loop
guard fails and the body is not to be executed any more, which means that it
decrements one past zero.
 
B

Ben Pfaff

Kaz Kylheku said:
As pointed out upthread, it's an infinite loop should ct ever happen
to be unsigned. Learning disability?

It seems likely to me that the compiler would warn that the loop
condition cannot ever be true.
 
J

James Kuyper

It seems likely to me that the compiler would warn that the loop
condition cannot ever be true.

"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.
 
B

Ben Pfaff

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.

The compiler is smarter than that (I see that it isn't actually
specific to loop conditionals):

blp@blp:~/nicira/openflow(0)$ cat foo.c
int
main(void)
{
unsigned x = 5;
while (--x >= 0) {
;
}
while (1) {
;
}
return 0;
}
blp@blp:~/nicira/openflow(0)$ gcc -Wall -Wextra foo.c
foo.c: In function 'main':
foo.c:5: warning: comparison of unsigned expression >= 0 is always true
blp@blp:~/nicira/openflow(0)$
 
K

Keith Thompson

jgharston said:
It's not a feature of C, it's a feature of subroutines.
I can't think of any programming language where the
passed parameters are not local to the subroutine.

I'm happy for somebody to enlighten me, though.

It's a feature of C.

Another example: in Ada, parameters (unless they're marked "in out" or
"out") are local to the subroutine, but they're constant (read-only).
If you try to assign a value to a parameter, the compiler will complain.
 
P

Phil Carmody

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

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


It helps to retain sanity.

e.g.

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

for (j = 0; j < len; j++)
{
fob->a = fq(...);
fob++;
}

...

for (j = 0; j < len; j++)
{
fob->b = fr(...);
fob++;
}

...
}

Oops!

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

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.

Phil
 

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,952
Messages
2,570,115
Members
46,702
Latest member
TheronM691

Latest Threads

Top