Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.std.c > #1745 > unrolled thread

null pointer constant, address constant

Started bySzabolcs Nagy <nsz@port70.net>
First post2012-12-18 13:25 +0000
Last post2012-12-18 16:18 -0500
Articles 20 on this page of 31 — 8 participants

Back to article view | Back to comp.std.c


Contents

  null pointer constant, address constant Szabolcs Nagy <nsz@port70.net> - 2012-12-18 13:25 +0000
    Re: null pointer constant, address constant James Kuyper <jameskuyper@verizon.net> - 2012-12-18 09:53 -0500
      Re: null pointer constant, address constant Szabolcs Nagy <nsz@port70.net> - 2012-12-18 20:06 +0000
        Re: null pointer constant, address constant James Kuyper <jameskuyper@verizon.net> - 2012-12-18 15:39 -0500
          Re: null pointer constant, address constant Szabolcs Nagy <nsz@port70.net> - 2012-12-19 00:44 +0000
            Re: null pointer constant, address constant James Kuyper <jameskuyper@verizon.net> - 2012-12-18 20:24 -0500
              Re: null pointer constant, address constant Szabolcs Nagy <nsz@port70.net> - 2012-12-19 10:27 +0000
                Re: null pointer constant, address constant James Kuyper <jameskuyper@verizon.net> - 2012-12-19 07:15 -0500
                Re: null pointer constant, address constant Phil Carmody <thefatphil_demunged@yahoo.co.uk> - 2012-12-21 03:03 +0200
                  Re: null pointer constant, address constant James Kuyper <jameskuyper@verizon.net> - 2012-12-21 12:13 -0500
                  Re: null pointer constant, address constant Szabolcs Nagy <nsz@port70.net> - 2013-01-09 00:44 +0000
                    Re: null pointer constant, address constant James Kuyper <jameskuyper@verizon.net> - 2013-01-08 20:21 -0500
                      Re: null pointer constant, address constant Eric Sosman <esosman@comcast-dot-net.invalid> - 2013-01-08 20:46 -0500
            Re: null pointer constant, address constant Shao Miller <sha0.miller@gmail.com> - 2012-12-18 20:37 -0500
        Re: null pointer constant, address constant Shao Miller <sha0.miller@gmail.com> - 2012-12-18 16:15 -0500
          Re: null pointer constant, address constant Keith Thompson <kst-u@mib.org> - 2012-12-18 15:52 -0800
            Re: null pointer constant, address constant Shao Miller <sha0.miller@gmail.com> - 2012-12-18 19:12 -0500
              Re: null pointer constant, address constant Shao Miller <sha0.miller@gmail.com> - 2012-12-18 19:51 -0500
            Re: null pointer constant, address constant Keith Thompson <kst-u@mib.org> - 2012-12-18 17:02 -0800
            Re: null pointer constant, address constant "christian.bau" <christian.bau@cbau.wanadoo.co.uk> - 2012-12-20 05:34 -0800
              Re: null pointer constant, address constant James Kuyper <jameskuyper@verizon.net> - 2012-12-20 10:33 -0500
                Re: null pointer constant, address constant James Kuyper <jameskuyper@verizon.net> - 2012-12-20 12:28 -0500
                  Re: null pointer constant, address constant Tim Rentsch <txr@alumni.caltech.edu> - 2012-12-20 21:13 -0800
                Re: null pointer constant, address constant Tim Rentsch <txr@alumni.caltech.edu> - 2012-12-20 21:05 -0800
              Re: null pointer constant, address constant Shao Miller <sha0.miller@gmail.com> - 2012-12-20 12:17 -0500
              Re: null pointer constant, address constant Keith Thompson <kst-u@mib.org> - 2012-12-20 12:10 -0800
                Re: null pointer constant, address constant Tim Rentsch <txr@alumni.caltech.edu> - 2012-12-20 21:25 -0800
    Re: null pointer constant, address constant Shao Miller <sha0.miller@gmail.com> - 2012-12-18 14:02 -0500
      Re: null pointer constant, address constant Szabolcs Nagy <nsz@port70.net> - 2012-12-18 20:09 +0000
        Re: null pointer constant, address constant James Kuyper <jameskuyper@verizon.net> - 2012-12-18 15:54 -0500
          Re: null pointer constant, address constant Shao Miller <sha0.miller@gmail.com> - 2012-12-18 16:18 -0500

Page 1 of 2  [1] 2  Next page →


#1745 — null pointer constant, address constant

FromSzabolcs Nagy <nsz@port70.net>
Date2012-12-18 13:25 +0000
Subjectnull pointer constant, address constant
Message-ID<kapqt2$es4$1@speranza.aioe.org>
it seems the definition of 'null pointer constant'
relies on integer constant expressions (6.3.2.3p3)
http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3

so eg.

 (void*)((char*)1 - 1)

is not a null pointer constant because pointer
arithmetics cannot give an integer constant expression

but at least this could be used in pointer initializers
since address constants are allowed there (6.6p7)
http://port70.net/~nsz/c/c11/n1570.html#6.6p7

but it seems

 0 ? (void*)0 : (void*)0

is not a null pointer constant, nor an address constant

it is mildly unexpected that this cannot be used as a
null pointer constant
(this matters when determining the type of ?: (6.5.15)
expressions, assigning it to function pointers (6.5.16.1)
or comparing it to function pointers (6.5.9))

but (for me at least) it's more surprising that this is
not even a constant expression that can be used in
initializers, ie.

 void *p = 0?(void*)0:(void*)0;

is undefined behaviour if i read section 6.6 correctly
(not even a constraint violation that requires a diagnostic)

another similar surprising case is

 void *q = (void*)((char*)1 - (char*)1);

the initializer is not an address constant, not a null pointer
constant nor an address constant +- an integer constant
expression.

i wonder if my interpretation is right and if there is
some rational behind this

(my personal intuition is that integer constant expressions
should be defined more generously and may be the result of
pointer arithmetics if the pointers are casted integer
constants or the difference of address constants, then
these surprises would go away)

[toc] | [next] | [standalone]


#1746

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-12-18 09:53 -0500
Message-ID<kaq01g$6jp$1@dont-email.me>
In reply to#1745
On 12/18/2012 08:25 AM, Szabolcs Nagy wrote:
> it seems the definition of 'null pointer constant'
> relies on integer constant expressions (6.3.2.3p3)
> http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3
> 
> so eg.
> 
>  (void*)((char*)1 - 1)
> 
> is not a null pointer constant because pointer
> arithmetics cannot give an integer constant expression
> 
> but at least this could be used in pointer initializers
> since address constants are allowed there (6.6p7)

Two things to keep in mind:

When converting an arbitrary integer to a pointer type "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." (6.3.2.3p1)
Now, alignment cannot be an issue with char*, but trap representation
certainly is. Unless the compiler you're using tells you something
useful about where (char*)1 points, you're asking for big trouble trying
to use it.

Subtraction of an integer from a pointer is defined only for a pointer
that points at an element of an array, or one past the end of the array,
and only if the defined result of the subtraction would also be in that
array (6.5.6p8). Unless the compiler you're using defines that (char*)1
points at an element of an array, other than the first element of that
array, the subtraction has undefined behavior. If your compiler does
provide such a guarantee, then you're right, this could be used as a
pointer constant expression.

However, I get the impression that you were expecting

    (char*)1-1 == (char*)0.

The standard says nothing of the kind - in fact, if that expression has
defined behavior, it's guaranteed to be false. Here's why: if the
implementation does define (char*)1 as pointing at an element, other
than the first, of an array, then (char*)1 - 1 will point at the
immediately preceding element of the array, and therefore, points at an
object. Now (char*)0 is a null pointer, and a null pointer can never
compare equal to a pointer to an object (6.3.2.3p3). Therefore, if the
implementation-defined value of (char*)1 is such as to give the
subtraction standard-defined behavior, that behavior is guaranteed to
NOT result in a pointer that compares equal to (char*)0.

> http://port70.net/~nsz/c/c11/n1570.html#6.6p7
> 
> but it seems
> 
>  0 ? (void*)0 : (void*)0
> 
> is not a null pointer constant, nor an address constant

Correct. But it is a null pointer with the type 'void*'.

> it is mildly unexpected that this cannot be used as a
> null pointer constant
> (this matters when determining the type of ?: (6.5.15)

I may be missing something, but as far as I can tell, the rules given in
6.5.15p6 give the same result whether the second or third operand is a
null pointer constant, or merely a constant expression with the type
void* and a null value. Could you provide a counter-example?

However, that difference does make one case a constraint violation. The
expression

    condition ? null_pointer_constant : function_pointer

is well-defined, and has the type of the function pointer, but

    condition ? constant_null_pointer_to_void : function_pointer

is a constraint violation. (6.5.15p3)

> expressions, assigning it to function pointers (6.5.16.1)
> or comparing it to function pointers (6.5.9))

You're right on those two counts; both cases are constraint violations,
but would not be if it qualified as null pointer constant.

> but (for me at least) it's more surprising that this is
> not even a constant expression that can be used in
> initializers, ie.
> 
>  void *p = 0?(void*)0:(void*)0;
> 
> is undefined behaviour if i read section 6.6 correctly
> (not even a constraint violation that requires a diagnostic)

That does seem a bit surprising, but I believe you're correct. Note,
however, that a constant expression is required only if 'p' has static
or thread storage duration. It's perfectly usable as an initializer for
a variable with automatic storage duration.

> another similar surprising case is
> 
>  void *q = (void*)((char*)1 - (char*)1);

And only a problem if q has static or thread storage duration.

> the initializer is not an address constant, not a null pointer
> constant nor an address constant +- an integer constant
> expression.

Well, given the potential problems with (char*)1, I don't see that as a
big problem. Even if (char*)1 is defined by the implementation as having
a valid value, the subtraction is an expression with a type of ptrdiff_t
and a value of 0. It doesn't qualify as an integer constant expression,
and it's conversion to void* is therefore not required to produce a null
pointer constant. It's covered only by the same rule (6.3.2.3p1) that
covers (char*)1, and it therefore has all of the same problems as (char*)1.

> i wonder if my interpretation is right and if there is
> some rational behind this

The places where integer constant expressions are required are places
where they must be evaluated at compile time to generate the right code.
This imposes a bit of a strain on the implementor, who would otherwise
be allowed to delay evaluation of such expressions until run-time. The
rules for integer constant expressions were intended to keep them
simple, to make them easy to identify and evaluate.
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#1748

FromSzabolcs Nagy <nsz@port70.net>
Date2012-12-18 20:06 +0000
Message-ID<kaqibr$gj3$1@speranza.aioe.org>
In reply to#1746
James Kuyper <jameskuyper@verizon.net> wrote:
> On 12/18/2012 08:25 AM, Szabolcs Nagy wrote:
>> it seems the definition of 'null pointer constant'
>> relies on integer constant expressions (6.3.2.3p3)
>> http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3
>> 
>> so eg.
>> 
>>  (void*)((char*)1 - 1)
>> 
>> is not a null pointer constant because pointer
>> arithmetics cannot give an integer constant expression
>> 
>> but at least this could be used in pointer initializers
>> since address constants are allowed there (6.6p7)
> 
> Two things to keep in mind:
> 
> When converting an arbitrary integer to a pointer type "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." (6.3.2.3p1)

does not matter, your concerns only matter if the expression is
evaluated, but it is not:

 sizeof( *(0?(double*)0:(void*)((char*)1 - 1)) )

is this equivalent to sizeof(double) or sizeof(void)?
(now that the expression after : is not a null pointer constant
we know the result is sizeof(void) and thus a constraint violation)

you don't need to evaluate the expression to use the "constness"
of it so any argument about trap representations and alignment is
moot

> However, I get the impression that you were expecting
> 
>    (char*)1-1 == (char*)0.
> 
> The standard says nothing of the kind - in fact, if that expression has
> defined behavior, it's guaranteed to be false. Here's why: if the
> implementation does define (char*)1 as pointing at an element, other
> than the first, of an array, then (char*)1 - 1 will point at the
> immediately preceding element of the array, and therefore, points at an
> object. Now (char*)0 is a null pointer, and a null pointer can never
> compare equal to a pointer to an object (6.3.2.3p3). Therefore, if the

this is a valid argument

> I may be missing something, but as far as I can tell, the rules given in
> 6.5.15p6 give the same result whether the second or third operand is a
> null pointer constant, or merely a constant expression with the type
> void* and a null value. Could you provide a counter-example?

yes

 (0 ? (double*)0 : (void*)0)

has type double*, while

 (0 ? (double*)0 : (void*)1)

has type void*

to exploit this difference you can use sizeof as above


>> but (for me at least) it's more surprising that this is
>> not even a constant expression that can be used in
>> initializers, ie.
>> 
>>  void *p = 0?(void*)0:(void*)0;
>> 
>> is undefined behaviour if i read section 6.6 correctly
>> (not even a constraint violation that requires a diagnostic)
> 
> That does seem a bit surprising, but I believe you're correct. Note,

ok i was wrong about this, an address constant can be a
null pointer (not just null pointer constant)

> This imposes a bit of a strain on the implementor, who would otherwise
> be allowed to delay evaluation of such expressions until run-time. The
> rules for integer constant expressions were intended to keep them
> simple, to make them easy to identify and evaluate.

my problem is that they are not easy to identify

 !(int)1.0
 (void*)(1-1)

are integer constant expressions and null pointer constants

 (int)!1.0
 (void*)(char*)0
 (char*)(void*)0
 (void*)(void*)0
 (void*)((char*)0-(char*)0)

are neither

[toc] | [prev] | [next] | [standalone]


#1750

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-12-18 15:39 -0500
Message-ID<50D0D492.20007@verizon.net>
In reply to#1748
On 12/18/2012 03:06 PM, Szabolcs Nagy wrote:
> James Kuyper <jameskuyper@verizon.net> wrote:
>> On 12/18/2012 08:25 AM, Szabolcs Nagy wrote:
>>> it seems the definition of 'null pointer constant'
>>> relies on integer constant expressions (6.3.2.3p3)
>>> http://port70.net/~nsz/c/c11/n1570.html#6.3.2.3p3
>>>
>>> so eg.
>>>
>>>  (void*)((char*)1 - 1)
>>>
>>> is not a null pointer constant because pointer
>>> arithmetics cannot give an integer constant expression
>>>
>>> but at least this could be used in pointer initializers
>>> since address constants are allowed there (6.6p7)
>>
>> Two things to keep in mind:
>>
>> When converting an arbitrary integer to a pointer type "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." (6.3.2.3p1)
> 
> does not matter, your concerns only matter if the expression is
> evaluated, but it is not:
> 
>  sizeof( *(0?(double*)0:(void*)((char*)1 - 1)) )

You didn't mention that in your original message.

...
>> I may be missing something, but as far as I can tell, the rules given in
>> 6.5.15p6 give the same result whether the second or third operand is a
>> null pointer constant, or merely a constant expression with the type
>> void* and a null value. Could you provide a counter-example?
> 
> yes
> 
>  (0 ? (double*)0 : (void*)0)
> 
> has type double*, while
> 
>  (0 ? (double*)0 : (void*)1)
> 
> has type void*

You're right - I missed that.

...
>> This imposes a bit of a strain on the implementor, who would otherwise
>> be allowed to delay evaluation of such expressions until run-time. The
>> rules for integer constant expressions were intended to keep them
>> simple, to make them easy to identify and evaluate.
> 
> my problem is that they are not easy to identify
> 
>  !(int)1.0
>  (void*)(1-1)
> 
> are integer constant expressions and null pointer constants

(void*)(1-1) is not an integer constant expression, though it is a null
pointer constant.

>  (int)!1.0
>  (void*)(char*)0
>  (char*)(void*)0
>  (void*)(void*)0
>  (void*)((char*)0-(char*)0)
> 
> are neither

While the list of things prohibited in ICEs is long, that fact is
normally only of importance to compiler writers, who must write code
that reliably and accurately detects them in the source code.  The
contexts where ICEs are required (array dimensions, enumeration values,
and bit-field sizes) are not normally reasonable contexts for any
expressions that would push the limits on what is permitted.
Can you give an example where it was reasonable, in a context where an
ICE was required, to use an expression that pushed (or exceeded) the
limits on what is permitted for an ICE?

I can come up with only three different null pointer constant
expressions that most people would ever have any good reason to use in C:
0: for compatibility with C++
(void*)0: for better type-checking than you get with a plain 0
NULL: self-documenting, and a lot easier to search for than the other two.
Can you give any good reason for using any other null pointer constant
expressions? Can you claim that such situations are commonplace?

[toc] | [prev] | [next] | [standalone]


#1756

FromSzabolcs Nagy <nsz@port70.net>
Date2012-12-19 00:44 +0000
Message-ID<kar2ku$o89$1@speranza.aioe.org>
In reply to#1750
James Kuyper <jameskuyper@verizon.net> wrote:
> On 12/18/2012 03:06 PM, Szabolcs Nagy wrote:
>> does not matter, your concerns only matter if the expression is
>> evaluated, but it is not:
>> 
>>  sizeof( *(0?(double*)0:(void*)((char*)1 - 1)) )
> 
> You didn't mention that in your original message.

sorry

> Can you give an example where it was reasonable, in a context where an
> ICE was required, to use an expression that pushed (or exceeded) the
> limits on what is permitted for an ICE?

it is required for an elaborate hack to workaround a
silliness of the standard that is tgmath.h

(ie. i have an implementation of tgmath.h which is
done with minimal compiler support using the typeof
extension present in many compilers)
(yes c11 _Generic could solve the issue but is not
practical as it is not supported on the compilers i
care about and i cannot modify the compilers, i'm
only implementing the standard library)

while testing the implementation it turned out that the
tested compilers disagreed about what is an ICE or a
null pointer constant (so it's not that trivial matter
after all)

eg on some compilers

 #define __is_integral(x) !((1?1:x)/2)
 __is_integral(1.0)

is an ICE and null pointer constant on others not

 0 ? (double*)0 : 0 ? (void*)0 : (void*)0

has type double* on some compilers on others void*

none of the tested compilers are completely standard
conformant (gcc, clang) which means i can implement
tgmath.h with typeof only, if they were conformant i
could not

[toc] | [prev] | [next] | [standalone]


#1759

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-12-18 20:24 -0500
Message-ID<kar51c$oaj$1@dont-email.me>
In reply to#1756
On 12/18/2012 07:44 PM, Szabolcs Nagy wrote:
...
>> Can you give an example where it was reasonable, in a context where an
>> ICE was required, to use an expression that pushed (or exceeded) the
>> limits on what is permitted for an ICE?
> 
> it is required for an elaborate hack to workaround a
> silliness of the standard that is tgmath.h
> 
> (ie. i have an implementation of tgmath.h which is
> done with minimal compiler support using the typeof
> extension present in many compilers)
> (yes c11 _Generic could solve the issue but is not
> practical as it is not supported on the compilers i
> care about and i cannot modify the compilers, i'm
> only implementing the standard library)
> 
> while testing the implementation it turned out that the
> tested compilers disagreed about what is an ICE or a
> null pointer constant (so it's not that trivial matter
> after all)

That is, I hope you'll admit, a rather unusual situation.

> eg on some compilers
> 
>  #define __is_integral(x) !((1?1:x)/2)
>  __is_integral(1.0)
> 
> is an ICE and null pointer constant on others not

Because 1.0 is not the immediate operand of a cast, that doesn't qualify
as an ICE, and therefore does not qualify as an NPC. The compilers that
disagree are wrong.

How did you use this macro, such that it needed to be an ICE or an NPC?

>  0 ? (double*)0 : 0 ? (void*)0 : (void*)0
> 
> has type double* on some compilers on others void*

The inner ?: expression is a constant expression with a null pointer
value, but not a null pointer constant. Therefore, the outer ?:
expression should have the type void*.

> none of the tested compilers are completely standard
> conformant (gcc, clang) which means i can implement
> tgmath.h with typeof only, if they were conformant i
> could not

I believe that gcc can fully conform to C90, and probably to C99, with
the right compiler options. I thought the same was true of clang, but
I'm not as familiar with that compiler. If you need to use the compiler
in a non-conforming mode, I suppose it doesn't help to know that it also
has a conforming mode.
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#1763

FromSzabolcs Nagy <nsz@port70.net>
Date2012-12-19 10:27 +0000
Message-ID<kas4pv$to2$1@speranza.aioe.org>
In reply to#1759
James Kuyper <jameskuyper@verizon.net> wrote:
> On 12/18/2012 07:44 PM, Szabolcs Nagy wrote:
>>  #define __is_integral(x) !((1?1:x)/2)
> 
> How did you use this macro, such that it needed to be an ICE or an NPC?

typeof(0?(double*)0:(void*)!__is_integral(x))

or sizeof with dereference, but then it may be sizeof(void)

now i found a conformant and NPC solution so the case is closed:

#define __is_integral(x) (sizeof((x)+1.0f)!=sizeof((x)+1ULL))

float must be ieee 32bit format otherwise the whole math
implementation is invalid and unsigned long long must be
assumed to be (at least) 64bit

[toc] | [prev] | [next] | [standalone]


#1764

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-12-19 07:15 -0500
Message-ID<kasb4r$h8f$1@dont-email.me>
In reply to#1763
On 12/19/2012 05:27 AM, Szabolcs Nagy wrote:
> James Kuyper <jameskuyper@verizon.net> wrote:
>> On 12/18/2012 07:44 PM, Szabolcs Nagy wrote:
>>>  #define __is_integral(x) !((1?1:x)/2)
>>
>> How did you use this macro, such that it needed to be an ICE or an NPC?
> 
> typeof(0?(double*)0:(void*)!__is_integral(x))
> 
> or sizeof with dereference, but then it may be sizeof(void)
> 
> now i found a conformant and NPC solution so the case is closed:
> 
> #define __is_integral(x) (sizeof((x)+1.0f)!=sizeof((x)+1ULL))
> 
> float must be ieee 32bit format otherwise the whole math
> implementation is invalid and unsigned long long must be
> assumed to be (at least) 64bit

Well, if it doesn't issue a diagnostic because of your use of 1ULL, it
can't be fully conforming to C90, and if you need to provide your own
<tgmath.h>, it can't be fully conforming to any later version of the
standard, so making one assumption not guaranteed by the standard is not
unreasonable.
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#1772

FromPhil Carmody <thefatphil_demunged@yahoo.co.uk>
Date2012-12-21 03:03 +0200
Message-ID<87pq24gqt2.fsf@bazspaz.fatphil.org>
In reply to#1763
Szabolcs Nagy <nsz@port70.net> writes:
> James Kuyper <jameskuyper@verizon.net> wrote:
> > On 12/18/2012 07:44 PM, Szabolcs Nagy wrote:
> >>  #define __is_integral(x) !((1?1:x)/2)
> > 
> > How did you use this macro, such that it needed to be an ICE or an NPC?
> 
> typeof(0?(double*)0:(void*)!__is_integral(x))
> 
> or sizeof with dereference, but then it may be sizeof(void)
> 
> now i found a conformant and NPC solution so the case is closed:
> 
> #define __is_integral(x) (sizeof((x)+1.0f)!=sizeof((x)+1ULL))
> 
> float must be ieee 32bit format otherwise the whole math
> implementation is invalid

Woh, when did that happen? I have a box which has non-IEEE754 floats,
and it aren't even in 36-bit lala-land.

Phil
-- 
I'm not saying that google groups censors my posts, but there's a strong link
between me saying "google groups sucks" in articles, and them disappearing.

Oh - I guess I might be saying that google groups censors my posts.

[toc] | [prev] | [next] | [standalone]


#1793

FromJames Kuyper <jameskuyper@verizon.net>
Date2012-12-21 12:13 -0500
Message-ID<50D498A6.10204@verizon.net>
In reply to#1772
On 12/20/2012 08:03 PM, Phil Carmody wrote:
> Szabolcs Nagy <nsz@port70.net> writes:
...
>> now i found a conformant and NPC solution so the case is closed:
>>
>> #define __is_integral(x) (sizeof((x)+1.0f)!=sizeof((x)+1ULL))
>>
>> float must be ieee 32bit format otherwise the whole math
>> implementation is invalid
> 
> Woh, when did that happen? I have a box which has non-IEEE754 floats,
> and it aren't even in 36-bit lala-land.

I could be mistaken, but I assumed when he posted that, that he was
talking about his own math implementation. He is, after all,
implementing <tgmath.h>, so he's fiddling around in the implementor's
domain, even if he isn't himself an implementor of C. His math
implementation apparently makes assumptions that would prevent it from
being portable to your box. For the "whole math implementation" to be
invalid, those assumptions must apparently be very pervasive - I can't
imagine why that would be the case.

[toc] | [prev] | [next] | [standalone]


#1829

FromSzabolcs Nagy <nsz@port70.net>
Date2013-01-09 00:44 +0000
Message-ID<kciegj$bam$1@speranza.aioe.org>
In reply to#1772
Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
> Szabolcs Nagy <nsz@port70.net> writes:
>> float must be ieee 32bit format otherwise the whole math
>> implementation is invalid
> 
> Woh, when did that happen? I have a box which has non-IEEE754 floats,
> and it aren't even in 36-bit lala-land.

happened in c99

the only way to have usable floating-point in c currently is to assume
c99 annex f (which is unfortunately not widely supported by compilers)
(with annex f float is ieee binary32 and double is ieee binary64)

without annex f there is no guarantee about the precision (and various
other properties) of float operations which is not adequate for
practical usage
(basic assumptions might not hold, eg. .2+.3 > .4 can be false, which
makes writing robust numerical algorithms hard/impossible)

(one can of course rely on the implementation defined floating-point
behaviour of the given platform, but that's outside the semantics
specified by c hence not portable, the necessary amount of ifdeffery
to make up for that is impractical)

some ppl think that weak semantics for floating-point arithmetics
is enough to write working numeric code, but the history of the
previous failed attempts at that hints otherwise

[toc] | [prev] | [next] | [standalone]


#1830

FromJames Kuyper <jameskuyper@verizon.net>
Date2013-01-08 20:21 -0500
Message-ID<kcign5$82j$1@dont-email.me>
In reply to#1829
On 01/08/2013 07:44 PM, Szabolcs Nagy wrote:
> Phil Carmody <thefatphil_demunged@yahoo.co.uk> wrote:
>> Szabolcs Nagy <nsz@port70.net> writes:
>>> float must be ieee 32bit format otherwise the whole math
>>> implementation is invalid
>>
>> Woh, when did that happen? I have a box which has non-IEEE754 floats,
>> and it aren't even in 36-bit lala-land.
> 
> happened in c99

What happened in C99 is that additional guarantees were added, but only
if __STDC_IEC_559__ was pre-#defined by the compiler. Before C99, IEC
60559 (==IEEE 754) platforms had the same lack of guarantees from the C
standard that non-IEEE754 platforms still have.

> the only way to have usable floating-point in c currently is to assume
> c99 annex f (which is unfortunately not widely supported by compilers)
> (with annex f float is ieee binary32 and double is ieee binary64)
> 
> without annex f there is no guarantee about the precision (and various
> other properties) of float operations which is not adequate for
> practical usage
> (basic assumptions might not hold, eg. .2+.3 > .4 can be false, which
> makes writing robust numerical algorithms hard/impossible)

More accurately, without annex F, the C standard provides no guarantees
about those things. However, it's entirely possible for something other
than the C standard (such as the documentation for the compiler) to
provide all of the guarantees needed to make the implementation usable,
even if it's not compatible with IEEE 754.

> (one can of course rely on the implementation defined floating-point
> behaviour of the given platform, but that's outside the semantics
> specified by c hence not portable, the necessary amount of ifdeffery
> to make up for that is impractical)

Whether it's impractical depends entirely upon the application. If the
application is specifically targeted to a particular non-IEEE 754
platform (such as, presumably, Phil Carmody's applications), no
#ifdeffery is needed (except possibly for #error directives indicating
that it's being compiled for the wrong platform).

Too many people seem to think that if a program relies upon something
that's not guaranteed by the C standard, it's not useful. Not all code
needs to be portable to all platforms that have a conforming
implementation of C.

> some ppl think that weak semantics for floating-point arithmetics
> is enough to write working numeric code, but the history of the
> previous failed attempts at that hints otherwise,
-- 
James Kuyper

[toc] | [prev] | [next] | [standalone]


#1831

FromEric Sosman <esosman@comcast-dot-net.invalid>
Date2013-01-08 20:46 -0500
Message-ID<kcii6h$ens$1@dont-email.me>
In reply to#1830
On 1/8/2013 8:21 PM, James Kuyper wrote:
>[...]
> Too many people seem to think that if a program relies upon something
> that's not guaranteed by the C standard, it's not useful. Not all code
> needs to be portable to all platforms that have a conforming
> implementation of C.

     The Rationale says this, explicitly.

     Also, "portability" is not a Boolean attribute.  I do not think
it is even a merely scalar attribute.

-- 
Eric Sosman
esosman@comcast-dot-net.invalid

[toc] | [prev] | [next] | [standalone]


#1761

FromShao Miller <sha0.miller@gmail.com>
Date2012-12-18 20:37 -0500
Message-ID<kar5oo$rkm$1@dont-email.me>
In reply to#1756
On 12/18/2012 19:44, Szabolcs Nagy wrote:
>
> while testing the implementation it turned out that the
> tested compilers disagreed about what is an ICE or a
> null pointer constant (so it's not that trivial matter
> after all)
>
> eg on some compilers
>
>   #define __is_integral(x) !((1?1:x)/2)
>   __is_integral(1.0)
>
> is an ICE and null pointer constant on others not
>

So if I can follow along, '1.0' is unsuffixed, so it has type 
'double'[6.4.4.2p4].  The usual arithmetic conversions cause the value 
of the ternary's second (middle) operand to be converted to 
'double'[6.3.1.8p1, 6.5.15p5].  That'd be '1.0' again.  Then division by 
'2' yields '0.5'.  Then '0.5 == 0' yields 0[6.5.3.3p5].

Oh, but 6.6p6 wants '1.0' to be the operand of a cast, rather than an 
operand of the ternary operator.  6.6p10 appears to grant license for 
the discrepancy you observe with different compilers.

>   0 ? (double*)0 : 0 ? (void*)0 : (void*)0
>
> has type double* on some compilers on others void*
>

Would you be so kind as to mention which compiler (and mode) says this 
has type 'double *'?  It sure seems like it ought to have type 'void *', 
to me.  Even if 6.6p10 allows '(0 ? ((void *) 0) : ((void *) 0))' to be 
a constant expression, it doesn't seem to qualify as an NPC, as it 
wouldn't be an ICE nor an ICE cast to 'void *'.  Maybe it warrants 
clarification from the compiler team?

Very interesting. :)

- Shao Miller

[toc] | [prev] | [next] | [standalone]


#1752

FromShao Miller <sha0.miller@gmail.com>
Date2012-12-18 16:15 -0500
Message-ID<kaqmd8$u9m$1@dont-email.me>
In reply to#1748
On 12/18/2012 15:06, Szabolcs Nagy wrote:
>>
>> When converting an arbitrary integer to a pointer type "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." (6.3.2.3p1)

(Using your reference, I think James meant p5.)

>
> does not matter, your concerns only matter if the expression is
> evaluated, but it is not:
>
>   sizeof( *(0?(double*)0:(void*)((char*)1 - 1)) )
>
> is this equivalent to sizeof(double) or sizeof(void)?
> (now that the expression after : is not a null pointer constant
> we know the result is sizeof(void) and thus a constraint violation)
>
> you don't need to evaluate the expression to use the "constness"
> of it so any argument about trap representations and alignment is
> moot
>

Why don't you need to?  It might be worth noting that this discussion 
happens under 6.3 "Conversions" and not under 6.5 "Expressions".

If you're correct, does that mean that an implementation is not allowed 
to convert '(char *) 1' to a different value every time it is used in 
the source code?

Possibly related, if we have:

   int x = 42;
   x -= func_asking_user_for_number_and_they_provide_42();
   char * ptr = (char *) x;

Does 'ptr' contain a null pointer value?  We know that a null pointer 
constant can be used to produce a null pointer value, but what about the 
simple value 0 without the additional properties that an NPC has?  It 
seems to me that it might be useful to distinguish between the two...

On x86 with BIOS, physical address 0 would be the first byte of the 
Interrupt Vector Table.  If we wish to allow access to this as an 
object, then surely we would want to distinguish that object from a null 
pointer, which oughtn't to point to any object.

Similarly, if the implementation defines that '(char *) 1' points to the 
second element of an array, we might expect that '((char *) 1) - 1' 
points to the first element of the array, rather than suddenly becoming 
a null pointer value.

One thing that might be worth noting is that "result" seems to be used 
to mean one or more of "type" and "value" and "behaviour" in Standard C. 
  So even if the type is defined for your sizeof-with-ternary example, 
it might still be that "the result"-ing behaviour is 
implementation-defined.  "Result" is a bit too loose, I think, sometimes.

I could be misunderstanding.

- Shao Miller

[toc] | [prev] | [next] | [standalone]


#1754

FromKeith Thompson <kst-u@mib.org>
Date2012-12-18 15:52 -0800
Message-ID<lnzk1ark9h.fsf@nuthaus.mib.org>
In reply to#1752
Shao Miller <sha0.miller@gmail.com> writes:
[...]
> Possibly related, if we have:
>
>    int x = 42;
>    x -= func_asking_user_for_number_and_they_provide_42();
>    char * ptr = (char *) x;
>
> Does 'ptr' contain a null pointer value?

Maybe.

>                                           We know that a null pointer 
> constant can be used to produce a null pointer value, but what about the 
> simple value 0 without the additional properties that an NPC has?  It 
> seems to me that it might be useful to distinguish between the two...

The standard says that a constant arithmetic expression with the value
0, converted to a pointer type, yields a null pointer.  It does *not*
say the same thing about a non-constant arithmetic expression with the
value 0.

Consider a system that represents null pointers as 0xFFFFFFFF
(let's assume for simplicity that all pointer types have the same
representation).  The standard requires `(char*)0` yields a null
pointer, which means the conversion is non-trivial, though it takes
place at compile time.  But any conversion that's *not* required
to yield a null pointer can simply reinterpret the representation.
So `(char*)zero`, where `zero` is a variable whose value is currently
0, would yield a pointer with an all-bits-zero representation,
*not* a null pointer.  On the other hand, `(char*)0xFFFFFFFF`
would (happen to) yield a null pointer value, though it's not a
null pointer constant.

There are myriads of other possible implementations where `(char*)zero`
does not yield a null pointer.  All one really needs to know is that the
standard doesn't define the behavior; concrete (but hypothetical)
examples can be useful in understanding why the standard does or does
not make certain guarantees.

> On x86 with BIOS, physical address 0 would be the first byte of the 
> Interrupt Vector Table.  If we wish to allow access to this as an 
> object, then surely we would want to distinguish that object from a null 
> pointer, which oughtn't to point to any object.

A program that dereferences physical address 0 (which, BTW, is typically
impossible in hosted implementations with virtual memory) most likely
has undefined behavior.  It's perfectly valid for the result of that
undefined behavior to be that it accesses the Interrupt Vector Table.

[...]

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
    Will write code for food.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#1755

FromShao Miller <sha0.miller@gmail.com>
Date2012-12-18 19:12 -0500
Message-ID<kar0q1$3au$1@dont-email.me>
In reply to#1754
On 12/18/2012 18:52, Keith Thompson wrote:
> Shao Miller <sha0.miller@gmail.com> writes:
> [...]
>> Possibly related, if we have:
>>
>>     int x = 42;
>>     x -= func_asking_user_for_number_and_they_provide_42();
>>     char * ptr = (char *) x;
>>
>> Does 'ptr' contain a null pointer value?
>
> Maybe.
>

And maybe not.

>>                                            We know that a null pointer
>> constant can be used to produce a null pointer value, but what about the
>> simple value 0 without the additional properties that an NPC has?  It
>> seems to me that it might be useful to distinguish between the two...
>
> The standard says that a constant arithmetic expression with the value
> 0, converted to a pointer type, yields a null pointer.  It does *not*
> say the same thing about a non-constant arithmetic expression with the
> value 0.
>

That's rather what I was suggesting to the original poster.

> Consider a system that represents null pointers as 0xFFFFFFFF
> (let's assume for simplicity that all pointer types have the same
> representation).  The standard requires `(char*)0` yields a null
> pointer, which means the conversion is non-trivial, though it takes
> place at compile time.  But any conversion that's *not* required
> to yield a null pointer can simply reinterpret the representation.
> So `(char*)zero`, where `zero` is a variable whose value is currently
> 0, would yield a pointer with an all-bits-zero representation,
> *not* a null pointer.  On the other hand, `(char*)0xFFFFFFFF`
> would (happen to) yield a null pointer value, though it's not a
> null pointer constant.
>

Good elaboration.  Precisely what I was thinking.  I am a tad worried 
that the original poster is confusing "null pointer value" with "the 
zero address in a linear address space," and casts of integers to 
pointers with "pointing to particular addresses in a linear address space."

> There are myriads of other possible implementations where `(char*)zero`
> does not yield a null pointer.  All one really needs to know is that the
> standard doesn't define the behavior; concrete (but hypothetical)
> examples can be useful in understanding why the standard does or does
> not make certain guarantees.
>
>> On x86 with BIOS, physical address 0 would be the first byte of the
>> Interrupt Vector Table.  If we wish to allow access to this as an
>> object, then surely we would want to distinguish that object from a null
>> pointer, which oughtn't to point to any object.
>
> A program that dereferences physical address 0 (which, BTW, is typically
> impossible in hosted implementations with virtual memory) most likely
> has undefined behavior.  It's perfectly valid for the result of that
> undefined behavior to be that it accesses the Interrupt Vector Table.
>
> [...]
>

Agreed.  My "surely" was intended to suggest a possible reason for _why_ 
the Standard makes it implementation-defined.  In free-standing 
boot-loaders like Syslinux, it's really rather useful for the undefined 
behaviour of _using_ a null pointer (even when constructed directly with 
a null pointer constant) to happen to do what you might desire on x86 
with BIOS*.

- Shao Miller

Off-topic post-script: *This is an example of "a real program that 
depends on undefined behaviour" (by Standard C, but obviously defined by 
the implementation)...  The very phrase that caused James K. to plonk 
me.  Oops! :)

[toc] | [prev] | [next] | [standalone]


#1757

FromShao Miller <sha0.miller@gmail.com>
Date2012-12-18 19:51 -0500
Message-ID<kar326$ehc$1@dont-email.me>
In reply to#1755
On 12/18/2012 19:12, Shao Miller wrote:
> On 12/18/2012 18:52, Keith Thompson wrote:
>
>> There are myriads of other possible implementations where `(char*)zero`
>> does not yield a null pointer.  All one really needs to know is that the
>> standard doesn't define the behavior; concrete (but hypothetical)
>> examples can be useful in understanding why the standard does or does
>> not make certain guarantees.
>>
>>> On x86 with BIOS, physical address 0 would be the first byte of the
>>> Interrupt Vector Table.  If we wish to allow access to this as an
>>> object, then surely we would want to distinguish that object from a null
>>> pointer, which oughtn't to point to any object.
>>
>> A program that dereferences physical address 0 (which, BTW, is typically
>> impossible in hosted implementations with virtual memory) most likely
>> has undefined behavior.  It's perfectly valid for the result of that
>> undefined behavior to be that it accesses the Interrupt Vector Table.
>>
>> [...]
>>
>
> Agreed.  My "surely" was intended to suggest a possible reason for _why_
> the Standard makes it implementation-defined.  In free-standing
> boot-loaders like Syslinux, it's really rather useful for the undefined
> behaviour of _using_ a null pointer (even when constructed directly with
> a null pointer constant) to happen to do what you might desire on x86
> with BIOS*.
>

In case it's as clear as mud (a gift, to be sure), I was trying to 
simultaneously support:

1. An x86 with BIOS implementation _might_ wish to be able to 
distinguish a null pointer from "physical address 0", à la your 0xFFFFFFFF.

2. An x86 with BIOS implementation _might_ wish to be able to _use_ a 
null pointer without crashing, à la accessing the IVT.

Both of these are supported by having null pointer constants produce 
null pointers, and non-zero integer constants and non-constant integers 
cast to pointers having implementation-defined results.

- Shao Miller

[toc] | [prev] | [next] | [standalone]


#1758

FromKeith Thompson <kst-u@mib.org>
Date2012-12-18 17:02 -0800
Message-ID<lnvcbyrgzi.fsf@nuthaus.mib.org>
In reply to#1754
Keith Thompson <kst-u@mib.org> writes:
[...]
> The standard says that a constant arithmetic expression with the value
> 0, converted to a pointer type, yields a null pointer.  It does *not*
> say the same thing about a non-constant arithmetic expression with the
> value 0.

Correction: I should have written "integer constant expression" rather
than "constant arithmetic expression".

[...]

-- 
Keith Thompson (The_Other_Keith) kst-u@mib.org  <http://www.ghoti.net/~kst>
    Will write code for food.
"We must do something.  This is something.  Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"

[toc] | [prev] | [next] | [standalone]


#1767

From"christian.bau" <christian.bau@cbau.wanadoo.co.uk>
Date2012-12-20 05:34 -0800
Message-ID<89afe21d-1a8b-4749-8ad1-ec98cc65a818@a8g2000vby.googlegroups.com>
In reply to#1754
On Dec 18, 11:52 pm, Keith Thompson <ks...@mib.org> wrote:

> The standard says that a constant arithmetic expression with the value
> 0, converted to a pointer type, yields a null pointer.  It does *not*
> say the same thing about a non-constant arithmetic expression with the
> value 0.

It doesn't have to. The standard doesn't have to state something
explicitly if it follows from other rules that are stated.

Converting _any_ integer to _any_ pointer gives an implementation-
defined result. "implementation defined" means it is defined. So the
implementation must define what you get if you write "int x = 0; char*
p = (char *) x; char* q = (char *) ++x; char* r = (char *) ++x;".
There is also the requirement for the special case that an integer is,
by chance, a null pointer constant, where the result must be a null
pointer. So whatever definition the implementation gives, it must give
a definition that converts 0 to a null pointer if the zero was a null
pointer constant, therefore the definition must convert all zeroes to
null pointers.

[toc] | [prev] | [next] | [standalone]


Page 1 of 2  [1] 2  Next page →

Back to top | Article view | comp.std.c


csiph-web