1
#include <iostream> using namespace std; struct test { int i; double h; int j; }; int main() { test te; te.i = 5; te.h = 6.5; te.j = 10; cout << "size of an int: " << sizeof(int) << endl; // Should be 4 cout << "size of a double: " << sizeof(double) << endl; //Should be 8 cout << "size of test: " << sizeof(test) << endl; // Should be 24 (word size of 8 for double) //These two should be the same cout << "start address of the object: " << &te << endl; cout << "address of i member: " << &te.i << endl; //These two should be the same cout << "start address of the double field: " << &te.h << endl; cout << "calculate the offset of the double field: " << (&te + sizeof(double)) << endl; //NOT THE SAME return 0; } 

Output:

size of an int: 4 size of a double: 8 size of test: 24 start address of the object: 0x7fffb9fd44e0 address of i member: 0x7fffb9fd44e0 start address of the double field: 0x7fffb9fd44e8 calculate the offset of the double field: 0x7fffb9fd45a0 

Why do the last two lines produce different values? Something I am doing wrong with pointer arithmetic?

4
  • 5
    (&te + sizeof(double)) should be (&te + sizeof(int)) (or the size of all elements prior to the one you want the offset for ;) Commented Dec 19, 2011 at 21:08
  • 1
    I would think this object would be padded by 8 for each field, as seen by the total size of test being 24. Commented Dec 19, 2011 at 21:09
  • @windfinder: Why? Is it 64bit OS? Commented Dec 19, 2011 at 21:10
  • It is 64 bits. Each field would be 8 bytes I'd think, as the size of the largest field is 8, and I didn't instruct the compiler to pack it. Commented Dec 19, 2011 at 21:13

7 Answers 7

8
(&te + sizeof(double)) 

This is the same as:

&((&te)[sizeof(double)]) 

You should do:

(char*)(&te) + sizeof(int) 
Sign up to request clarification or add additional context in comments.

5 Comments

What do you mean by "byte*"? That isn't a valid keyword in C++, so I assume that is shorthand.
You can use unsigned char* or size_t. Keep in mind that the comments about alignment are also true and you cannot really rely on this behavior.
Does that really work? Doesn't that just have you go forward sizeof(char*) * sizeof(int) bytes?
It should go sizeof(char)*sizeof(int) bytes forward. And sizeof(char) should be 1.
This works, but it is sizeof(double), not size of int. One annoying thing is, that when you make it a char* (and try to pass it to std::out) it prints a blank line. I just casted it back to a test*: (test*)(((char*)(&te) + sizeof(double)))
4

You are correct -- the problem is with pointer arithmetic.

When you add to a pointer, you increment the pointer by a multiple of that pointer's type

Therefore, &te + 1 will be 24 bytes after &te.

Your code &te + sizeof(double) will add 24 * sizeof(double) or 192 bytes.

4 Comments

It's not clear to me what you want. If you want to calculate the offset of that member variable, offsetof(test,h) will work.
Just playing around with serialization/deserialization. Trying to see how I would calculate fields positions if I have access to a void*, or something like that.
Then offsetof(test,h) will tell you.
Cool, but how do I actually traverse to that location?
3

Firstly, your code is wrong, you'd want to add the size of the fields before h (i.e. an int), there's no reason to assume double. Second, you need to normalise everything to char * first (pointer arithmetic is done in units of the thing being pointed to).

More generally, you can't rely on code like this to work. The compiler is free to insert padding between fields to align things to word boundaries and so on. If you really want to know the offset of a particular field, there's an offsetof macro that you can use. It's defined in <stddef.h> in C, <cstddef> in C++.

Most compilers offer an option to remove all padding (e.g. GCC's __attribute__ ((packed))).


I believe it's only well-defined to use offsetof on POD types.

12 Comments

I've seen code using __attribute__ ((packed)) blow up when you try to use the address of a misaligned member.
@KeithThompson: I would hope that the compiler wouldn't be stupid enough to generate code that would fail. If the platform doesn't support misaligned accesses, I would want the compiler to do either (a) fail to compile, or (b) do the accesses the longwinded way...
@windfinder: But as I pointed out, code like is fundamentally not to be relied on, so it doesn't really matter...
@OliCharlesworth: I hoped so too, but I was disappointed. See this program. The problem is that once you take the address of a misaligned member, the compiler has no way of knowing that it's misaligned.
@windfinder: It's not inaccurate; there is no reason to assume that the compiler is using sizeof(double) as its alignment...
|
2
struct test { int i; int j; double h; }; 

Since your largest data type is 8 bytes, the struct adds padding around your ints, either put the largest data type first, or think about the padding on your end! Hope this helps!

3 Comments

Or just don't worry about it. Declaring the members in a logical order may be worth the cost of extra padding -- especially since the padding is going to vary from one system to another.
Playing around with my compiler I don't see this rearrangement. Are there specific compilers/settings that trigger this? Currently using G++.
@windfinder: I'm not sure what you mean. The language guarantees that members will be allocated in the order in which they're declared, possibly with padding. The answer suggests manually rearranging the members in your source code to avoid padding. It's a valid suggestion; my comment suggests that it might or might not be worthwhile.
2

&te + sizeof(double) is equivalent to &te + 8, which is equivalent to &((&te)[8]). That is — since &te has type test *, &te + 8 adds eight times the size of a test.

3 Comments

Right, this is what Drew is saying as well. What is the correct syntax then? Thanks!
@windfinder: Well, you already saw that &te.h gives the answer you want . . . or are you looking for something different? I'm a bit unclear on what you're trying to do, sorry. :-/
Just playing around with serialization/deserialization. Trying to see how I would calculate fields positions if I have access to a void*, or something like that.
1

You can see what's going on more clearly using the offsetof() macro:

#include <iostream> #include <cstddef> using namespace std; struct test { int i; double h; int j; }; int main() { test te; te.i = 5; te.h = 6.5; te.j = 10; cout << "size of an int: " << sizeof(int) << endl; // Should be 4 cout << "size of a double: " << sizeof(double) << endl; // Should be 8 cout << "size of test: " << sizeof(test) << endl; // Should be 24 (word size of 8 for double) cout << "i: size = " << sizeof te.i << ", offset = " << offsetof(test, i) << endl; cout << "h: size = " << sizeof te.h << ", offset = " << offsetof(test, h) << endl; cout << "j: size = " << sizeof te.j << ", offset = " << offsetof(test, j) << endl; return 0; } 

On my system (x86), I get the following output:

size of an int: 4 size of a double: 8 size of test: 16 i: size = 4, offset = 0 h: size = 8, offset = 4 j: size = 4, offset = 12 

On another system (SPARC), I get:

size of an int: 4 size of a double: 8 size of test: 24 i: size = 4, offset = 0 h: size = 8, offset = 8 j: size = 4, offset = 16 

The compiler will insert padding bytes between struct members to ensure that each member is aligned properly. As you can see, alignment requirements vary from system to system; on one system (x86), double is 8 bytes but only requires 4-byte alignment, and on another system (SPARC), double is 8 bytes and requires 8-byte alignment.

Padding can also be added at the end of a struct to ensure that everything is aligned properly when you have an array of the struct type. On SPARC, for example, the compile adds 4 bytes pf padding at the end of the struct.

The language guarantees that the first declared member will be at an offset of 0, and that members are laid out in the order in which they're declared. (At least that's true for simple structs; C++ metadata might complicate things.)

Comments

1

Compilers are free to space out structs however they want past the first member, and usually use padding to align to word boundaries for speed.

See these:
C struct sizes inconsistence
Struct varies in memory size?
et. al.

2 Comments

Not quite however they want. The first member is always at offset 0, and the members are always laid out in declared order (at least for C and, in C++, for POD types).
@KeithThompson Yes, yes. Rephrased.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.