Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Re: Pointer Arithmetic & UB

Reply
Thread Tools

Re: Pointer Arithmetic & UB

 
 
Tim Rentsch
Guest
Posts: n/a
 
      12-27-2012
Ken Brody <> writes:

> On 12/22/2012 3:15 AM, Tim Rentsch wrote:
>> glen herrmannsfeldt <> writes:

> [... mega-snip ...]
>>> volatile int x;
>>> y=2*x;
>>> z=x+x;
>>>
>>> In this case, y should always be even, z has the possibility
>>> of not being even, and the compiler should allow for that.

>>
>> Not even that. Even ignoring the possible undefined behavior
>> because of overflow, after the second statement is done
>> y could have any value at all, because accessing 'x' in
>> 'z = x + x;' might have the side effect of storing into
>> y, and the compiler isn't allowed to know that or assume
>> that it doesn't happen. Any use of y after the second
>> assignment statement must refetch y, for just this reason.

>
> I agree with just about everything you've said, up until this
> point. Assuming that "y" is a non-volatile value, then the
> accesses of "x" in "z=x+x" are not allowed to change the value
> of "y". [snip elaboration]


[correcting the bad reference in my last reply]

Look at the wording of 6.7.3 p 7, specifically the first
sentence:

An object that has volatile-qualified type may be modified
in ways unknown to the implementation _or have other unknown
side effects_. [my emphasis]

The Standard puts no limitations on what the unknown side effects
might be, only that they are unknown (ie, both to the Standard
and to the implementation). Hence those unknown side effects may
include changing (some of) the memory locations holding 'y';
whatever occurs is, by definition, outside the ability of the
implementation to control or even be aware of.
 
Reply With Quote
 
 
 
 
Tim Rentsch
Guest
Posts: n/a
 
      12-27-2012
Ken Brody <> writes:

> On 12/22/2012 12:32 PM, Tim Rentsch wrote:
>> glen herrmannsfeldt <> writes:
>>
>>> Tim Rentsch <> wrote:

> [...]
>>>>> volatile int x;
>>>>> y=2*x;
>>>>> z=x+x;
>>>>>
>>>>> In this case, y should always be even, z has the possibility
>>>>> of not being even, and the compiler should allow for that.
>>>>
>>>> Not even that. Even ignoring the possible undefined behavior
>>>> because of overflow, after the second statement is done
>>>> y could have any value at all, because accessing 'x' in
>>>> 'z = x + x;' might have the side effect of storing into
>>>> y, and the compiler isn't allowed to know that or assume
>>>> that it doesn't happen. Any use of y after the second
>>>> assignment statement must refetch y, for just this reason.
>>>
>>> Even if y isn't volatile? [noting only x is volatile]

>>
>> Yes. Accessing a volatile object may have any side effect(s)
>> whatsoever, including modifying unrelated variables; moreover
>> the Standard stipulates that such effects are unknown, in
>> particular to the implementation (which includes the compiler).
>> That's why no assumptions are safe across accessing a volatile.

>
> If "y" is not defined as "volatile', then the compiler is free
> to assume that its value does not change unless explicitly
> changed.


Actually the implementation is prohibited from making any
such assumption (or more accurately, acting on one). Again
look at the wording of 6.7.3 p 7, this time the second
sentence:

Therefore any expression referring to such an object shall
be evaluated strictly according to the rules of the abstract
machine, as described in 5.1.2.3.

The expression 'z=x+x;' must be evaluated (ie, in the actual
machine) strictly according to the rules of 5.1.2.3. Part of
those rules require that the evaluations (again, in the actual
machine) of all previous expressions be done /before/ evaluating
'z=x+x;', because of the sequence point between this full
expression and the one before, and the evaluations (again, in the
actual machine) of all subsequent expressions be done /after/
evaluating 'z=x+x;', because of the sequence point between this
full expression and the one after. These evaluations include any
access to y; in particular, any use of y's value in an expression
subsequent to 'z=x+x;' must actually read the corresponding
object, and not rely on some cached or assumed value (for the
first such subsequent access; naturally others may be optimized).
Otherwise the stipulations of 6.7.3 p 7 and 5.1.2.3 are not being
followed.

> Otherwise, the mere presence of an access to a volatile object
> would mean that every byte of memory could change, and all
> optimization based on "this value won't change" must be thrown
> out the window.


If we take the Standard at its word, then Yes: any access to a
volatile object prevents all optimizations from flowing across
said access. (Optimizations in between two volatile accesses are
allowed, of course.) Do you have some reason to believe the
meaning intended for this part of the Standard is different from
what the text apparently implies? More specifically, do you have
any evidence to offer that might support such a belief?
 
Reply With Quote
 
 
 
 
Tim Rentsch
Guest
Posts: n/a
 
      12-27-2012
Ben Bacarisse <> writes:

> Tim Rentsch <> writes:
> <snip -- I hoe too dramatic a snip>
>>> Yes, the consequences of a volatile access are unconstrained, and the
>>> standard has no business restricting what an implementation may do in
>>> such circumstances, but the standard *does* have a legitimate role in
>>> determining that some programs are undefined as far it is concerned.
>>> One thing that makes the behaviour undefined is the situation described
>>> in 6.5 p2.

>>
>> (Since you have responded briefly I will also, on just one
>> point. We can get back to the others sometime later if
>> that's important.)
>>
>> Can you identify for me which scalar object is modified
>> twice as a result of executing 'int i = x + x;', with x
>> being declared volatile? Remember, a volatile-qualified
>> access is a side effect, but it is a non-specific one, not
>> one that modifies the object being accessed. There is no
>> guarantee that the side effect of accessing a particular
>> volatile object will modify any given scalar object, or even
>> any object at all.

>
> Agree, but I don't see why that is relevant. The wording has
> changed, I thought, so that side-effects of any type are the
> issue. So 6.5 p2 talks only about side-effects on scalar
> objects, and simply accessing a volatile object constitutes a
> side effect. What, if anything, changes it not obviously
> relevant.


First let me be sure I understand you correctly. The wording
describing sequencing and access rules obviously has changed
between, well, let's be specific, N1256 and N1570. Do you
believe the intended meaning of these passages is significantly
different between these two versions of the Standard? I don't
think they are (or maybe I should say I believe they are not)
significantly different. AFAIAA the intended meanings of N1256
and N1570 are (for these areas) basically the same, expect that
N1570 uses more precise language and eliminates some potential
ambiguities there were present in N1256. (There is more to say
on this topic but I expect it's a side issue so I will stop
here--can expand later if that's needed.)

Second, sticking just to N1570, obviously which object is
modified makes a difference. Consider:

int i, j, k, *p = &k;
volatile int u, v;

++i + ++j; // okay
++i + ++i; // UB
++i + ++*p; // okay
++k + ++*p; // UB
i = j + u; // okay (I think most will agree)
i = j + u + v; // okay (IMO although some may argue)
i = j + u + u; // the case at issue (or much like it)

All statements have multiple side effects in them. For the lines
not involving any volatiles, the lines marked UB have undefined
behavior because a scalar object (i in one case, k in the other)
is being modified twice in unsequenced evaluations.

For the lines with volatiles, how are the conditions of 6.5 p 2
satisfied? As far as the implementation knows the only scalar
object being modified is i. Since the implementation cannot know
what (other) scalar objects (or indeed if any) are modified, or if
i is otherwise accessed, we cannot conclude there is undefined
behavior in these statements. Indeed on any actual data processing
system I'm aware of, the only object that would be modified is i,
nor would i be read. Ergo the conditions of 6.5 p 2 are not met in
such cases. Hence it does not apply, and the implementation (which
cannot know if it does) must act accordingly -- ie, as if the
volatle accesses don't do anything but access the variables
involved. (Of course the other rules about strictly following
5.1.2.3 must be followed also.)

>> If an implementation cannot identify a scalar object that is
>> going to be modified twice by the volatile accesses, then it
>> must behave as if 6.5 p2 has not been violated (by those
>> accesses), for indeed it may not have been.

>
> My view is that 6.5. p2 makes it undefined if there are two
> unsequenced accesses not matter that the actual consequences
> are.


How do you reconcile this viewpoint with 6.5 p 2 talking only
about cases where there is a side effect on a scalar object?
Certainly there are other kinds of side effects, such as
modifying floating point status bits, that are known not to
involve modifying any scalar object. Surely 6.5 p 2 is meant
to apply only to accesses that modify a (particular) scalar
object, and also have another unsequenced access to that same
scalar object -- isn't it?

Maybe I see what you're getting at -- accessing a volatile object
is supposed to count as a side effect "on that scalar object". But
I don't see any text in the Standard that supports that conclusion.
Can you offer any? Or am I still misunderstanding you?
 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      12-27-2012
Shao Miller <> writes:

> On 12/22/2012 12:16, Tim Rentsch wrote:
>> Shao Miller <> writes:
>>
>>> On 12/22/2012 01:54, Tim Rentsch wrote:
>>>> Shao Miller <> writes:
>>>>
>>>>>> [..snip..snip..snip..]
>>>>>
>>>>> I read the other response. [...snip...]
>>>>
>>>> Then apparently you didn't understand it.
>>>
>>> Well in that case, I'd really like to enhance that understanding,
>>> if possible. [snip]

>>
>> My suggestions are: read more carefully; think more deeply; try
>> to organize your thoughts more systematically; and make an effort
>> in your writing to express ideas more clearly and more concisely.

>
> The response immediately above does not appear to be an instance of
> valuable discussion. A response to the points, instead of the person,
> would be more valuable. [snip]


What I was responding to was your statement. You said you'd like
a better understanding, and I gave some suggestions for what to
do to achieve that. I'm sorry if my ideas there weren't along
the same lines as your own. If you're still interested, how
about going back to my long posting in the other subthread and
responding to that by just paraphrasing what I was saying there
to make sure you're understanding my comments. I think that
would be a good way to continue.
 
Reply With Quote
 
Ken Brody
Guest
Posts: n/a
 
      12-28-2012
On 12/27/2012 11:01 AM, Tim Rentsch wrote:
> Ken Brody <> writes:
>
>> On 12/22/2012 3:15 AM, Tim Rentsch wrote:
>>> glen herrmannsfeldt <> writes:

>> [... mega-snip ...]
>>>> volatile int x;
>>>> y=2*x;
>>>> z=x+x;
>>>>
>>>> In this case, y should always be even, z has the possibility
>>>> of not being even, and the compiler should allow for that.
>>>
>>> Not even that. Even ignoring the possible undefined behavior
>>> because of overflow, after the second statement is done
>>> y could have any value at all, because accessing 'x' in
>>> 'z = x + x;' might have the side effect of storing into
>>> y, and the compiler isn't allowed to know that or assume
>>> that it doesn't happen. Any use of y after the second
>>> assignment statement must refetch y, for just this reason.

>>
>> I agree with just about everything you've said, up until this
>> point. Assuming that "y" is a non-volatile value, then the
>> accesses of "x" in "z=x+x" are not allowed to change the value
>> of "y". [snip elaboration]

>
> [correcting the bad reference in my last reply]
>
> Look at the wording of 6.7.3 p 7, specifically the first
> sentence:
>
> An object that has volatile-qualified type may be modified
> in ways unknown to the implementation _or have other unknown
> side effects_. [my emphasis]
>
> The Standard puts no limitations on what the unknown side effects
> might be, only that they are unknown (ie, both to the Standard
> and to the implementation). Hence those unknown side effects may
> include changing (some of) the memory locations holding 'y';
> whatever occurs is, by definition, outside the ability of the
> implementation to control or even be aware of.


Perhaps, but the compiler is still free to assume that anything *not* marked
"volatile" *won't* change unless explicitly changed.


 
Reply With Quote
 
Ken Brody
Guest
Posts: n/a
 
      12-28-2012
(Sorry for not snipping, as there didn't appear to be anything to snip.)

On 12/27/2012 12:03 PM, Tim Rentsch wrote:
> Ken Brody <> writes:
>
>> On 12/22/2012 12:32 PM, Tim Rentsch wrote:
>>> glen herrmannsfeldt <> writes:
>>>
>>>> Tim Rentsch <> wrote:

>> [...]
>>>>>> volatile int x;
>>>>>> y=2*x;
>>>>>> z=x+x;
>>>>>>
>>>>>> In this case, y should always be even, z has the possibility
>>>>>> of not being even, and the compiler should allow for that.
>>>>>
>>>>> Not even that. Even ignoring the possible undefined behavior
>>>>> because of overflow, after the second statement is done
>>>>> y could have any value at all, because accessing 'x' in
>>>>> 'z = x + x;' might have the side effect of storing into
>>>>> y, and the compiler isn't allowed to know that or assume
>>>>> that it doesn't happen. Any use of y after the second
>>>>> assignment statement must refetch y, for just this reason.
>>>>
>>>> Even if y isn't volatile? [noting only x is volatile]
>>>
>>> Yes. Accessing a volatile object may have any side effect(s)
>>> whatsoever, including modifying unrelated variables; moreover
>>> the Standard stipulates that such effects are unknown, in
>>> particular to the implementation (which includes the compiler).
>>> That's why no assumptions are safe across accessing a volatile.

>>
>> If "y" is not defined as "volatile', then the compiler is free
>> to assume that its value does not change unless explicitly
>> changed.

>
> Actually the implementation is prohibited from making any
> such assumption (or more accurately, acting on one). Again
> look at the wording of 6.7.3 p 7, this time the second
> sentence:
>
> Therefore any expression referring to such an object shall
> be evaluated strictly according to the rules of the abstract
> machine, as described in 5.1.2.3.
>
> The expression 'z=x+x;' must be evaluated (ie, in the actual
> machine) strictly according to the rules of 5.1.2.3. Part of
> those rules require that the evaluations (again, in the actual
> machine) of all previous expressions be done /before/ evaluating
> 'z=x+x;', because of the sequence point between this full
> expression and the one before, and the evaluations (again, in the
> actual machine) of all subsequent expressions be done /after/
> evaluating 'z=x+x;', because of the sequence point between this
> full expression and the one after. These evaluations include any
> access to y; in particular, any use of y's value in an expression
> subsequent to 'z=x+x;' must actually read the corresponding
> object, and not rely on some cached or assumed value (for the
> first such subsequent access; naturally others may be optimized).
> Otherwise the stipulations of 6.7.3 p 7 and 5.1.2.3 are not being
> followed.
>
>> Otherwise, the mere presence of an access to a volatile object
>> would mean that every byte of memory could change, and all
>> optimization based on "this value won't change" must be thrown
>> out the window.

>
> If we take the Standard at its word, then Yes: any access to a
> volatile object prevents all optimizations from flowing across
> said access. (Optimizations in between two volatile accesses are
> allowed, of course.) Do you have some reason to believe the
> meaning intended for this part of the Standard is different from
> what the text apparently implies? More specifically, do you have
> any evidence to offer that might support such a belief?


Well, I will fully admit that I am no expert in Standardese. However, I
find it hard to believe that the intent was that *any* access to *any*
volatile object means that the compiler *must* assume that *all*
non-volatile objects may have changed value.

Consider:

extern volatile int x;

int foo(void)
{
const int y = x;
int z = x + x;

/* Must the compiler assume that "y" may have changed? */

return y;
}

I can tell you that "gcc -O9 -ansi" assumes that "y" has not changed.
(Ditto without the "const".)

 
Reply With Quote
 
Philip Lantz
Guest
Posts: n/a
 
      12-28-2012
Tim Rentsch wrote:
> Ken Brody writes:
> > Tim Rentsch wrote:
> >> glen herrmannsfeldt writes:
> >>> Even if y isn't volatile? [noting only x is volatile]
> >>
> >> Yes. Accessing a volatile object may have any side effect(s)
> >> whatsoever, including modifying unrelated variables; moreover
> >> the Standard stipulates that such effects are unknown, in
> >> particular to the implementation (which includes the compiler).
> >> That's why no assumptions are safe across accessing a volatile.

> >
> > If "y" is not defined as "volatile', then the compiler is free
> > to assume that its value does not change unless explicitly
> > changed.

>
> Actually the implementation is prohibited from making any
> such assumption (or more accurately, acting on one). Again
> look at the wording of 6.7.3 p 7, this time the second
> sentence:
>
> Therefore any expression referring to such an object shall
> be evaluated strictly according to the rules of the abstract
> machine, as described in 5.1.2.3.
>
> The expression 'z=x+x;' must be evaluated (ie, in the actual
> machine) strictly according to the rules of 5.1.2.3. Part of
> those rules require that the evaluations (again, in the actual
> machine) of all previous expressions be done /before/ evaluating
> 'z=x+x;', because of the sequence point between this full
> expression and the one before, and the evaluations (again, in the
> actual machine) of all subsequent expressions be done /after/
> evaluating 'z=x+x;', because of the sequence point between this
> full expression and the one after. These evaluations include any
> access to y; in particular, any use of y's value in an expression
> subsequent to 'z=x+x;' must actually read the corresponding
> object, ...


Okay, but who's to say where the corresponding object is? If y is not
volatile and its address is not taken, isn't the compiler free to store
its value wherever it likes? And isn't the compiler free to change that
location at its convenience? If whatever machinery lives behind the
volatile nature of x is able to find where the compiler is keeping y at
the present time, then let it go ahead and change it if it can. That
doesn't force the compiler to alter its behavior with respect to y.
 
Reply With Quote
 
Philip Lantz
Guest
Posts: n/a
 
      12-28-2012
Ken Brody wrote:
> Tim Rentsch wrote:
> > Ken Brody wrote:
> >> Otherwise, the mere presence of an access to a volatile object
> >> would mean that every byte of memory could change, and all
> >> optimization based on "this value won't change" must be thrown
> >> out the window.

> >
> > If we take the Standard at its word, then Yes: any access to a
> > volatile object prevents all optimizations from flowing across
> > said access. (Optimizations in between two volatile accesses are
> > allowed, of course.) Do you have some reason to believe the
> > meaning intended for this part of the Standard is different from
> > what the text apparently implies? More specifically, do you have
> > any evidence to offer that might support such a belief?

>
> Well, I will fully admit that I am no expert in Standardese. However, I
> find it hard to believe that the intent was that *any* access to *any*
> volatile object means that the compiler *must* assume that *all*
> non-volatile objects may have changed value.
>
> Consider:
>
> extern volatile int x;
>
> int foo(void)
> {
> const int y = x;
> int z = x + x;
>
> /* Must the compiler assume that "y" may have changed? */
>
> return y;
> }
>
> I can tell you that "gcc -O9 -ansi" assumes that "y" has not changed.
> (Ditto without the "const".)


Another way to look at this (as I alluded to in my previous post) is
that gcc chooses to store y in eax (assuming for the sake of discussion
an x86 compiler; substitute whatever location your favorite compiler
uses for an int return value). I assume that all will agree that it is
the compiler's prerogative where to store the values of variables. If
the subsequent accesses to x manage to change eax, then y will change.

(Not that it matters for this theoretical discussion, but this isn't
totally hypothetical: the access to x could trigger a virtualization
event or an SMI, and the VMM or SMM could easily change the value in a
register.)
 
Reply With Quote
 
glen herrmannsfeldt
Guest
Posts: n/a
 
      12-28-2012
Philip Lantz <> wrote:
> Tim Rentsch wrote:
>> Ken Brody writes:

(snip)
>> > If "y" is not defined as "volatile', then the compiler is free
>> > to assume that its value does not change unless explicitly
>> > changed.


>> Actually the implementation is prohibited from making any
>> such assumption (or more accurately, acting on one). Again
>> look at the wording of 6.7.3 p 7, this time the second
>> sentence:


>> Therefore any expression referring to such an object shall
>> be evaluated strictly according to the rules of the abstract
>> machine, as described in 5.1.2.3.


>> The expression 'z=x+x;' must be evaluated (ie, in the actual
>> machine) strictly according to the rules of 5.1.2.3. Part of
>> those rules require that the evaluations (again, in the actual
>> machine) of all previous expressions be done /before/ evaluating
>> 'z=x+x;', because of the sequence point between this full
>> expression and the one before, and the evaluations (again, in the
>> actual machine) of all subsequent expressions be done /after/
>> evaluating 'z=x+x;', because of the sequence point between this
>> full expression and the one after. These evaluations include any
>> access to y; in particular, any use of y's value in an expression
>> subsequent to 'z=x+x;' must actually read the corresponding
>> object, ...


> Okay, but who's to say where the corresponding object is? If y is not
> volatile and its address is not taken, isn't the compiler free to store
> its value wherever it likes? And isn't the compiler free to change that
> location at its convenience? If whatever machinery lives behind the
> volatile nature of x is able to find where the compiler is keeping y at
> the present time, then let it go ahead and change it if it can. That
> doesn't force the compiler to alter its behavior with respect to y.


It used to be that computers had front panels with switches and lights
that would let you stop programs, alter memory, and continue.

Now, many debuggers let us do that.

I suppose one could use volatile to allow one to change variables
at surprising times through the front panel (or virtualization
of one) or a debugger.

-- glen
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      12-28-2012
glen herrmannsfeldt <> writes:
[...]
> It used to be that computers had front panels with switches and lights
> that would let you stop programs, alter memory, and continue.
>
> Now, many debuggers let us do that.
>
> I suppose one could use volatile to allow one to change variables
> at surprising times through the front panel (or virtualization
> of one) or a debugger.


More likely you already *can* change variables through such means;
using volatile lets the program behave properly in the presence of
such changes.

--
Keith Thompson (The_Other_Keith) kst- <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"
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Pointer to pointer or reference to pointer A C++ 7 07-05-2011 07:49 PM
Pointer to pointer Vs References to Pointer bansalvikrant@gmail.com C++ 4 07-02-2009 10:20 AM
passing the address of a pointer to a func that doesnt recieve a pointer-to-a-pointer jimjim C Programming 16 03-27-2006 11:03 PM
Usual Arithmetic Conversions-arithmetic expressions joshc C Programming 5 03-31-2005 02:23 AM
Pointer-to-pointer-to-pointer question masood.iqbal@lycos.com C Programming 10 02-04-2005 02:57 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57