about NULL as a parameter to a libc function

J

jacob navia

Le 29/10/11 00:42, Kaz Kylheku a écrit :
This is neither a good debugging aid for those who regard the above to be a
bug, nor a very meaningful extension of behavior for those who don't.

yeah sure, just crash. That's the best solution for any error.

Forgot something? Did a small mistake somewhere?

Crash everything, that's THE BEST SOLUTION FOR ALL ERRORS.

That way there isn't any way of writing something solid in C,
ànd you have to hear again...

"Go to C++, C is just fucked up"

Mr Kylheju asserts (without any arguments since his word suffices)
"This is not a good debugging aid", returning an error code. Crashing
is better.

And why? Couldn't those "who regard the above to be a bug" TEST for
an error code?

No, that would be too easy. Just crash. That's a good solution.

And why setting errno and returning an error code is "not very
meaningful extension"?

Because he says so.
 
N

Nobody

Agreed. If the programmer isn't paying attention to whether they're
passing null pointers to strcmp() etc, they aren't going to be paying
attention to whether it flagged an error.
yeah sure, just crash. That's the best solution for any error.

If you want Java, you know where to find it.
 
A

Angel

Agreed. If the programmer isn't paying attention to whether they're
passing null pointers to strcmp() etc, they aren't going to be paying
attention to whether it flagged an error.
Seconded. I prefer a program crashing during the test phase, telling the
programmer that he is an idiot, over the program being used as a point
of entry by some malicious hacker in the future.
If you want Java, you know where to find it.

Sweeping generalization from mr Navia. Nobody claimed crashing is the sane
thing to do in all situations, but he apparently has trouble accepting
it's the sane thing to do in some situations.

C assumes the programmer knows what he is doing and punishes the
programmer who does not. Java does neither, and thus managers tend to
assume every blind idiot can program in Java. In my daily work as an
admin, I'm regularly confronted with the results of the latter, and let
me tell you that they are not pretty.
 
P

Phil Carmody

BartC said:
I agree. It was sloppy programming, possibly done for performance at
one time. But C programmers are so used to it, they seem to think it's
a good idea, and any other way of doing it is inferior!

If it's that good an idea, perhaps we should all give up testing for
null pointers!

That's truly abyssmal logic. Just because one component can say "it's
not my responsibility, it's someone else's", doesn't mean that every,
or even any, component can say "it's not my responsibility".

Phil
 
B

BartC

Keith Thompson said:
One could make exactly the same argument for a simple assignment
(without the N, of course). Perhaps it would be simpler to be able to
write:

*A = *B;

and have it quietly do nothing if either A or B is a null pointer.

With a basic language statement like assignment, you wouldn't expect it to
be backed up with logic to make sure operands are not null. And it can't be
called remotely, where there would be no opportunity for the writer of the
*A=*B to put in extra checks.

A function (any function really, rather than just memcpy) is different; it
can be called remotely, and it is easy to add checks, and to define specific
behaviours.
as others have said in parallel followups, when you're writing a
statement that copies data, you should generally have enough awareness
of the context to *know* that there's actually something to be copied.

Do you actually find yourself making such checks in your own code?

Yes. Both when calling C functions (and often these are called from
higher-level scripting languages, which use null for empty strings, and can
crash when directly calling a C function), and when writing my own functions
where the caller cannot quite be trusted 100%, and even then I might put
checks in anyway as I don't know what other callers might do in future.

Of course these are not always as performance-critical as memcpy() where
there is less room for manoeuvre. I can understand that requiring extra
checks means the compiler cannot, for example, optimise a 4-byte memcpy into
a couple of inline instructions.
 
P

Phil Carmody

James Kuyper said:
I wouldn't recommend such a change, but if it were made, it's often not
that difficult to come up with reasonable definitions of the behavior in
the cases that are currently undefined. In this case, the following
would be one plausible approach:

int strcmp(const char *s1, const char *s2)
{
if(s1 && s2)
return old_strcmp(s1, s2);
return !!s1 - !!s2;

Why not !s2 - !s1 ?
 
P

Phil Carmody

Keith Thompson said:
Mine says the same thing -- and it's wrong. It compares the strings
*pointed to by* s1 and s2. (Yes, the meaning is clear enough,
but it's potentially misleading to inexperienced C programmers who
might thing that strings are really pointers.)

I think you've mentioned this flub before on c.l.c. Did you raise it
with c.s.c, or even members of the committee themselves? It's an easy
fix that doesn't need to harm readability, so it would be good if it
was pushed forwards. (One lazy hack fix is to add a "sometimes as
shorthand a pointer to a string will be refered to as a string"
disclaimer at definition time, but that's perhaps too much an
admission of sloppiness.)

Phil
 
N

Nick Keighley

On 10/28/2011 09:49 AM, gaoqiang wrote:
a good function should deal with unwanted [invalid?] parameters.

yes but if the caller *knows* the parameter is valid it can be
inefficient to check over and over again

/* how many times do we have to check s1 isn't NULL? */
strcpy (s1, s2);
strcpy (s1, s3);
....
strcpy (s1, sn);

/* public key generator. Both parameters must be prime */
/* this could be fantastically expensive to check */
BigNum keygen (BigNum p1, BigNum p2);

/* cmp() must terminate */
/* this could be impossible to verify */
void sort (T a[], size_t n, int (*cmp) (T, T));

I don't think it explains. NULL is not a string,then what about
memcmp ? NULL is a pointer now ...

from (a draft copy of) the standard

****
4.11.1 String function conventions
[...]
but in all cases a char * or void * argument points to the initial
(lowest
addressed) character of the array.
****

NULL does not satisfy this criterion
you remind me .

I just got an idea that NULL pointer is nothing different with other
invalid
pointer.

oh but it is
If a function checks the NULL pointer ,then why not other
invalid pointers? to say void*p=0x01 ?

because it could be very expensive (if not impossible in some cases)
to check. NULL may not be zero and 0x1 may be a valid address.

void f (void)
{
char somearray [50];
free (somearray);
}

is somearray a valid address?

void f (char* dst)
{
char somearray1 [10];
char somearray2 [10];
char somearray3 [10];

memcpy (dst, &somearray2[-1], 3);
}

is &somearray2[-1] a valid address? The above program likely exhibits
Undefined Behaviour and yet that address is likely an accessible on
most implementations.
 
N

Nick Keighley

On 10/28/2011 08:58 AM, Mike Manilone wrote:
...


A null pointer is not invalid for assignment or comparison with other
pointers for equality.


Not all systems have protected memory, not all systems are UNIX, and not
all systems give you a message when you dereference a null pointer. On
some systems, you might not discover the bad consequences of doing so
until long after the fact.

not even all UNIX have protected memory. I believe some Crays didn't
have virtual memory and so possibly didn't have protected memory. The
C compiler for HPUX made it an option whether dereferencing a null
pointer gave a segmentaion error. I've no idea why (kernel
programming?).
 
N

Nick Keighley

I think you've mentioned this flub before on c.l.c. Did you raise it
with c.s.c, or even members of the committee themselves?

the standard doesn't say that. It says

"The strcmp function compares the string pointed to by s1 to the
string pointed to by s2 ."
 
I

Ike Naar

That's truly abyssmal logic. Just because one component can say "it's
not my responsibility, it's someone else's", doesn't mean that every,
or even any, component can say "it's not my responsibility".

Just because you say so, doesn't mean anybody can say so ;-)
 
J

jacob navia

Le 29/10/11 11:11, Nobody a écrit :
Agreed. If the programmer isn't paying attention to whether they're
passing null pointers to strcmp() etc, they aren't going to be paying
attention to whether it flagged an error.

Error analysis and error reporting is then, completely unnecessary.

Just crash, so they will learn how to program.
If you want Java, you know where to find it.

Speak about error analysis and the answer is always the same:

error analysis is for bad programmers that make errors. Good programmers
that use C never make any error and error analysis is just not necessary
for those super-heroes.
 
N

Nick Keighley

When you use a function such as memcpy, you often have to check that each
operand is in fact not null:

if (A && B && N) memcpy(A, B, N);

when A, B, N are themselves arguments to your code, for example. Wouldn'tit
be a lot easier to just write:

memcpy(A, B, N);

and know that no copying is done when any argument is null or zero? Having a
null source or destination is not necessarily an error (and if it is, you
check it conventionally).


What value does memcpy() return? Couldn't it have returned the number of
bytes actually copied?

the first parameter
 
N

Nick Keighley

If this combination of inputs were to be defined, I would expect the null
pointer not to compare equal to any string, since it isn't one.

The remaining question, then, is where the null pointer lands in a
lexicographic comparison against strings. I would place it ahead of
strings, so that if you a null mixed up among a set of strings, it
makes itself obvious by taking the top position in a sorted list.

Furthermore, strcmp(NULL, NULL) ought return zero, since it is astonishing
if a thing is not deemed equal to itself (reflexive property of the equivalence
relation).

NaNs are astonishing
 
N

Nick Keighley

Le 29/10/11 00:42, Kaz Kylheku a écrit :



yeah sure, just crash. That's the best solution for any error.

you're putting words in his mouth. There are arguments for very low
level stuff not doing such checking, whilst higher level stuff checks.
If you're not sure about your arguments to memcpy() you could stick
some asserts in.

Forgot something? Did a small mistake somewhere?

Crash everything, that's THE BEST SOLUTION FOR ALL ERRORS.

That way there isn't any way of writing something solid in C,
ànd you have to hear again...

"Go to C++, C is just fucked up"

which would be silly as C++ has a similar philosophy to C. For
instance memcpy() is also aC++ function. And much of the STLis
unchecked.

std::vector v [20];

v[-1]=0;
v[20]=0;
Mr Kylheju asserts (without any arguments since his word suffices)
"This is not a good debugging aid", returning an error code. Crashing
is better.

crashing is better for those who don't check error codes.

<snip rant>
 
B

BartC

Nick Keighley said:
On 10/28/2011 09:49 AM, gaoqiang wrote:
a good function should deal with unwanted [invalid?] parameters.

yes but if the caller *knows* the parameter is valid it can be
inefficient to check over and over again

/* how many times do we have to check s1 isn't NULL? */
strcpy (s1, s2);
strcpy (s1, s3);
...
strcpy (s1, sn);

That's not really a likely piece of code. A better example is:

strcpy(s2,s1);
strcpy(s3,s1);
...

where s1==NULL can mean don't copy, or it can equally mean treat as an empty
string. The latter is handy, but is rather lazy and also cannot apply in all
situations. So it would up to each function what it might do.

And ideally there would be two versions of the functions: safe and unsafe.
The safe versions do the checks and apply meanings to null parameters. The
unsafe version versions work as they are currently defined by the standard.
But neither set can guarantee no crashes. This is still C.
/* public key generator. Both parameters must be prime */
/* this could be fantastically expensive to check */
BigNum keygen (BigNum p1, BigNum p2);

In this case whatever or whoever created p1 and p2 would be responsible. It
would be unreasonable to expect the callee to do these checks.
/* cmp() must terminate */
/* this could be impossible to verify */
void sort (T a[], size_t n, int (*cmp) (T, T));

You're jumping a bit from checking for an uninitialised pointer, to proving
the correctness of potentially thousands of lines of code! So if the latter
is difficult to do, we do nothing about the simpler cases either?
 
J

jacob navia

Le 29/10/11 16:39, Nick Keighley a écrit :
crashing is better for those who don't check error codes.

What a moron!

How can you check an error code if the program crashes?

Go figure, maybe he runs his applications always inside a debugger.

There is no point in discussing with people like this any further.
 
K

Keith Thompson

BartC said:
With a basic language statement like assignment, you wouldn't expect it to
be backed up with logic to make sure operands are not null. And it can't be
called remotely, where there would be no opportunity for the writer of the
*A=*B to put in extra checks.

A function (any function really, rather than just memcpy) is different; it
can be called remotely, and it is easy to add checks, and to define specific
behaviours.

memcpy(), in a very real sense, is a basic language feature. Yes, it's
in the library rather than in the core language, but its performance is
just as important as the performance of an assignment.

And having memcpy() check for null pointers and do nothing makes certain
errors *more difficult to detect*. If I write

memcpy(target, source, size);

and either target or source happens to be a null pointer, it's almost
certainly because I made a mistake. A memcpy() that hides that mistake
from me isn't doing me any favors; it's by no means certain that doing
nothing is the correct behavior that will "fix" the bug.
Yes. Both when calling C functions (and often these are called from
higher-level scripting languages, which use null for empty strings, and can
crash when directly calling a C function), and when writing my own functions
where the caller cannot quite be trusted 100%, and even then I might put
checks in anyway as I don't know what other callers might do in future.

Are these scripting languages of your own design? Perhaps using null
for empty strings wasn't the best choice. Is there a way to represent
an empty string using a non-null pointer? If so, do you really gain
much benefit from having two distinct representations for the same
thing? You'd have to do extra checks on almost every string operation.

Perhaps you have a good reason for doing it that way -- but then you
have to expect some overhead.

As for untrusted callers, when you check for an error condition, you
have to know how to handle it. How do you know that the way you handle
it (say, doing nothing for a null pointer) is the correct behavior?
Of course these are not always as performance-critical as memcpy() where
there is less room for manoeuvre. I can understand that requiring extra
checks means the compiler cannot, for example, optimise a 4-byte memcpy into
a couple of inline instructions.

Welcome to C.
 
K

Keith Thompson

Phil Carmody said:
I think you've mentioned this flub before on c.l.c. Did you raise it
with c.s.c, or even members of the committee themselves? It's an easy
fix that doesn't need to harm readability, so it would be good if it
was pushed forwards. (One lazy hack fix is to add a "sometimes as
shorthand a pointer to a string will be refered to as a string"
disclaimer at definition time, but that's perhaps too much an
admission of sloppiness.)

It's not an error in the standard, so I haven't mentioned it on c.s.c or
to the committee. But I should mention it to the maintainers of the
Linux man pages. Thanks for reminding me. (A data point: the Solaris 9
man page doesn't make this mistake.)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,082
Messages
2,570,589
Members
47,211
Latest member
Shamestone

Latest Threads

Top