473,770 Members | 2,630 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Questions about pointers to objects and pointers to functions

Hello,

I've got a few questions:

1) Given the two structs
struct A {
int x;
};

and

struct B {
struct A y;
int z;
};

is it ok to treat a "pointer to an object of type struct B" as a
"pointer to an object of type struct A"?
(I think someone asked something like this some time ago, but
unfortunately I can't find the article anymore)

2) When I now have a function pointer of type
void (*fpa)(struct A *, int);
and a function pointer of type
void (*fpb)(struct B *, int);

and the corresponding functions
void fa(struct A*,int);
and
void fb(struct B*,int);

is it ok to assign a "pointer to fa" to fpb and call the function
through fpb with a "pointer to an object of type struct B" as the first
parameter?

Thanks in advance
Marc Thrun
Nov 15 '05
15 1994
"S.Tobias" <si***@FamOuS.B edBuG.pAlS.INVA LID> writes:
Tim Rentsch <tx*@alumnus.ca ltech.edu> wrote:
"S.Tobias" <si***@FamOuS.B edBuG.pAlS.INVA LID> 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.
Nov 15 '05 #11
Tim Rentsch <tx*@alumnus.ca ltech.edu> wrote:
"S.Tobias" <si***@FamOuS.B edBuG.pAlS.INVA LID> writes:
Tim Rentsch <tx*@alumnus.ca ltech.edu> wrote:
> "S.Tobias" <si***@FamOuS.B edBuG.pAlS.INVA LID> 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 si***@FamOuS.Be dBuG.pAlS.INVALID | sed s/[[:upper:]]//g`
Nov 15 '05 #12
S.Tobias wrote:
Tim Rentsch <tx*@alumnus.ca ltech.edu> wrote:
"S.Tobias" <si***@FamOuS.B edBuG.pAlS.INVA LID> writes:
Tim Rentsch <tx*@alumnus.ca ltech.edu> wrote:

"S.Tobias " <si***@FamOuS.B edBuG.pAlS.INVA LID> 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.
Nov 15 '05 #13
Marc Thrun <Te********@gmx .de>:
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/
Nov 15 '05 #14
"S.Tobias" <si***@FamOuS.B edBuG.pAlS.INVA LID> writes:
Tim Rentsch <tx*@alumnus.ca ltech.edu> wrote:
"S.Tobias" <si***@FamOuS.B edBuG.pAlS.INVA LID> writes:
Tim Rentsch <tx*@alumnus.ca ltech.edu> wrote:
> "S.Tobias" <si***@FamOuS.B edBuG.pAlS.INVA LID> 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.
Nov 15 '05 #15
Flash Gordon <sp**@flash-gordon.me.uk> writes:
S.Tobias wrote:
Tim Rentsch <tx*@alumnus.ca ltech.edu> wrote:
"S.Tobias" <si***@FamOuS.B edBuG.pAlS.INVA LID> writes:

Tim Rentsch <tx*@alumnus.ca ltech.edu> wrote:

>"S.Tobias " <si***@FamOuS.B edBuG.pAlS.INVA LID> 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'.
Nov 15 '05 #16

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

71
5944
by: cj | last post by:
Dear friends, I have one more questions for everyone in the newsgroup: I am preparing for an interview on UNIX/C++. Could you please identify some of the most important questions which might be asked, so that I could best prepare for it? Thank you, C++J
2
2013
by: Thomas Matthews | last post by:
Hi, I would like to create a table (or vector) of pointers to templated functions. 1. How do I declare a typedef of a pointer to a templated function? For example, I have some functions that print out a type's name to an istream:
14
1986
by: MSR | last post by:
I have a couple of questions. 1. Copy Constructor. class A { private: int a1; double d1; char *ptr;
9
1632
by: Gonçalo Rodrigues | last post by:
Hi all, I have a few questions on primitive types that I will divide in two main questions. I realize that some of these questions -- especially 2. below -- are not directly related to C++ as a language itself, so, if there is a better newsgroup to make them would you be so kind and please direct me to it? 1. Assume that in your platform (OS + compiler) all pointer types have the same size. Is there a platform independent way to get...
8
1612
by: V.Ch. | last post by:
In near future I can face a prospect of writing some stuff in C. Being a C++ programmer, I've got practically no experience in C. I'd be obliged if someone could answer the following questions (from specific to more general): 1. Having looked though some C sources I was horrified by the number of macros. E.g., what could be the purpose of using this: #define CFG_REF(c, f) ((c) -> f) #define SERVER_ADDRESS(c) CFG_REF (c, server_address)
7
1763
by: alternativa | last post by:
Hello, I have a few questions concerning classes. 1) Why some people use default constructos, i.e constructors with no parameters? To me it doesn't make any sense, is there something I should know? For example, I'd declare a class in a following way: class Sample { int number; string title;
22
1768
by: Guru Jois | last post by:
Hai all, I have some question. Please answer. 1. What is walking pointer? 2. What is difference between procedure and subroutine? 3. What is template of main in C? 4. What is padding of structure ( or structure padding )? 5. What is advantages of using pointers to functions?.
7
8247
by: irshadjat | last post by:
Question No.01 Which of the following is true about streams? A. It is a sequence of bytes B. It is an ordered sequence C. All bytes can go through the stream simultaneously D. Bytes that enters first into the stream will go out at last 0 A only 0 C only 0 A and B
7
1430
by: tradevol | last post by:
as this example, question: If my purpose is initialize data from xml files and store them in the vector, so they can be used in class B by other member functions, do you think functionP is a viable function(will a could go away after out of the function)? If not, is there a better solution than using functionPt?
0
9439
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10071
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven tapestry of website design and digital marketing. It's not merely about having a website; it's about crafting an immersive digital experience that captivates audiences and drives business growth. The Art of Business Website Design Your website is...
1
10017
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
1
7431
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new presenter, Adolph Dupré who will be discussing some powerful techniques for using class modules. He will explain when you may want to use classes instead of User Defined Types (UDT). For example, to manage the data in unbound forms. Adolph will...
0
6690
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5467
by: adsilva | last post by:
A Windows Forms form does not have the event Unload, like VB6. What one acts like?
1
3987
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3589
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2832
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.