Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

the loop control

37 views
Skip to first unread message

Joe keane

unread,
Oct 29, 2011, 5:38:28 PM10/29/11
to
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++;
}

...
}

Ian Collins

unread,
Oct 29, 2011, 5:48:00 PM10/29/11
to
On 10/30/11 10:38 AM, Joe keane wrote:
> Which is better?

Start by defining better!
--
Ian Collins

Kaz Kylheku

unread,
Oct 29, 2011, 6:10:46 PM10/29/11
to
On 2011-10-29, Joe keane <j...@panix.com> wrote:
> 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[i] ...
}

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 */
}
}
Message has been deleted

Joe Pfeiffer

unread,
Oct 30, 2011, 1:11:15 AM10/30/11
to
j...@panix.com (Joe keane) writes:

> Which is better?

<snip>

No.

Philip Lantz

unread,
Oct 30, 2011, 1:47:44 AM10/30/11
to
On Sat, 29 Oct 2011, Joe keane wrote:

> Which is better?
>
> ...

int fc(int fool, struct foo *foob)
{
...

for (int i = 0; i < fool; i++)
{
...(foob[i])
}

...
}

Joe keane

unread,
Oct 31, 2011, 11:21:49 AM10/31/11
to
In article <201110291...@kylheku.com>,
Kaz Kylheku <k...@kylheku.com> wrote:
>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!

James Kuyper

unread,
Oct 31, 2011, 11:35:39 AM10/31/11
to
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 :-) ).

Joe keane

unread,
Oct 31, 2011, 12:36:18 PM10/31/11
to
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.

Seebs

unread,
Oct 31, 2011, 1:18:32 PM10/31/11
to
On 2011-10-31, Joe keane <j...@panix.com> wrote:
> In article <201110291...@kylheku.com>,
> Kaz Kylheku <k...@kylheku.com> wrote:
>>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.

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.

-s
--
Copyright 2011, all wrongs reversed. Peter Seebach / usenet...@seebs.net
http://www.seebs.net/log/ <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/Fair_Game_(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.

jgharston

unread,
Oct 31, 2011, 1:27:09 PM10/31/11
to
James Kuyper wrote:
> Kaz Kylheku wrote:
> > Firstly, you're neglecting to take advantage of parameters being
> > local variables.
> 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

Seebs

unread,
Oct 31, 2011, 2:36:03 PM10/31/11
to
On 2011-10-31, jgharston <j...@arcade.demon.co.uk> wrote:
> James Kuyper wrote:
>> Kaz Kylheku wrote:
>> > Firstly, you're neglecting to take advantage of parameters being
>> > local variables.
>> 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.

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.

Robert Wessel

unread,
Oct 31, 2011, 2:49:19 PM10/31/11
to
Cobol, Fortran, VB, and Perl, for example, use call-by-reference by
default. Those also support call-by-value as an option (at least in
more recent incarnations - older versions of Cobol, for example, did
not, but the current standards do). Conversely, the (more common)
languages that use call-by-value, very often have a way to implement
call-by-reference (in C you can explicitly pass a pointer, of course,
and arrays are always call-by-reference, in C++ you specify a
parameter as a, *ahem*, reference).

Sjouke Burry

unread,
Oct 31, 2011, 2:49:33 PM10/31/11
to
jgharston <j...@arcade.demon.co.uk> wrote in news:c173def7-db03-4bf0-adf5-
255127...@a12g2000vbz.googlegroups.com:
Ever heard of FORTRAN?

James Kuyper

unread,
Oct 31, 2011, 3:07:10 PM10/31/11
to
On 10/31/2011 01:27 PM, jgharston wrote:
> James Kuyper wrote:
>> Kaz Kylheku wrote:
>>> Firstly, you're neglecting to take advantage of parameters being
>>> local variables.
>> I don't like that "feature" of C.

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.

Kaz Kylheku

unread,
Oct 31, 2011, 3:23:28 PM10/31/11
to
On 2011-10-31, Joe keane <j...@panix.com> wrote:
> In article <201110291...@kylheku.com>,
> Kaz Kylheku <k...@kylheku.com> wrote:
>>Firstly, you're neglecting to take advantage of parameters being
>>local variables.
>
> 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.

>>Do you need fool and foob to retain the values they had on entry into
>>the function?
>
> 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(...);

Kaz Kylheku

unread,
Oct 31, 2011, 3:30:20 PM10/31/11
to
On 2011-10-31, Joe keane <j...@panix.com> wrote:
> 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.

Ben Pfaff

unread,
Oct 31, 2011, 3:38:19 PM10/31/11
to
Kaz Kylheku <k...@kylheku.com> writes:

> On 2011-10-31, Joe keane <j...@panix.com> wrote:
>> 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?

It seems likely to me that the compiler would warn that the loop
condition cannot ever be true.
--
char a[]="\n .CJacehknorstu";int putchar(int);int main(void){unsigned long b[]
={0x67dffdff,0x9aa9aa6a,0xa77ffda9,0x7da6aa6a,0xa67f6aaa,0xaa9aa9f6,0x11f6},*p
=b,i=24;for(;p+=!*p;*p/=4)switch(0[p]&3)case 0:{return 0;for(p--;i--;i--)case+
2:{i++;if(i)break;else default:continue;if(0)case 1:putchar(a[i&15]);break;}}}

James Kuyper

unread,
Oct 31, 2011, 3:51:51 PM10/31/11
to
On 10/31/2011 03:38 PM, Ben Pfaff wrote:
> Kaz Kylheku <k...@kylheku.com> writes:
>
>> On 2011-10-31, Joe keane <j...@panix.com> wrote:
>>> 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?
>
> 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.

Ben Pfaff

unread,
Oct 31, 2011, 4:06:25 PM10/31/11
to
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)$

--
"To get the best out of this book, I strongly recommend that you read it."
--Richard Heathfield

Keith Thompson

unread,
Oct 31, 2011, 4:56:39 PM10/31/11
to
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.

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Phil Carmody

unread,
Oct 31, 2011, 5:07:12 PM10/31/11
to
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
--
Unix is simple. It just takes a genius to understand its simplicity
-- Dennis Ritchie (1941-2011), Unix Co-Creator

Keith Thompson

unread,
Oct 31, 2011, 5:17:34 PM10/31/11
to
James Kuyper <james...@verizon.net> writes:
[...]
> 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.

Phil Carmody

unread,
Oct 31, 2011, 5:20:32 PM10/31/11
to
j...@panix.com (Joe keane) writes:
> 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 Carmody

unread,
Oct 31, 2011, 5:30:19 PM10/31/11
to
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.

Jorgen Grahn

unread,
Oct 31, 2011, 5:38:28 PM10/31/11
to
On Mon, 2011-10-31, Joe keane wrote:
> In article <201110291...@kylheku.com>,
> Kaz Kylheku <k...@kylheku.com> wrote:
>>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.

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

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

pete

unread,
Oct 31, 2011, 7:01:12 PM10/31/11
to
Joe keane wrote:

> Anyway readability is key; to me code like
>
> ct = len;
> fop = fob;
> while (--ct >= 0)
> {
> ...(fop)
> fop++;
> }
>
> is so idiomatic that it's easiest.

More idiomatic is:

while (ct-- != 0) {
;
}

It's a natural for counting down through an array.

unsigned array[10];
unsigned ct = sizeof array / sizeof *array;

while (ct-- != 0) {
array[ct] = ct;
}

--
pete

Ike Naar

unread,
Oct 31, 2011, 8:11:42 PM10/31/11
to
On 2011-10-31, Phil Carmody <thefatphi...@yahoo.co.uk> wrote:
> 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.

James Kuyper

unread,
Oct 31, 2011, 10:01:14 PM10/31/11
to
On 10/31/2011 08:11 PM, Ike Naar wrote:
> On 2011-10-31, Phil Carmody <thefatphi...@yahoo.co.uk> wrote:
>> 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.

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.

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

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.
--
James Kuyper

Kaz Kylheku

unread,
Oct 31, 2011, 10:51:51 PM10/31/11
to
On 2011-11-01, James Kuyper <james...@verizon.net> wrote:
> On 10/31/2011 08:11 PM, Ike Naar wrote:
>> On 2011-10-31, Phil Carmody <thefatphi...@yahoo.co.uk> wrote:
>> 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;

*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.

Ike Naar

unread,
Nov 1, 2011, 5:51:23 AM11/1/11
to
On 2011-11-01, James Kuyper <james...@verizon.net> wrote:
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.

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

pete

unread,
Nov 1, 2011, 6:34:40 AM11/1/11
to
Ike Naar wrote:

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

My preference is to alter the value of parameters
at every given oportunity.

void strcpy(char *s1, const char *s2)
{
while ((*s1++ = *s2++) != '\0') {
;
}
}

If it turns out that I need the original value
of a parameter throughout the code,
then my preference is to do that by initializing
a const qualified local variable with the original value
of the parameter.

That's very easy to do,
even as an alteration to code which was originally written
without preserving the parameter value.

char *strcpy(char *s1, const char *s2)
{
char *const p1 = s1;

while ((*s1++ = *s2++) != '\0') {
;
}
return p1;
}

--
pete

James Kuyper

unread,
Nov 1, 2011, 7:23:39 AM11/1/11
to
On 10/31/2011 10:51 PM, Kaz Kylheku wrote:
> On 2011-11-01, James Kuyper <james...@verizon.net> wrote:
...
>> *point = 0;
>
> *point++ = 0;

Aaagh! My dogs urgently needed walking, so I didn't spend enough time
proofreading.
--
James Kuyper

James Kuyper

unread,
Nov 1, 2011, 7:30:14 AM11/1/11
to
On 11/01/2011 05:51 AM, Ike Naar wrote:
> On 2011-11-01, James Kuyper <james...@verizon.net> wrote:
>> On 10/31/2011 08:11 PM, Ike Naar wrote:
...
>> 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;

Correction:
*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.

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.

...
>>> 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.
>
> 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.
--
James Kuyper

James Kuyper

unread,
Nov 1, 2011, 7:35:42 AM11/1/11
to
On 11/01/2011 05:51 AM, Ike Naar wrote:
> On 2011-11-01, James Kuyper <james...@verizon.net> wrote:
...
>> 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;
>> }
...
> ... 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);
--
James Kuyper

Nick Keighley

unread,
Nov 1, 2011, 7:35:43 AM11/1/11
to
On Oct 31, 5:18 pm, Seebs <usenet-nos...@seebs.net> wrote:
> On 2011-10-31, Joe keane <j...@panix.com> wrote:
>
> > In article <20111029175230....@kylheku.com>,
> > Kaz Kylheku  <k...@kylheku.com> wrote:
> >>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.
>
> 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

Seebs

unread,
Nov 1, 2011, 5:44:09 PM11/1/11
to
On 2011-10-31, Keith Thompson <ks...@mib.org> wrote:
> James Kuyper <james...@verizon.net> writes:
> [...]
>> 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.

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

Seebs

unread,
Nov 1, 2011, 5:44:10 PM11/1/11
to
On 2011-11-01, Nick Keighley <nick_keigh...@hotmail.com> wrote:
> On Oct 31, 5:18?pm, Seebs <usenet-nos...@seebs.net> wrote:
>> On 2011-10-31, Joe keane <j...@panix.com> wrote:
>> > In article <20111029175230....@kylheku.com>,
>> > Kaz Kylheku ?<k...@kylheku.com> wrote:
>> >>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.

>> 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

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.

Phil Carmody

unread,
Nov 1, 2011, 7:20:01 PM11/1/11
to
pete <pfi...@mindspring.com> writes:
> Joe keane wrote:
>
> > Anyway readability is key; to me code like
> >
> > ct = len;
> > fop = fob;
> > while (--ct >= 0)
> > {
> > ...(fop)
> > fop++;
> > }
> >
> > is so idiomatic that it's easiest.
>
> More idiomatic is:
>
> while (ct-- != 0) {

Don't forget the pretty, but too cute for its own good:

while (ct --> 0)

> ;
> }

Yeah, that's the kind of smiley my snippet deserves!

Joe keane

unread,
Nov 1, 2011, 11:36:51 PM11/1/11
to
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]

Ian Collins

unread,
Nov 2, 2011, 12:09:10 AM11/2/11
to
On 11/ 2/11 04:36 PM, Joe keane wrote:
> 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.

--
Ian Collins

Joe keane

unread,
Nov 2, 2011, 12:47:11 AM11/2/11
to
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

...
}

88888 Dihedral

unread,
Nov 2, 2011, 4:25:29 AM11/2/11
to
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.


> ...
> }
>
> int fb(int fool, struct foo *foob)
> {
> struct foo *foop;
> struct foo *fooe;
>
> ...
>
> foop = foob;
> fooe = foob + fool;
>
> while (foop < fooe)
> {
> ...(foop)
> foop++;
> }
>
> ...
> }

Nick Keighley

unread,
Nov 2, 2011, 4:16:48 AM11/2/11
to
On Nov 1, 9:44 pm, Seebs <usenet-nos...@seebs.net> wrote:
> On 2011-11-01, Nick Keighley <nick_keighley_nos...@hotmail.com> wrote:
>
> > On Oct 31, 5:18?pm, Seebs <usenet-nos...@seebs.net> wrote:
> >> On 2011-10-31, Joe keane <j...@panix.com> wrote:
> >> > In article <20111029175230....@kylheku.com>,
> >> > Kaz Kylheku ?<k...@kylheku.com> wrote:

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

Keith Thompson

unread,
Nov 2, 2011, 6:56:40 AM11/2/11
to
Ian Collins <ian-...@hotmail.com> writes:
> On 11/ 2/11 04:36 PM, Joe keane wrote:
[...]
>> 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.

Reason number 1 for not using expressions with side effects as
arguments to function-like macros.

I don't suggest that function-like macros are necessarily a good idea,
especially when inline functions are available, but they *can* be used
reasonably safely if you exercise a reasonable amount of care.

Joe keane

unread,
Nov 3, 2011, 5:29:08 PM11/3/11
to
I use it all the time.

The idiom

while (--ct >= 0)
...

has the nice property that if ct is negative coming in [almost], the
body is not executed, which is probably what you want.

And similar:

for (j = upp; j < low; j++)
...

e.g.

a = 3;
b = 2;

for (j = a; j < b; j++)
...

You'd be rather surprised if this executed four billion times.

If you need unsigned for a loop counter, there must be some reason for
doing so.

James Kuyper

unread,
Nov 3, 2011, 6:00:13 PM11/3/11
to
On 11/03/2011 05:29 PM, Joe keane wrote:
> I use it all the time.
>
> The idiom
>
> while (--ct >= 0)
> ...
>
> has the nice property that if ct is negative coming in [almost], the
> body is not executed, which is probably what you want.

A property it shares with
while(ct-- > 0)

which, in addition to the minor advantage of being one character
shorter, also works as intended even if ct is unsigned.

...
> If you need unsigned for a loop counter, there must be some reason for
> doing so.

That is correct.

Kaz Kylheku

unread,
Nov 3, 2011, 8:43:01 PM11/3/11
to
On 2011-11-03, Joe keane <j...@panix.com> wrote:
> I use it all the time.
>
> The idiom
>
> while (--ct >= 0)
> ...

This is not an idiom; the meaning of this construct is exactly what
the rules for that combination of symbols say it is.

> has the nice property that if ct is negative coming in [almost], the

Almost, yes. What if it is INT_MIN already?

It also has the property that if ct is zero, it is not executed,
but you didn't catch that because it's not obvious, see below.

> body is not executed, which is probably what you want.

This has the stupid property of testing a value that is not the one
which came in. This makes it awkward to think about.

For what values of ct is the loop executed? It is ct - 1 which is tested, not
ct.

while (ct - 1 >= 0)

which is the same thing as

ct >= 1

which is the same as

ct > 0

Aha! Well, doh, why don't you use THAT condition in the fist place???

while (ct-- > 0) {
}

This still has the property of not executing the loop if ct is negative
coming in. It's algebraically equivalent, get it?

Furthermore, maybe you don't want to unconditionally decrement the variable,
whether or not the loop body executes!

If ct comes in as 0 is it okay to make it -1? That's fine if ct has no
next use after the loop.

Variations on the following are generally better:

for (; ct > 0; ct--) {

}

The loop guard controls the loop AND the increment step, which is part
of the loop.

This still has the virtue that the loop is not executed when ct comes
in as zero or negative. Furthermore, it has the virtue that ct is not
touched either if it is zero or negative. (Furthermore, it won't
decrement down from INT_MIN.)

> And similar:
>
> for (j = upp; j < low; j++)
> ...

This is not similar at all.

> If you need unsigned for a loop counter, there must be some reason for
> doing so.

Like maybe it occurs in code written by someone else you're maintaing?

Joe keane

unread,
Nov 10, 2011, 9:24:37 PM11/10/11
to
I really don't care if you like

while (ct-- > 0)

versus

while (--ct >= 0)

Pick one you like! And know what issues may occur. Nothing is
foolproof here, and it can't be.

Hans Vlems

unread,
Nov 11, 2011, 3:38:47 AM11/11/11
to
On Nov 1, 3:01 am, James Kuyper <jameskuy...@verizon.net> wrote:
> On 10/31/2011 08:11 PM, Ike Naar wrote:
>
I happen to know Ike and that last sentence had me ROTFL....
0 new messages