about NULL as a parameter to a libc function

G

gaoqiang

a good function should deal with unwanted parameters.

why memcpy,strcmp .etc ,these libc functions, do not

accept NULL pointer ? called with NULL, just get segment

fault. for efficiency ?

any idea ?
 
M

Mark Bluemel

a good function should deal with unwanted parameters.

That's one point of view. Another point of view is that code should
abide by the contract of the api and pass the appropriate arguments to
to functions.
why memcpy,strcmp .etc ,these libc functions, do not
accept NULL pointer ? called with NULL, just get segment
fault. for efficiency ?

For example, my man page for strcmp states "The strcmp() function
compares the two strings s1 and s2" - a NULL pointer isn't a string, so
why should the function deal with it?
 
M

Malcolm McLean

a good function should deal with unwanted parameters.

why memcpy,strcmp .etc ,these libc functions, do not

accept NULL pointer ? called with NULL, just get  segment

fault.  for efficiency ?

any idea ?
In the first incarnation of C, location zero in memory was always set
to all bits clear. So a null pointer and the empty string were the
same thing. It was soon realised that this was a bad idea, a string of
no sausages isn't the same thing as no string of sausages. So a null
pointer means "there's nothing here".

There's an argument for silently ignoring null pointers in functions.
But it's generally better to segfault, and the require caller to check
for null. That way errors are more likely to be discovered, and it's
more likely that, if the pointer is null, the program will do the
right thing.
 
G

gaoqiang

That's one point of view. Another point of view is that code should
abide by the contract of the api and pass the appropriate arguments to
to functions.


For example, my man page for strcmp states "The strcmp() function
compares the two strings s1 and s2" - a NULL pointer isn't a string, so
why should the function deal with it?

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

you remind me .

I just got an idea that NULL pointer is nothing different with other
invalid
pointer. If a function checks the NULL pointer ,then why not other
invalid
pointers? to say void*p=0x01 ?
 
M

Mark Bluemel

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

It's not a pointer to anything useful. It's specifically not a pointer
to "n" bytes of data that can be compared to another "n" bytes of data.
you remind me .

I just got an idea that NULL pointer is nothing different with other
invalid pointer. If a function checks the NULL pointer ,then why not other
invalid
pointers? to say void*p=0x01 ?

What makes you think that location 0x01 is universally invalid?
 
J

James Kuyper

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

you remind me .

I just got an idea that NULL pointer is nothing different with other
invalid
pointer. If a function checks the NULL pointer ,then why not other
invalid
pointers? to say void*p=0x01 ?

First, some nit picking. "NULL" is a macro, which is required to expand
into an expression which is a null pointer constant. "null" is an
adjective which can be applied to, among other things, a C pointer
value. Please try to clearly distinguish between "NULL" and "null".

There are many operations that can be performed on pointer values; some
pointer values are valid for use with some operations, but not with
others. A null pointer is a valid pointer value, but it's the least
valid type of pointer value - there's only two things that can be done
with it: it can be assigned to a pointer variable, and it can be
compared for equality with other pointer values. What this means is that
in C, a null pointer value is used for the very special purpose of not
pointing at anything. It is in fact prohibited for a null pointer to
compare equal to any pointer value that actually points at any object or
function, while it is required that null pointers compare equal to each
other (6.5.9p6).

The definition

void *p = 0x01;

is a constraint violation. Initialization of a scalar object like p is
handled according to the rules of simple assignment (6.7.8p11), and
initializing a pointer object with an integer doesn't fit any of the
allowed combinations of left and right operands for a simple assignment
given in 6.5.16.1p1. There would be special handling in this context if
the initializer were an integer constant expression with a value of
zero; such an expression would qualify as a null pointer constant, and
that is one of the cases that would be permitted by 6.5.16.1p1. It would
result in p being initialized with a null pointer value of type void*.
No such special handling applies to 0x01. What you meant is probably
better expressed by

void *p = (void*)0x01;

However, there's still a problem - the expression (void*)0x01 is NOT
guaranteed to be a valid pointer value for ANY purpose: "the result is
implementation-defined, might not be correctly aligned, might not point
to an entity of the referenced type, and might be a trap
representation.56)" (6.3.2.3p5).
 
J

James Kuyper

a good function should deal with unwanted parameters.

why memcpy,strcmp .etc ,these libc functions, do not

accept NULL pointer ? called with NULL, just get segment

fault. for efficiency ?

For some functions, a null pointer can be used to indicate that
corresponding optional behavior should not occur. For example,
setlocale(LC_ALL, NULL) is perfectly legal; it still returns a pointer
to the current locale, but passing a null pointer turns off the feature
of changing the current locale.

That doesn't apply to these functions. There's no reason to bother
calling these functions with null pointers; the only behavior that could
reasonably be turned off by passing a null pointer is the entire
behavior of the function.
They could have been defined as checking whether the argument is null,
but that would mean that every caller of the function would have to pay
the small cost of checking whether it's null. One of the design
principles for C has been that "you don't pay for what you don't use".
Most code that calls these functions does so only in contexts where it's
already guaranteed that the pointer is not null, so such a check would
be a waste of time. Of course, that's not always true. Sometimes either
the source or the destination of a copy might be a null pointer - but in
that case the the cost of checking for the null pointer should be paid
by calling code, rather than in the subroutine:

if(destination)
memcpy(destination, source, length);
else
{
// special handling when destination is null.
}

Note that an implementation of the C standard library is permitted to
check for null pointer arguments for these functions, and to deal with
them as it wishes. However, it was not considered appropriate for the
standard to mandate such behavior.
 
E

Eric Sosman

a good function should deal with unwanted parameters.

A good caller should not pass incorrect parameters. So there!
why memcpy,strcmp .etc ,these libc functions, do not

accept NULL pointer ? called with NULL, just get segment

fault. for efficiency ?

any idea ?

There's the problem of inventing some kind of "good behavior"
for functions with bogus arguments:

strcpy(NULL, "What should happen here?");
printf("How many %s can dance on a %s?\n");
longjmp(NULL, 42);

It would be a whole lot of work to define suitable behaviors for
these clearly erroneous calls, and both the compiler and library
implementors might need to expend a good deal of effort to ensure
the defined behaviors did in fact occur. (The second example might
require a very large overhaul of function linkage conventions.)
And the benefit from all this work would be ... what? To hide a
programmer's mistakes a little longer, making them harder to find
and fix? Is that a Good Thing?

There are a few cases where "suitable behavior" might not be
too hard to define. memcpy(NULL, NULL, 0) could be defined as a
no-op, for example, and that might be a "reasonable" outcome. But
c'mon: It's quite reasonable that the count could be zero in a
limiting case, but is it reasonable for a program to supply either
of the pointer arguments as NULL? Insisting on a well-defined
outcome for such an implausible scenario seems of little value,
especially if it threatens to penalize all the more sensible uses.

Finally, there are a few cases where the library does in fact
accept NULL arguments and define their meaning. free(NULL) is
well-defined, as is realloc(NULL, 42); NULL values arise "naturally"
in some limiting cases. The library also uses NULL's as special
values in setbuf(), setvbuf(), and fflush(), and there may be a few
more NULLable parameters I've overlooked. But by and large the
philosophy is "trust the programmer" (Rationale, section 0). Part
of that "trust" is "trust the programmer not to do silly things;"
the question of whether a language *should* be so trusting is fodder
for an entirely different debate.
 
M

Mike Manilone

In the first incarnation of C, location zero in memory was always set
to all bits clear. So a null pointer and the empty string were the
same thing. It was soon realised that this was a bad idea, a string of
no sausages isn't the same thing as no string of sausages. So a null
pointer means "there's nothing here".

There's an argument for silently ignoring null pointers in functions.
But it's generally better to segfault, and the require caller to check
for null. That way errors are more likely to be discovered, and it's
more likely that, if the pointer is null, the program will do the
right thing.

A null pointer always is invalid or protected by OS, so you won't to
read/write it.

In fact, when you read/write a protected memory, OS (UNIX) will send
you a message that told you something is wrong.

If you use a different it's not a problem (if the OS don't protect any
memory). You can read/write it correctly.
 
B

Ben Bacarisse

Malcolm McLean said:
In the first incarnation of C, location zero in memory was always set
to all bits clear. So a null pointer and the empty string were the
same thing. It was soon realised that this was a bad idea, a string of
no sausages isn't the same thing as no string of sausages. So a null
pointer means "there's nothing here".

That does not match my recollection. What incarnation of C had this
property, and when was it changed?

<snip>
 
J

James Kuyper

On 10/28/2011 08:58 AM, Mike Manilone wrote:
....
A null pointer always is invalid or protected by OS, so you won't to
read/write it.

A null pointer is not invalid for assignment or comparison with other
pointers for equality.
In fact, when you read/write a protected memory, OS (UNIX) will send
you a message that told you something is wrong.

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

James Kuyper

On 10/28/2011 08:58 AM, Mike Manilone wrote:
....
A null pointer always is invalid or protected by OS, so you won't to
read/write it.

A null pointer is not invalid for assignment or comparison with other
pointers for equality.
In fact, when you read/write a protected memory, OS (UNIX) will send
you a message that told you something is wrong.

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

Joe Pfeiffer

Ben Bacarisse said:
That does not match my recollection. What incarnation of C had this
property, and when was it changed?

I don't think it was ever anything to do with C specifically, but it was
true of at least one early Unix: BSD Unix on a VAX 11/780, and I wrote
a lot of code based on the misunderstanding that a null pointer was a
zero-length string. This was no longer the case when I was fixing my
code so it would run on Sun 680x0 machines.

It's actually my understanding that the fact it was ever true was just
an accident of the implementation.
 
B

BartC

gaoqiang said:
a good function should deal with unwanted parameters.

why memcpy,strcmp .etc ,these libc functions, do not

accept NULL pointer ? called with NULL, just get segment

fault. for efficiency ?

any idea ?

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

John Gordon

In said:
a good function should deal with unwanted parameters.
why memcpy,strcmp .etc ,these libc functions, do not
accept NULL pointer ? called with NULL, just get segment
fault. for efficiency ?

Let's take your example of memcpy.

The purpose of memcpy is to copy bytes from one memory location to
another.

If one of those locations is NULL, memcpy can't do anything useful.
You can't copy bytes to or from a NULL location.

If memcpy just returned without raising an exception, you wouldn't have
any way of knowing that the bytes didn't get copied.

memcpy is doing you a favor by alerting you to a problem: the data did
not get copied.
 
B

BartC

John Gordon said:
In <14abde31-4573-45e7-bdaf-eb70b22741d7@z28g2000pro.googlegroups.com>


Let's take your example of memcpy.

The purpose of memcpy is to copy bytes from one memory location to
another.

If one of those locations is NULL, memcpy can't do anything useful.
You can't copy bytes to or from a NULL location.
memcpy is doing you a favor by alerting you to a problem: the data did
not get copied.

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't it
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).
If memcpy just returned without raising an exception, you wouldn't have
any way of knowing that the bytes didn't get copied.

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

Anyway there's a million ways a program can silently go wrong; A, B and N
might not be zero, but how do we know they are correct?

Having a program deliberately crash is a crude way of detecting some classes
of error.
 
K

Kaz Kylheku

a good function should deal with unwanted parameters.

why memcpy,strcmp .etc ,these libc functions, do not

accept NULL pointer ? called with NULL, just get segment

This means that the funtions actually do accept the pointer and try to do
what they are told: move memory to or from the null pointer!

It's your virtual memory managment (CPU feature + operating system) that
doesn't like the null pointer.

If you dereference a null pointer to access memory, you will get this
segfault, no matter what syntax or function you use.
fault. for efficiency ?

No. It would be more efficient to just accept null inside the
memcpy function (as is already the case in your implementation!) AND also
not have the virtual memory management which catches the fault.

Trapping null to a segfault requires extra circuitry which complicates the
processor and introduces performance penalties (which are difficult to analyze,
since they have to do with the access pattern to pages, and the replacement
strategy of entries the translation cache.)
 
J

John Gordon

In said:
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).

If your code makes it as far as actually calling memcpy, a null argument
absolutely is an error. Copying bytes to or from a null location is a
nonsense operation. If a program is misbehaving so badly as to try to do
this, I WANT it to crash rather than proceed and potentially damage things
even further.

In the larger context of your code which calls memcpy, null arguments may
or may not be an error. Your code can handle those arguments any way it
wants to -- as long as it doesn't pass them to memcpy.
What value does memcpy() return?

memcpy returns the original value of the destination pointer.
Couldn't it have returned the number of bytes actually copied?

I assume there is a good reason why it returns what it does. I'm not
familiar enough with such things to guess at the reason.
 
N

Nobody

why memcpy,strcmp .etc ,these libc functions, do not
accept NULL pointer ? called with NULL, just get segment
fault. for efficiency ?

What would you expect strcmp("", NULL) to return?
 
J

James Kuyper

On 10/28/2011 04:13 PM, BartC wrote:
....
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);

Often? I just checked the most convenient body of code I had on hand,
consisting of 278 source code files with a total of 107238 lines, and
4017043 bytes of code. A large fraction of that code was written by me.
It contains 85 calls to memcpy(), and not a single instance where the
call was controlled directly by an if() based upon A, B, or N.

There were only two instances where the call was directly controlled by
an if(), but the if tested something other condition entirely - it
determined whether the data needed to be copied, not whether the
pointers were non-null or the count non-zero. Much more common was the
deliberate placement of the memcpy() call in a block that could only be
entered if the relevant pointers were non-null and the count was
non-zero; but that block existed for other reasons than making the
memcpy() safe, so putting the memcpy() call into that block did not
increase the number of conditions that needed to be tested.
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).

Given how rarely I need this feature, I prefer the current situation,
where those who need it can implement it just by writing:

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

If they need to do it frequently, they can even make it a macro or an
inline function.
 

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

No members online now.

Forum statistics

Threads
474,264
Messages
2,571,065
Members
48,770
Latest member
ElysaD

Latest Threads

Top