checking if pointer is NULL allowed?

R

Richard Tobin

Maybe, but that's not possible in standard C.
[/QUOTE]

You can tell whether it's null (which I suspect is what was meant) but
you can't check that it's "valid to dereference" - it might be a
free()ed pointer for example.

-- Richard
 
C

Chris Dollin

tedu said:

There's no way to find out if "the pointer is valid to dereference
or not".

However you try and construct one, you will find undefined
behaviour lying in wait.

(A /specific implementation/ might offer such a function, although
I can't see it being both reliable and cheap.)
 
R

Richard Heathfield

Chris Dollin said:
There's no way to find out if "the pointer is valid to dereference
or not".

Well, you know that it isn't valid to deref if it's NULL. So the trick here
is to ensure that all your pointers are *either* valid *or* NULL,
throughout your code. Change the paradigm. Instead of thinking "how do I
detect whether I've screwed up" (which is generally a good thing to think,
but in this case turns out to be impractical in portable code), think "what
coding habits can I adopt that minimise the risk of screwing up in the
first place?"
 
G

Guest

Keith said:
It's very difficult to tell which parts of your followup are quoted
from previous articles, since you didn't use "> " markers. You're
also missing some attribution lines, so it's hard to tell who you're
quoting. Google Groups does these for you automatically, so I'm not
sure what went wrong here.

He replied to two messages with one. Google quoted Ben Pfaff's message,
with an attribution line, and "> " markers. He added jaysome's message,
with a simple copy and paste.
jay wrote the last bit:
| And oddly enough, there may be something wrong with the following,
| understandble, mistake:
| /*let's print the value of the NULL pointer*/
| printf("%p\n", NULL);

One legal definition for the NULL macro is just 0, which is a null
pointer constant, but is only treated as a pointer in the right
context. Since printf is a variadic function (it takes a variable
number and type(s) of arguments), the compiler doesn't know that that
argument is supposed to be a pointer; it just passes a value of type
int with value 0, which may or may not look like a null pointer by the
time printf sees it.

The correct way to print the value of a null pointer is:

printf("%p\n", (void*)NULL);

The output is implementation-defined.

I would (and did) use (void *) 0. (void *) NULL is valid in this
context, but not in some others where (void *) 0 is, because it is not
necessarily a null pointer constant. All it is sure to be is a constant
null pointer.
 
J

jaysome

Chris Dollin said:


Well, you know that it isn't valid to deref if it's NULL. So the trick here
is to ensure that all your pointers are *either* valid *or* NULL,
throughout your code. Change the paradigm. Instead of thinking "how do I
detect whether I've screwed up" (which is generally a good thing to think,
but in this case turns out to be impractical in portable code), think "what
coding habits can I adopt that minimise the risk of screwing up in the
first place?"

Yes.

And thinking about both gives you the best of said both worlds.
Everyone screws up--so every smart person, process and tool you can
throw at these two pillars of your paradigm is worth it, and arguably
even priceless.

Regards
 
J

jaysome

[snip]
(A /specific implementation/ might offer such a function, although
I can't see it being both reliable and cheap.)

It depends on your definitions of "reliable" and "cheap".

On one specific implementation I know of:

#include <stdio.h>
#include <time.h>
#include <windows.h>
#define NUM 0x4000
#define NUM_RUNS 10
int main(void)
{
unsigned nn;
clock_t c1, c2;
for ( nn = 0; nn < NUM_RUNS; nn++ )
{
int ii, num_bad = 0;
c1 = clock();
for ( ii = 0; ii < NUM; ii++ )
{
num_bad += IsBadReadPtr((void*)ii, 1) != 0;//lint !e514
}
c2 = clock();
printf("tested: 0x%04X; bad: 0x%04X\n", NUM, num_bad);
printf("%.1f microseconds per call\n",
(c2 - c1) / (double)CLOCKS_PER_SEC / NUM * 1e6);
}
return 0;
}

Output:

tested: 0x4000; bad: 0x4000
8.5 microseconds per call
tested: 0x4000; bad: 0x4000
9.5 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
tested: 0x4000; bad: 0x4000
8.5 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
tested: 0x4000; bad: 0x4000
9.5 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
tested: 0x4000; bad: 0x4000
8.5 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
Press any key to continue

Regards
 
C

Chris Dollin

jaysome said:
[snip]
(A /specific implementation/ might offer such a function, although
I can't see it being both reliable and cheap.)

It depends on your definitions of "reliable" and "cheap".

Surely. I meant by "reliable" "gives the right answer so often it's
worth using" and by "cheap" "it's performance doesn't cripple the
application". Still room for manouever, of course.
On one specific implementation I know of:

#include <stdio.h>
#include <time.h>
#include <windows.h>
#define NUM 0x4000
#define NUM_RUNS 10
int main(void)
{
unsigned nn;
clock_t c1, c2;
for ( nn = 0; nn < NUM_RUNS; nn++ )
{
int ii, num_bad = 0;
c1 = clock();
for ( ii = 0; ii < NUM; ii++ )
{
num_bad += IsBadReadPtr((void*)ii, 1) != 0;//lint !e514
}
c2 = clock();
printf("tested: 0x%04X; bad: 0x%04X\n", NUM, num_bad);
printf("%.1f microseconds per call\n",
(c2 - c1) / (double)CLOCKS_PER_SEC / NUM * 1e6);
}
return 0;
}

Output:

tested: 0x4000; bad: 0x4000
8.5 microseconds per call
tested: 0x4000; bad: 0x4000
9.5 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
tested: 0x4000; bad: 0x4000
8.5 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
tested: 0x4000; bad: 0x4000
9.5 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
tested: 0x4000; bad: 0x4000
8.5 microseconds per call
tested: 0x4000; bad: 0x4000
8.6 microseconds per call
Press any key to continue

8 microseconds per call I'd put in the non-cheap category, assuming
the machine is of the order of a GHz: it's taken about 8000 instructions
to do that test.

For reliability I'd want to check things like:

assertTrue( SIZE > 0 );
char *spoo = malloc( SIZE );
assertTrue( canDereference( spoo ) );
assertFalse( canDereference( spoo + SIZE ) );
free( spoo );
assertFalse( canDereference( spoo ) );

and

int *broken( int arg ) { return &arg; }

assertFalse( canDereference( broken( 17 ) ) );

but it's possible I'm being too picky for the Clever Monkey's
context. (Mind you, I really do want to check that not only
is it legal to dereference something, but that it's mine to
dereference and not some random address taken at random out
of a random hat coloured a random shade of red.)
 
C

Chris Dollin

Chris said:
jaysome said:
[snip]
(A /specific implementation/ might offer such a function, although
I can't see it being both reliable and cheap.)

It depends on your definitions of "reliable" and "cheap".

Surely. I meant by "reliable" "gives the right answer so often it's
worth using" and by "cheap" "it's performance doesn't cripple the
application". Still room for manouever, of course.

.... and not forgetting that the context described was for /debugging/,
where it's more like "right often enough to spot bugs" and "doesn't
make debugging ridiculously slow".

Richard H's approach of "program in such a way that you don't need
to ask those kinds of questions [1]" seems ... useful.

[1] "Hardly ever" [2]

[2] But maybe often enough that you can remember what to do when
you've made a really subtle mistake.
 
F

Flash Gordon

Harald van Dijk wrote, On 26/01/07 08:35:
Keith Thompson wrote:


I would (and did) use (void *) 0. (void *) NULL is valid in this
context, but not in some others where (void *) 0 is, because it is not
necessarily a null pointer constant. All it is sure to be is a constant
null pointer.

In what context would "(void *)NULL" be invalid but "(void *)0" valid? I
can't think of any since since "(void *)NULL" will expand to either
"(void *)constant-expression-evaluating-to-0" or "(void *)(void
*)constant-expression-evaluating-to-0" and the cast from void* to void*
does no harm. Perhaps you where thinking of NULL on its own is not valid
in all contexts where (void *)0 is?
 
C

Clever Monkey


You can tell whether it's null (which I suspect is what was meant) but
you can't check that it's "valid to dereference" - it might be a
free()ed pointer for example.
[/QUOTE]
We check it for null, which is a valid enough check for a debug/logging
routine. If it has been freed and causes the debug to crash, then we
can consider this a "free" assert and can fix it, because we are
generally not interested in the pointer once it has been freed.

That is, in the context of a debug statement, a "valid pointer" is one
that is either null or can be dereferenced legally to pass to printf.
 
C

Clever Monkey

Ben said:
What kind of code do you work on?

The code I am most familiar with where we have such debug routines is a
chunk of legacy code that does a fair amount of string manipulation on
items that are passed around in "instance" pointers that refer to files
and describe relationships to other files and pointers.

When I got here the debug routines were already in place. Basically we
have a debug module that we link in that has a run-time component. We
check for a few environment variables on startup, and based on that
enable 1-4 levels of debug() statements. If the variables are not set
the debug statements are turned into no-ops.

The debugging routines are implemented as varargs (I recall), so for
strings we know might be null we pass them through a "nulString()"
helper function that returns the pointer unmaligned, or the equivalent
replacement string that simply says "null" (or something).

For other issues, like pointers that might be freed before logging them,
this would be a bug in the code and a failure in the logger may as well
be caught ASAP.

It isn't perfect, and may in fact break some conventions or rules by
today's standards, but it was put in place literally decades ago.
Changing it now would introduce significant risk without giving us
something obvious in return. It is unlikely we are going to port this
library to any more platforms. It already runs nicely on a gaggle of
Unices and a long history of Windows stretching back to Win16. (Though
I'm sure it would break nicely on Win16 now).

Alas, the sun is sinking on this code as more and more is subsumed by
newer code written in Java.
 
C

Chris Dollin

The debugging routines are implemented as varargs (I recall), so for
strings we know might be null we pass them through a "nulString()"
helper function that returns the pointer unmaligned, or the equivalent
replacement string that simply says "null" (or something).

Ahhh. So what you mean is, a function that says:

return arg == null ? "null" : arg;

Yes, that's fine in standard C, but that's not what I read "valid to
dereference" as meaning.
 
S

santosh

Clever said:
We check it for null, which is a valid enough check for a debug/logging
routine. If it has been freed and causes the debug to crash, then we
can consider this a "free" assert and can fix it, because we are
generally not interested in the pointer once it has been freed.

So what is done with the pointers after they've been free()'ed?
That is, in the context of a debug statement, a "valid pointer" is one
that is either null or can be dereferenced legally to pass to printf.

So the check doesn't really verify that a non-null pointer can be
validly deferenced. That much can be done with standard C, and IMHO,
it's not very useful.
 
C

Chris Dollin

santosh said:
So the check doesn't really verify that a non-null pointer can be
validly deferenced. That much can be done with standard C, and IMHO,
it's not very useful.

It's useful for debug prints, which is what Clever Monkey said they
were used in. Myself, I'd likely have a cut-down version of (va)printf,
with %s doing an explicit check for null: then I don't have to remember
to call the replace-null-with-"null" function in calls.
 
C

Chris Torek

(A /specific implementation/ might offer such a function, although
I can't see it being both reliable and cheap.)
[/QUOTE]

It depends on your definitions of "reliable" and "cheap".

Indeed. Particularly "reliable". :)

[snippage]
num_bad += IsBadReadPtr((void*)ii, 1) != 0;//lint !e514

Before using IsBad{Read,Write}Ptr on Windows machines, see
<http://blogs.msdn.com/larryosterman/archive/2004/05/18/134471.aspx>.
The way IsBadReadPtr affects stack guard pages is particularly
tricky.
 
C

Clever Monkey

Chris said:
It's useful for debug prints, which is what Clever Monkey said they
were used in. Myself, I'd likely have a cut-down version of (va)printf,
with %s doing an explicit check for null: then I don't have to remember
to call the replace-null-with-"null" function in calls.
We've thought about replacing this some time ago, but this is seriously
legacy code. The debug stuff is never on anymore anyway, since it is
called via JNI now.
 
C

Clever Monkey

santosh said:
So what is done with the pointers after they've been free()'ed?
Er, nothing? I'm not sure what you mean. Whether something has been
freed are it has not. Generally, during the lifetime of this library,
things are freed only in very specific places. Everywhere else they
should either be null (are not filled out in this context, or are
otherwise valid and can be used by the callee) or are valid (i.e., point
at something meaningful) in some manner.

That is, we do not malloc stuff a willy-nilly, freeing things whenever.
There are routines that allocate a chunk of memory to store a number
of structs and lists of structs (handling any allocation errors there),
and there are routines that these things up when we are done. There are
a few places where we allocate space for something, but it is generally
allocated and freed within the same function. None of our debug lines
would use such a pointer after it had been freed.

Violating this in the routines that have debug statements, when
debugging is enabled, will cause some sort of undefined behaviour. If
we catch it (whether or not the debug raises the problem) we would fix it.
 
A

Alef.Veld

Ok, so maybe i'm way off base here, but

a) why is a variable not initialized to say, NULL when it is declared
but the programmer forgot to assign some useful value to it? This also
would implicate to me that when free() frees a ptr, it assigns it null.
What other value could it possible have ? At least for pointers, it's
bad for them to point at nothingness, so why not always set them to
NULL ? I don't think this would impair the language in anyway or does
it?

I understand that it's the responsibility of the programmer et al, but
i'm pretty sure that by setting it to null _alot_ of problems would be
fixed.

Then again, maybe not :)

 
A

Alef.Veld

Err sorry i missed this:
Care to show how you would implement this ?

Ok, so maybe i'm way off base here, but

a) why is a variable not initialized to say, NULL when it is declared
but the programmer forgot to assign some useful value to it? This also
would implicate to me that when free() frees a ptr, it assigns it null.
What other value could it possible have ? At least for pointers, it's
bad for them to point at nothingness, so why not always set them to
NULL ? I don't think this would impair the language in anyway or does
it?

I understand that it's the responsibility of the programmer et al, but
i'm pretty sure that by setting it to null _alot_ of problems would be
fixed.

Then again, maybe not :)

 

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,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top