Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.c > #88991 > unrolled thread
| Started by | G G <gdotone@gmail.com> |
|---|---|
| First post | 2016-06-12 16:21 -0700 |
| Last post | 2016-06-16 21:39 -0700 |
| Articles | 20 on this page of 86 — 20 participants |
Back to article view | Back to comp.lang.c
setting pointers to NULL and testing question. G G <gdotone@gmail.com> - 2016-06-12 16:21 -0700
Re: setting pointers to NULL and testing question. Richard Heathfield <rjh@cpax.org.uk> - 2016-06-13 00:50 +0100
Re: setting pointers to NULL and testing question. G G <gdotone@gmail.com> - 2016-06-12 17:03 -0700
Re: setting pointers to NULL and testing question. Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2016-06-12 20:25 -0600
Re: setting pointers to NULL and testing question. James Kuyper <jameskuyper@verizon.net> - 2016-06-12 22:57 -0400
Re: setting pointers to NULL and testing question. Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2016-06-13 10:03 -0600
Re: setting pointers to NULL and testing question. Tim Rentsch <txr@alumni.caltech.edu> - 2016-06-15 14:43 -0700
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-13 08:06 -0700
Re: setting pointers to NULL and testing question. Richard Damon <Richard@Damon-Family.org> - 2016-06-16 08:27 -0400
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-16 06:27 -0700
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-16 07:31 -0700
Re: setting pointers to NULL and testing question. Richard Damon <Richard@Damon-Family.org> - 2016-06-18 13:39 -0400
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-18 11:37 -0700
Re: setting pointers to NULL and testing question. Richard Damon <Richard@Damon-Family.org> - 2016-06-18 17:17 -0400
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-18 17:22 -0700
Re: setting pointers to NULL and testing question. James Kuyper <jameskuyper@verizon.net> - 2016-06-13 14:10 -0400
Re: setting pointers to NULL and testing question. Keith Thompson <kst-u@mib.org> - 2016-06-13 11:28 -0700
Re: setting pointers to NULL and testing question. Joe Pfeiffer <pfeiffer@cs.nmsu.edu> - 2016-06-13 12:31 -0600
Re: setting pointers to NULL and testing question. Keith Thompson <kst-u@mib.org> - 2016-06-12 19:38 -0700
Re: setting pointers to NULL and testing question. Richard Heathfield <rjh@cpax.org.uk> - 2016-06-13 07:35 +0100
Re: setting pointers to NULL and testing question. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-06-13 11:40 +0100
Re: setting pointers to NULL and testing question. BartC <bc@freeuk.com> - 2016-06-13 11:56 +0100
Re: setting pointers to NULL and testing question. Martin Shobe <mashobe9@gmail.com> - 2016-06-13 06:22 -0500
Re: setting pointers to NULL and testing question. BartC <bc@freeuk.com> - 2016-06-13 12:39 +0100
Re: setting pointers to NULL and testing question. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-06-13 15:22 +0100
Re: setting pointers to NULL and testing question. BartC <bc@freeuk.com> - 2016-06-13 17:34 +0100
Re: setting pointers to NULL and testing question. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-06-13 19:31 +0100
Re: setting pointers to NULL and testing question. BartC <bc@freeuk.com> - 2016-06-13 20:17 +0100
Re: setting pointers to NULL and testing question. Keith Thompson <kst-u@mib.org> - 2016-06-13 12:47 -0700
Re: setting pointers to NULL and testing question. Tim Rentsch <txr@alumni.caltech.edu> - 2016-06-15 16:44 -0700
Re: setting pointers to NULL and testing question. Keith Thompson <kst-u@mib.org> - 2016-06-15 17:20 -0700
Re: setting pointers to NULL and testing question. Malcolm McLean <malcolm.mclean5@btinternet.com> - 2016-06-13 04:01 -0700
Re: setting pointers to NULL and testing question. Tim Rentsch <txr@alumni.caltech.edu> - 2016-06-16 12:57 -0700
Re: setting pointers to NULL and testing question. Richard Heathfield <rjh@cpax.org.uk> - 2016-06-13 12:18 +0100
Re: setting pointers to NULL and testing question. David Brown <david.brown@hesbynett.no> - 2016-06-13 14:01 +0200
Re: setting pointers to NULL and testing question. Richard Heathfield <rjh@cpax.org.uk> - 2016-06-13 14:15 +0100
Re: setting pointers to NULL and testing question. David Brown <david.brown@hesbynett.no> - 2016-06-13 15:37 +0200
Re: setting pointers to NULL and testing question. Richard Heathfield <rjh@cpax.org.uk> - 2016-06-13 14:48 +0100
Re: setting pointers to NULL and testing question. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-06-13 15:43 +0100
Re: setting pointers to NULL and testing question. Malcolm McLean <malcolm.mclean5@btinternet.com> - 2016-06-12 17:54 -0700
Re: setting pointers to NULL and testing question. John Bode <jfbode1029@gmail.com> - 2016-06-14 08:36 -0700
Re: setting pointers to NULL and testing question. Richard Heathfield <rjh@cpax.org.uk> - 2016-06-14 16:41 +0100
Re: setting pointers to NULL and testing question. Malcolm McLean <malcolm.mclean5@btinternet.com> - 2016-06-14 09:01 -0700
Re: setting pointers to NULL and testing question. "James R. Kuyper" <jameskuyper@verizon.net> - 2016-06-14 13:25 -0400
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-14 10:48 -0700
Re: setting pointers to NULL and testing question. Ian Collins <ian-news@hotmail.com> - 2016-06-15 08:24 +1200
Re: setting pointers to NULL and testing question. "James R. Kuyper" <jameskuyper@verizon.net> - 2016-06-14 16:57 -0400
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-14 14:29 -0700
Re: setting pointers to NULL and testing question. BartC <bc@freeuk.com> - 2016-06-14 22:41 +0100
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-14 15:16 -0700
Re: setting pointers to NULL and testing question. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-06-15 00:25 +0100
Re: setting pointers to NULL and testing question. Keith Thompson <kst-u@mib.org> - 2016-06-14 16:58 -0700
Re: setting pointers to NULL and testing question. David Brown <david.brown@hesbynett.no> - 2016-06-15 09:28 +0200
Re: setting pointers to NULL and testing question. Keith Thompson <kst-u@mib.org> - 2016-06-15 09:57 -0700
Re: setting pointers to NULL and testing question. David Brown <david.brown@hesbynett.no> - 2016-06-15 09:23 +0200
Re: setting pointers to NULL and testing question. Richard Heathfield <rjh@cpax.org.uk> - 2016-06-15 08:35 +0100
Re: setting pointers to NULL and testing question. Richard Heathfield <rjh@cpax.org.uk> - 2016-06-15 08:37 +0100
Re: setting pointers to NULL and testing question. David Brown <david.brown@hesbynett.no> - 2016-06-15 10:42 +0200
Re: setting pointers to NULL and testing question. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-06-15 13:28 +0100
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-15 11:41 -0700
Re: setting pointers to NULL and testing question. raltbos@xs4all.nl (Richard Bos) - 2016-06-15 19:43 +0000
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-15 13:09 -0700
Re: setting pointers to NULL and testing question. David Brown <david.brown@hesbynett.no> - 2016-06-15 22:31 +0200
Re: setting pointers to NULL and testing question. Eric Sosman <esosman@comcast-dot-net.invalid> - 2016-06-15 17:11 -0400
Re: setting pointers to NULL and testing question. raltbos@xs4all.nl (Richard Bos) - 2016-06-18 20:44 +0000
Re: setting pointers to NULL and testing question. Eric Sosman <esosman@comcast-dot-net.invalid> - 2016-06-18 17:20 -0400
Re: setting pointers to NULL and testing question. Malcolm McLean <malcolm.mclean5@btinternet.com> - 2016-06-14 23:22 -0700
Re: setting pointers to NULL and testing question. Ben Bacarisse <ben.usenet@bsb.me.uk> - 2016-06-15 13:23 +0100
Re: setting pointers to NULL and testing question. John Bode <jfbode1029@gmail.com> - 2016-06-20 13:36 -0700
Re: setting pointers to NULL and testing question. Eric Sosman <esosman@comcast-dot-net.invalid> - 2016-06-14 14:59 -0400
Re: setting pointers to NULL and testing question. "James R. Kuyper" <jameskuyper@verizon.net> - 2016-06-14 15:17 -0400
Re: setting pointers to NULL and testing question. Richard Heathfield <rjh@cpax.org.uk> - 2016-06-14 20:25 +0100
Re: setting pointers to NULL and testing question. Tim Rentsch <txr@alumni.caltech.edu> - 2016-06-15 19:48 -0700
Re: setting pointers to NULL and testing question. Eric Sosman <esosman@comcast-dot-net.invalid> - 2016-06-15 23:25 -0400
Re: setting pointers to NULL and testing question. James Kuyper <jameskuyper@verizon.net> - 2016-06-16 12:11 -0400
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-16 09:25 -0700
Re: setting pointers to NULL and testing question. Tim Rentsch <txr@alumni.caltech.edu> - 2016-06-16 21:46 -0700
Re: setting pointers to NULL and testing question. Gareth Owen <gwowen@gmail.com> - 2016-06-17 06:13 +0100
Re: setting pointers to NULL and testing question. Robert Wessel <robertwessel2@yahoo.com> - 2016-06-17 02:03 -0500
Re: setting pointers to NULL and testing question. Martin Shobe <mashobe9@gmail.com> - 2016-06-17 07:07 -0500
Re: setting pointers to NULL and testing question. supercat@casperkitty.com - 2016-06-17 03:18 -0700
Re: setting pointers to NULL and testing question. James Kuyper <jameskuyper@verizon.net> - 2016-06-17 09:16 -0400
Re: setting pointers to NULL and testing question. Tim Rentsch <txr@alumni.caltech.edu> - 2016-06-21 11:03 -0700
Re: setting pointers to NULL and testing question. Tim Rentsch <txr@alumni.caltech.edu> - 2016-06-21 10:58 -0700
Re: setting pointers to NULL and testing question. Keith Thompson <kst-u@mib.org> - 2016-06-21 11:28 -0700
Re: setting pointers to NULL and testing question. Tim Rentsch <txr@alumni.caltech.edu> - 2016-06-16 21:39 -0700
Page 1 of 5 [1] 2 3 4 5 Next page →
| From | G G <gdotone@gmail.com> |
|---|---|
| Date | 2016-06-12 16:21 -0700 |
| Subject | setting pointers to NULL and testing question. |
| Message-ID | <31b753b3-2d70-40f3-885e-2a75b98cf739@googlegroups.com> |
when should a pointer to set to NULL and test whether it is NULL?
[toc] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2016-06-13 00:50 +0100 |
| Message-ID | <njksgm$8mt$1@dont-email.me> |
| In reply to | #88991 |
On 13/06/16 00:21, G G wrote:
> when should a pointer to set to NULL and test whether
> it is NULL?
That's an excellent question.
A pointer can be in any of three states:
(1) a null pointer;
(2) a valid pointer to an existing object or function;
(3) indeterminate.
In which of these states can we legitimately test a pointer (compare its
value to some other value)? Answer: state 1 (a null pointer) and state 2
(a valid pointer), but NOT state 3.
There are two things we want to avoid:
(1) trying to dereference a null pointer;
(2) trying to dereference an indeterminate pointer.
Here comes the answer to your second question.
We can avoid dereferencing a null pointer easily, simply by testing
before dereferencing. Thus, if there is any possibility that a pointer
might have a null value, we should test before dereferencing:
if(p != NULL)
{
*p ...
}
This is not necessary if the pointer /cannot/ be a null pointer:
int i = 42;
int *p = &i;
*p ... is fine, without the test, because p can't be null, because &i
can't be null.
Now for your first question.
What we /can't/ do is test an indeterminate pointer in this way. If p is
indeterminate, simply comparing it to NULL invokes undefined behaviour
(which, in this case, is Very Bad).
What we /can/ do is ensure that we are never put into that position. So
we should always make sure that our pointer is in one of three states:
(1) a null pointer (because we can test this);
(2) a valid pointer;
(3) an indeterminate pointer that's just about to go out of scope anyway
so we don't care.
So let's just pretend that we have a pointer whose initial value we are
not yet in a position to decide. Set It To NULL!
int *p = NULL;
Later in the code, we can test for this.
if(some_condition)
{
p = some_function_yielding_a_pointer(args);
}
if(p != NULL)
{
all is well.
We're not worried about state (2), valid pointers, because they're...
well, valid.
But what about when we render a pointer /in/valid? This can happen in
either of two circumstances:
(1) we know for a fact that we're about to go out of scope:
free(p);
return z;
}
In that circumstance, setting p to NULL is of course perfectly legal but
might be regarded in some quarters as being mildly paranoid.
(2) we /don't/ know for a fact that we're about to go out of scope:
free(p);
p = NULL;
By doing this, we retain our ability to compare p to NULL. Thus, code
that comes /after/ this code can safely test p before using it. If we
didn't do that:
free(p); /* fine */
; /* haven't set p to NULL - could be a problem later */
if(p != NULL) /* undefined behaviour */
So the answer to your questions, in brief, is:
(a) we should give a pointer a null value if we don't have a valid value
to give it (unless it's about to go out of scope);
(b) we should compare a pointer's value to NULL before dereferencing it
(if there is even the slightest possibility that it might be a null
pointer).
--
Richard Heathfield
Email: rjh at cpax dot org dot uk
"Usenet is a strange place" - dmr 29 July 1999
Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
| From | G G <gdotone@gmail.com> |
|---|---|
| Date | 2016-06-12 17:03 -0700 |
| Message-ID | <62624d21-46a0-40ad-a995-136f4254501c@googlegroups.com> |
| In reply to | #88992 |
WoW! Thanks Richard. really helpful. g.
[toc] | [prev] | [next] | [standalone]
| From | Joe Pfeiffer <pfeiffer@cs.nmsu.edu> |
|---|---|
| Date | 2016-06-12 20:25 -0600 |
| Message-ID | <1ba8iprfjq.fsf@pfeifferfamily.net> |
| In reply to | #88992 |
Excellent response, but I do have one question. You say Richard Heathfield <rjh@cpax.org.uk> writes: > > What we /can't/ do is test an indeterminate pointer in this way. If p > is indeterminate, simply comparing it to NULL invokes undefined > behaviour (which, in this case, is Very Bad). In the C99 standard (note -- I've actually only got a committee draft from 2005, and don't know what differences the final standard might have) I find in 6.5.9 3 The == (equal to) and != (not equal to) operators are analogous to the relational operators except for their lower precedence.91) Each of the operators yields 1 if the specified relation is true and 0 if it is false. The result has type int. For any pair of operands, exactly one of the relations is true. and in 6.5.10 6 Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.92) So it would seem as if in the case that a pointer is compared to null, it will compare equal if the pointer is also null, or not equal in all other circumstances. The comparison is useless in the case of an indeterminate pointer, of course, since an indeterminate pointer has a vanishingly small possibility of being null. But I'm not seeing why it's undefined behavior.
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2016-06-12 22:57 -0400 |
| Message-ID | <njl7f6$2kr$1@dont-email.me> |
| In reply to | #88995 |
On 06/12/2016 10:25 PM, Joe Pfeiffer wrote: > Excellent response, but I do have one question. You say > Richard Heathfield <rjh@cpax.org.uk> writes: >> >> What we /can't/ do is test an indeterminate pointer in this way. If p >> is indeterminate, simply comparing it to NULL invokes undefined >> behaviour (which, in this case, is Very Bad). > > In the C99 standard (note -- I've actually only got a committee draft > from 2005, and don't know what differences the final standard might > have) I find in 6.5.9 > > 3 The == (equal to) and != (not equal to) operators are analogous to the relational > operators except for their lower precedence.91) Each of the operators yields 1 if the > specified relation is true and 0 if it is false. The result has type int. For any pair of > operands, exactly one of the relations is true. > > and in 6.5.10 > > 6 Two pointers compare equal if and only if both are null pointers, both are pointers to the > same object (including a pointer to an object and a subobject at its beginning) or function, > both are pointers to one past the last element of the same array object, or one is a pointer > to one past the end of one array object and the other is a pointer to the start of a different > array object that happens to immediately follow the first array object in the address > space.92) > > So it would seem as if in the case that a pointer is compared to null, > it will compare equal if the pointer is also null, or not equal in all > other circumstances. > > The comparison is useless in the case of an indeterminate pointer, of > course, since an indeterminate pointer has a vanishingly small > possibility of being null. But I'm not seeing why it's undefined > behavior. The sections you are citing refer to the behavior of the comparison operators. Before those operators can be applied to a pointer value, that value must be read from the pointer object. Here's what the standard says about that: 3.19.2: indeterminate value either an unspecified value or a trap representation 6.2.6.1p5: Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. ... Such a representation is called a trap representation.
[toc] | [prev] | [next] | [standalone]
| From | Joe Pfeiffer <pfeiffer@cs.nmsu.edu> |
|---|---|
| Date | 2016-06-13 10:03 -0600 |
| Message-ID | <1b60tdqdna.fsf@pfeifferfamily.net> |
| In reply to | #88997 |
James Kuyper <jameskuyper@verizon.net> writes: <snip my stuff> > The sections you are citing refer to the behavior of the comparison > operators. Before those operators can be applied to a pointer value, > that value must be read from the pointer object. Here's what the > standard says about that: > > 3.19.2: > indeterminate value > either an unspecified value or a trap representation > > 6.2.6.1p5: > Certain object representations need not represent a value of the object > type. If the stored value of an object has such a representation and is > read by an lvalue expression that does not have character type, the > behavior is undefined. ... Such a representation is called > a trap representation. Ah, OK. Loading a trap representation itself being undefined behavior hadn't occurred to me.
[toc] | [prev] | [next] | [standalone]
| From | Tim Rentsch <txr@alumni.caltech.edu> |
|---|---|
| Date | 2016-06-15 14:43 -0700 |
| Message-ID | <kfnk2hq5dsd.fsf@x-alumni2.alumni.caltech.edu> |
| In reply to | #89022 |
Joe Pfeiffer <pfeiffer@cs.nmsu.edu> writes: > James Kuyper <jameskuyper@verizon.net> writes: > <snip my stuff> > >> The sections you are citing refer to the behavior of the comparison >> operators. Before those operators can be applied to a pointer value, >> that value must be read from the pointer object. Here's what the >> standard says about that: >> >> 3.19.2: >> indeterminate value >> either an unspecified value or a trap representation >> >> 6.2.6.1p5: >> Certain object representations need not represent a value of the object >> type. If the stored value of an object has such a representation and is >> read by an lvalue expression that does not have character type, the >> behavior is undefined. ... Such a representation is called >> a trap representation. > > Ah, OK. Loading a trap representation itself being undefined behavior > hadn't occurred to me. A better way to think of this is that using an indeterminate pointer value is undefined behavior, whenever it is used, no matter where the value comes from. The reasoning above isn't exactly right: a pointer value may be valid when it is read from a pointer object, and even participate in defined operations, but become indeterminate "in flight" at some later point, even before it has been stored. (Not typical, but possible.) When a pointer value becomes indeterminate, even if is just a value somewhere in an expression being evaluated, any further use of that value gives undefined behavior.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-06-13 08:06 -0700 |
| Message-ID | <61d20284-ac0d-432c-bb61-6d0cb54d3c37@googlegroups.com> |
| In reply to | #88995 |
On Sunday, June 12, 2016 at 9:25:28 PM UTC-5, Joe Pfeiffer wrote:
> The comparison is useless in the case of an indeterminate pointer, of
> course, since an indeterminate pointer has a vanishingly small
> possibility of being null. But I'm not seeing why it's undefined
> behavior.
The reason that it's undefined behavior is that some machines have hardware
traps which will fire if an attempt is made to load a pointer register with
an invalid pointer. The authors of the Standard were willing to burden
implementations on such machines with a requirement that loading a null
pointer for purposes of equality comparison must not cause a trap, but were
not willing to impose such a burden for invalid pointers in general. For
many years, the behavior could be regarded as defined on machines which did
not use such hardware.
More recently, however, it has become fashionable for compilers to use
places where the Standard imposes no requirements as opportunities to change
the meaning of surrounding code. While something like:
void adjust_size(size_t new_size)
{
void *temp = realloc(data_store, new_size);
if (!temp) FATAL_ERROR("Couldn't resize");
if (temp != data_store)
{
data_store = temp;
recalculate_pointers();
}
}
used to be a helpful optimization in cases where a realloc would be
unlikely to move an object, and where code could handle the object-not-
moved case more efficiently than the object-moved case. A hyper-modern
compiler, however, could recognize that reaching the second "if" with
temp not equal to "NULL" would invoke Undefined Behavior, and thus
replace the above code with:
void adjust_size(size_t new_size)
{
realloc(data_store, new_size);
FATAL_ERROR("Couldn't resize");
if (data_store)
{
data_store = 0;
recalculate_pointers();
}
}
[toc] | [prev] | [next] | [standalone]
| From | Richard Damon <Richard@Damon-Family.org> |
|---|---|
| Date | 2016-06-16 08:27 -0400 |
| Message-ID | <%Ww8z.12636$C_1.1578@fx13.iad> |
| In reply to | #89021 |
On 6/13/16 11:06 AM, supercat@casperkitty.com wrote: > On Sunday, June 12, 2016 at 9:25:28 PM UTC-5, Joe Pfeiffer wrote: >> The comparison is useless in the case of an indeterminate pointer, of >> course, since an indeterminate pointer has a vanishingly small >> possibility of being null. But I'm not seeing why it's undefined >> behavior. > > The reason that it's undefined behavior is that some machines have hardware > traps which will fire if an attempt is made to load a pointer register with > an invalid pointer. The authors of the Standard were willing to burden > implementations on such machines with a requirement that loading a null > pointer for purposes of equality comparison must not cause a trap, but were > not willing to impose such a burden for invalid pointers in general. For > many years, the behavior could be regarded as defined on machines which did > not use such hardware. > Wrong, maybe by someone ignoring the Standard they could 'regard' the behavior as defined. Unless the implementation formally documents this behavior, the behavior is undefined (and if documented, the use of that behavior makes the implementation dependent). In many case as you describe, the undefined behavior is benign, but it is still undefined. This is your common fallacy, something 'worked' a given way which you liked, so you assume that that result of undefined behavior must be th definition, and when it changes you blame the implementation for breaking a promise it never made. This attitude has caused more problems in the history of programming, resulting in the canonical faults like the assumption that int = 16 bits, int smaller than long, int = 32 bits, int equivalent to long, ints could hold pointers, longs could hold pointers, etc. All of these were commonly held beliefs, and many programmers wrote code, erroneously, assuming them, only to have the code break on the next generation of machines. If you want promises beyond what the standard provides, fine, document the language extensions you need and use implementations that are DOCUMENTED to provide those. Just realize that you now can only use implementation that are documented to provide those extensions. You have repeatedly made this complaint, it used to work this way (never promised, but YOU ASS-U-ME it) and now it doesn't work on later implementations, so you blame the Standard and Implementation for not keeping a promise they never made. The strangest part of the complaint, is that the implementations that you rail against actually do DOCUMENT ways to invoke them to have most of the things you want them to have, but you seem to refuse to use that. They also are specifically NOT 'Standards Compliant' in their default mode of execution, but have some non-compliant extensions to the language enabled by default, so you really NEED to read the documentation to properly use them.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-06-16 06:27 -0700 |
| Message-ID | <87d98c39-391b-4e2a-9060-1fa472edc879@googlegroups.com> |
| In reply to | #89259 |
On Thursday, June 16, 2016 at 7:27:49 AM UTC-5, Richard Damon wrote: > This is your common fallacy, something 'worked' a given way which you > liked, so you assume that that result of undefined behavior must be th > definition, and when it changes you blame the implementation for > breaking a promise it never made. This attitude has caused more problems > in the history of programming, resulting in the canonical faults like > the assumption that int = 16 bits, int smaller than long, int = 32 bits, > int equivalent to long, ints could hold pointers, longs could hold > pointers, etc. > All of these were commonly held beliefs, and many programmers wrote > code, erroneously, assuming them, only to have the code break on the > next generation of machines. If a simplistic compiler for a language for a particular platform, fed a particular piece of source code X, will yield 100% reliable machine code to perform that task with a certain degree of performance, then if the language is any good there should be a piece of source code Y which is can be guaranteed to yield those results without significant sacrifices in performance or readability. If one regards the C language as inheriting the traits of the platforms upon which it runs, it's a good language. If one does not, it's a crummy language. > If you want promises beyond what the standard provides, fine, document > the language extensions you need and use implementations that are > DOCUMENTED to provide those. Just realize that you now can only use > implementation that are documented to provide those extensions. Many things which are undefined today were well-defined in C89. Why should a compiler have been expected to document that of struct "foo" has a field "x" of type "int" at offset 12, and struct "bar" has a field with the same name and offset, then casting to "foo" a pointer to either type and accessing field "x" will access the same thing, given that 1. That behavior was specified in C74 2. No compilers since had deviated from it With regard to two's-complement integers, they are mathematically incapable of overflow in the same way as unsigned integers, since the two's-complement integers of any given size form an abstract algebraic ring which is isomorphic to the unsigned integers of the same size. > The strangest part of the complaint, is that the implementations that > you rail against actually do DOCUMENT ways to invoke them to have most > of the things you want them to have, but you seem to refuse to use that. > They also are specifically NOT 'Standards Compliant' in their default > mode of execution, but have some non-compliant extensions to the > language enabled by default, so you really NEED to read the > documentation to properly use them. I object in part to the notion that things were never defined, when in fact programmers were merely expecting that traditional defined behaviors would continue, at least until the better alternatives behave available. I would also suggest that in many cases the level of optimization that can be achieved by completely revoking the guarantees would be inferior to the level that could be achieved by weakening them but not eliminating them altogether. Do you disagree with either point?
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-06-16 07:31 -0700 |
| Message-ID | <d94e00a3-e3ad-475e-a94a-35f34a3aabbb@googlegroups.com> |
| In reply to | #89263 |
On Thursday, June 16, 2016 at 8:27:22 AM UTC-5, supe...@casperkitty.com wrote: > > The strangest part of the complaint, is that the implementations that > > you rail against actually do DOCUMENT ways to invoke them to have most > > of the things you want them to have, but you seem to refuse to use that. > > They also are specifically NOT 'Standards Compliant' in their default > > mode of execution, but have some non-compliant extensions to the > > language enabled by default, so you really NEED to read the > > documentation to properly use them. A further objection is that a good language should make it practical to write code which is highly unlikely to become undefined in any future version of the language. That could be accomplished by ensuring that for every construct upon which the Standard imposes no requirements but for which there is no practical alternative, an implementation describes a version that will work "forever" along with a directive to indicate that all uses of the old form have been replaced with the new form. As it is, though, there are a number of forms of UB that are required in many kinds of practical programming which gcc doesn't yet "punish", but for which it does not so far as I can tell provide a clean alternative either. How can one use gcc to write or work with code which requires such constructs and have any confidence that a gcc maintainers won't break it in a future version of gcc just because they can?
[toc] | [prev] | [next] | [standalone]
| From | Richard Damon <Richard@Damon-Family.org> |
|---|---|
| Date | 2016-06-18 13:39 -0400 |
| Message-ID | <FHf9z.2464$HD2.31@fx37.iad> |
| In reply to | #89263 |
On 6/16/16 9:27 AM, supercat@casperkitty.com wrote: > On Thursday, June 16, 2016 at 7:27:49 AM UTC-5, Richard Damon wrote: >> This is your common fallacy, something 'worked' a given way which you >> liked, so you assume that that result of undefined behavior must be th >> definition, and when it changes you blame the implementation for >> breaking a promise it never made. This attitude has caused more problems >> in the history of programming, resulting in the canonical faults like >> the assumption that int = 16 bits, int smaller than long, int = 32 bits, >> int equivalent to long, ints could hold pointers, longs could hold >> pointers, etc. > >> All of these were commonly held beliefs, and many programmers wrote >> code, erroneously, assuming them, only to have the code break on the >> next generation of machines. > > If a simplistic compiler for a language for a particular platform, fed a > particular piece of source code X, will yield 100% reliable machine code > to perform that task with a certain degree of performance, then if the > language is any good there should be a piece of source code Y which is > can be guaranteed to yield those results without significant sacrifices > in performance or readability. > > If one regards the C language as inheriting the traits of the platforms > upon which it runs, it's a good language. If one does not, it's a > crummy language. > 100% Reliable requires something to PROMISE that it will always work. The hardware documentation for the processor may make the promise that the given sequence of assembly instructions are 'Reliable', but unless you have something which PROMISES that a given Statement will generate a given assembly sequence (which the Standard doesn't, and in fact CAN'T) you are not 'Reliable', but just counting on happenstance. >> If you want promises beyond what the standard provides, fine, document >> the language extensions you need and use implementations that are >> DOCUMENTED to provide those. Just realize that you now can only use >> implementation that are documented to provide those extensions. > > Many things which are undefined today were well-defined in C89. Why should > a compiler have been expected to document that of struct "foo" has a field > "x" of type "int" at offset 12, and struct "bar" has a field with the same > name and offset, then casting to "foo" a pointer to either type and accessing > field "x" will access the same thing, given that > > 1. That behavior was specified in C74 > 2. No compilers since had deviated from it > > With regard to two's-complement integers, they are mathematically > incapable of overflow in the same way as unsigned integers, since the > two's-complement integers of any given size form an abstract algebraic > ring which is isomorphic to the unsigned integers of the same size. I have worked on machines that for 16 bit two's complement numbers, 32767+1 was NOT -32768, on one machine it was 32767 and on another it was a trap. The definition (that the standard uses) of signed numbers does NOT define them as a ring (like it does for unsigned) > >> The strangest part of the complaint, is that the implementations that >> you rail against actually do DOCUMENT ways to invoke them to have most >> of the things you want them to have, but you seem to refuse to use that. >> They also are specifically NOT 'Standards Compliant' in their default >> mode of execution, but have some non-compliant extensions to the >> language enabled by default, so you really NEED to read the >> documentation to properly use them. > > I object in part to the notion that things were never defined, when in > fact programmers were merely expecting that traditional defined behaviors > would continue, at least until the better alternatives behave available. For something to be DEFINED, someone has to give a DEFINITION. Just because something has seemed to work, doesn't make it a law that it MUST work. History is full of people who assume that things that 'just seem to work' will continue to work to their detriment. > > I would also suggest that in many cases the level of optimization that > can be achieved by completely revoking the guarantees would be inferior to > the level that could be achieved by weakening them but not eliminating > them altogether. > > Do you disagree with either point? > I don't know well enough what options are really available for optimization, as that is not my field of expertise. It does seem logical to me that making no promises about behavior in a situation should give the compiler more options than a partial promise, as anything that could be done with the latter, could also have been done in the former. Maybe in the strange cases you like to propose where you don't care about getting right answers, just limited undefined behavior, the partial promises might be useful. I will observe that the writers of the compilers seem to disagree with you, and think that the typical programmer wants the better optimizations and is willing to avoid undefined behavior to get it.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-06-18 11:37 -0700 |
| Message-ID | <3b8e8a97-10fb-49cd-9e9d-e7e20d1b0aac@googlegroups.com> |
| In reply to | #89539 |
On Saturday, June 18, 2016 at 12:39:57 PM UTC-5, Richard Damon wrote:
> On 6/16/16 9:27 AM, supercat wrote:
> > If a simplistic compiler for a language for a particular platform, fed a
> > particular piece of source code X, will yield 100% reliable machine code
> > to perform that task with a certain degree of performance, then if the
> > language is any good there should be a piece of source code Y which is
> > can be guaranteed to yield those results without significant sacrifices
> > in performance or readability.
> >
> > If one regards the C language as inheriting the traits of the platforms
> > upon which it runs, it's a good language. If one does not, it's a
> > crummy language.
>
> 100% Reliable requires something to PROMISE that it will always work.
> The hardware documentation for the processor may make the promise that
> the given sequence of assembly instructions are 'Reliable', but unless
> you have something which PROMISES that a given Statement will generate a
> given assembly sequence (which the Standard doesn't, and in fact CAN'T)
> you are not 'Reliable', but just counting on happenstance.
If a simplistic compiler would generate 100% reliable machine code to
perform a task, that would suggest that making a compiler capable of
generating such machine code should not be overly difficult (since even
a simplistic compiler can do it). The simplistic compiler may be forgoing
other optimizations that could offer a 50% performance boost, but if by
so doing it enables a programmer to make a hot loop run four times as fast,
I'd suggest that time spent on those other optimizations is not well-
spent.
> > Many things which are undefined today were well-defined in C89. Why should
> > a compiler have been expected to document that of struct "foo" has a field
> > "x" of type "int" at offset 12, and struct "bar" has a field with the same
> > name and offset, then casting to "foo" a pointer to either type and accessing
> > field "x" will access the same thing, given that
> >
> > 1. That behavior was specified in C74
> > 2. No compilers since had deviated from it
> >
> > With regard to two's-complement integers, they are mathematically
> > incapable of overflow in the same way as unsigned integers, since the
> > two's-complement integers of any given size form an abstract algebraic
> > ring which is isomorphic to the unsigned integers of the same size.
>
> I have worked on machines that for 16 bit two's complement numbers,
> 32767+1 was NOT -32768, on one machine it was 32767 and on another it
> was a trap. The definition (that the standard uses) of signed numbers
> does NOT define them as a ring (like it does for unsigned)
Did the hardware platforms and/or compilers document the behaviors in
question? I would be rather surprised if they didn't.
If one is running code on a hardware platform which does not perform
signed arithmetic using an algebraic ring, then it would of course be
unreasonable to expect that math would behave in such a fashion. How
many twentieth-century implementations do you know of where:
1. The underlying hardware performed wrapping two's-complement math.
2. The compiler's generated code would not always generate values whose
lower bits matched the arithmetically-correct ones (with the caveat
that temporary values may sometimes hold "extra" precision, so the
value might not be truncated to the range of the type)?
3. No deviations from the wrapping two's-complement math behaviors were
noted in the compiler documentation?
I've certainly seen plenty of compilers which document ways in which math
might deviate from silent-wraparound behavior (though for compilers of that
era even the possibility that temporary values could exceed the range of
"int" would have been regarded as an oddity worth noting). Is there any
evidence that it would have been considered "acceptable" for compilers of
that era that targeted silent-wraparound hardware to implement contrary
semantics without documenting it?
Why do you suppose the authors of the C Standard would have considered
the fact that the majority of then-current implementations treated signed
math as an algebraic ring except cases where the result was used in a way
where upper bits would matter, if they didn't feel that implementations
made any promise in that regard?
> > I object in part to the notion that things were never defined, when in
> > fact programmers were merely expecting that traditional defined behaviors
> > would continue, at least until the better alternatives behave available.
>
> For something to be DEFINED, someone has to give a DEFINITION. Just
> because something has seemed to work, doesn't make it a law that it MUST
> work. History is full of people who assume that things that 'just seem
> to work' will continue to work to their detriment.
In C74, given:
struct s1 { int x,y; float z; ];
struct s1 *p1 = ...;
the meaning of p1->y was defined as being equivalent to
(int*)( [[offset of y]] + (char*)p1)
Would a guarantee of such behavior in compilers implementing subsequent
dialects of C have been regarded as an "extension", or merely a
continuation of established behavioral practice?
> > I would also suggest that in many cases the level of optimization that
> > can be achieved by completely revoking the guarantees would be inferior to
> > the level that could be achieved by weakening them but not eliminating
> > them altogether.
> >
> > Do you disagree with either point?
> >
>
> I don't know well enough what options are really available for
> optimization, as that is not my field of expertise. It does seem logical
> to me that making no promises about behavior in a situation should give
> the compiler more options than a partial promise, as anything that could
> be done with the latter, could also have been done in the former. Maybe
> in the strange cases you like to propose where you don't care about
> getting right answers, just limited undefined behavior, the partial
> promises might be useful. I will observe that the writers of the
> compilers seem to disagree with you, and think that the typical
> programmer wants the better optimizations and is willing to avoid
> undefined behavior to get it.
A lot of people have historically been willing to tolerate applications
which core-dump when given invalid data, and such people would probably
have said that they don't care what a program does when given invalid data
because they would expect that memory protections would ensure that the
program will do nothing worse than producing bad data or dumping core.
Do you think such people would be happy if the program e.g. randomly
changed a few bytes here and there on disk? The only reason people don't
think they care what a program does when given invalid input is that they
assume its behavior will stay within some bounds. I suspect the number of
people who would be willing to tolerate a program which would have truly
disastrous consequences if fed invalid input is actually quite low. Would
you disagree?
[toc] | [prev] | [next] | [standalone]
| From | Richard Damon <Richard@Damon-Family.org> |
|---|---|
| Date | 2016-06-18 17:17 -0400 |
| Message-ID | <oTi9z.5960$9s2.3340@fx41.iad> |
| In reply to | #89543 |
On 6/18/16 2:37 PM, supercat@casperkitty.com wrote:
> On Saturday, June 18, 2016 at 12:39:57 PM UTC-5, Richard Damon wrote:
>> On 6/16/16 9:27 AM, supercat wrote:
>>> If a simplistic compiler for a language for a particular platform, fed a
>>> particular piece of source code X, will yield 100% reliable machine code
>>> to perform that task with a certain degree of performance, then if the
>>> language is any good there should be a piece of source code Y which is
>>> can be guaranteed to yield those results without significant sacrifices
>>> in performance or readability.
>>>
>>> If one regards the C language as inheriting the traits of the platforms
>>> upon which it runs, it's a good language. If one does not, it's a
>>> crummy language.
>>
>> 100% Reliable requires something to PROMISE that it will always work.
>> The hardware documentation for the processor may make the promise that
>> the given sequence of assembly instructions are 'Reliable', but unless
>> you have something which PROMISES that a given Statement will generate a
>> given assembly sequence (which the Standard doesn't, and in fact CAN'T)
>> you are not 'Reliable', but just counting on happenstance.
>
> If a simplistic compiler would generate 100% reliable machine code to
> perform a task, that would suggest that making a compiler capable of
> generating such machine code should not be overly difficult (since even
> a simplistic compiler can do it). The simplistic compiler may be forgoing
> other optimizations that could offer a 50% performance boost, but if by
> so doing it enables a programmer to make a hot loop run four times as fast,
> I'd suggest that time spent on those other optimizations is not well-
> spent.
>
Your case shows that it is possible FOR THAT MACHINE to generate such
code. And the Standard does not prohibit an implementation from do so.
What the Standard does not do, that you want to insist, is that the
implementation MUST do so. The issue is that on some machine (now more
rare than before) it is very expensive to do so. The Standards Committee
long ago had a decision to make, do we define the C language to be
usable on a wide assortment of machines, and not mandate highest level
of promises on them, (leaving this possible to a more restricted
standard, somewhat lie POSIX) or defining the language in a more
restricted manner for only a more limited range of machines. The
committee took the lead from the original authors of the language to
keep the definition broad.
There is NOTHING in the Standard to prohibit an implementation for
making such a promise, and I would imagine that if there was enough
demand, it would be present. In fact, since GCC is open source, and you
seem such an expert on how to optimize code, why don't YOU develop an
option for GCC to provide it.
>>> Many things which are undefined today were well-defined in C89. Why should
>>> a compiler have been expected to document that of struct "foo" has a field
>>> "x" of type "int" at offset 12, and struct "bar" has a field with the same
>>> name and offset, then casting to "foo" a pointer to either type and accessing
>>> field "x" will access the same thing, given that
>>>
>>> 1. That behavior was specified in C74
>>> 2. No compilers since had deviated from it
>>>
>>> With regard to two's-complement integers, they are mathematically
>>> incapable of overflow in the same way as unsigned integers, since the
>>> two's-complement integers of any given size form an abstract algebraic
>>> ring which is isomorphic to the unsigned integers of the same size.
>>
>> I have worked on machines that for 16 bit two's complement numbers,
>> 32767+1 was NOT -32768, on one machine it was 32767 and on another it
>> was a trap. The definition (that the standard uses) of signed numbers
>> does NOT define them as a ring (like it does for unsigned)
>
> Did the hardware platforms and/or compilers document the behaviors in
> question? I would be rather surprised if they didn't.
Yes they did. But their existence is a disproof of your assertion.
>
> If one is running code on a hardware platform which does not perform
> signed arithmetic using an algebraic ring, then it would of course be
> unreasonable to expect that math would behave in such a fashion. How
> many twentieth-century implementations do you know of where:
>
> 1. The underlying hardware performed wrapping two's-complement math.
>
> 2. The compiler's generated code would not always generate values whose
> lower bits matched the arithmetically-correct ones (with the caveat
> that temporary values may sometimes hold "extra" precision, so the
> value might not be truncated to the range of the type)?
>
> 3. No deviations from the wrapping two's-complement math behaviors were
> noted in the compiler documentation?
>
> I've certainly seen plenty of compilers which document ways in which math
> might deviate from silent-wraparound behavior (though for compilers of that
> era even the possibility that temporary values could exceed the range of
> "int" would have been regarded as an oddity worth noting). Is there any
> evidence that it would have been considered "acceptable" for compilers of
> that era that targeted silent-wraparound hardware to implement contrary
> semantics without documenting it?
>
> Why do you suppose the authors of the C Standard would have considered
> the fact that the majority of then-current implementations treated signed
> math as an algebraic ring except cases where the result was used in a way
> where upper bits would matter, if they didn't feel that implementations
> made any promise in that regard?
The C Standard did NOT base promises on 'majority', but on what could be
reasonably expected to be achievable on all practical machines. Thus
(unless C11 changed this) it doesn't even require 2s complement to be
used. It does provide some types (which are optional) that, if present,
are 2s complement, but even with these, EXPLICITLY defines 2s complement
as undefined behavior, so don't mandate the ring behavior.
The standard has no words that require an implementation to behave more
definitely just because the hardware has some desirable behavior (and
also doesn't prohibit it from making additional promises based on the
hardware).
>
>>> I object in part to the notion that things were never defined, when in
>>> fact programmers were merely expecting that traditional defined behaviors
>>> would continue, at least until the better alternatives behave available.
>>
>> For something to be DEFINED, someone has to give a DEFINITION. Just
>> because something has seemed to work, doesn't make it a law that it MUST
>> work. History is full of people who assume that things that 'just seem
>> to work' will continue to work to their detriment.
>
> In C74, given:
>
> struct s1 { int x,y; float z; ];
> struct s1 *p1 = ...;
>
> the meaning of p1->y was defined as being equivalent to
>
> (int*)( [[offset of y]] + (char*)p1)
>
> Would a guarantee of such behavior in compilers implementing subsequent
> dialects of C have been regarded as an "extension", or merely a
> continuation of established behavioral practice?
C74 was obsoleted, and as I remember only really defined for a
particular machine. K&R changed many things including this,
INTENTIONALLY. If you want to write in C74, go ahead an find a copy of
it and us it, good luck finding a machine to run it. For STANDARDS,
C89/C90 is the first version, which was largely based on K&R C which was
NOT standardized as there were multiple (incompatible) dialects derived
from K&R, which was one of the motivations to develop a standard.
>
>>> I would also suggest that in many cases the level of optimization that
>>> can be achieved by completely revoking the guarantees would be inferior to
>>> the level that could be achieved by weakening them but not eliminating
>>> them altogether.
>>>
>>> Do you disagree with either point?
>>>
>>
>> I don't know well enough what options are really available for
>> optimization, as that is not my field of expertise. It does seem logical
>> to me that making no promises about behavior in a situation should give
>> the compiler more options than a partial promise, as anything that could
>> be done with the latter, could also have been done in the former. Maybe
>> in the strange cases you like to propose where you don't care about
>> getting right answers, just limited undefined behavior, the partial
>> promises might be useful. I will observe that the writers of the
>> compilers seem to disagree with you, and think that the typical
>> programmer wants the better optimizations and is willing to avoid
>> undefined behavior to get it.
>
> A lot of people have historically been willing to tolerate applications
> which core-dump when given invalid data, and such people would probably
> have said that they don't care what a program does when given invalid data
> because they would expect that memory protections would ensure that the
> program will do nothing worse than producing bad data or dumping core.
> Do you think such people would be happy if the program e.g. randomly
> changed a few bytes here and there on disk? The only reason people don't
> think they care what a program does when given invalid input is that they
> assume its behavior will stay within some bounds. I suspect the number of
> people who would be willing to tolerate a program which would have truly
> disastrous consequences if fed invalid input is actually quite low. Would
> you disagree?
>
>
The only real form of security is to stop someone from doing something
wrong before they do it, not to minimize the damage they can do. If you
avoid letting Undefined Behavior from happening, then you have promises
from the Standard on what you program will do. If you don't get a
promise you want from the Standard, you can possible get a promise from
some additional standard or documented by the implementation, then you
can trust in those promises but you then are dependent on more than the
standard (which is ok if you know it). A lot of programs have been
written that do undefined behavior, and the programmer figured the
results were benign and left it, and THAT causes security issues. You
shouldn't blame the make of the tools if the user hasn't been using it
right.
[toc] | [prev] | [next] | [standalone]
| From | supercat@casperkitty.com |
|---|---|
| Date | 2016-06-18 17:22 -0700 |
| Message-ID | <acc944b7-220a-4fa0-9fe7-597dd3550e61@googlegroups.com> |
| In reply to | #89570 |
On Saturday, June 18, 2016 at 4:17:16 PM UTC-5, Richard Damon wrote:
> Your case shows that it is possible FOR THAT MACHINE to generate such
> code. And the Standard does not prohibit an implementation from do so.
> What the Standard does not do, that you want to insist, is that the
> implementation MUST do so. The issue is that on some machine (now more
> rare than before) it is very expensive to do so. The Standards Committee
> long ago had a decision to make, do we define the C language to be
> usable on a wide assortment of machines, and not mandate highest level
> of promises on them, (leaving this possible to a more restricted
> standard, somewhat lie POSIX) or defining the language in a more
> restricted manner for only a more limited range of machines. The
> committee took the lead from the original authors of the language to
> keep the definition broad.
The notion that the world only needed two kinds of C implementations
(freestanding and embedded) is one of the most preposterous notions ever.
What was and is needed is a collection of dialects, which have some
unifying features, so that code which won't benefit from architectural
guarantees that are only available on 99% of machines will be able to run
on 100% of machines, including the 1% that can't offer the guarantee, but
code whose performance would be improved by using such features can do so
at the expense of reduced compatibility.
> There is NOTHING in the Standard to prohibit an implementation for
> making such a promise, and I would imagine that if there was enough
> demand, it would be present. In fact, since GCC is open source, and you
> seem such an expert on how to optimize code, why don't YOU develop an
> option for GCC to provide it.
Compilers that mirrored the underlying hardware very closely used to be
the norm, and those that deviated were careful to note such deviation. If
a programmer noted that a platform's hardware worked a certain way and the
compiler writer didn't document the compiler as doing anything different,
the fact that the code worked was not "happenstance".
I don't think the different parts of gcc of different vintages would be
compatible with each other, or else I'd pair a somewhat older optimization
engine with a code generator for the ARM Cortex-m0 and m3 series (which I
don't think were supported when some of the optimizations started getting
too aggressive).
> > Did the hardware platforms and/or compilers document the behaviors in
> > question? I would be rather surprised if they didn't.
>
> Yes they did. But their existence is a disproof of your assertion.
My assertion is that those two's-complement implementations which didn't
document contrary behavior would generate a result whose lower bits were
correct, without side-effects (I wouldn't expect that implementations which
don't always truncate the result to the size of the operands would document
that behavior, but would not be terribly surprised if there were some for
platforms where some operations work with values longer than "int" would
sometimes keep extra precision without documenting it).
> > If one is running code on a hardware platform which does not perform
> > signed arithmetic using an algebraic ring, then it would of course be
> > unreasonable to expect that math would behave in such a fashion. How
> > many twentieth-century implementations do you know of where:
> >
> > 1. The underlying hardware performed wrapping two's-complement math.
> >
> > 2. The compiler's generated code would not always generate values whose
> > lower bits matched the arithmetically-correct ones (with the caveat
> > that temporary values may sometimes hold "extra" precision, so the
> > value might not be truncated to the range of the type)?
> >
> > 3. No deviations from the wrapping two's-complement math behaviors were
> > noted in the compiler documentation?
> >
> I've certainly seen plenty of compilers which document ways in which math
> might deviate from silent-wraparound behavior (though for compilers of that
> era even the possibility that temporary values could exceed the range of
> "int" would have been regarded as an oddity worth noting). Is there any
> evidence that it would have been considered "acceptable" for compilers of
> that era that targeted silent-wraparound hardware to implement contrary
> semantics without documenting it?
Sure. Evidence of code getting broken when fed to implementations which
met the aforementioned description. If any significant number of such
implementations existed, that should have happened fairly often, and some
evidence of it should exist.
> > Why do you suppose the authors of the C Standard would have considered
> > the fact that the majority of then-current implementations treated signed
> > math as an algebraic ring except cases where the result was used in a way
> > where upper bits would matter, if they didn't feel that implementations
> > made any promise in that regard?
>
> The C Standard did NOT base promises on 'majority', but on what could be
> reasonably expected to be achievable on all practical machines. Thus
> (unless C11 changed this) it doesn't even require 2s complement to be
> used. It does provide some types (which are optional) that, if present,
> are 2s complement, but even with these, EXPLICITLY defines 2s complement
> as undefined behavior, so don't mandate the ring behavior.
The C89 rationale observes that on two's-complement silent-wraparound
implementations, i.e. the majority of then-current implementations, the
behavior of most operators is identical when given signed and unsigned
operands is identical except when BOTH of the following conditions apply:
1. The MSB of the result is set.
2. The result is promoted to a longer type, used as the left operand
of >>, or as either operand of %, /, or any comparison operator.
Why do you suppose the authors of the rationale would have mentioned the
second condition, if they didn't believe that the implementations in
question defined behavior in cases where the first condition applied but
the second one did not?
I can see how such an observation would have been relevant if the authors
of the Standard expected that the observation would hold true of future
implementations, but can't see why they would have thought it relevant
otherwise. Can you offer any other suggestion?
> The standard has no words that require an implementation to behave more
> definitely just because the hardware has some desirable behavior (and
> also doesn't prohibit it from making additional promises based on the
> hardware).
That is true. It also has no words which imply that it describes
everything that is required for an implementation to be good and useful
for all of the purposes to which the language might be put.
> C74 was obsoleted, and as I remember only really defined for a
> particular machine. K&R changed many things including this,
> INTENTIONALLY. If you want to write in C74, go ahead an find a copy of
> it and us it, good luck finding a machine to run it. For STANDARDS,
> C89/C90 is the first version, which was largely based on K&R C which was
> NOT standardized as there were multiple (incompatible) dialects derived
> from K&R, which was one of the motivations to develop a standard.
C compilers existed prior to C89, which made provisions to allow for
architectures that would not have been practical with C74 (I'm not sure
which concessions were made by which intervening language documents,
though don't think all originated with C89). In C74, there was a well-
defined relationship between integers and pointers. Some hardware
platforms could not practically support such a relationship, and
loosening the rules meant that code which did not rely upon such a
relationship could run on hardware which didn't support it. On the
other hand, I don't think the notion that pre-existing code which relied
upon such a relationship before the guarantee was waived shouldn't work
the same way *on hardware platforms where the relationship holds*.
> > A lot of people have historically been willing to tolerate applications
> > which core-dump when given invalid data, and such people would probably
> > have said that they don't care what a program does when given invalid data
> > because they would expect that memory protections would ensure that the
> > program will do nothing worse than producing bad data or dumping core.
> > Do you think such people would be happy if the program e.g. randomly
> > changed a few bytes here and there on disk? The only reason people don't
> > think they care what a program does when given invalid input is that they
> > assume its behavior will stay within some bounds. I suspect the number of
> > people who would be willing to tolerate a program which would have truly
> > disastrous consequences if fed invalid input is actually quite low. Would
> > you disagree?
>
> The only real form of security is to stop someone from doing something
> wrong before they do it, not to minimize the damage they can do. If you
> avoid letting Undefined Behavior from happening, then you have promises
> from the Standard on what you program will do. If you don't get a
> promise you want from the Standard, you can possible get a promise from
> some additional standard or documented by the implementation, then you
> can trust in those promises but you then are dependent on more than the
> standard (which is ok if you know it). A lot of programs have been
> written that do undefined behavior, and the programmer figured the
> results were benign and left it, and THAT causes security issues. You
> shouldn't blame the make of the tools if the user hasn't been using it
> right.
In many cases, DEFINING constraints on otherwise-undefined behaviors would
reduce the amount of both source and machine code required to limit the
consequences of a program receiving invalid input. If a video player
receives a corrupt batch of data, how important is it that it display
some particular exact combination of pixels? How important is it that
it not allow the creator of that corrupt batch of data to take over the
machine? I would suggest that a good language should make it easy for a
programmer to achieve the second without requiring the CPU to waste time
on the first.
[toc] | [prev] | [next] | [standalone]
| From | James Kuyper <jameskuyper@verizon.net> |
|---|---|
| Date | 2016-06-13 14:10 -0400 |
| Message-ID | <njmsv5$tsr$1@dont-email.me> |
| In reply to | #88995 |
On 06/12/2016 10:25 PM, Joe Pfeiffer wrote: ... > In the C99 standard (note -- I've actually only got a committee draft > from 2005, and don't know what differences the final standard might > have) I find in 6.5.9 If you're interested in C99, the best source to use is n1256.pdf. It contains the final C99 standard, plus the modifications that were approved as Technical Corrigenda 1, 2, and 3. While those modifications were officially approved, the version of the standard with those modifications carried out never has been. Despite the lack of approval, it is more useful than the official standard because it includes those modifications. At the top of every page of n1256 except the first, it says "Septermber 7, 2007", and as of 2009-09-21, according to Lawrence Jones (the C committee's secretary), the only defects in n1256.pdf that had been reported between those two dates have been: 1) The typo in "Septermber 7, 2007". 2) "the predefined macro __STDC_MB_MIGHT_NEQ_WC__ should appear in 6.10.8p2 (optional predefined macros) rather than p1 (required predefined macros)." Personally, I tend to pay more attention to n1570.pdf, a draft which is almost identical to the final C2011 standard. The only known defect it contains is that in two locations, it claims that _Alignof() can be applied to an expression, just like sizeof. In the final approved standard, it can only be applied to a type.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2016-06-13 11:28 -0700 |
| Message-ID | <lna8ip3pv3.fsf@kst-u.example.com> |
| In reply to | #89026 |
James Kuyper <jameskuyper@verizon.net> writes:
[...]
> Personally, I tend to pay more attention to n1570.pdf, a draft which is
> almost identical to the final C2011 standard. The only known defect it
> contains is that in two locations, it claims that _Alignof() can be
> applied to an expression, just like sizeof. In the final approved
> standard, it can only be applied to a type.
That's not *quite* an accurate description of the defect. The
grammar in 6.5.3 clearly indicates _Alignof, unlike sizeof, cannot
be applied to an expression. (I've never seen an explanation for
this restriction.) The defect is that 6.3.2.1p3 lists the operand
of _Alignof as one of the contexts where an array expression is
not converted to a pointer. That was corrected in the published
C11 standard.
Another defect in N1570 is that the definitions of __STD_VERSION__
and __STDC_LIB_EXT1__ are incorrect (both are shown as `201ymmL`,
with the "ymm" in italics). This error also appears in the published
C11 standard, but it was corrected in the first (and so far only)
Technical Corrigendum (both are now `201112L`).
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Joe Pfeiffer <pfeiffer@cs.nmsu.edu> |
|---|---|
| Date | 2016-06-13 12:31 -0600 |
| Message-ID | <1b1t41q6sq.fsf@pfeifferfamily.net> |
| In reply to | #89026 |
James Kuyper <jameskuyper@verizon.net> writes: > On 06/12/2016 10:25 PM, Joe Pfeiffer wrote: > ... >> In the C99 standard (note -- I've actually only got a committee draft >> from 2005, and don't know what differences the final standard might >> have) I find in 6.5.9 > > If you're interested in C99, the best source to use is n1256.pdf. It > contains the final C99 standard, plus the modifications that were > approved as Technical Corrigenda 1, 2, and 3. While those modifications > were officially approved, the version of the standard with those > modifications carried out never has been. Despite the lack of approval, > it is more useful than the official standard because it includes those > modifications. Just got it -- thanks.
[toc] | [prev] | [next] | [standalone]
| From | Keith Thompson <kst-u@mib.org> |
|---|---|
| Date | 2016-06-12 19:38 -0700 |
| Message-ID | <lntwgx4xux.fsf@kst-u.example.com> |
| In reply to | #88992 |
Richard Heathfield <rjh@cpax.org.uk> writes:
> On 13/06/16 00:21, G G wrote:
>> when should a pointer to set to NULL and test whether
>> it is NULL?
>
> That's an excellent question.
>
> A pointer can be in any of three states:
>
> (1) a null pointer;
> (2) a valid pointer to an existing object or function;
(2a) a valid pointer just past the end of an array object. (A non-array
object is treated as an array of length 1.)
> (3) indeterminate.
[...]
--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.net/~kst>
Working, but not speaking, for JetHead Development, Inc.
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
[toc] | [prev] | [next] | [standalone]
| From | Richard Heathfield <rjh@cpax.org.uk> |
|---|---|
| Date | 2016-06-13 07:35 +0100 |
| Message-ID | <njlk7u$v7m$1@dont-email.me> |
| In reply to | #88996 |
On 13/06/16 03:38, Keith Thompson wrote: > Richard Heathfield <rjh@cpax.org.uk> writes: >> On 13/06/16 00:21, G G wrote: >>> when should a pointer to set to NULL and test whether >>> it is NULL? >> >> That's an excellent question. >> >> A pointer can be in any of three states: >> >> (1) a null pointer; >> (2) a valid pointer to an existing object or function; > > (2a) a valid pointer just past the end of an array object. (A non-array > object is treated as an array of length 1.) > >> (3) indeterminate. > > [...] Thank you for the correction. Our *four* main weapons are... -- Richard Heathfield Email: rjh at cpax dot org dot uk "Usenet is a strange place" - dmr 29 July 1999 Sig line 4 vacant - apply within
[toc] | [prev] | [next] | [standalone]
Page 1 of 5 [1] 2 3 4 5 Next page →
Back to top | Article view | comp.lang.c
csiph-web