views:

441

answers:

8

I have a testing struct definition as follows:

struct test{
    int a, b, c;
    bool d, e;
    int f;
    long g, h;
};

And somewhere I use it this way:

test* t = new test;   // create the testing struct
int* ptr = (int*) t;
ptr[2] = 15;          // directly manipulate the third word
cout << t->c;         // look if it really affected the third integer

This works correctly on my Windows - it prints 15 as expected, but is it safe? Can I be really sure the variable is on the spot in memory I want it to be - expecially in case of such combined structs (for example f is on my compiler the fifth word, but it is a sixth variable)?

If not, is there any other way to manipulate struct members directly without actually having struct->member construct in the code?

+4  A: 

No you can't be sure. The compiler is free to introduce padding between structure members.

anon
+12  A: 

It looks like you are asking two questions

Is it safe to treat &test as a 3 length int arrray?

It's probably best to avoid this. This may be a defined action in the C++ standard but even if it is, it's unlikely that everyone you work with will understand what you are doing here. I believe this is not supported if you read the standard because of the potential to pad structs but I am not sure.

Is there a better way to access a member without it's name?

Yes. Try using the offsetof macro/operator. This will provide the memory offset of a particular member within a structure and will allow you to correctly position a point to that member.

size_t offset = offsetof(mystruct,c);
int* pointerToC = (int*)((char*)&someTest + offset);

Another way though would be to just take the address of c directly

int* pointerToC = &(someTest->c);
JaredPar
This seems to be what I was looking for, thanks
CommanderZ
Thanks a million. Did not know `offsetof`! Been calling myself a C++ programmer for the last 6 years.
Vulcan Eager
+1  A: 

It's probably not safe and is 100% un-readable; thus making that kind of code unacceptable in real life production code.

Max
+2  A: 

You are probably looking for the offsetof macro. This will get you the byte offset of the member. You can then manipulate the member at that offset. Note though, this macro is implementation specific. Include stddef.h to get it to work.

dirkgently
A: 

use set methods and boost::bind for create functor which will change this variable.

bb
+2  A: 

To add to JaredPar's answer, another option in C++ only (not in plain C) is to create a pointer-to-member object:

struct test
{
  int a, b, c;
  bool d, e;
  int f;
  long g, h;
};

int main(void)
{
  test t1, t2;

  int test::*p;  // declare p as pointing to an int member of test
  p = &test::c;  // p now points to 'c', but it's not associating with an object
  t1->*p = 3;    // sets t1.c to 3
  t2->*p = 4;    // sets t2.c to 4

  p = &test::f;
  t1->*p = 5;    // sets t1.f to 5
  t2->*p = 6;    // sets t2.f to 6
}
Adam Rosenfield
A: 

Aside from padding/alignment issues other answers have brought up, your code violates strict aliasing rules, which means it may break for optimized builds (not sure how MSVC does this, but GCC -O3 will break on this type of behavior). Essentially, because test *t and int *ptr are of different types, the compiler may assume they point to different parts of memory, and it may reorder operations.

Consider this minor modification:

test* t = new test;
int* ptr = (int*) t;

t->c = 13;
ptr[2] = 15;
cout << t->c;

The output at the end could be either 13 or 15, depending on the order of operations the compiler uses.

Tom
No - as the valid statement `int* ptr = ` shows, an int* may alias a test* if struct test contains (directly or indirectly) at least one int.
MSalters
Tom
A: 

According to paragraph 9.2.17 of the standard, it is in fact legal to cast a pointer-to-struct to a pointer to its first member, providing the struct is POD:

A pointer to a POD-struct object, suitably converted using a reinterpret_cast, points to its initial member (or if that member is a bit-field, then to the unit in which it resides) and vice versa. [Note: There might therefore be unnamed padding within a POD-struct object, but not at its beginning, as necessary to achieve appropriate alignment. ]

However, the standard makes no guarantees about the layout of structs -- even POD structs -- other than that the address of a later member will be greater than the address of an earlier member, provided there are no access specifiers (private:, protected: or public:) between them. So, treating the initial part of your struct test as an array of 3 integers is technically undefined behaviour.

j_random_hacker