Accessing private member of a class through type-casting

P

Phlip

Noah said:
The word illegal has a well defined definition in the English language,
let us look to it:

You snipped the part of my post where I flamed you for what I knew you'd
reply.

Please calm down.
 
T

Tomás

dragoncoder posted:
Consider the code

class A {
private:
int a;
};

int main(void) {
A x;
int* ptr = (int*)&x;
*x = 10;
return 0;
}

I understand that I can not change the private attributes of a class,
but what does standard have to say about the code above?


I got lost lower down in the thread so I'll start again here.

Someone was trying to do something akin to the following:

std::string object("Hello");

std::string* p = (std::string*)(int*) &object;


And they implied that the above is perfectly okay, such that the programmer
can go on to use "p" as follows:

cout << *p;


Firstly, let's break that code up and turn it into:

std::string object("Hello"); //Line 1

int* p_int = (int*) &object; //Line 2

std::string* p_str = (std::string*) p_int; //Line 3

cout << *p_str; //Line 4


On Line 2, we are casting an "std::string*" to an "int*" and storing the
result in a variable of type "int*". The Standard gives no guarantee
whatsoever that an "std::string*" can be cast or stored accurately as an
"int*". It is possible that, on a particular platform, that the cast may
work perfectly, but it is not guaranteed to work, and so is not 100%
portable.

On Line 3, we are casting an "int*" to an "std::string*" and storing the
result in a variable of type "std::string*". The first thing to note here is
that the Standard gives no guarantee whatsoever that the value in "p_int" is
valid -- it may be corrupt. Even if we cast it back to an "std::string*",
we've no guarantee that it points to the object called "object". It's quite
like going from "double" to "int" and then back to "double" -- you won't
have the original double's value.

Line 4 might print "Hello", then again, it might print the New Testament if
you've got the bible open in Adobe Acrobat at the same time.


Then someone posed the argument that you can cast a pointer to an "int" and
back to the original pointer type and that it will work perfectly. Here's
some sample code:

std::string* p = new std::string("Hello");

int num = (int)p;

std::string* k = (std::string*)num;

cout << *k;

delete p;


The Standard gives no guarantee whatsoever that the above code will work.
The Standard says that you can cast a pointer value to an integral type, and
then back to the original pointer type, but the final value will only be
legitimate if the integral type had enough bits to store the pointer value.
Nowhere in the Standard are we given any guarantee that any of the integral
types are large enough to hold any of the pointer values; so even if you use
an "unsigned long", you still can't be sure it will work. It's not 100%
portable, and so, it has no place in the realms of Standard C++.


-Tomás
 
V

Victor Bazarov

Tomás said:
[..]
Then someone posed the argument that you can cast a pointer to an
"int" and back to the original pointer type and that it will work
perfectly.

Do you think you could actually find out who did pose that argument?
Not that it matters terribly.
Here's some sample code:

std::string* p = new std::string("Hello");

int num = (int)p;

std::string* k = (std::string*)num;

cout << *k;

delete p;


The Standard gives no guarantee whatsoever that the above code will
work. The Standard says that you can cast a pointer value to an
integral type, and then back to the original pointer type, but the
final value will only be legitimate if the integral type had enough
bits to store the pointer value. Nowhere in the Standard are we given
any guarantee that any of the integral types are large enough to hold
any of the pointer values; so even if you use an "unsigned long", you
still can't be sure it will work. It's not 100% portable, and so, it
has no place in the realms of Standard C++.

While the Standard does not specify which type is "large enough", or that
such type exists at all, all implemenations I've encountered do provide
such type. I agree with your assessment of the situation WRT Standard
C++ language. I only wanted to mention real-world cases here. Anyone
who might be faced with the necessity to store a pointer in a variable
of an integral type, shouldn't immediately dismiss the idea, but should
simply look for a type that might be able to accommodate them.

As an example, MS Visual C++ for Win64 has its 'unsigned long' only
half the size of an object pointer, for example. However, it does have
the __int64 type (implementation-specific) that can hold a value of
an object pointer.

V
 
N

Noah Roberts

Tomás wrote:
The first thing to note here is
that the Standard gives no guarantee whatsoever that the value in "p_int" is
valid -- it may be corrupt. Even if we cast it back to an "std::string*",
we've no guarantee that it points to the object called "object".

Actually, the standard has something to say about this.

"A pointer to an object may be explicitly converted to a pointer to an
object of a different type [65]. Except that converting an rvalue of
type "pointer to T1" to the type "pointer to T2" (where T1 and T2 are
object types and where the alignment requirements of T2 are no stricter
than those of T1) and back to its original type yields the original
pointer value, the result of such a pointer conversion is unspecified."

The term unspecified has a specific meaning different than undefined in
that the implementation is not free to call it an error. It can still
result in any useful or not useful pointer value. However, as you can
see above, unless the two objects have different alignment requirements
the operation is reversable.

footnote 65 states: "The types may have different cv-qualifiers,
subject to the overall restriction that a reinterpret_cast cannot cast
away constness."

Now, if such an operation had no place in std c++ then I suppose you
should tell the people that wrote the standard as they explicitly layed
out what such an operation's effects would be.

Now, you compare this operation to casting from double to int back to
double, but that has a totally different effect. I already explained
why in a different post.
 
M

Markus Schoder

Victor said:
Tomás said:
[..]
Then someone posed the argument that you can cast a pointer to an
"int" and back to the original pointer type and that it will work
perfectly.

As an example, MS Visual C++ for Win64 has its 'unsigned long' only
half the size of an object pointer, for example. However, it does have
the __int64 type (implementation-specific) that can hold a value of
an object pointer.

There is int64_t and uint64_t which are at least C99.
 
P

Phlip

Noah said:
footnote 65 states: "The types may have different cv-qualifiers,
subject to the overall restriction that a reinterpret_cast cannot cast
away constness."

....or volatility.

I thought it also cannot cast-in constness (or volatility).
 
N

Noah Roberts

Phlip said:
...or volatility.

My quote is a direct quote...hence the marks.
I thought it also cannot cast-in constness (or volatility).

The standard says what it says. The standard only specifies that it
can't be cast away. Most things having to do with const are like
that...can become const but not not.
 
T

Tomás

Now, if such an operation had no place in std c++ then I suppose you
should tell the people that wrote the standard as they explicitly layed
out what such an operation's effects would be.

I look at C++ programming from two viewpoints:

1) Writing implementation-independant code. Basically, when I want to
solve a problem by writing an algorithm, I will write Standard portable
code. I will refrain from any practises which could behave differently on
different plaforms. I wouldn't store the address of an std::string in an
int*.

2) Writing implementation-specific code. This is mostly when it comes to
writing the Graphical User Interface, or when I seek features that the
language and its Standard Libraries don't provide. When writing
implementation-specific code, you're free to use "tricks" which you have
a guarantee will work on the specific plaform. For instance, in Microsoft
Windows, it's common to pass a pointer to a function via an integral
function argument. Something like:

void SendMessage(long); //defined elsewhere

int p;

SendMessage( (long)*p );


But I won't do this in my portable code.


On this newsgroup, which is to do with Standard C++, I only ever mention
portable Standard practises, and I shun on other practises. If I was on a
MS Windows programming newsgroup, I'd conceed that there's nothing wrong
with storing an object's address in an integral type.


-Tomás
 
N

Noah Roberts

Tomás wrote:
If I was on a
MS Windows programming newsgroup, I'd conceed that there's nothing wrong
with storing an object's address in an integral type.

That is also necessary on other platforms. It is the whole reason the
standard guarantees that casting from one pointer type to the other and
back results in the same pointer value...given certain implementation
defined requirements that the programmer is expected to know. It is a
valid technique (necessary when dealing with any C based api), it is
topical, and it is in the standard.
 
V

Vladimir Lushnikov

Why would you want to? Reading the replies above, it's could be quite
unsafe to do that.

Reflection is what you need...
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top