[comp.sources.bugs] pointer checking

martin@littlei.UUCP (martin) (02/17/88)

I do not want to start another net wars, but I am tired of seeing
people write programs that are non-portable between C compilers.

The material I am referencing is the latest software called "perl".

If you look at the source you will see that the authors blatenly
compaired pointers to zero without thinking what happens if
sizeof(ptr) != sizeof(int).  I know that C does not gripe if
you do something like this, but can't people program better?


Example:

char *ptr;

if (ptr) {		/* WRONG !!!!!! */
    do something
}

if (ptr != (char *)NULL) { 	/* RIGHT !!!!!! */
    do something
}

If you want people to use your software, then write it so that
it can be ported easier.


Standard disclaimer applies.

jesup@pawl20.pawl.rpi.edu (Randell E. Jesup) (02/17/88)

In article <228@gandalf.littlei.UUCP> martin@littlei.UUCP (martin) writes:
>I do not want to start another net wars, but I am tired of seeing
>people write programs that are non-portable between C compilers.

>If you look at the source you will see that the authors blatenly
>compaired pointers to zero without thinking what happens if
>sizeof(ptr) != sizeof(int).  I know that C does not gripe if
>you do something like this, but can't people program better?

>char *ptr;

>if (ptr) {		/* WRONG !!!!!! */
>    do something
>}

>if (ptr != (char *)NULL) { 	/* RIGHT !!!!!! */
>    do something
>}

	Excuse me, nothing seems wrong to me with "if (ptr)".  From K&R,
page 52:
	"Since an if simply tests the numeric value of an expression, certain
coding shortcuts are possible.  The most obvious is writing
		if (expression)
instead of
		if (expression != 0)"

Also, from K&R page 192:
	"... it is guaranteed that assignment of the constant 0 to a pointer
will produce a null pointer distinguishable from a pointer to any object."

So, NULL must be equivalent to 0.

I will note that there are a couple of broken compilers out there that MIGHT
get this wrong.  However, it is most certainly absolutely correct C.

You are right, however, to note that there are far too many programs that
assume sizeof(int) == sizeof(pointer), and that this breaks things (or assume
that sizeof(int) == sizeof(long))!

     //	Randell Jesup			      Lunge Software Development
    //	Dedicated Amiga Programmer            13 Frear Ave, Troy, NY 12180
 \\//	beowulf!lunge!jesup@steinmetz.UUCP    (518) 272-2942
  \/    (uunet!steinmetz!beowulf!lunge!jesup) BIX: rjesup

danny@itm.UUCP (Danny) (02/17/88)

In article <228@gandalf.littlei.UUCP> martin@littlei.UUCP (martin) writes:
~...
~sizeof(ptr) != sizeof(int).  I know that C does not gripe if

    Yes, indeed, it is wrong to assume that sizeof (ptr) == sizeof (int),
BUT: read on!

~Example:
~
~char *ptr;
~
~if (ptr) {		/* WRONG !!!!!! */
~    do something
~}

    Would you expect a C compiler to generate erroneous code for the
following?

    char   a;
    int    b;
    long   c;
    double d;
    
    if (a) {
      ...
    if (b) {
      ...
    if (c) {
      ...
    if (d) {
      ...
    
    The compiler knows exactly what size each of the above types is.
Pointers, as far as comparison goes, are exactly the same.

Any compiler that doesn't properly compile
    char *p;
    
    if (p) {
        do something;
    }
is broken.  The compiler knows the size of the pointer, and _should_
compare against the same size of a constant zero.

                                            Danny
-- 
				Daniel S. Cox
				(seismo!gatech!itm!danny)

jpn@teddy.UUCP (John P. Nelson) (02/18/88)

In article <228@gandalf.littlei.UUCP> martin@littlei.UUCP (martin) writes:
>I do not want to start another net wars, but I am tired of seeing
>people write programs that are non-portable between C compilers.

Please, people, DON'T followup to this.  This subject was hashed
to death in comp.lang.c.  The construct cited in the article
" if (ptr) { do something }". is both valid and 100% portable, at least
according to both Kernigan and Richie, and the proposed ANSI standard.
Perhaps Martin has a buggy compiler that will not accept this construct,
but all correct C compilers handle it as written.

janc@palam (Jan Wolter) (02/18/88)

NULL is a pointer with an illegal value.  In some architectures, the address
0 is a perfectly reasonable address, so it is concievabale that someone might
set up a system with NULL defined as ((char *) -1), or some such.  However,
I've never heard of anyone doing that, and, in fact, it would be contrary
to K&R's definition of C.  Usually some other action is taken to assure
that the memory at address 0 will not contain anything anyone will want to
point to.

Nevertheless, I think in terms of readiblity,

	if (ptr == NULL)

is superior to

	if (!ptr)

It makes what you are doing clearer, generates the same code, and might even
prove more portable to some goofy machine.  Doing a type cast like
(ptr == (char *)NULL) shouldn't be necessary, since NULL should be defined
as (char *)0.  (Some 32-bit systems define NULL as 0, which just helps
perpetuate the confusion between integers and pointers which Vax programmers
seem to sufferer from).

The real portability problems arise most commonly from people not declaring
routines that return a pointer or an integer.  It's common for people to use
lseek() or malloc() without including:

	char *malloc();
	long lseek();

This is sure to screw up any 16-bit system, an a very nasty and hard to debug
way.  I recommend that you explicitly declare anything who's returned value
you are using, even if it returns an int.  Defaults stink.  Another common
error is in the passing of arguments.  For example, the man page on my Sun
gives the function synopsis for execl() as:

	execl(name,arg0,arg1, ..., argn, 0)
	char *name, *arg0, *arg1, ..., *argn;

which is dead wrong.  That should be a NULL, not a 0.  It's no wonder that
this kind of error is made all the time.  I wish enough compilers supported
function prototyping so that people would actually start using it.  It's
astonishing the number of people who don't know the difference between NULL,
0L and 0.

These two types of things account for 90% of all errors I encounter when
porting software to systems with 16-bit integers.  The rest are mainly
using %d to print a long in a printf(), and assuming you can put 32-bit
numbers in an int.  These are comparitively scarce.

				Jan Wolter
				janc@crim.eecs.umich.edu

rbj@icst-cmr.arpa (Root Boy Jim) (02/18/88)

   I do not want to start another net wars, but I am tired of seeing
   people write programs that are non-portable between C compilers.

You had better go check your facts, because you are dead wrong.

   The material I am referencing is the latest software called "perl".

Larry Wall, the author, takes great pains to allow his software to
run in incredibly diverse environments. Look at the configure script.

   If you look at the source you will see that the authors blatenly
   compaired pointers to zero without thinking what happens if
   sizeof(ptr) != sizeof(int).  I know that C does not gripe if
   you do something like this, but can't people program better?


   Example:

   char *ptr;

   if (ptr) {		/* WRONG !!!!!! */
       do something
   }

YOU are wrong. This has always been allowed.

   if (ptr != (char *)NULL) { 	/* RIGHT !!!!!! */
       do something
   }

Also correct, altho the cast is really not needed.

   If you want people to use your software, then write it so that
   it can be ported easier.

Does your compiler barf? If so, it is incorrect.

   Standard disclaimer applies.

	(Root Boy) Jim Cottrell	<rbj@icst-cmr.arpa>
	National Bureau of Standards
	Flamer's Hotline: (301) 975-5688
	I'm into SOFTWARE!

phil@amdcad.AMD.COM (Phil Ngai) (02/18/88)

In article <228@gandalf.littlei.UUCP> martin@littlei.UUCP (martin) writes:
>I do not want to start another net wars, but I am tired of seeing
>people write programs that are non-portable between C compilers.

The problem is not in the program but in your compiler and/or your
understanding of C. But I shouldn't be surprised, considering that you
are posting from an Intel site, you're probably using a brain-damaged
processor. It wouldn't be an 8088, would it?

-- 
I speak for myself, not the company.

Phil Ngai, {ucbvax,decwrl,allegra}!amdcad!phil or phil@amd.com

martin@littlei.UUCP (martin) (02/19/88)

Okay, I spoke my word and some others did, let's end it now.  Some of
you have a good point.  Thanks for the follow-ups...

bc@halley.UUCP (Bill Crews) (02/21/88)

All right, I know and I am sure most of you out there know that Jan is
supplying us with incorrect information in this message.  Please let's not
follow up (in THIS group, anyway) on this subject.  I have directed follow-ups
to comp.lang.c, but I discourage postings there, too, because this subject
comes up every couple of MONTHS, it seems.  This is one of those rare instances
where one side is RIGHT and the other side WRONG.  If you really don't
understand why he (she?) is wrong, please REPLY to me, read K&R CAREFULLY, or
consult your local guru.  Thanks.

In article <779@zippy.eecs.umich.edu> janc@palam.eecs.umich.edu (Jan Wolter) writes:

>NULL is a pointer with an illegal value.  In some architectures, the address
>0 is a perfectly reasonable address, so it is concievabale that someone might
>set up a system with NULL defined as ((char *) -1), or some such.
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If it does, it is DEAD WRONG!  The 0 is interpreted by the COMPILER and by the
LIBRARY, NOT by the machine!  NULL is NOT an ADDRESS!

>                                                    NULL should be defined
>as (char *)0.  (Some 32-bit systems define NULL as 0, which just helps
>perpetuate the confusion between integers and pointers which Vax programmers
>seem to sufferer from).

(char *) 0 is no better that just 0.  If I were going to cast it to anything, I
might select (void *) 0, but it really wouldn't solve any problems.  The
programmer simply must know to cast NULL in argument lists, structure member
lists (when you care to match external data), etc.  Note that, at least in
general, casting NULL in EXPRESSIONS is NOT required.

>	execl(name,arg0,arg1, ..., argn, 0)
>	char *name, *arg0, *arg1, ..., *argn;
>
>which is dead wrong.  That should be a NULL, not a 0.

Wrong, also.  Using just NULL leaves the type of the argument up in the air.
You are right that 0 is wrong, though.  Only (char *) NULL [or (char *) 0] is
right.  [Note that (void *) 0 would be wrong here, also.]  I pick (char *)
because that is the type execl() is expecting.  We wouldn't want to disappoint
execl(), now would we?

-bc
-- 
Bill Crews                                   Tandem Computers
bc@halley.UUCP                               Austin, Texas
..!rutgers!im4u!halley!bc                    (512) 244-8350

bc@halley.UUCP (Bill Crews) (02/21/88)

In article <20432@amdcad.AMD.COM> phil@amdcad.UUCP (Phil Ngai) writes:
>In article <228@gandalf.littlei.UUCP> martin@littlei.UUCP (martin) writes:
>>I do not want to start another net wars, but I am tired of seeing
>>people write programs that are non-portable between C compilers.
>
>The problem is not in the program but in your compiler and/or your
>understanding of C. But I shouldn't be surprised, considering that you
>are posting from an Intel site, you're probably using a brain-damaged
>processor. It wouldn't be an 8088, would it?

I am trying to discourage this discussion in this group, but I must correct the
misimpression created by your article.  While I am certainly no fan of Intel
processor architectures, martin's example of if(ptr) is not a problem on any of
the C compilers I have ever used on Intel machines (Microsoft, Lattice, DeSmet,
etc).  If it were, the compiler would not be for the C language.

-bc
-- 
Bill Crews                                   Tandem Computers
bc@halley.UUCP                               Austin, Texas
..!rutgers!im4u!halley!bc                    (512) 244-8350

janc@crim (Jan Wolter) (02/22/88)

In article <325@halley.UUCP> bc@halley.uucp (Bill Crews) writes:
> All right, I know and I am sure most of you out there know that Jan is
> supplying us with incorrect information in this message.  [...]  If you
> really don't understand why he (she?) is wrong, please REPLY to me, read
> K&R CAREFULLY, or consult your local guru.  Thanks.

> In article <779@zippy.eecs.umich.edu> janc@palam.eecs.umich.edu (Jan Wolter) writes:
>
> >NULL is a pointer with an illegal value.  In some architectures, the address
> >0 is a perfectly reasonable address, so it is concievabale that someone might
> >set up a system with NULL defined as ((char *) -1), or some such.
>                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>
> If it does, it is DEAD WRONG!  The 0 is interpreted by the COMPILER and by the
> LIBRARY, NOT by the machine!  NULL is NOT an ADDRESS!

I might suggest that as a third alternative, people who don't understand this
might read the next sentance in my response quoted above.  The response says:

> NULL is a pointer with an illegal value.  In some architectures, the address
> 0 is a perfectly reasonable address, so it is concievabale that someone might
> set up a system with NULL defined as ((char *) -1), or some such.  However,
> I've never heard of anyone doing that, and, in fact, it would be contrary
> to K&R's definition of C.

In the future I will take care to state my facts with plenty of CAPS and
exclaimation points so that Bill will not overlook them and proclaim my
idiocy so stridently over the net.

I can claim little expertise in what is correct C, but I do know what is
portable.  Bill's suggestion of casting NULL as (void *) is a dubious one,
as many older compilers do not support the void type at all, or just do
#define void int.  I'm am not familiar with any system in which it is
necessary to type cast NULL in argument lists.  I don't know on what basis
Bill says NULL is not a pointer (K&R says nothing to suggest that) but I
know code that treats it as such is very portable.

Bill claims that this has been discussed before.  I would certainly not want
to say anything on the net that is not new.  I enter this here only to correct
the misquote.  Follow-ups should go to comp.lang.c.  Thank you for your
patience,

				Jan Wolter
				janc@crim.eecs.umich.edu

janc@crim (Jan Wolter) (02/23/88)

I fear that in my previous submission I misunderstood Bill Crew's response
as thoroughly as I misunderstood the NULL pointer.  I offer my sincerest
apologies to Bill and the net.

Much abashed,
				- Jan Wolter
				  janc@crim.eecs.umich.edu

allbery@ncoast.UUCP (Brandon Allbery) (02/25/88)

As quoted from <11878@brl-adm.ARPA> by rbj@icst-cmr.arpa (Root Boy Jim):
+---------------
(Some ignorant idiot comments:)
|    If you look at the source you will see that the authors blatenly
|    compaired pointers to zero without thinking what happens if
|    sizeof(ptr) != sizeof(int).  I know that C does not gripe if
|    you do something like this, but can't people program better?
+---------------

Jim's right:  this is legal, although I would call it stylistically incorrect.
This works fine as long as it is a literal zero; the definition of the C
language is such that the constant 0 is promoted to a pointer of the required
type.  A cast is only required for (inherently non-portable) nonzero constants
or for variables (zero or not).  Me, I use the cast always, to make sure the
reader of my code knows that I'm talking pointers.

+---------------
| Does your compiler barf? If so, it is incorrect.
+---------------

WHICH is incorrect?  Ncoast's compiler barfs on perl... mainly because it
tickles a bug with relational operators.  Normally this shows up as a warning
"illegal pointer/integer combination, op =" when I use something like:

	if ((win = newwin(0, 0, 0, 0)) != (WINDOW *) 0)

using curses -- the fix is an explicit cast (of course, "newwin()" is defined
as WINDOW *; it still has to be cast to avoid the warning!), but in the case of
perl, in arg.c there is some code:

	value = (double) (var != var);		/* paraphrased */

which gets "compiler error: no table entry for op =" unless I phrase it as

	value = (double) ((int) (var != var));

Actually, this is one of three compiler bugs; nethack (of all things) shows
all three.  Except that perl may have tickled a REAL weird one, since with
return values always going through ints by the time yylex has returned to
yyparse the token number has somehow been truncated into a char....

Anyone got a WORKING computer to give us?  ;-) ;-(
-- 
	      Brandon S. Allbery, moderator of comp.sources.misc
       {well!hoptoad,uunet!hnsurg3,cbosgd,sun!mandrill}!ncoast!allbery
KABOOM!!! Worf: "I think I'm sick." LaForge: "I'm sure half the ship knows it."