Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > Questions about pointers to objects and pointers to functions

Reply
Thread Tools

Questions about pointers to objects and pointers to functions

 
 
Tim Rentsch
Guest
Posts: n/a
 
      09-24-2005
"S.Tobias" <(E-Mail Removed)> writes:

> Tim Rentsch <(E-Mail Removed)> wrote:
> > "S.Tobias" <(E-Mail Removed)> writes:

>
> >> TYPEA a;
> >> TYPEB b;
> >> //both types are incompatible
> >> b = something_b;
> >> //put something into b
> >> /*...*/
> >> b = something_else_b;
> >> //put something else into b... but wait, let's cache
> >> //it in a register for now, and see what's next
> >> a = *(TYPEA volatile*)&b;
> >> //take the address of b, convert it to ptr to `volatile TYPEA',
> >> //dereference, take value and put into `a'
> >> //lvalue is type `volatile TYPEA'... hmm... nooooo, of course
> >> //it can't mean b here, let's read what that object contains,
> >> //and update b later...
> >> /* a contains something_b */

> >
> > It's an interesting argument, but the reasoning is not quite sound.
> >
> > During code generation, the compiler keeps track (in the data flow
> > sense) of where the value of 'b' is held at any given moment. When
> > the address is taken ('&b'), the compiler is going to use the address
> > of the location where 'b' is currently held.

>
> But taking the address of `b' does not automatically mean that
> the object `b' is going to be accessed.


That's true. But a compiler must assume that taking an address
is going to result in accessing the object, unless the compiler
can "prove" otherwise.


> The decision which object
> _may_ be accessed can be based only on the type of lvalue and
> effective type of the object, and I believe that this optimization
> above is allowed.


What I think you're saying is that, even though the compiler
knows that the casted address refers to 'b', the rule in the
Standard about effective type allows the compiler to forget
that it does. And that's right; the Standard does allow
that.

But no actual compiler is going to do that. When optimizing, a
compiler always wants to use the best information available,
because that information might enable further optimization. So
the compiler is going to remember that the casted address points
to 'b' even though it's "gone through" the cast.


> That's how I imagine aliasing rule works.
> But I'd still appreciate others' comments on this.


The reason for the effective type rule has to do with alias
analysis, which happens earlier in the compilation process. The
question is, when you have a pointer and it isn't known where it
points, what assumptions can you make when making an access
through that pointer? A typical case for this to happen is when
a function has a pointer parameter; suppose for example the
parameter has type 'float *'. Then any access through that
pointer can be assumed to access floats and not (for example)
ints.

But in this case the compiler knows where the pointer points.
It's to the optimizer's advantage to keep track; furthermore, it
had to have kept track in order to know whether the address was
used to access the object (which of course it was in this case).
If we imagine that the compiler "forgot" where the pointer
points, then it wouldn't have been able to prove to itself that
the pre-cast address '&b' wasn't used to access 'b'; that means
it would have had to store the register value of 'b' back in its
memory location. So either way, the right thing happens.


> >If 'b' is currently held
> > in a register, and the address value might escape the context that the
> > compiler can analyze, the register value of 'b' will be written back
> > to its regular memory location so that the address will point to the
> > currently meaningful value. Of course, what's going to happen in a
> > real compiler in this situation is that the compiler will know that
> > the (casted) address refers to 'b', and the register holding 'b' will
> > simply be stored into 'a'.

>
> But my point is that the context does *not* escape, it's just that
> the compiler determines that `b' cannot be accessed because
> the lvalue type doesn't match its effective type.


Please read the above again. Both cases are covered.


> Since all information is in one expression, it's easy to infer that
> in this case object `b' is going to be accessed. However, a compiler
> need not be that wise. Have you tried this with DS9000?


Not a relevant question, since we agree on what judgment the
Standard renders in such cases. Rather, the question is what do
actual compilers do. So if you want to make your case, look for
an actual compiler where the generated code for an assignment
statement

a = * (TYPEA volatile*) &b;

is along the lines you suggest.
 
Reply With Quote
 
 
 
 
S.Tobias
Guest
Posts: n/a
 
      09-26-2005
Tim Rentsch <(E-Mail Removed)> wrote:
> "S.Tobias" <(E-Mail Removed)> writes:
>> Tim Rentsch <(E-Mail Removed)> wrote:
>> > "S.Tobias" <(E-Mail Removed)> writes:


....
>> >> a = *(TYPEA volatile*)&b;
>> >> //take the address of b, convert it to ptr to `volatile TYPEA',
>> >> //dereference, take value and put into `a'
>> >> //lvalue is type `volatile TYPEA'... hmm... nooooo, of course
>> >> //it can't mean b here, let's read what that object contains,
>> >> //and update b later...

....

>> The decision which object
>> _may_ be accessed can be based only on the type of lvalue and
>> effective type of the object, and I believe that this optimization
>> above is allowed.

>
> What I think you're saying is that, even though the compiler
> knows that the casted address refers to 'b', the rule in the
> Standard about effective type allows the compiler to forget
> that it does. And that's right; the Standard does allow
> that.
>
> But no actual compiler is going to do that. When optimizing, a
> compiler always wants to use the best information available,
> because that information might enable further optimization. So
> the compiler is going to remember that the casted address points
> to 'b' even though it's "gone through" the cast.


[snip]

>> Since all information is in one expression, it's easy to infer that
>> in this case object `b' is going to be accessed. However, a compiler
>> need not be that wise. Have you tried this with DS9000?

>
> Not a relevant question, since we agree on what judgment the
> Standard renders in such cases. Rather, the question is what do
> actual compilers do. So if you want to make your case, look for
> an actual compiler where the generated code for an assignment
> statement
>
> a = * (TYPEA volatile*) &b;
>
> is along the lines you suggest.


Okay, I guess I'm talking just theory, you're making a practical point.

I don't really know what compilers do (and I don't especially want to).
I once had this code, where I unwisely tried to save on temporary
variables:

int Mem_dosomething(const void *s1, const void *s2) {
for (; *(char*)s1 == *(char*)s2; ++*(char**)&s1, ++*(char**)&s2) ;

and gcc gave the warning (in -O2 mode):
t.c:3: warning: dereferencing type-punned pointer will break
strict-aliasing rules
What this says to me is that the implementors have done something
"clever" and warn me that the "lvalue cast" construct is not
safe, despite that char* and void* have the same representation.
I don't actually know if the warning is really applicable at that
particular point, or is merely printed by default before the compiler
even considers whether it's going to do an optimization at all.
Better safe than sorry. I corrected above code to:

const unsigned char *c1 = s1, *c2 = s2;
for (; *c1 == *c2; ++c1, ++c2) ;

which is always portable.

--
Stan Tobias
mailx `echo http://www.velocityreviews.com/forums/(E-Mail Removed)LID | sed s/[[:upper:]]//g`
 
Reply With Quote
 
 
 
 
Flash Gordon
Guest
Posts: n/a
 
      09-26-2005
S.Tobias wrote:
> Tim Rentsch <(E-Mail Removed)> wrote:
>
>>"S.Tobias" <(E-Mail Removed)> writes:
>>
>>>Tim Rentsch <(E-Mail Removed)> wrote:
>>>
>>>>"S.Tobias" <(E-Mail Removed)> writes:

> ...
>
>>>>> a = *(TYPEA volatile*)&b;
>>>>> //take the address of b, convert it to ptr to `volatile TYPEA',
>>>>> //dereference, take value and put into `a'
>>>>> //lvalue is type `volatile TYPEA'... hmm... nooooo, of course
>>>>> //it can't mean b here, let's read what that object contains,
>>>>> //and update b later...

>
> ...
>>>The decision which object
>>>_may_ be accessed can be based only on the type of lvalue and
>>>effective type of the object, and I believe that this optimization
>>>above is allowed.

>>
>>What I think you're saying is that, even though the compiler
>>knows that the casted address refers to 'b', the rule in the
>>Standard about effective type allows the compiler to forget
>>that it does. And that's right; the Standard does allow
>>that.
>>
>>But no actual compiler is going to do that. When optimizing, a
>>compiler always wants to use the best information available,
>>because that information might enable further optimization. So
>>the compiler is going to remember that the casted address points
>>to 'b' even though it's "gone through" the cast.


Based on the gcc info mages I think gcc might...

>>>Since all information is in one expression, it's easy to infer that
>>>in this case object `b' is going to be accessed. However, a compiler
>>>need not be that wise. Have you tried this with DS9000?

>>
>>Not a relevant question, since we agree on what judgment the
>>Standard renders in such cases. Rather, the question is what do
>>actual compilers do. So if you want to make your case, look for
>>an actual compiler where the generated code for an assignment
>>statement
>>
>> a = * (TYPEA volatile*) &b;
>>
>>is along the lines you suggest.

>
> Okay, I guess I'm talking just theory, you're making a practical point.
>
> I don't really know what compilers do (and I don't especially want to).
> I once had this code, where I unwisely tried to save on temporary
> variables:
>
> int Mem_dosomething(const void *s1, const void *s2) {
> for (; *(char*)s1 == *(char*)s2; ++*(char**)&s1, ++*(char**)&s2) ;
>
> and gcc gave the warning (in -O2 mode):
> t.c:3: warning: dereferencing type-punned pointer will break
> strict-aliasing rules
> What this says to me is that the implementors have done something
> "clever" and warn me that the "lvalue cast" construct is not
> safe, despite that char* and void* have the same representation.
> I don't actually know if the warning is really applicable at that
> particular point, or is merely printed by default before the compiler
> even considers whether it's going to do an optimization at all.


If you read the information on that warning
http://gcc.gnu.org/onlinedocs/gcc-4....arning-Options
you will find it says "It warns about code which might break the
strict aliasing rules that the compiler is using for optimization."

If you read the info on -fstrict-aliasing
http://gcc.gnu.org/onlinedocs/gcc-4....timize-Options
you will then find that it activates optimisations based on the type of
the expression and "In particular, an object of one type is assumed
never to reside at the same address as an object of a different type,
unless the types are almost the same."

The example it gives of something that might not work is:
union a_union {
int i;
double d;
};

int f() {
a_union t;
int* ip;
t.d = 3.0;
ip = &t.i;
return *ip;
}

With the following being explicitly allowed even though it is undefined
behaviour in the standard:
int f() {
a_union t;
t.d = 3.0;
return t.i;
}

> Better safe than sorry. I corrected above code to:
>
> const unsigned char *c1 = s1, *c2 = s2;
> for (; *c1 == *c2; ++c1, ++c2) ;
>
> which is always portable.


Indeed.

Of course, the specifics of what gcc does are not really topical, but as
an example of a compiler that might produce code that does not do what
is expected I think it is valid.
--
Flash Gordon
Living in interesting times.
Although my email address says spam, it is real and I read it.
 
Reply With Quote
 
Friedhelm Usenet Waitzmann
Guest
Posts: n/a
 
      09-28-2005
Marc Thrun <(E-Mail Removed)>:
>PS: Where can I get a copy of the standard (maybe a draft version)?
>Might help me next time .


Draft version:
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n869/
 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      10-04-2005
"S.Tobias" <(E-Mail Removed)> writes:

> Tim Rentsch <(E-Mail Removed)> wrote:
> > "S.Tobias" <(E-Mail Removed)> writes:
> >> Tim Rentsch <(E-Mail Removed)> wrote:
> >> > "S.Tobias" <(E-Mail Removed)> writes:

>
> ...
> >> >> a = *(TYPEA volatile*)&b;
> >> >> //take the address of b, convert it to ptr to `volatile TYPEA',
> >> >> //dereference, take value and put into `a'
> >> >> //lvalue is type `volatile TYPEA'... hmm... nooooo, of course
> >> >> //it can't mean b here, let's read what that object contains,
> >> >> //and update b later...

> ...
>
> >> The decision which object
> >> _may_ be accessed can be based only on the type of lvalue and
> >> effective type of the object, and I believe that this optimization
> >> above is allowed.

> >
> > What I think you're saying is that, even though the compiler
> > knows that the casted address refers to 'b', the rule in the
> > Standard about effective type allows the compiler to forget
> > that it does. And that's right; the Standard does allow
> > that.
> >
> > But no actual compiler is going to do that. When optimizing, a
> > compiler always wants to use the best information available,
> > because that information might enable further optimization. So
> > the compiler is going to remember that the casted address points
> > to 'b' even though it's "gone through" the cast.

>
> [snip]
>
> >> Since all information is in one expression, it's easy to infer that
> >> in this case object `b' is going to be accessed. However, a compiler
> >> need not be that wise. Have you tried this with DS9000?

> >
> > Not a relevant question, since we agree on what judgment the
> > Standard renders in such cases. Rather, the question is what do
> > actual compilers do. So if you want to make your case, look for
> > an actual compiler where the generated code for an assignment
> > statement
> >
> > a = * (TYPEA volatile*) &b;
> >
> > is along the lines you suggest.

>
> Okay, I guess I'm talking just theory, you're making a practical point.


That seems like a fair summary.


> I don't really know what compilers do (and I don't especially want to).
> I once had this code, where I unwisely tried to save on temporary
> variables:
>
> int Mem_dosomething(const void *s1, const void *s2) {
> for (; *(char*)s1 == *(char*)s2; ++*(char**)&s1, ++*(char**)&s2) ;
>
> and gcc gave the warning (in -O2 mode):
> t.c:3: warning: dereferencing type-punned pointer will break
> strict-aliasing rules
> What this says to me is that the implementors have done something
> "clever" and warn me that the "lvalue cast" construct is not
> safe, despite that char* and void* have the same representation.


That's possible. I think it's more likely to indicate an aggressive
warning check than to reflect whether the compiler considers the
generated code doesn't match the intention.


> I don't actually know if the warning is really applicable at that
> particular point, or is merely printed by default before the compiler
> even considers whether it's going to do an optimization at all.
> Better safe than sorry. I corrected above code to:
>
> const unsigned char *c1 = s1, *c2 = s2;
> for (; *c1 == *c2; ++c1, ++c2) ;
>
> which is always portable.


It seems clear that the second writing is preferable to the first.
 
Reply With Quote
 
Tim Rentsch
Guest
Posts: n/a
 
      10-04-2005
Flash Gordon <(E-Mail Removed)> writes:

> S.Tobias wrote:
> > Tim Rentsch <(E-Mail Removed)> wrote:
> >
> >>"S.Tobias" <(E-Mail Removed)> writes:
> >>
> >>>Tim Rentsch <(E-Mail Removed)> wrote:
> >>>
> >>>>"S.Tobias" <(E-Mail Removed)> writes:

> > ...
> >
> >>>>> a = *(TYPEA volatile*)&b;
> >>>>> //take the address of b, convert it to ptr to `volatile TYPEA',
> >>>>> //dereference, take value and put into `a'
> >>>>> //lvalue is type `volatile TYPEA'... hmm... nooooo, of course
> >>>>> //it can't mean b here, let's read what that object contains,
> >>>>> //and update b later...

> >
> > ...
> >>>The decision which object
> >>>_may_ be accessed can be based only on the type of lvalue and
> >>>effective type of the object, and I believe that this optimization
> >>>above is allowed.
> >>
> >>What I think you're saying is that, even though the compiler
> >>knows that the casted address refers to 'b', the rule in the
> >>Standard about effective type allows the compiler to forget
> >>that it does. And that's right; the Standard does allow
> >>that.
> >>
> >>But no actual compiler is going to do that. When optimizing, a
> >>compiler always wants to use the best information available,
> >>because that information might enable further optimization. So
> >>the compiler is going to remember that the casted address points
> >>to 'b' even though it's "gone through" the cast.

>
> Based on the gcc info mages I think gcc might...
>
> >>>Since all information is in one expression, it's easy to infer that
> >>>in this case object `b' is going to be accessed. However, a compiler
> >>>need not be that wise. Have you tried this with DS9000?
> >>
> >>Not a relevant question, since we agree on what judgment the
> >>Standard renders in such cases. Rather, the question is what do
> >>actual compilers do. So if you want to make your case, look for
> >>an actual compiler where the generated code for an assignment
> >>statement
> >>
> >> a = * (TYPEA volatile*) &b;
> >>
> >>is along the lines you suggest.

> >
> > Okay, I guess I'm talking just theory, you're making a practical point.
> >
> > I don't really know what compilers do (and I don't especially want to).
> > I once had this code, where I unwisely tried to save on temporary
> > variables:
> >
> > int Mem_dosomething(const void *s1, const void *s2) {
> > for (; *(char*)s1 == *(char*)s2; ++*(char**)&s1, ++*(char**)&s2) ;
> >
> > and gcc gave the warning (in -O2 mode):
> > t.c:3: warning: dereferencing type-punned pointer will break
> > strict-aliasing rules
> > What this says to me is that the implementors have done something
> > "clever" and warn me that the "lvalue cast" construct is not
> > safe, despite that char* and void* have the same representation.
> > I don't actually know if the warning is really applicable at that
> > particular point, or is merely printed by default before the compiler
> > even considers whether it's going to do an optimization at all.

>
> If you read the information on that warning
> http://gcc.gnu.org/onlinedocs/gcc-4....arning-Options
> you will find it says "It warns about code which might break the
> strict aliasing rules that the compiler is using for optimization."
>
> If you read the info on -fstrict-aliasing
> http://gcc.gnu.org/onlinedocs/gcc-4....timize-Options
> you will then find that it activates optimisations based on the type of
> the expression and "In particular, an object of one type is assumed
> never to reside at the same address as an object of a different type,
> unless the types are almost the same."
>
> The example it gives of something that might not work is:
> union a_union {
> int i;
> double d;
> };
>
> int f() {
> a_union t;
> int* ip;
> t.d = 3.0;
> ip = &t.i;
> return *ip;
> }
>
> With the following being explicitly allowed even though it is undefined
> behaviour in the standard:
> int f() {
> a_union t;
> t.d = 3.0;
> return t.i;
> }


The information in the gcc pages was interesting reading.
Thank you for posting this.

Despite what the gcc documentation currently says, I'd be surprised if
this example remains apropos as an example. The reason for having
aliasing rules is to allow a compiler to generate better code, not to
excuse it for generating bad code. In cases where both goals can be
met (generating good code and generating correct code), that's what
compiler writers will want to do. That's the case here. So it will
be interesting to see how the compilers and documentation evolve as
these kinds of efforts go forward.

I'm still interested to hear about any actual compiler where code
like this:

... any calculation(s) of b ...

a = * (TYPEA volatile *) &b;

... any further calculation(s) of b ...

generates code that gets "the wrong value" into the variable 'a'.
 
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
Do not cast pointers to functions to pointers to primitive types ajaybgr C Programming 18 09-07-2012 04:34 AM
class objects, method objects, function objects 7stud Python 11 03-20-2007 06:05 PM
Pointers to char pointers in functions newbie C Programming 9 09-24-2006 10:31 AM
Function pointers, variable argument functions calling other variable-argument functions (sort of) S?ren Gammelmark C Programming 1 01-07-2005 09:41 PM
please help me in distinguish redefining functions, overloading functions and overriding functions. Xiangliang Meng C++ 1 06-21-2004 03:11 AM



Advertisments