467,921 Members | 1,260 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 467,921 developers. It's quick & easy.

crashing qsort

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.
Nov 13 '05 #1
  • viewed: 3718
Share:
34 Replies
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.
Nov 13 '05 #2
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 : bi****@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
Nov 13 '05 #3
In article <f0********************************@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.
Nov 13 '05 #4
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 <de******@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.


Nov 13 '05 #5
Two other people gave comments that quickly helped me find the
problem.

On Thu, 14 Aug 2003 04:36:39 +0000 (UTC), Richard Heathfield
<do******@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?


Nov 13 '05 #6
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 <no****@elf.eng.bsdi.com>
wrote:
In article <f0********************************@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.


Nov 13 '05 #7
richard <me@here.there> wrote in message news:<f0********************************@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"
Nov 13 '05 #8
*** Evil top-posting corrected ***

richard wrote:
Richard Heathfield <do******@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 (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 13 '05 #9
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.
Nov 13 '05 #10
In <ot********************************@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: Da*****@ifh.de
Nov 13 '05 #11
richard <me@here.there> writes:
On 14 Aug 2003 16:32:34 GMT, Da*****@cern.ch (Dan Pop) wrote:
In <ot********************************@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) ks*@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"
Nov 13 '05 #12
"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 (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 13 '05 #13
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 : bi****@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
Nov 13 '05 #14
In article <87************@pfaff.stanford.edu>, bl*@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.
Nov 13 '05 #15
On Thu, 14 Aug 2003 18:48:44 GMT,
richard <me@here.there> wrote
in Msg. <jr********************************@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)
Nov 13 '05 #16

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
Nov 13 '05 #17

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
Nov 13 '05 #18
"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 (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 13 '05 #19
CBFalconer <cb********@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) ks*@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"
Nov 13 '05 #20
Keith Thompson wrote:

CBFalconer <cb********@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 (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!

Nov 13 '05 #21
On Sat, 16 Aug 2003 03:45:26 GMT
CBFalconer <cb********@yahoo.com> wrote:
Keith Thompson wrote:

CBFalconer <cb********@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
Nov 13 '05 #22
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 <Ch***@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL |
|_____________________________________________|___ ____________________|
Nov 13 '05 #23
pete wrote:
Do you mean that you would like
strcpy(s1, NULL);
to have the exact same side effect as
*s1 = '\0';


Yes.

--
|_ CJSonnack <Ch***@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL |
|_____________________________________________|___ ____________________|
Nov 13 '05 #24
"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 <Ch***@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL |
|_____________________________________________|___ ____________________|
Nov 13 '05 #25
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
Nov 13 '05 #26
Programmer Dude <cj*******@mmm.com> wrote:
# pete wrote:
#
# > Do you mean that you would like
# > strcpy(s1, NULL);
# > to have the exact same side effect as
# > *s1 = '\0';
#
# Yes.

You are, of course, free to define your own string copy on top of or instead of
the usual one.

--
Derk Gwen http://derkgwen.250free.com/html/index.html
Death is the worry of the living. The dead, like myself,
only worry about decay and necrophiliacs.
Nov 13 '05 #27
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 (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 13 '05 #28
"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 (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 13 '05 #29
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

Nov 13 '05 #30
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 <Ch***@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL |
|_____________________________________________|___ ____________________|
Nov 13 '05 #31
Derk Gwen wrote:
Do you mean that you would like
strcpy(s1, NULL);
to have the exact same side effect as
*s1 = '\0';


Yes.


You are, of course, free to define your own string copy on top
of or instead of the usual one.


Of course, but just FYI, I believe this is more about creating
our own stringish functions. (But I tuned in late, so maybe not.)

--
|_ CJSonnack <Ch***@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL |
|_____________________________________________|___ ____________________|
Nov 13 '05 #32
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 <Ch***@Sonnack.com> _____________| How's my programming? |
|_ http://www.Sonnack.com/ ___________________| Call: 1-800-DEV-NULL |
|_____________________________________________|___ ____________________|
Nov 13 '05 #33

"Programmer Dude" <cj*******@mmm.com> wrote in message news:3F***************@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.
Nov 13 '05 #34
John L wrote:

"Programmer Dude" <cj*******@mmm.com> wrote in message news:3F***************@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
Nov 13 '05 #35

This discussion thread is closed

Replies have been disabled for this discussion.

By using this site, you agree to our Privacy Policy and Terms of Use.