Logical Value Of A Pointer

J

James Kanze

It makes the intent of the coder clearer?
If I see "if (a != NULL)" I immediately know that a is a
pointer and that we're checking that it's not a null pointer.
Or more precisely, if this is /not/ the case, then I'm dealing
with such a perverse coder that all bets are off...
If I see "if (a != 0)" then I have fewer clues what's going on
and quite likely have to check a lot of other code to find
out.

That wasn't the point. While I also prefer NULL to 0 when
pointers are involved, there are serious problems with both
solution, and I understand the arguments for 0. I don't think
that there is a conclusive argument one way or the other
there---it's a question of which problems you consider the most
serious. The issue is
if ( a == NULL )
or if ( a == 0 )
vs if (!a)
; the implicit conversion to bool.
 
A

Andrew Koenig

For the same reason implicit conversions in general are to be
avoided. A pointer isn't a bool, so it doesn't make sense to
use it as one; doing so only leads to confusion. Say what you
mean, and mean what you say.
(BTW: you're the co-author of the original proposal for bool.
And there, unless I'm remembering wrong, you proposed
deprecating the above conversion. Have you changed your mind?
And if so, what made you change it?)

I'd love to deprecate conversion of pointers to bool ... but I don't
consider

if (p) { /* ... */ }

to be an instance of such a conversion. After all, this usage was around
for years before anyone considered the conversion!

Rather, I consider

if (p) { /* ... */ }

to be an abbreviation for

if (p != 0) { /* ... */ }

which is an implicit conversion of 0 to a pointer, not of p to bool.

I understand that the standard doesn't describe it that way, but that's just
a matter of descriptive convenience, and doesn't affect how I personally
think about it.
 
A

Andrew Koenig

It makes the intent of the coder clearer?
If I see "if (a != NULL)" I immediately know that a is a pointer and that
we're checking that it's not a null pointer. Or more precisely, if this
is /not/ the case, then I'm dealing with such a perverse coder that all
bets are off...

To me, "if (a != NULL)" is saying the same thing twice. That is... I don't
think that code should mention a variable's type more than once if it is
reasonably possible to avoid doing so.
 
L

Lionel B

That wasn't the point.

Yes, I jumped into he middle of the thread.
While I also prefer NULL to 0 when pointers are
involved, there are serious problems with both solution, and I
understand the arguments for 0. I don't think that there is a
conclusive argument one way or the other there---it's a question of
which problems you consider the most serious.

Sure, that's been done to death (repeatedly) on this ng.
The issue is
if ( a == NULL )
or if ( a == 0 )
vs if (!a)
; the implicit conversion to bool.

See my posts else thread.
 
L

Lionel B

To me, "if (a != NULL)" is saying the same thing twice.

Pardon?

To me, it just looks like comparing 'a' with something called 'NULL'
which is a bit of bodge, but nonetheless associated with pointers. So I
would /suspect/ that 'a' is something pointer-like, although to be sure I
would have to check some code.
 
P

peter koch

I'd love to deprecate conversion of pointers to bool ... but I don't
consider

    if (p) { /* ... */ }

to be an instance of such a conversion.  After all, this usage was around
for years before anyone considered the conversion!

Rather, I consider

    if (p) { /* ... */ }

to be an abbreviation for

    if (p != 0) { /* ... */ }

which is an implicit conversion of 0 to a pointer, not of p to bool.

I understand that the standard doesn't describe it that way, but that's just
a matter of descriptive convenience, and doesn't affect how I personally
think about it.

I have mixed feelings about if (ptr): when I began programming C, I
always wrote if (ptr != NULL). That was many years ago, and I have now
changed attitude and consider it idiomatic (replacing NULL with 0 and
C with C+++) and in line with other common shorthands such as while
(stream >> i).
What is a big shame in my opinion is the automatic conversion to (and
also from) bool: It has caused so many problems and inconveniences for
everyone. I wonder what the rationale was.

/Peter
 
J

James Kanze

"James Kanze" <[email protected]> wrote in message
I'd love to deprecate conversion of pointers to bool ... but I don't
consider
if (p) { /* ... */ }
to be an instance of such a conversion.

But the standard does. And IIRC, so did your original paper.
After all, this usage was around for years before anyone
considered the conversion!

Yes, because early C was rather flippant about types. An
attitude left over from B, I suppose. (In B, and in other
"untyped" languages, like AWK, I have no problems with this.
Although even in AWK, if I'm using a variable as a number or a
string, rather than as a boolean, I'll write the test out.)
Rather, I consider
if (p) { /* ... */ }
to be an abbreviation for
if (p != 0) { /* ... */ }
Why?

which is an implicit conversion of 0 to a pointer, not of p to
bool.
I understand that the standard doesn't describe it that way, but that's just
a matter of descriptive convenience, and doesn't affect how I personally
think about it.

I'm not sure I follow you: are you saying that anytime a
variable (pointer or arithmetic type) is used in a condition, it
should automatically be treated as if there was a != 0 behind
it? A sort of a short cut way of writing it?
 
J

James Kanze

This may go without saying, but you obviously have a very deep
understanding of how C++ syntax can be used to reflect
high-level semantic meaning. I wish that level of
understanding were more widespread.
To a human reader, the != 0 ought to be considered noise,
AFAICS.

It specifies what you are testing for. If a pointer only had
two possible values, it would be noise. Since that's not the
case, it's important information.
The ability to test things other than raw bools is not just
for pointers, either; e.g: while (std::cin >> s) { ... }. I
don't consider p != 0 any better than b != false. And don't
even get me started on NULL. :)
This argument seems to be a perennial favorite on Usenet. It
seems to come down to a division between people who think
generically, and others who think (or at least code)
orthogonally.

The argument goes back long before generic programming. The
argument is between those who believe in static type checking,
and those who are too lazy to type.

Just kidding, of course---but the issue IS type checking.

With regards to "generic" programming, I can understand the need
for some sort of "generic" is valid. The problem is that we
don't have it. Whether you write "if ( p == NULL )", or just
"if (p)", the constraints on p are identical. The difference is
that in the first case, it's clearly apparent what those
constraints are.
The first of these camps, the Generics, believe that syntax
should convey abstract meaning, with low-level details
supplied by context. The second camp, the Orthogonals,
believe that a particular syntax should mean exactly the same
thing in all contexts, and that even (or especially) subtly
things should always look different.

I don't think you've understood the argument. In a well
designed language, pointers aren't boolean values (since they
have more than two values), and can't be used as such. And type
checking is strict, because that reduces the number of errors.

The problem is that the implicit conversion doesn't cover all of
the cases. Even in the case of pointers, we have three cases
which can legally occur: the pointer points to an object, it
points to one behind the end of an array of objects, or it is
null. So which one do you priviledge by the conversion?
I consider myself in the Generic camp. The advantage of this
style is that similar ideas are implemented by similar-looking
code. A single piece of source code can often be reused in
multiple contexts, even if the compiler generates wildly
different object code for each of them. For example, I like
the fact that the following all look the same:
throw_if(!p, "null pointer");
throw_if(!cin, "input error");
throw_if(!divisor, "attempted division by zero");

throw_if( p != 0, "null pointer");
throw_if( cin != 0, "input error");
throw_if( divisor != 0, "attempted division by zero");

They all look the same to me. Even better:

throw_if( isValid( p ), "null pointer");
throw_if( isValid( cin ), "input error");
throw_if( isValid( divisor ), "attempted division by zero");

With isValid defined with an appropriate overload (which would
return false for a pointer one past the end as well---except
that I don't know how to implement that).
Once I understand the pattern, I don't want to have to
optically grep the details of expressions like p == NULL,
!cin.good(), and divisor == 0. They all have the same
high-level meaning, and that's usually what I care about.

The problem is that no one really knows what that high-level
meaning is, since it is radically different for each type.
The apparent down side to this style is that subtle mistakes
can hide in it. It's easy for the reader to see the author's
intent,

The problem is that it's impossible for the reader to see the
author's intent (except from the string literal in your
example---but that's not typically present). It's a recepe for
unreadable code.
but relatively difficult to see the bit-level reality.
However, I would argue that this is the price we pay for
abstraction, and attempts to include context-specific
information in our code without loss of abstraction give only
the illusion of clarity.
The ostensible advantage of something like p != NULL is that p
is clearly a pointer.

No. The advantage is that it isn't "p != 0", and that 0 clearly
isn't a pointer.

The whose system is broken. The problem is that both "p != 0"
and "p != NULL" are lies. The next version of the standard will
fix it somewhat, with nullptr (but for historical reasons, of
course, 0 and NULL must still remain legal).
Or is it? The compiler has no problem with a comparison of
the form std::cin != NULL. In p != NULL, the word NULL seems
to hint that p is a pointer, but that information is neither
relevant to the expression, nor enforceable by the compiler.

G++ warns if you use NULL in a non-pointer context. It could
just as easily generate an error. So this is enforceable by the
compiler.
When it becomes necessary to understand that p is a pointer,
the fact should become evident, as in an expression like
p->size(). Even then, p might be a raw pointer, an iterator,
or some kind of fancy smart pointer. In decent C++ code, IMO,
we shouldn't have to know.

Except that we do, since neither "if (p)" nor "if ( p != NULL )"
nor "if ( p != 0 )" work if p is an iterator.

The real solution here is an appropriate set of overloaded
functions. I use Gabi::begin(), and Gabi::end(), for example;
they work with C style arrays, and are easily made to work with
STL containers. (In other words, if the STL had been designed
for genericity, it would have defined a function begin(
std::vector<> ), and not std:vector<>::begin().)
 
B

Bertrand

Pete said:
The term "over redundant" is brought to you by the Department of
Redundancy Department.


But why stop there?

if ((condition == true) == true)

ad infinitum.
Yep! you got my point. ;-)
 
B

Bertrand

Lionel said:
Sure, I'm not claiming it's "wrong" to write 'if (p)', simply that 'if (p
== NULL)' - or, for that matter, 'if (p == 0)' - is clearer in its intent.
just about, imho. if you like clarity and you think if( p ) is not clear
enough, then you should prefer this: if( is_valid_ptr( p )), with
something like that defined somewhere:
template< typename T >
bool is_valid_ptr( T* p ) { return p != 0; }

Note that here a simple return p; would cause MSVC to issue the
``stupid'' performance warning mentioned elsewhere in this thread.
Actually my argument re. STL was about container iterators.
No worries, I was looking for examples in a well accepted library, not
really replying to something specific you wrote.
Sure the streams implicit conversion is useful and such a common idiom
that it seems perfectly natural in that context. I guess I'm arguing
against general usage where the context is not necessarily that clearcut.


I guess you meant 'if( !p.get() )' ... now which style was clearer? ;-)
Ah, ah, sure. It does not mean /more/, but it does mean something
/different/ then ;-)
I meant to write if( p.get() != 0 ) which was the follow up on the
initial if( a != 0 ) or if( a != NULL ). But it was already late for me...
Nevertheless, even with if( !p.get() ) I stick to my favourite style.
 
A

Andrew Koenig


Because it's an idiom that has been in common usage for more than 30 years.
I'm not sure I follow you: are you saying that anytime a
variable (pointer or arithmetic type) is used in a condition, it
should automatically be treated as if there was a != 0 behind
it? A sort of a short cut way of writing it?

I'm saying that that's how people who have been programming in C or C++ for
a long time often think about it.

I find it easier to read

if (p && p->thing == "foo") { ... }

than to read

if (p != NULL && p->thing == "foo") { ... }

because the "!= NULL" in the second example is redundant and makes me stop
to think "Why did the author of that statement put the redundant comparison
in?"
 
J

James Kanze

If the variable 'a' is defined as having a pointer type, then
the "!= NULL" is just restating something we already know.

How's that? It's stating that we're comparing it with a null
pointer. And not, for example, comparing it with a pointer to
one past the end. We're testing whether a pointer has a
specific value: the != tells us that we consider the results
true if it *doesn't* have this value, and the NULL tells us that
the value in question is a null pointer. Both are very
pertinent information.
 
J

James Kanze

Because it's an idiom that has been in common usage for more
than 30 years.

In some circles. In others, no. In the groups I've worked
with, ``if (p)'' has never been used. Personally, I find it
confusing, and I have to stop and think about it each time I see
it. (And I've probably got almost as much experience in C and
C++ as you do:).)
I'm saying that that's how people who have been programming in
C or C++ for a long time often think about it.

How some people think about it, perhaps. But it's certainly not
universal, and in itself, is confusing.
I find it easier to read
if (p && p->thing == "foo") { ... }
than to read
if (p != NULL && p->thing == "foo") { ... }
because the "!= NULL" in the second example is redundant and
makes me stop to think "Why did the author of that statement
put the redundant comparison in?"

Maybe because he wanted to make it clear to others what he was
testing?

I think that the widespread adoption STL iterator idiom makes
this even more important. I don't write:

if ( iter && iter->... )

, for the obvious reason that I can't. People expect to see a
comparison when an iterator is used, and this expectation
carries over to pointers. If we accept the ``if (p)'' idiom,
then logically, we should insist on the GoF pattern for
iterators, with an implicit conversion to bool, returning
!isDone.

While I prefer the GoF pattern myself (and use it at least as
often as the STL pattern, since I like filtering iterators and
such), I don't agree with the implicit conversion either. Say
whay you mean, and mean what you say---an iterator or a pointer
is not a bool, and I don't like to pretend it is.
 
N

Noah Roberts

James said:
>
>
>
> How's that? It's stating that we're comparing it with a null
> pointer.

Like he said, we know that already.
> And not, for example, comparing it with a pointer to
> one past the end.

We know that also.
 
A

Andrew Koenig

How's that? It's stating that we're comparing it with a null
pointer.

Right. Therefore, we are restating that 'a' has a type that can be compared
with a null pointer.

But if we have already defined 'a' as having a pointer type, we know that we
can compare 'a' with a null pointer, so I think we should not restate that
fact in our code if we can reasonably avoid doing so.
 
A

Andrew Koenig

I think that the widespread adoption STL iterator idiom makes
this even more important. I don't write:

if ( iter && iter->... )
, for the obvious reason that I can't. People expect to see a
comparison when an iterator is used, and this expectation
carries over to pointers.

If you feel that way, then you shouldn't be writing

if (p != NULL)

either because there is no equivalent to a null pointer in the iterator
universe.
 
J

James Kanze

if ( iter && iter->... )
If you feel that way, then you shouldn't be writing
if (p != NULL)
either because there is no equivalent to a null pointer in the
iterator universe.

I don't use it very often; for reasons of consistency, I tend to
use the iterator idioms even when dealing with pointers. (Even
if I think that the idiom is not very optimal.) But you're
missing my point entirely---a pointer isn't a bool, because it
has more than two values. And because you can compare it to
many different things, you should state what you're comparing it
to. (I don't approve of things like "if ( booleanVariabe ==
true )", for example. That shows a lack of understanding of the
type system. Just like "if (p)":).)
 
J

James Kanze

But then how would an algorithm pass off a subset of the range
to another function? It could use ranges, but then you'd need
two interfaces for each algorithm.

Define an iterator which represents a range.

In practice, about the only time I've found that I need to pass
sub-ranges is when parsing. In which case, I either have a
string (and could use indexes), or an istreambuf_iterator (and
have to copy anyway). In addition, when parsing, it's usually
better if iterators have reference semantics; when a function
consumes characters, you want them consumed in the calling
function as well. So I tend to use C++'s other iterator idiom
for this: std::streambuf. (Actually, I've since implemented my
own ParserSource hierarchy. On one hand, to avoid all the extra
baggage of buffer management when not needed, and on the other,
to provide for automatic copying when needed.)
 

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
473,774
Messages
2,569,598
Members
45,144
Latest member
KetoBaseReviews
Top