Re: [PATCH] Fixes sentinel warnings on GCC4

classic Classic list List threaded Threaded
26 messages Options
12
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Fixes sentinel warnings on GCC4

Karl Hegbloom
On Tue, 2005-12-06 at 08:43 -0800, Karl Hegbloom wrote:

> On Mon, 2005-12-05 at 21:25 -0500, Chris Shoemaker wrote:
> > If NULL is ((void*)0) you won't have any problem using NULL as a
> > sentinel, but using that definition of NULL opens up the possibility
> > of writing code that works with one implementation and not with
> > another.  Specifically, you couldn't say "int *i = ((int *) NULL)" and
> > be sure it would mean the same thing for every compiler.
>
> Yes you could.  The whole point of the (void *) type is to represent a
> generic pointer that can be assigned to any other pointer type _without_
> a cast.  That's why it was invented.  In C, the variable holds the type,
> not the value, so the (void *)0 automatically becomes an (int *) without
> a cast.

To disambiguate... here I mean that the type information is held by the
internal compiler struct that represents the variable, not by a struct
that represents the variable's value.  For contrast, in Lisp, the value
has a type, but the variable location can hold an object of any type.

--
Karl Hegbloom <[hidden email]>

_______________________________________________
gnucash-devel mailing list
[hidden email]
https://lists.gnucash.org/mailman/listinfo/gnucash-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Fixes sentinel warnings on GCC4

Chris Shoemaker
In reply to this post by Karl Hegbloom
On Tue, Dec 06, 2005 at 08:46:00AM -0800, Karl Hegbloom wrote:
> On Tue, 2005-12-06 at 00:55 -0500, Chris Shoemaker wrote:
> > In the gcc ML thread where the -Wstrict-null-sentinel warning was
> > added, it was claimed that at least some versions of HPUX and Solaris
> > 2.8 on 64-bit machines #define NULL 0.
>
> Then those system headers are wrong,

You may prefer #define NULL ((void*)0), but #define NULL 0 is not
"wrong", if "wrong" means violating some standard.

C has always required that "0" in a pointer context represents the
null pointer constant.  (see K&R, 2ed p. 102) Furthermore, C99
clarifies that *any* integer constant expression with the value 0 is
the null pointer constant.  (This is why, e.g. "GncBudget *bgt = 0;"
is 100% portable.)  It also says the same thing about any such
constant cast to (void *).

Also, according to the standard, NULL may expand to any
implementation-defined null pointer constant.  That includes both "0"
and "(void*)0".  This is compeletely valid and useful behavior,
because any standard-compliant compiler must read both "0" (when in a
pointer context) and "(void*)0" as valid null pointer constants.

> not the code that uses NULL as a (void *)0.

(This is a separate issue, and if you care to define "wrong" as
implementation-dependent and not portable, then, yes, it is "wrong".)
Any code that _depends_ on NULL expanding to (void*)0, is depending on
something that is not guaranteed by the language.  It is *not* 100%
portable.  Look:

  int i = NULL;  // BAD IDEA!

Yes, this *works*!  IFF NULL expands to "0".  But, it's not portable
because NULL *may* expand to (void*)0.  This is *exactly* why using
"NULL" (or even "0") as a sentinel is not portable.  Portable code
*must* explicitly cast sentinels.  Don't blame me - blame Ritchie.

Remember: arguments to variadic functions are NOT in a pointer
context.  

-chris
_______________________________________________
gnucash-devel mailing list
[hidden email]
https://lists.gnucash.org/mailman/listinfo/gnucash-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Fixes sentinel warnings on GCC4

Chris Shoemaker
In reply to this post by Karl Hegbloom
On Tue, Dec 06, 2005 at 08:43:21AM -0800, Karl Hegbloom wrote:
> On Mon, 2005-12-05 at 21:25 -0500, Chris Shoemaker wrote:
> > If NULL is ((void*)0) you won't have any problem using NULL as a
> > sentinel, but using that definition of NULL opens up the possibility
> > of writing code that works with one implementation and not with
> > another.  Specifically, you couldn't say "int *i = ((int *) NULL)" and
> > be sure it would mean the same thing for every compiler.

Sorry, this was a bad example off the top of my head.  The above actually
is well-defined because of the special behavior of void.  The only
downside to #define NULL ((void*)0) is that it can cause wrong
programs to work anyway on certain machines.  See
(http://www.spinellis.gr/news/8.html).

>
> Yes you could.  The whole point of the (void *) type is to represent a
> generic pointer that can be assigned to any other pointer type _without_
> a cast.  That's why it was invented.  In C, the variable holds the type,
> not the value, so the (void *)0 automatically becomes an (int *) without
> a cast.

Maybe this is a (the?) source of confusion.  (void*)0 is not a
variable.  It is a special implementation-defined constant.

>
> A pointer to a char is the same size as a pointer to an int and is the
> same size as a pointer to void.  

This is not generally true for typed null pointers.  Implementations
are free to use different representations for null pointers of
different types.  This has in fact been done.  Using the null-pointer
of the wrong type is not portable.  This is why David was right about
(void*) being better than (char*) on the sentinel.

-chris

> The only difference is that when you
> increment a char*, it clicks by one byte, and when you increment an
> int*, it clicks by sizeof(int) bytes.  Since int *i, above, is a pointer
> to int, it works as expected even though you initialize it with NULL.
>
> --
> Karl Hegbloom <[hidden email]>
>
> _______________________________________________
> gnucash-devel mailing list
> [hidden email]
> https://lists.gnucash.org/mailman/listinfo/gnucash-devel
_______________________________________________
gnucash-devel mailing list
[hidden email]
https://lists.gnucash.org/mailman/listinfo/gnucash-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Fixes sentinel warnings on GCC4

Stuart D. Gathman
On Tue, 6 Dec 2005, Chris Shoemaker wrote:

> > A pointer to a char is the same size as a pointer to an int and is the
> > same size as a pointer to void.  
>
> This is not generally true for typed null pointers.  Implementations
> are free to use different representations for null pointers of
> different types.  This has in fact been done.  Using the null-pointer
> of the wrong type is not portable.  This is why David was right about
> (void*) being better than (char*) on the sentinel.

Pointers are generally the same size on byte-addressable machines like
i386 and ppc.  You run into trouble with old architectures and embedded
RISC.  

<old-timer>
My favorite old architecture was the DEC-20.  It had 36-bit words. C types:

char 9 bits
short 18 bits
int 36 bits
long 72 bits
float 36 bits
double 72 bits
char * 36 bits
short * 36 bits
int * 18 bits
long * 18 bits
int ** 36 bits
char ** 36 bits
...

Most pointers were word pointers.  Char and short pointers used a special
subfield pointer extension that had its own load/store/inc/dec instructions.

I find it useful to ask myself whether the code would run properly on that
system when considering a portablity question.  Not that a DEC-20 would
be all that useful today.  2^18 * 36 bits = 2Meg * 9-bits byte addressable RAM.
The system I used in college had 256K words = 1M * 9-bit RAM.  If you
were lucky enough to be allowed to use the console, it had a memory mapped
graphics display, and the extended pointer format could also address individual
pixels!  The display was featured in my favorite "Flatland" fiction:
        _The_Planiverse_
        http://www.amazon.com/gp/product/0387989161/
</old-timer>

More relevant today are graphics processors (which I guess is a type of
embedded), which often have varying pointer sizes, including pointers
to bits and nybbles, to optimize bit pixel operations.

--
              Stuart D. Gathman <[hidden email]>
    Business Management Systems Inc.  Phone: 703 591-0911 Fax: 703 591-6154
"Confutatis maledictis, flamis acribus addictis" - background song for
a Microsoft sponsored "Where do you want to go from here?" commercial.

_______________________________________________
gnucash-devel mailing list
[hidden email]
https://lists.gnucash.org/mailman/listinfo/gnucash-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Fixes sentinel warnings on GCC4

Karl Hegbloom
In reply to this post by Chris Shoemaker
On Tue, 2005-12-06 at 16:07 -0500, Chris Shoemaker wrote:
> Yes, this *works*!  IFF NULL expands to "0".  But, it's not portable
> because NULL *may* expand to (void*)0.  This is *exactly* why using
> "NULL" (or even "0") as a sentinel is not portable.  Portable code
> *must* explicitly cast sentinels.  Don't blame me - blame Ritchie.
>
> Remember: arguments to variadic functions are NOT in a pointer
> context.

After reading what Stuart D. Gathman wrote in this thread, I'm thinking
that it's not Ritchie's fault either.  It's a matter of mapping the C
language to the hardware and it's assembly language.

I was going to respond that I think it's BS to have to cast the NULL
pointer, but now I see why it is necessary.  It does have to do with
context.  If it's a pointer to a type where the pointer is 8 bits, and
the stdarg code expects a (void *)0 that turns out to be 32 bits... the
input argument is not automatically extended to 32 bits because the
compiler cannot see the need for it unless you tell it with the cast or
an assignment to a variable with a larger sized pointer type.

We probably should not even assume that (equalp NULL 0).  Maybe it's
0xff?  In either case, the explicit cast to (void *) will port, right?

Q: Why can't the stdarg.h code perform that typecast internally so that
client code does not have to?

And... from info libc, on variadic functions:

8<------------------------------------------------------------------------>8
        Since the prototype doesn't specify types for optional
        arguments, in a call to a variadic function the default argument
        promotions are performed on the optional argument values. This
        means the objects of type char or short int (whether signed or
        not) are promoted to either int or unsigned int, as appropriate;
        and that objects of type float are promoted to type double. So,
        if the caller passes a char as an optional argument, it is
        promoted to an int, and the function can access it with va_arg
        (ap, int).

        Conversion of the required arguments is controlled by the
        function prototype in the usual way: the argument expression is
        converted to the declared argument type as if it were being
        assigned to a variable of that type.
8<------------------------------------------------------------------------>8

It says that this "default argument promotions" feature is required by
ISO C.  So shouldn't a small pointer type be promoted automatically to a
larger pointer type, by the compiler?  (Of course it cannot happen at
run-time in C.)

What settles it once and for all is this, from info libc, "The NULL
Pointer Constant":

8<------------------------------------------------------------------------>8
        If you use the null pointer constant as a function argument,
        then for complete portability you should make sure that the
        function has a prototype declaration. Otherwise, if the target
        machine has two different pointer representations, the compiler
        won't know which representation to use for that argument. You
        can avoid the problem by explicitly casting the constant to the
        proper pointer type, but we recommend instead adding a prototype
        for the function you are calling.
8<------------------------------------------------------------------------>8

--
Karl Hegbloom <[hidden email]>

_______________________________________________
gnucash-devel mailing list
[hidden email]
https://lists.gnucash.org/mailman/listinfo/gnucash-devel
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] Fixes sentinel warnings on GCC4

Stuart D. Gathman
On Tue, 6 Dec 2005, Karl Hegbloom wrote:

> After reading what Stuart D. Gathman wrote in this thread, I'm thinking
> that it's not Ritchie's fault either.  It's a matter of mapping the C
> language to the hardware and it's assembly language.

Ritchie could have made nil/null an actual language keyword - then
you wouldn't have to worry about ptr vs. int context.  

--
              Stuart D. Gathman <[hidden email]>
    Business Management Systems Inc.  Phone: 703 591-0911 Fax: 703 591-6154
"Confutatis maledictis, flamis acribus addictis" - background song for
a Microsoft sponsored "Where do you want to go from here?" commercial.

_______________________________________________
gnucash-devel mailing list
[hidden email]
https://lists.gnucash.org/mailman/listinfo/gnucash-devel
12