2

As we can set the private variable of class like this

I was trying to set the private member variable int for below case.

#include <iostream>
#include <string>

using namespace std;

class Test
{
private:
    string s;
    int data;
public:
    Test() : s("New") , data(0) { }
    int getData() { return data; }
};

int main()
{
    Test t;
    int* ptr = (int*)&t;
    *(ptr+sizeof(string)) = 10;
    cout << t.getData();
    return 0;
}

But it is not able to print 10.

I know there are other way to set this using setter function, but was checking to set using method shown here

This is purely hack and not valid way though to learn.

Swapnil
  • 1,424
  • 2
  • 19
  • 30
  • The comma is not a statement separator in C++. Perhaps you were looking for a semicolon? Also consider a member initialization list. Also, the code you're using here in `main` is dangerous and broken. Why are you doing this? – Cody Gray - on strike Dec 21 '16 at 17:45
  • 3
    It is [fairly dangerous](https://stackoverflow.com/questions/12378271/what-does-an-object-look-like-in-memory) to try to assign to specific fields in objects using pointers and offsets, I wouldn't depend on that working in general cases. – Cory Kramer Dec 21 '16 at 17:45
  • @CodyGray It looks like the hack they are doing in `main` is an attempted workaround to assigning to a `private` member using (incorrectly) assumed memory layout of the object – Cory Kramer Dec 21 '16 at 17:46
  • There could be padding introduced between the class members. I think this is probably undefined behavior. What is your main objective? This seems like an xy problem at the moment. – wally Dec 21 '16 at 17:47
  • Most of the time this question is asked in interview and hence was making hand dirty. – Swapnil Dec 21 '16 at 17:49
  • 1
    @Swapnil You simply have _undefined behavior_, so asking about such stuff is pretty futile. – πάντα ῥεῖ Dec 21 '16 at 17:52
  • 3
    If someone ask you this kind of stuff in an interview, a reasonable reaction would be to say "thank you" and walk away. You don't want to work there. – n. m. could be an AI Dec 21 '16 at 17:53
  • In some circumstances (the class is POD and standard-layout) it is possible to use `offsetof()`. However, the code as shown has undefined behaviour, since there is no guarantee about what is accessed by dereferencing `ptr+sizeof(std::string)` – Peter Dec 21 '16 at 17:54
  • Re: "we can set the private variable of class like this" -- no, you can't. There is nothing in the C++ language definition that says this will work. The fact that it happens to do what you expect is just bad luck. – Pete Becker Dec 21 '16 at 18:28
  • @Pete Becker - the OP is unclear, but it looks like the code is not doing what he expects. Which isn't that surprising, really, since there is no guarantee that it will. – Peter Dec 21 '16 at 18:48

3 Answers3

4

I reckon this'll solve it:

char* ptr = (char*)&t;
*(int*)(ptr+sizeof(string)) = 10;

sizeof will return the size in bytes. Pointer increments will go for the size of the object its pointing at. char is a byte in size.

If you want to read up on it: C pointer arithmetic article

Just to reiterate what I think you've said: you're just hacking around as a point of learning, and you know there's no way you should ever do this in real life... !!

noelicus
  • 14,468
  • 3
  • 92
  • 111
  • 1
    No, besides ignoring any possible padding, that violates [the strict aliasing rule](http://stackoverflow.com/questions/98650/what-is-the-strict-aliasing-rule) and is undefined behavior. – Andrew Henle Dec 21 '16 at 17:54
  • @AndrewHenle Isn't there an exception for `char*`? – wally Dec 21 '16 at 17:58
  • @Muscampester It's being cast back to an `int *`, then dereferenced. – Andrew Henle Dec 21 '16 at 17:59
  • @AndrewHenle Yes, but the data at that address is an `int` (or at least it should be if we ignore the padding issue). So I'm curious to learn if the strict aliasing rule is being broken here. – wally Dec 21 '16 at 18:00
  • Yes as I said this is not going to be a real life solution. Thanks your solution works and clear why it is not working with int. – Swapnil Dec 21 '16 at 18:01
  • @Muscampester In general, there's no way to ensure that `ptr+sizeof(string)`, given `char *ptr`, points to an actual `int` value. Doing that ignores any alignment restrictions on the member variable or padding in the class structure. Even `offsetof` isn't guaranteed to produce the proper offset in some cases. – Andrew Henle Dec 21 '16 at 18:07
  • @AndrewHenle So, in summary it is only the padding that is at issue here? – wally Dec 21 '16 at 18:10
  • 2
    @Muscampester Given that alignment restrictions drive padding, I'd say that's accurate. – Andrew Henle Dec 21 '16 at 18:11
2

Why it doesn't work

Really just never write code like this. There is a way to set private data and that is via public members on the class. Otherwise, it's private for a reason. This being C++, there are actually non-UB ways of doing this but I'm not going to link them here because don't.

Why it doesn't work, specifically

Hint: What does ptr + sizeof(string) actually mean, for ptr being an int*? What is ptr pointing to after this line?

Barry
  • 286,269
  • 29
  • 621
  • 977
1

The pointer arithmetic should be done with byte pointers, not integer pointers.

#include <iostream>
#include <string>

using namespace std;

class Test
{
private:
    string s;
    int data;
public:
    Test() { s = "New", data = 0; }
    int getData() { return data; }
};

int main()
{
   Test t;
   char* ptr = (char*)&t;
   *(int*)(ptr+sizeof(string)) = 10;
   cout << t.getData();
   return 0;
}
default
  • 2,637
  • 21
  • 44