Mailing List Archive


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: argc loop [tlug]



Jim writes:

 > >  > while the latter code requires another test inside the loop 
 > >  > to avoid dereferencing a null pointer. 
 > > 
 > > Not in my environment (PPC Mac OS X 10.4.9).
 > 
 > Don't be so parochial. 

s/parochial/precise/

 > Strive for portability instead. 

Specifying the environment in which you make your observations is a
prerequisite to grokking portability.  By doing so, I explicitly
abandon a claim to portability.  That's OK, not everything can be
portable.

 > There are standards compliant environments for which argc is 
 > legitimately zero, so it's better to avoid assuming argc>0. 

argc?  Of course I did not assume argc > 0; the whole point of
"<= bound" instead of the more usual "< bound" is that I want it
executed at least once.

Is argc ever initialized to a negative value?  That would be a problem!

 > Portability is a good thing. 
 > Adopt code patterns that are more portable. 
 > Make it a habit. 

Well, no.

Where I want portability and intelligibility, I would be coding in
Python ("There's One Obvious Way To Do It" is an explicit goal of the
language's design).  If intelligibility can be sacrificed, I might use
Lisp. :-)  The fact that I'm coding in C at all implies that I want to
do something that is nonportable in some sense, or that efficiency is
more important than portability (I actually found an instance of
Duff's Device in the XEmacs sources yesterday, although it was in a
comment ;-).

 > So 
 > 
 >    do { ... } while (*argv++ != NULL);
 > 
 > is deprecated. 
 > 
 > Let's put the "Don't stop at the first bug." guideline into practice. 
 > 
 > >  > while the latter code requires another test inside the loop 
 > >  > to avoid dereferencing a null pointer. 
 > > 
 > > Not in my environment (PPC Mac OS X 10.4.9).
 > 
 > Don't be so sure. 
 > 
 > Even for typical parsing (i.e., when argc>0), the last pass through 
 > the do-while loop has to test for *argv==NULL, to avoid dereferencing 
 > a null pointer. So the do-while loop is not just deprecated. 

Haanya?  I did no parsing.  If I actually was using the arguments, I
would have written

    int i;
    /* let the compiler catch inconsistencies */
    char *emerson = "A foolish consistency is the hobgoblin of small minds.\n"
    printf (emerson);
    for (i = 0; i < argc; ++i) {        /* old habit from Z80 Small-C */
        foo (argv[i]);
        printf (emerson);
    }

almost as you would have.  But I wouldn't like the code duplication,
and I don't like it in Python, either.  The C idiom

    #include <stdio.h>
    int ch_or_eof;
    while ((ch_or_eof = getch()) != EOF) {
        /* do something with ch_or_eof */
    }

is really a remarkable achievement in clarity to my mind, and giving
it up because many people don't grok functional programming,
especially when written as an imperative assignment, is a shame.

 > Tenure's a wonderful thing, eh? 

I don't have tenure in computer science, I'm merely a talented amateur.

 > This just reinforces my preference for 
 > 
 >    for (i=0;i<argc;i++)
 > 
 > for being so straightforward and easy to read, 
 > over any of the code that increments argv.

It's easy to read because it is an idiom, albeit one designed into the
language.  If you'll forgive the linear formatting, though, the idiom
"while (*target++ = *source++) ;" is just as portable and easy to read
once you're used to it, although people bitch about it all the time.

The fact that it's similar to "while (*s1++ == *s2++) ;" is a real
problem.  However, judicious use of such idioms can make the loop body
simpler, and thus more readable.

 > Here's some code for y'all to play with: 

As long as we're all having fun!  Here's my 2 hundredths of an ECU:

Urk.  What happens if somebody changes *two* of those "#if 0"s to
"#if 1"s?  BLOOEY!  Better

/* The first of these #define'd to 1 selects the idiom used. */
#define STANDARD_ARRAY_IDIOM 0
#define WHILE_POINTER_IDIOM 0
#define FOR_POINTER_IDIOM 0
#define DOWHILE_POINTER_IDIOM 0
#define NONDETERMINISTIC_MEMORY_DUMP 0

 > #include <stdlib.h>
 > #include <stdio.h>
 > 
 > int main(int argc,char *argv[])
 > {

#if STANDARD_ARRAY_IDIOM

 >    int i;
 > 
 >    for (i=0;i<argc;i++)
 >       printf("%s\n",argv[i]);

#elif WHILE_POINTER_IDIOM

 >    while (*argv++!=NULL) {
 >       int c;
 > 
 >       printf("%s\n",*argv);
 >       c=**argv; /* to provoke dereferencing null pointer */

/* I would use "assert (*argv);" instead. ;-)
   Note that in this case the comment is misleading.  Bad, bad Jim! */

 >       if (c!='\0')
 >          printf("%c\n",c);
 >    }

#elif FOR_POINTER_IDIOM

 >    for ( ;*argv!=NULL;argv++)
 >       printf("%s\n",*argv);

#elif DOWHILE_POINTER_IDIOM

 >    do {
 >       int c;
 > 
 >       printf("%s\n",*argv);
 >       c=**argv; /* to provoke dereferencing null pointer */
 >       if (c!='\0')
 >          printf("%c\n",c);
 >    } while (*argv++ != NULL);

#elif NONDETERMINISTIC_MEMORY_DUMP

 >    do {
 >       printf("%08lX\n",*argv); /* highly non-portable code */
 >    } while (*argv++ != NULL);

#else
#error Your configuration does not select an idiom!

 > #endif
 > 
 >    exit(EXIT_SUCCESS);
 > }




Home | Main Index | Thread Index

Home Page Mailing List Linux and Japan TLUG Members Links