Accessing private member of a class through type-casting

D

dragoncoder

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?
 
N

Noah Roberts

dragoncoder said:
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?

That its behavior is undefined. It might work...it might not...it
might end the world as you know it...undefined.
 
T

Tomás

dragoncoder posted:
Consider the code

class A {
private:
int a;
};

int main(void) {
A x;

You've create an object of the type "A". This object contains within it
an object of the type "int".

int* ptr = (int*)&x;

The expression "&x" is of the type "A*" (leaving out const). You have
used an old C-style cast to turn it into an "int*". This is illegal.

Even if your code was legal, you make the assumption that the address of
"x" is equal to the address of "x.a". I'm not sure if the Standard gives
any such guarantee. If the object was of a class which contained virtual
methods, your code would be even less reliable.

The value of "x" is undefined, so your the behaviour of the above
statement is undefined.
return 0;
}


-Tomás
 
N

Noah Roberts

dragoncoder said:
int main(void) {
A x;
int* ptr = (int*)&x;
*x = 10;

Oh, I missed something here. *ptr = 10; would have been undefined. *x
= 10 doesn't compile as A doesn't have an operator*.

BTW, (int*)&x is totally legal. It is a C-style cast and as is the
case with all C-style casts it can do things unexpectedly. What this
resolves to is a reinterpret_cast<int*>(&x). This is of course legal
but has undefined results. A static_cast would be illegal and using
the C++ casting mechanism would have warned you about the undefined
nature of the cast...this is why C-Style casts are bad.
 
R

red floyd

Tomás said:
dragoncoder posted:


You've create an object of the type "A". This object contains within it
an object of the type "int".



The expression "&x" is of the type "A*" (leaving out const). You have
used an old C-style cast to turn it into an "int*". This is illegal.
Not quite illegal, merely undefined.
 
P

Pete C

dragoncoder said:
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?

If I turn my car to drive the wrong way up a one-way street, it obeys
me! Hah! Stupid car. Sometimes I will even get where I want to go.
 
N

Noah Roberts

Pete said:
If I turn my car to drive the wrong way up a one-way street, it obeys
me! Hah! Stupid car. Sometimes I will even get where I want to go.

But the result is pretty well defined even if there are a couple
alternative end scenarios. It is also, as apposed to the code in
question, quite illegal.

;)
 
D

dragoncoder

If I correct the code so that *x = 10 becomes *ptr = 10, even then is
the behaviour undefined ?

I want to know which step I am doing wrong ? Is it illegal to assign
(int*)&x to ptr ? Or is it illegal to do *ptr = 10 ? I am confused. The
corrected is posted again.

class A {
private:
int a;
};

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

Victor Bazarov

dragoncoder said:
If I correct the code so that *x = 10 becomes *ptr = 10, even then is
the behaviour undefined ?
Yes.

I want to know which step I am doing wrong ? Is it illegal to assign
(int*)&x to ptr ? Or is it illegal to do *ptr = 10 ? I am confused.

It is illegal to use 'ptr' after obtaining it by reinterpret_cast'ing
it from '&x'. The Standard says that if two pointers are unrelated (and
they are), all you can do with the pointer is to cast it back and get
the same pointer value iff the alignment requirements are no less strict
for the destination ("temporary") pointer. Something like that, anyway.

V
 
N

Noah Roberts

Victor said:
It is illegal to use 'ptr' after obtaining it by reinterpret_cast'ing
it from '&x'.

The result is undefined...it is not illegal to do so.
 
N

Noah Roberts

dragoncoder said:
If I correct the code so that *x = 10 becomes *ptr = 10, even then is
the behaviour undefined ?

It becomes undefined. *x = 10 is illegal and won't compile.
 
P

Phlip

dragoncoder said:
int* ptr = (int*)&x;
*ptr = 10;

Let me try to explain the illegality.

(int*) in C is the same as reinterpret_cast<> in C++. C++ only defines the
result of such a cast if it casts a pointer to what the data really is.

Such typecasts make the compiler pretend that a class A is an int. This
would be legal:

A * p = (A *)(int *) &x; // both could use reinterpret_cast

Your code might do anything (including appear to work correctly) because you
don't know if the address of A::a is the same as the address of its A. So
your cast might not end up pointing to an int.

Your code will most likely appear to work, because a compiler has no reason
to put any other data in A except the int a. Because the behavior is
undefined, it might break if you change something about your compiler, or
class A. For example, if you add a virtual destructor, your code will most
likely fail.
 
T

Tomás

Phlip posted:
Let me try to explain the illegality.

(int*) in C is the same as reinterpret_cast<> in C++.


Not really. "reinterpret_cast" can't cast pointers to functions if I recall
correctly, while C-style casts can.
C++ only defines
the result of such a cast if it casts a pointer to what the data really
is.

Such typecasts make the compiler pretend that a class A is an int. This
would be legal:

A * p = (A *)(int *) &x; // both could use reinterpret_cast


I don't think this should work. The two types, "A*" and "int*" are
unrelated. Yes, they are both pointers, but what they point to are totally
different. In three steps, here's what you're doing:

1: Start off with the expression "&x" which is of type "A*".
2: You then use a C-style cast to turn it into an "int*". The Standard
doesn't guarantee that an "int*" can reliably hold the address of an object
of type "A", so the value might get corrupted.
3: Now you use a C-style cast again to turn it into an "A*", but the value
may already have been corrupted.

It's quite like the following:

double k = 32.2342;

double f = (double) (int) k;

f will have the value of 32 rather than 32.2342.

Your code might do anything (including appear to work correctly)
because you don't know if the address of A::a is the same as the
address of its A. So your cast might not end up pointing to an int.


I believe this is true.


-Tomás
 
N

Noah Roberts

Phlip said:
Let me try to explain the illegality.

(int*) in C is the same as reinterpret_cast<> in C++. C++ only defines the
result of such a cast if it casts a pointer to what the data really is.

Such typecasts make the compiler pretend that a class A is an int. This
would be legal:

A * p = (A *)(int *) &x; // both could use reinterpret_cast

Your code might do anything (including appear to work correctly) because you
don't know if the address of A::a is the same as the address of its A. So
your cast might not end up pointing to an int.

Your code will most likely appear to work, because a compiler has no reason
to put any other data in A except the int a. Because the behavior is
undefined, it might break if you change something about your compiler, or
class A. For example, if you add a virtual destructor, your code will most
likely fail.

You are explaining undefined behavior. The word "illegal" never
appears in the standard that I know of and at least not in the
definitions section. I would take "illegal" to mean against the
standard, which would equate to ill-formed. Such a cast is not against
the standard. The standard explicitly defines the mapping of such cast
as implementation-defined and that "a pointer to an object can be
explicitly cast to a pointer to an object of a different type." This
means that the cast of A* to int* is _allowed_ by the standard and the
result is /implementation-defined/.

The use of the resulting pointer is as undefined as using any other
random pointer. As such, the standard imposes no requirements
whatsoever so the construct is still not dissallowed or "illegal" by
any definition of the word I know of.
 
P

Phlip

Noah said:
This
means that the cast of A* to int* is allowed by the standard and the
result is implementation-defined.

I'm aware, in our culture, that "illegal" does not necessarily mean "you
shouldn't do it".

class A {
public:
A() { assert((int)this == (int)&a); }
private:
  int a;
};

There. If that assertion would pass, then the cast is "legal". And note that
"legal" also does not appear in The Standard, yack yack yack, etc.
 
N

Noah Roberts

Tomás said:
Phlip posted:



Not really. "reinterpret_cast" can't cast pointers to functions if I recall
correctly, while C-style casts can.

See 5.2.10. P4 states you can convert a pointer to an int. P5 states
you can convert a function pointer to another function pointer.
I don't think this should work. The two types, "A*" and "int*" are
unrelated. Yes, they are both pointers, but what they point to are totally
different.

The behavior of the above expression is actually well defined. Given
certain requirements the result is the same as &x. Failing to meet
those requirements has "unspecified" results.

In three steps, here's what you're doing:
1: Start off with the expression "&x" which is of type "A*".
2: You then use a C-style cast to turn it into an "int*". The Standard
doesn't guarantee that an "int*" can reliably hold the address of an object
of type "A", so the value might get corrupted.
3: Now you use a C-style cast again to turn it into an "A*", but the value
may already have been corrupted.

It's quite like the following:

double k = 32.2342;

double f = (double) (int) k;

f will have the value of 32 rather than 32.2342.

Actually, it is nothing like that. double and int have well defined
conversions. That particular C-style cast is the same as two
static_cast operations. The previous is two reinterpret_cast
operations. Totally different casts...again, this is the problem with
c-style casting...they look the same but are not.
 
P

Phlip

Noah said:
Actually, it is nothing like that.  double and int have well defined
conversions

Let's try "well defined _lossy_ conversions".

I'm beginning to suspect (because you read The Standard for me), that
pointer conversions are well-defined as lossless.
 
N

Noah Roberts

Phlip said:
I'm aware, in our culture, that "illegal" does not necessarily mean "you
shouldn't do it".

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

1. Prohibited by law.
2. Prohibited by official rules: an illegal pass in football.
3. Unacceptable to or not performable by a computer: an illegal
operation.

#1 does not apply - law in this case being the standard.
#2 also similarly to #1 does not apply.

The standard _allows_ the cast so by elimination it is not prohibited
;)

#3 is the only possibilty however since the operation IS performable I
don't see how it applies.

The standard _allows_ the cast...all implementations therefore have to
allow it and be able to perform it. The standard does not specify how.

So, you can redefine words if you wish but then you may as well be
speaking in Greek...or Martian for that matter.
 

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,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top