crashing qsort

  • Follow


What might cause qsort to crash? 

I'm using qsort to sort a few thousand items of a not too complex
structure.  Tried it with one data set - worked fine.  Tried another
similarly sized data set and the program dies in the midst of the
qsort.

My comparison function doesn't do anything fancy - just some
strnicmp's and =='s. 

Any idea what I should look for?  It's kinda hard to debug something
that fails in the midst of a library call (a few printf's sprinkled in
the cmp function didn't reveal anything useful).

I don't believe it's an out-of-memory issue.

I'm using MinGW gcc 3.2 on window2k.
0
Reply me2376 (33) 8/14/2003 2:37:42 AM

r# Any idea what I should look for?  It's kinda hard to debug something
# that fails in the midst of a library call (a few printf's sprinkled in
# the cmp function didn't reveal anything useful).

If you have anything like gdb or dbx on your system, you can find where it crashes,
and then backtrack out of the library into your code. You can then check the
arguments passed in to see if you're passing in invalid arguments, like passing
in a NULL to a str* routine. Otherwise you need to use the printfs to make sure
you've isolated in which statement it crashes. At that point print everything
that can be valid to verify the values are acceptable. For strings, be sure to
print the address and the contents.

--
Derk Gwen http://derkgwen.250free.com/html/index.html
Raining down sulphur is like an endurance trial, man. Genocide is the
most exhausting activity one can engage in. Next to soccer.
0
Reply derkgwen (343) 8/14/2003 3:53:52 AM


richard wrote:

> What might cause qsort to crash?

Using it improperly.

> I'm using qsort to sort a few thousand items of a not too complex
> structure.  Tried it with one data set - worked fine.  Tried another
> similarly sized data set and the program dies in the midst of the
> qsort.
> 
> My comparison function doesn't do anything fancy - just some
> strnicmp's and =='s.

C has no standard strnicmp function.

> Any idea what I should look for?

Have a look at your source code. That's where the bug is.

> It's kinda hard to debug something
> that fails in the midst of a library call

Now try closing your eyes, and then try to do what you're asking us to do - 
i.e. debugging your program without being able to see the source code. It's 
even harder, isn't it?

-- 
Richard Heathfield : binary@eton.powernet.co.uk
"Usenet is a strange place." - Dennis M Ritchie, 29 July 1999.
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
K&R answers, C books, etc: http://users.powernet.co.uk/eton
0
Reply dontmail (1884) 8/14/2003 4:36:39 AM

In article <f0tljvgtr2pr6rhn45tjiou3vquii7jqb8@4ax.com>
richard  <me@here.there> writes:
>What might cause qsort to crash? 

Any number of things, but I suspect these three are the most common:

 - Passing incorrect input parameters to qsort (invalid "base" pointer,
   wrong "number of elements", or wrong "width of element" values --
   the comparison function pointer will probably be the one you meant
   though :-) ).

 - Getting the "type signature" of the comparison function wrong.
   This has two sub-aspects, only one of which bites on today's
   common hardware.  Suppose, for instance, you are qsort()ing a
   table of "char *"s.  The comparison function's type signature
   must be:

        int(const void *, const void *)

   so passing strcmp -- which is int(const char *, const char *) --
   is incorrect (but due to "same representation" clause, almost
   certain to work anyway for other cases).  Worse, the comparison
   function gets *pointers* to the elements to compare, which in our
   case is "pointer to <char *>".  Thus this is closer, but still
   wrong:

       int my_compare(char *const *l, char *const *r) {
           return strcmp(*l, *r);
       }

    Here what you need is something like:

        int my_compare(const void *l0, const void *r0) {
            char *const *l = l0;
            char *const *r = r0;

            return strcmp(*l, *r);
        }

    (The difference between these two is not strictly academic,
    and the first version will in fact fail on a Data General
    Eclipse, where "void *" uses a byte pointer but "char *const
    *" uses a word pointer.  Since today's 32-bit x86 only has one
    pointer format, and "all the world's a (32-bit) x86", the first
    -- incorrect -- version of my_compare is likely to work ...
    today.  As x86-64 variants become common, there will be much
    wailing and gnashing of teeth as people discover that in fact,
    *not* all the world is a 32-bit x86 after all.)

Finally, and if my crystal ball is working again, the third common
problem is the one you are actually running into.  If a comparison
function is not 100% consistent in deciding that, when a<b, b>a,
some qsort() variants will run wild.  Hence this:

        int bad_compare(const void *l, const void *r) {
            return (rand() % 3) - 1;
        }

is a terrible function to use, and causes real qsort()s to run off
into the weeds.
-- 
In-Real-Life: Chris Torek, Wind River Systems (BSD engineering)
Salt Lake City, UT, USA (40�39.22'N, 111�50.29'W)  +1 801 277 2603
email: forget about it   http://67.40.109.61/torek/index.html (for the moment)
Reading email is like searching for food in the garbage, thanks to spammers.
0
Reply nospam240 (154) 8/14/2003 9:15:08 AM

I'm passing NULL to a str* routine.  I had thought str* was smart
enough to know this.

thanks

On Thu, 14 Aug 2003 03:53:52 -0000, Derk Gwen <derkgwen@HotPOP.com>
wrote:

>r# Any idea what I should look for?  It's kinda hard to debug something
># that fails in the midst of a library call (a few printf's sprinkled in
># the cmp function didn't reveal anything useful).
>
>If you have anything like gdb or dbx on your system, you can find where it crashes,
>and then backtrack out of the library into your code. You can then check the
>arguments passed in to see if you're passing in invalid arguments, like passing
>in a NULL to a str* routine. Otherwise you need to use the printfs to make sure
>you've isolated in which statement it crashes. At that point print everything
>that can be valid to verify the values are acceptable. For strings, be sure to
>print the address and the contents.

0
Reply me2376 (33) 8/14/2003 11:20:03 AM

Two other people gave comments that quickly helped me find the
problem.  

On Thu, 14 Aug 2003 04:36:39 +0000 (UTC), Richard Heathfield
<dontmail@address.co.uk.invalid> wrote:

>richard wrote:
>
>> What might cause qsort to crash?
>
>Using it improperly.
>
>> I'm using qsort to sort a few thousand items of a not too complex
>> structure.  Tried it with one data set - worked fine.  Tried another
>> similarly sized data set and the program dies in the midst of the
>> qsort.
>> 
>> My comparison function doesn't do anything fancy - just some
>> strnicmp's and =='s.
>
>C has no standard strnicmp function.
>
>> Any idea what I should look for?
>
>Have a look at your source code. That's where the bug is.
>
>> It's kinda hard to debug something
>> that fails in the midst of a library call
>
>Now try closing your eyes, and then try to do what you're asking us to do - 
>i.e. debugging your program without being able to see the source code. It's 
>even harder, isn't it?

0
Reply me2376 (33) 8/14/2003 11:21:44 AM

The problem (or at least, the first problem I found) was trying to
strcmp a NULL.

fwiw, the comparison function starts:

int cmp(const void *p1, const void *p2) {
	const Item *sp1 = *(Item * const *)p1;
	const Item *sp2 = *(Item * const *)p2;
	int r;
	
	r = stricmp(sp1->artist, sp2->artist);
.....

and the call to qsort is 

qsort((void *)(list.item), list.entries, sizeof(Item*), cmp);

list is

typedef struct {
	int entries;
	Item **item
} List

and Item contains char *artist, among other things

Thank you

On 14 Aug 2003 03:15:08 -0600, Chris Torek <nospam@elf.eng.bsdi.com>
wrote:

>In article <f0tljvgtr2pr6rhn45tjiou3vquii7jqb8@4ax.com>
>richard  <me@here.there> writes:
>>What might cause qsort to crash? 
>
>Any number of things, but I suspect these three are the most common:
>
> - Passing incorrect input parameters to qsort (invalid "base" pointer,
>   wrong "number of elements", or wrong "width of element" values --
>   the comparison function pointer will probably be the one you meant
>   though :-) ).
>
> - Getting the "type signature" of the comparison function wrong.
>   This has two sub-aspects, only one of which bites on today's
>   common hardware.  Suppose, for instance, you are qsort()ing a
>   table of "char *"s.  The comparison function's type signature
>   must be:
>
>        int(const void *, const void *)
>
>   so passing strcmp -- which is int(const char *, const char *) --
>   is incorrect (but due to "same representation" clause, almost
>   certain to work anyway for other cases).  Worse, the comparison
>   function gets *pointers* to the elements to compare, which in our
>   case is "pointer to <char *>".  Thus this is closer, but still
>   wrong:
>
>       int my_compare(char *const *l, char *const *r) {
>           return strcmp(*l, *r);
>       }
>
>    Here what you need is something like:
>
>        int my_compare(const void *l0, const void *r0) {
>            char *const *l = l0;
>            char *const *r = r0;
>
>            return strcmp(*l, *r);
>        }
>
>    (The difference between these two is not strictly academic,
>    and the first version will in fact fail on a Data General
>    Eclipse, where "void *" uses a byte pointer but "char *const
>    *" uses a word pointer.  Since today's 32-bit x86 only has one
>    pointer format, and "all the world's a (32-bit) x86", the first
>    -- incorrect -- version of my_compare is likely to work ...
>    today.  As x86-64 variants become common, there will be much
>    wailing and gnashing of teeth as people discover that in fact,
>    *not* all the world is a 32-bit x86 after all.)
>
>Finally, and if my crystal ball is working again, the third common
>problem is the one you are actually running into.  If a comparison
>function is not 100% consistent in deciding that, when a<b, b>a,
>some qsort() variants will run wild.  Hence this:
>
>        int bad_compare(const void *l, const void *r) {
>            return (rand() % 3) - 1;
>        }
>
>is a terrible function to use, and causes real qsort()s to run off
>into the weeds.

0
Reply me2376 (33) 8/14/2003 11:25:13 AM

richard <me@here.there> wrote in message news:<f0tljvgtr2pr6rhn45tjiou3vquii7jqb8@4ax.com>...
> What might cause qsort to crash? 
> 
> I'm using qsort to sort a few thousand items of a not too complex
> structure.  Tried it with one data set - worked fine.  Tried another
> similarly sized data set and the program dies in the midst of the
> qsort.

You don't say if the program crashes or just 'hangs'.

If the latter _AND_ the qsort() you are using uses the quick sort
algorithm it may be that the data is making it go quadratic, ie.
taking time proportional to n*n rather than the more usual n*ln(n)

Try doing a Google search for "A Killer Adversary for Quicksort"
0
Reply p.riebold (2) 8/14/2003 11:41:06 AM

*** Evil top-posting corrected ***

richard wrote:
> Richard Heathfield <dontmail@address.co.uk.invalid> wrote:
> >richard wrote:
> >
> >> What might cause qsort to crash?
> >
> > Using it improperly.
> >
> >> I'm using qsort to sort a few thousand items of a not too
> >> complex structure.  Tried it with one data set - worked fine. 
> >> Tried another similarly sized data set and the program dies
> >> in the midst of the qsort.
> >>
> >> My comparison function doesn't do anything fancy - just some
> >> strnicmp's and =='s.
> >
> > C has no standard strnicmp function.
> >
> >> Any idea what I should look for?
> >
> > Have a look at your source code. That's where the bug is.
> >
> >> It's kinda hard to debug something
> >> that fails in the midst of a library call
> >
> > Now try closing your eyes, and then try to do what you're
> > asking us to do -i.e. debugging your program without being
> > able to see the source code. It's even harder, isn't it?
> 
> Two other people gave comments that quickly helped me find
> the problem.

Which doesn't alter the fact that you posted no code, nor the
accuracy of RHs comments.  Elsewhere you stated you were passing
NULL to some str*() function.  This certainly qualifies as a bug
in your source code.

In addition, kindly do NOT toppost in c.l.c.  It will rapidly get
people ticked off in here, and is rude and unsightly.

-- 
Chuck F (cbfalconer@yahoo.com) (cbfalconer@worldnet.att.net)
   Available for consulting/temporary embedded and systems.
   <http://cbfalconer.home.att.net>  USE worldnet address!


0
Reply cbfalconer (19183) 8/14/2003 12:59:05 PM

richard <me@here.there> writes:

> I'm passing NULL to a str* routine.  I had thought str* was smart
> enough to know this.

Nope, most (all?) str*() functions yield undefined behavior on
null pointer arguments.
-- 
Go not to Usenet for counsel, for they will say both no and yes.
0
Reply blp (3953) 8/14/2003 4:19:29 PM

In <otrmjvgh9ck5ne372g47vi5k1lstdh4a15@4ax.com> richard <me@here.there> writes:

>I'm passing NULL to a str* routine.  I had thought str* was smart
>enough to know this.

And what should a str* function do when passed a null pointer instead of
a string?

The language specification clearly says that you cannot pass null pointers
to functions expecting strings (unless that function's description
explicitly allows them).

Dan
-- 
Dan Pop
DESY Zeuthen, RZ group
Email: Dan.Pop@ifh.de
0
Reply Dan.Pop (3615) 8/14/2003 4:32:34 PM

richard <me@here.there> writes:
> On 14 Aug 2003 16:32:34 GMT, Dan.Pop@cern.ch (Dan Pop) wrote:
> >In <otrmjvgh9ck5ne372g47vi5k1lstdh4a15@4ax.com> richard
> ><me@here.there> writes:
> >
> >>I'm passing NULL to a str* routine.  I had thought str* was smart
> >>enough to know this.
> >
> >And what should a str* function do when passed a null pointer instead of
> >a string?
> 
> If I had a choice, I'd like str* to treat a null pointer as 0 length
> string, rather than crashing.

If you think about it, that makes about as much sense as expecting a
null int pointer to be treated as a pointer to an object with the
value 0.

-- 
Keith Thompson (The_Other_Keith) kst@cts.com  <http://www.ghoti.net/~kst>
San Diego Supercomputer Center           <*>  <http://www.sdsc.edu/~kst>
Schroedinger does Shakespeare: "To be *and* not to be"
0
Reply kst (247) 8/14/2003 9:41:16 PM

"Arthur J. O'Dwyer" wrote:
> On Thu, 14 Aug 2003, Neil Cerutti wrote:
> >
.... snip ...
> >
> > Can you give an example where a str* function treating the null
> > pointer as "" is useful to you?
> 
> Sure, I can.  I've often wished for the ability to "defer"
> error checking until the end of a series of operations,
> rather than stopping after each one to check for NULLs.
> 
>   return strcpy(malloc(100), "hello world");
> 
> If strcpy(NULL, foo) was a no-op returning NULL, the
> above would be very useful, instead of incorrect.
> For example, one could write a Strdup() macro, instead
> of having to define a function to do it.
> 
> That's the only example that comes to mind right now,
> because it came up earlier today.  But obviously if
> str* functions *were* to take NULL in a defined manner,
> strlen(NULL) would probably have to equal either 0
> or (size_t)-1, and I would prefer 0.  So in that one
> particular case, str*(NULL) would "act like" str*(""),
> although for different reasons.

My implementations of strlcpy and strlcat treat _source_ string
pointers of NULL as empty strings, but obviously not destination
pointers.  This will disappoint those who want immediate blow-ups
for such usage.  Available at:

  <http://cbfalconer.home.att.net/download/>

-- 
Chuck F (cbfalconer@yahoo.com) (cbfalconer@worldnet.att.net)
   Available for consulting/temporary embedded and systems.
   <http://cbfalconer.home.att.net>  USE worldnet address!


0
Reply cbfalconer (19183) 8/15/2003 3:04:25 AM

richard wrote:

> Two other people gave comments that quickly helped me find the
> problem.

Is your goal to minimise the pool of people from which you can draw 
assistance by making it as difficult as possible to help you?

-- 
Richard Heathfield : binary@eton.powernet.co.uk
"Usenet is a strange place." - Dennis M Ritchie, 29 July 1999.
C FAQ: http://www.eskimo.com/~scs/C-faq/top.html
K&R answers, C books, etc: http://users.powernet.co.uk/eton
0
Reply dontmail (1884) 8/15/2003 7:15:16 AM

In article <873cg4rz3i.fsf@pfaff.stanford.edu>, blp@cs.stanford.edu 
says...
> richard <me@here.there> writes:
> 
> > I'm passing NULL to a str* routine.  I had thought str* was smart
> > enough to know this.
> 
> Nope, most (all?) str*() functions yield undefined behavior on
> null pointer arguments.

Perhaps surprisingly, people seem to prefer this behavior as well.


0
Reply randy.howard (624) 8/15/2003 8:01:10 AM

On Thu, 14 Aug 2003 18:48:44 GMT,
  richard <me@here.there> wrote
  in Msg. <jrlnjvs0tjaju17g1g7fe7ip49qmopua66@4ax.com>

> If I had a choice, I'd like str* to treat a null pointer as 0 length
> string, rather than crashing.

I'd like str* functions to return NULL when passed NULL because that'd be
useful for malloc()s and strdup()s buried in nested str* function calls.

Treating NULL as "" is braindead.

--Daniel


-- 
"With me is nothing wrong! And with you?" (from r.a.m.p)
0
Reply haude (49) 8/15/2003 9:41:36 AM

On Fri, 15 Aug 2003, CBFalconer wrote:
>
> "Arthur J. O'Dwyer" wrote:
> > On Thu, 14 Aug 2003, Neil Cerutti wrote:
> > >
> > > Can you give an example where a str* function treating the null
> > > pointer as "" is useful to you?
> >
> > Sure, I can.  I've often wished for the ability to "defer"
> > error checking until the end of a series of operations,
> > rather than stopping after each one to check for NULLs.
> >
> >   return strcpy(malloc(100), "hello world");
> >
> > If strcpy(NULL, foo) was a no-op returning NULL, the
> > above would be very useful, instead of incorrect.
> > For example, one could write a Strdup() macro, instead
> > of having to define a function to do it.
[snip]

> My implementations of strlcpy and strlcat treat _source_ string
> pointers of NULL as empty strings, but obviously not destination
> pointers.

:-)  That's exactly the *opposite* of what I consider logical.
Why on earth would I want to assign *from* something that might
be NULL?  All I can think of would be if I were making a function
that took a "default" filename or some such by the client's
passing NULL to the function -- but then I still wouldn't want
to treat that NULL as if it were "", but "temp.dat" or whatever.

YMOV.

-Arthur
0
Reply ajo12 (264) 8/15/2003 1:07:33 PM

On Fri, 15 Aug 2003, CBFalconer wrote:
>
> "Arthur J. O'Dwyer" wrote:
> > On Fri, 15 Aug 2003, CBFalconer wrote:
> > > "Arthur J. O'Dwyer" wrote:
> > > > On Thu, 14 Aug 2003, Neil Cerutti wrote:
> > > > >
> > > > > Can you give an example where a str* function treating the
> > > > > null pointer as "" is useful to you?
> > > >
> > > > Sure, I can.  I've often wished for the ability to "defer"
> > > > error checking until the end of a series of operations,
> > > > rather than stopping after each one to check for NULLs.
> >
> > > My implementations of strlcpy and strlcat treat _source_ string
> > > pointers of NULL as empty strings, but obviously not destination
> > > pointers.
> >
> > :-)  That's exactly the *opposite* of what I consider logical.
[snip]

> For string operations on s, there are generally three cases. 1: s
> is a valid string pointer; 2: s is NULL, and 3: s is not a valid
> string pointer.
>
> Nobody has any problems with 1. 2 and 3 generally yield undefined
> behaviour, which may or may not provoke immediate crashes.  My
> attitude is that using 2 to represent an empty string is a
> suitable resolution of UB, and may well be useful.  A developer
> can easily add an assert statement to catch such usage.  I can
> hardly conceive of a usable assert statement to catch 3.
>
> Meanwhile, the behaviour of the routine using NULL is defined for
> a greater variety of inputs.

In other words, "Because I can."

Not "Because the user might want to."

That's kind of what I expected you'd say.  I know it's easy to
write string functions that do weird things with null pointers,
but why bother if the behavior is not useful?  The only useful
behavior I can imagine is the behavior that you didn't implement
because it was hard. :-)

If you'd come back with a code example showing how a user might
productively *use* the behavior your strlcpy() defines, I'd
be more mollified.

-Arthur
0
Reply ajo12 (264) 8/15/2003 8:02:50 PM

"Arthur J. O'Dwyer" wrote:
> On Fri, 15 Aug 2003, CBFalconer wrote:
> >
.... snip ...
> >
> > Meanwhile, the behaviour of the routine using NULL is defined
> > for a greater variety of inputs.
> 
.... snip ...
> 
> If you'd come back with a code example showing how a user might
> productively *use* the behavior your strlcpy() defines, I'd
> be more mollified.

The only choices are: UB on that input, and define the action in
an orderly manner.  It is not a matter of using the behaviour, but
of avoiding possible nasal demons.  If the user doesn't want to
feed it a NULL, then he should not do so.  Nothing is lost.

However, if you could guarantee that every dereference of a NULL
pointer in every system would produce an immediate halt and
diagnostic, I would be perfectly happy not to provide the
behaviour.  All I see in the standard is UB.

I call this defensive programming.  If it avoids an east coast
blackout every 40 odd years it has paid for itself.  

-- 
Chuck F (cbfalconer@yahoo.com) (cbfalconer@worldnet.att.net)
   Available for consulting/temporary embedded and systems.
   <http://cbfalconer.home.att.net>  USE worldnet address!


0
Reply cbfalconer (19183) 8/16/2003 12:54:17 AM

CBFalconer <cbfalconer@yahoo.com> writes:
[...]
> The only choices are: UB on that input, and define the action in
> an orderly manner.  It is not a matter of using the behaviour, but
> of avoiding possible nasal demons.  If the user doesn't want to
> feed it a NULL, then he should not do so.  Nothing is lost.
> 
> However, if you could guarantee that every dereference of a NULL
> pointer in every system would produce an immediate halt and
> diagnostic, I would be perfectly happy not to provide the
> behaviour.  All I see in the standard is UB.
> 
> I call this defensive programming.  If it avoids an east coast
> blackout every 40 odd years it has paid for itself.  

Sorry, but I think treating NULL as "" is counterintuitive -- enough
so that it's nearly likele to cause a blackout as to prevent one.

If you want to program defensively, I suggest aborting on NULL rather
than silently hiding the client's error.

-- 
Keith Thompson (The_Other_Keith) kst@cts.com  <http://www.ghoti.net/~kst>
San Diego Supercomputer Center           <*>  <http://www.sdsc.edu/~kst>
Schroedinger does Shakespeare: "To be *and* not to be"
0
Reply kst (247) 8/16/2003 2:35:43 AM

Keith Thompson wrote:
> 
> CBFalconer <cbfalconer@yahoo.com> writes:
> [...]
> > The only choices are: UB on that input, and define the action in
> > an orderly manner.  It is not a matter of using the behaviour, but
> > of avoiding possible nasal demons.  If the user doesn't want to
> > feed it a NULL, then he should not do so.  Nothing is lost.
> >
> > However, if you could guarantee that every dereference of a NULL
> > pointer in every system would produce an immediate halt and
> > diagnostic, I would be perfectly happy not to provide the
> > behaviour.  All I see in the standard is UB.
> >
> > I call this defensive programming.  If it avoids an east coast
> > blackout every 40 odd years it has paid for itself.
> 
> Sorry, but I think treating NULL as "" is counterintuitive -- enough
> so that it's nearly likele to cause a blackout as to prevent one.
> 
> If you want to program defensively, I suggest aborting on NULL rather
> than silently hiding the client's error.

Well, I showed (for my strlcpy) how easy it was to change.  I
still think it is ridiculous to accept erroneous inputs when they
can be given a reasonable interpretation.  Aborting should not be
an option - a better interface would return an error indicator,
but such is not available for strlcpy.

I know my attitude is correct :-)  I just don't seem to be able to
find the right arguments to convince others of it.  Maybe this
should drift to religious wars about formatting style. :-)  I
think I'll just go throw a tantrum.

-- 
Chuck F (cbfalconer@yahoo.com) (cbfalconer@worldnet.att.net)
   Available for consulting/temporary embedded and systems.
   <http://cbfalconer.home.att.net>  USE worldnet address!

0
Reply cbfalconer (19183) 8/16/2003 3:45:26 AM

On Sat, 16 Aug 2003 03:45:26 GMT
CBFalconer <cbfalconer@yahoo.com> wrote:

> Keith Thompson wrote:
> > 
> > CBFalconer <cbfalconer@yahoo.com> writes:
> > [...]
> > > The only choices are: UB on that input, and define the action in
> > > an orderly manner.  It is not a matter of using the behaviour, but
> > > of avoiding possible nasal demons.  If the user doesn't want to
> > > feed it a NULL, then he should not do so.  Nothing is lost.
> > >
> > > However, if you could guarantee that every dereference of a NULL
> > > pointer in every system would produce an immediate halt and
> > > diagnostic, I would be perfectly happy not to provide the
> > > behaviour.  All I see in the standard is UB.
> > >
> > > I call this defensive programming.  If it avoids an east coast
> > > blackout every 40 odd years it has paid for itself.
> > 
> > Sorry, but I think treating NULL as "" is counterintuitive -- enough
> > so that it's nearly likele to cause a blackout as to prevent one.
> > 
> > If you want to program defensively, I suggest aborting on NULL
> > rather than silently hiding the client's error.
> 
> Well, I showed (for my strlcpy) how easy it was to change.  I
> still think it is ridiculous to accept erroneous inputs when they
> can be given a reasonable interpretation.  Aborting should not be
> an option - a better interface would return an error indicator,
> but such is not available for strlcpy.
> 
> I know my attitude is correct :-)  I just don't seem to be able to
> find the right arguments to convince others of it.  Maybe this
> should drift to religious wars about formatting style. :-)  I
> think I'll just go throw a tantrum.

With the stuff I currently work on I would always prefer the application
to abort if a dodgy library call is made. That way the user reports it
to us, we fix it and no data is corrupted.

On the projects I've worked on in the past, embedded avionics systems
for example, I would prefer the library call to do the best it could so
that the application continues to run to the best of its ability, except
when I'm testing/debugging it in which case I want it to fail horribly
as quickly as possible.

The right thing to do on erroneous input to a function is very
application dependant.
-- 
Mark Gordon
0
Reply spamtrap606 (150) 8/16/2003 11:55:53 AM

CBFalconer wrote:

> I know my attitude is correct :-)  I just don't seem to be able to
> find the right arguments to convince others of it.

Well,... I don't know if this helps or hurts your position, but
I agree with you, and my library code often treats a const char*
argument as "" if passed NULL.

My reasoning is simple: I can't AT ALL count on what happens if I
ignore a NULL value and use it.  If I *could* my stance might change.

Also I want my library code to be bullet-proof and NOT CAPABLE of
causing a crash (or nasal issuance).  I do not consider it the
libraries domain to troubleshoot client code.

Further, in many cases (at least in my code), a NULL char* pointer
has a "no string" semantic, and I consider that very close to an
empty string.  Close enough to treat it thus in a "strlen" function
or "strcpy" function.

-- 
|_ CJSonnack <Chris@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL  |
|_____________________________________________|_______________________|
0
Reply cjsonnack (415) 8/22/2003 5:13:54 PM

pete wrote:

> Do you mean that you would like
>     strcpy(s1, NULL);
> to have the exact same side effect as
>     *s1 = '\0';

Yes.

-- 
|_ CJSonnack <Chris@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL  |
|_____________________________________________|_______________________|
0
Reply cjsonnack (415) 8/22/2003 6:22:56 PM

"Arthur J. O'Dwyer" wrote:

> I feel compelled to reiterate my opinion that
> 
>    strcpy(s1, NULL)
> 
> really has no reasonable meaning.

No, but if the second argument is a variable (that may sometimes
be NULL), it does.  The point is, often a char* variable has the
general semantic of "string or NULL" with NULL meaning "no string
YET assigned" or "string was optional" or like that.  In such a
case, NULL == "" is not a far stretch, particularly when checking
length or copying.

> (And what do you think of this one?  UB again?)
> 
>    strcpy(NULL, NULL)

That's why I said "const char*".  You'd need to return NULL on
that one.

>> Also I want my library code to be bullet-proof and NOT CAPABLE of
>> causing a crash (or nasal issuance).  I do not consider it the
>> libraries domain to troubleshoot client code.
> 
> Aren't those two goals contradictory?

Imagine they aren't and go from there.  (-:

> If you want to bullet-proof your library code, then you do need
> to check for NULLs and whatnot (thus effectively "troubleshooting"
> whatever junk the client code throws at you).

Perhaps that is a side effect, but the check is to *protect* the
lib code from being able to invoke unknowable behavior.

> I prefer not to troubleshoot the client's code, either.  I think
> that's the client's job.  If he passes NULL to something that
> doesn't expect NULL (and he knows it), then he's being foolish
> and can troubleshoot his own dang code. :-)

IME (which means both In My Experience and In My Environment), a
crash is *more* likely if you use the NULL than if you pretend it's
an empty string.  The behavior of the system may be unexpected
(leading you to go troubleshooting), but (again, IME) that behavior
is preferrable to crashing (much of my code runs on production
lines that must not crash).

If I could be certain there were no bugs in the client code, it
wouldn't be a concern.  But since that certainty is impossible,
I *must* (IMO) do what I can to prevent crashes.  For me, that
means making a "best effort" with all data passed to me.

> [Probably different meanings for the word "troubleshoot."
> I'm using it in the sense of "fix the errors post-facto,"
> as distinct from "prevent" ("stop errors from happening")
> or "ignore" ("ignore").]

So am I.  The opinion expressed when I had this debate previously
was that by using the NULL, you crash and alert the programmer to
the problem (allowing her to troubleshoot it).  If I could be
100% certain this would always occur *during* development, then
I'd probably change my view.

Obviously I can't be 100% certain, so I try to make the library
code as crash-resistant as possible.

-- 
|_ CJSonnack <Chris@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL  |
|_____________________________________________|_______________________|
0
Reply cjsonnack (415) 8/22/2003 7:51:49 PM

Arthur J. O'Dwyer wrote:
> 
> On Fri, 22 Aug 2003, Programmer Dude wrote:
> >
> > pete wrote:
> > >
> > > Do you mean that you would like
> > >     strcpy(s1, NULL);
> > > to have the exact same side effect as
> > >     *s1 = '\0';
> >
> > Yes.
> 
> I feel compelled to reiterate my opinion that
> 
>    strcpy(s1, NULL)
> 
> really has no reasonable meaning.  As such, I'd
> much rather it *didn't* write data anywhere,
> because it's probably a bug.  (And what do you
> think of this one?  UB again?)
> 
>    strcpy(NULL, NULL)
> 
> Programmer Dude also wrote:
> > Also I want my library code to be bullet-proof and NOT CAPABLE of
> > causing a crash (or nasal issuance).  I do not consider it the
> > libraries domain to troubleshoot client code.
> 
> Aren't those two goals contradictory?  If you want to bullet-proof
> your library code, then you do need to check for NULLs and whatnot
> (thus effectively "troubleshooting" whatever junk the client code
> throws at you).
> 
> I prefer not to troubleshoot the client's code, either.  I think
> that's the client's job.  If he passes NULL to something that
> doesn't expect NULL (and he knows it), then he's being foolish
> and can troubleshoot his own dang code. :-)
> 
> [Probably different meanings for the word "troubleshoot."
> I'm using it in the sense of "fix the errors post-facto,"
> as distinct from "prevent" ("stop errors from happening")
> or "ignore" ("ignore").]

My opinion, is that NULL is just one of zillions of invalid pointer
arguments and there's no point in treating it special.

I don't see how you can make strcpy, bulletproof.
If s1 isn't NULL and doesn't point to an object, 
how can you tell from inside of strcpy?

char s1[] = "";
char s2[] = "";
strcpy(s1 + 1, s2);

-- 
pete
0
Reply pfiland (6614) 8/22/2003 8:23:32 PM

Programmer Dude wrote:
> CBFalconer wrote:
> 
> > I know my attitude is correct :-)  I just don't seem to be able to
> > find the right arguments to convince others of it.
> 
> Well,... I don't know if this helps or hurts your position, but
> I agree with you, and my library code often treats a const char*
> argument as "" if passed NULL.
> 
> My reasoning is simple: I can't AT ALL count on what happens if I
> ignore a NULL value and use it.  If I *could* my stance might change.
> 
> Also I want my library code to be bullet-proof and NOT CAPABLE of
> causing a crash (or nasal issuance).  I do not consider it the
> libraries domain to troubleshoot client code.
> 
> Further, in many cases (at least in my code), a NULL char* pointer
> has a "no string" semantic, and I consider that very close to an
> empty string.  Close enough to treat it thus in a "strlen" function
> or "strcpy" function.

At last, an oasis of sanity :-)  However I disagree on strlen,
since it is often used to find the end of a string for
modification purposes.  e.g.:

   if ((i = strlen(s)) && ('\n' == s[i-1])) 
      s[i-1] = '\0';

although this particular sample would work with such a strlen. 
Preferred would be:

   if (s && (i = strlen(s)) && ('\n' == s[i-1])) 
      s[i-1] = '\0';
   /* assert that s is NULL or is not terminated with \n */

A cpy function has no downside, as the output is converted into a
proper C string.

-- 
Chuck F (cbfalconer@yahoo.com) (cbfalconer@worldnet.att.net)
   Available for consulting/temporary embedded and systems.
   <http://cbfalconer.home.att.net>  USE worldnet address!


0
Reply cbfalconer (19183) 8/23/2003 2:09:25 AM

"Arthur J. O'Dwyer" wrote:
>
.... snip ...
> 
>  (And what do you think of this one?  UB again?)
> 
>    strcpy(NULL, NULL)

No, nobody has recommended that.  That is not a const string.

-- 
Chuck F (cbfalconer@yahoo.com) (cbfalconer@worldnet.att.net)
   Available for consulting/temporary embedded and systems.
   <http://cbfalconer.home.att.net>  USE worldnet address!


0
Reply cbfalconer (19183) 8/23/2003 2:09:27 AM

Chris Torek wrote:
[snip]
> 
> Finally, and if my crystal ball is working again, the third common
> problem is the one you are actually running into.  If a comparison
> function is not 100% consistent in deciding that, when a<b, b>a,
> some qsort() variants will run wild.  Hence this:
> 
>         int bad_compare(const void *l, const void *r) {
>             return (rand() % 3) - 1;
>         }
> 
> is a terrible function to use, and causes real qsort()s to run off
> into the weeds.

The above seems so stupid a mistake as to be impossible; however, a 
subtle variation that I *have* seen (and not in my code!) is:

int bad_compare(const void *l, const void *r) {
int d=strcmp(l, r);
if(d) return d; else return -1;
}

Can, and did, cause qsort to crash, screw up, and blow nasal demons. 
(This particular libc was O32 on SGI IRIX 6.2)

-- 
Michel Bardiaux
Peaktime Belgium S.A.  Bd. du Souverain, 191  B-1160 Bruxelles
Tel : +32 2 790.29.41

0
Reply mbardiaux (20) 8/27/2003 12:09:28 PM

pete wrote:

> My opinion, is that NULL is just one of zillions of invalid pointer
> arguments and there's no point in treating it special.

IMO, it's a more (much more) common value than any random values.
Because of that--and because it is detectable--I choose to check
for it.

> I don't see how you can make strcpy, bulletproof.
> If s1 isn't NULL and doesn't point to an object,
> how can you tell from inside of strcpy?

Obviously you can't.  Nothing is foolproof.  That doesn't mean you
don't do what you can (IMO).  Again, my primary goal is to reduce,
as best as *possible*, runtime nasal issuance.  The idea of helping
a developer (usually me) debug code during development is much less
important.

-- 
|_ CJSonnack <Chris@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL  |
|_____________________________________________|_______________________|
0
Reply cjsonnack (415) 8/27/2003 3:25:42 PM

CBFalconer wrote:

>> Further, in many cases (at least in my code), a NULL char* pointer
>> has a "no string" semantic, and I consider that very close to an
>> empty string.  Close enough to treat it thus in a "strlen" function
>> or "strcpy" function.
> 
> At last, an oasis of sanity :-)  However I disagree on strlen,
> since it is often used to find the end of a string for
> modification purposes.  e.g.:
> 
>    if ((i = strlen(s)) && ('\n' == s[i-1]))
>       s[i-1] = '\0';

If s is NULL, aren't you going to have problems anyway, regardless
of what strlen() does?

> Preferred would be:
> 
>    if (s && (i = strlen(s)) && ('\n' == s[i-1]))
>       s[i-1] = '\0';
>    /* assert that s is NULL or is not terminated with \n */

That is probably how I'd write it.

> A cpy function has no downside, as the output is converted into
> a proper C string.

Agreed.  Thing is, what are your options with a strlen() function?
It should return a size_t, so a negative return value isn't an
option to signal error, C has no exceptions, and zero is a valid
result.  What really are your choices?

	* You *do* deref the passed char* without checking
and whatever happens happens (not an option in my mind).

	* You check the char* and detect NULL.  Now what?  Since the
first is (for me) not an option, I have to return *something*.  I
can't think of any better value than 0.  Case of least worst to me.

-- 
|_ CJSonnack <Chris@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL  |
|_____________________________________________|_______________________|
0
Reply cjsonnack (415) 8/27/2003 3:34:08 PM

"Programmer Dude" <cjsonnack@mmm.com> wrote in message news:3F4CCF70.935196E5@mmm.com...
>
> Agreed.  Thing is, what are your options with a strlen() function?
> It should return a size_t, so a negative return value isn't an
> option to signal error, C has no exceptions, and zero is a valid
> result.  What really are your choices?
>
> * You *do* deref the passed char* without checking
> and whatever happens happens (not an option in my mind).
>
> * You check the char* and detect NULL.  Now what?  Since the
> first is (for me) not an option, I have to return *something*.  I
> can't think of any better value than 0.  Case of least worst to me.
>

What would happen if you returned -1 cast to size_t?
If the caller remembers to check ... but if not then
maybe that would be worse, I suppose.

John.


0
Reply jl94 (26) 8/27/2003 10:18:46 PM

John L wrote:
> 
> "Programmer Dude" <cjsonnack@mmm.com> wrote in message news:3F4CCF70.935196E5@mmm.com...
> >
> > Agreed.  Thing is, what are your options with a strlen() function?
> > It should return a size_t, so a negative return value isn't an
> > option to signal error, C has no exceptions, and zero is a valid
> > result.  What really are your choices?
> >
> > * You *do* deref the passed char* without checking
> > and whatever happens happens (not an option in my mind).
> >
> > * You check the char* and detect NULL.  Now what?  Since the
> > first is (for me) not an option, I have to return *something*.  I
> > can't think of any better value than 0.  Case of least worst to me.
> >
> 
> What would happen if you returned -1 cast to size_t?

The same thing that would happen if strlen measured a
string with a length of ((size_t)-1).

-- 
pete
0
Reply pfiland (6614) 8/28/2003 12:10:52 PM

32 Replies
38 Views

(page loaded in 1.651 seconds)

Similiar Articles:






7/14/2012 4:27:22 PM


Reply: