Size of structure with a char, a double, an int and a t [duplicate]
Asked Answered
T

9

16

When I run only the code fragment

int *t;
std::cout << sizeof(char)   << std::endl;
std::cout << sizeof(double) << std::endl;
std::cout << sizeof(int)    << std::endl;
std::cout << sizeof(t)      << std::endl;

it gives me a result like this:

1
8
4
4

Total: 17.

But when I test sizeof struct which contains these data types it gives me 24, and I am confused. What are the additional 7 bytes?

This is the code

#include <iostream>
#include <stdio.h>
struct struct_type{
    int i;
    char ch;
    int *p;
    double d;
} s;

int main(){
    int *t;
    //std::cout << sizeof(char)   <<std::endl;
    //std::cout << sizeof(double) <<std::endl;
    //std::cout << sizeof(int)    <<std::endl;
    //std::cout << sizeof(t)      <<std::endl;

    printf("s_type is %d byes long",sizeof(struct struct_type));

    return 0;
}

:EDIT

I have updated my code like this

#include <iostream>
#include <stdio.h>
struct struct_type{
    double d_attribute;
    int i__attribute__(int(packed));
    int * p__attribute_(int(packed));;
    char  ch;
} s;

int main(){
    int *t;
    //std::cout<<sizeof(char)<<std::endl;
    //std::cout<<sizeof(double)<<std::endl;
    //std::cout<<sizeof(int)<<std::endl;
    //std::cout<<sizeof(t)<<std::endl;

    printf("s_type is %d bytes long",sizeof(s));

    return 0;
}

and now it shows me 16 bytes. Is it good, or have I lost some important bytes?

Twill answered 15/8, 2010 at 9:33 Comment(0)
M
52

There is some unused bytes between some members to keep the alignments correct. For example, a pointer by default reside on 4-byte boundaries for efficiency, i.e. its address must be a multiple of 4. If the struct contains only a char and a pointer

struct {
  char a;
  void* b;
};

then b cannot use the adderss #1 — it must be placed at #4.

  0   1   2   3   4   5   6   7
+---+- - - - - -+---------------+
| a | (unused)  | b             |
+---+- - - - - -+---------------+

In your case, the extra 7 bytes comes from 3 bytes due to alignment of int*, and 4 bytes due to alignment of double.

  0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
+---------------+---+- - - - - -+---------------+- - - - - - - -+
| i             |ch |           | p             |               |
+---------------+---+- - - - - -+---------------+- - - - - - - -+
 10  11  12  13  14  15  16  17
+-------------------------------+
| d                             |
+-------------------------------+
Moulder answered 15/8, 2010 at 9:39 Comment(6)
+1 for the diagrammatic representation.Pergola
... XD. Just spent about 10 minutes typing up the full representation for his memory structure; even included an offer to yourself to steal it if you think it'd further your answer, and I post it to discover that you've already added your own. Ah well. XD.Ignatius
+1 for a very good answer. You answer deserved to be accepted. :)Pergola
+1, I would add that before using a packed attribute which will put the alignments off if allowed (which means losing efficiency) it can be interesting to simply reorganize the data so as to minimize the padding. In this case though the order doesn't really matter because of the double stringent requirement.Lazuli
very nice answer..., with diagrammatic representation. Deserved to be accepted.Sore
emacs artist mode power.Helyn
P
9

... it gives me 24, and I am confused. What are the additional 7 bytes?

These are padding bytes inserted by the compiler. Data structure padding is implementation dependent.

From Wikipedia, Data structure alignment:

Data alignment means putting the data at a memory offset equal to some multiple of the word size, which increases the system's performance due to the way the CPU handles memory. To align the data, it may be necessary to insert some meaningless bytes between the end of the last data structure and the start of the next, which is data structure padding.

Pergola answered 15/8, 2010 at 9:37 Comment(6)
Yes. But when I assume alignment=4 I would expect 20, not 24.Downall
@Henk: double on Windows x86 has alignment = 8.Moulder
@Kenny: when all members are 8-aligned you get 32. When only the char is padded to 4: 20.Downall
@Henk - when double has 8 alignment, that doesn't mean the other fields have 8 alignment - though the struct as a whole will do.Sandrasandro
On Windows (Visual C++), you can turn off padding with #pragma pack(1). Be sure to use #pragma pack(push/pop) when you do this.Solfatara
+1 for speed and correctness.Pergola
I
6

To expand slightly on KennyDM's excellent answer (Kenny - please do steal this to supplement your answer if you want), this is probably what your memory structure looks like once the compiler has aligned all of the variables:

  0    1    2    3    4    5    6    7
+-------------------+----+-----------+
| i                 | ch | (unused)  |
+-------------------+----+-----------+

  8    9   10   11   12   13   14   15
+-------------------+----------------+
| p                 |   (unused)     |
+-------------------+----------------+

 16   17   18   19   20   21   22   23
+------------------------------------+
| d                                  |
+------------------------------------+

So, because of the 3-byte gap between "ch" and "p", and the 4 byte gap between "p" and "d", you get a 7 byte padding for your structure, thus the size of 24 bytes. Since your environment's double has 8-byte alignment (i.e. it must reside in it's own block of 8-bytes, as you can see above), the entire struct will also be 8-byte aligned over-all, and so even re-ordering the variables will not alter the size from 24 bytes.

Ignatius answered 15/8, 2010 at 9:58 Comment(3)
Since the double has 8 byte alignment (otherwise the struct would have been 20 bytes!), there will be a 7-byte padding after ch even after rearrangement.Moulder
Ah, so the double having 8 byte alignment causes the entire struct to do so? I did not know that, thanks!Ignatius
yeah in general, the entire struct must have the same alignment as the "most-aligned" member. Imagine if you have an array of these structs, every one of them is required to align its double member correctly, which is only possible if the struct is given the same alignment as the double member.Cassady
M
1

It's 24 bytes due to padding. Most compilers pad data to a multiple of its size. So, a 4-byte int is padded to a multiple of 4 bytes. A 8-byte double is padded to a multiple of 8 bytes. For your structure, this means:

struct struct_type{
  int i; // offset 0 (0*4)
  char ch; // offset 4 (4*1)
  char padding1[3];
  int *p; // offset 8 (2*4)
  char padding1[4];
  double d; // offset 16 (2*8)
}s;

You can optimize your struct like that:

struct struct_type{
  double d;
  int i;
  int *p;
  char ch;
}s;

sizeof(s)==17 on most compilers (20 on some others)

Merimerida answered 15/8, 2010 at 9:48 Comment(1)
Even after reordering the sizeof should still be 24 because the double has 8-byte alignment (instead of 4).Moulder
H
0

The compiler is allowed to align the members of the structure to addresses for faster access. e.g. 32-bit-boundaries. It is only required by the standard, that the members of the object are stored in the order they are declared. So always make sure you use sizeof and offsetof when you need an exact position in memory.

Hackle answered 15/8, 2010 at 9:39 Comment(0)
G
0

See comp.lang.c FAQ list · Question 2.12:

Why is my compiler leaving holes in structures, wasting space and preventing ``binary'' I/O to external data files? Can I turn this off, or otherwise control the alignment of structure fields?

Gastelum answered 15/8, 2010 at 9:43 Comment(0)
R
0

The additional size comes from data alignment, i.e. the members are aligned to multiples 4 or 8 bytes.

Your compiler probably aligns int and pointers to multiples for 4 bytes and the double to multiples for 8 bytes.

If you move the double to a different position within the struct, you might be able to reduce the size of the struct from 24 to 20 bytes. But it depends on the compiler.

Returnee answered 15/8, 2010 at 9:44 Comment(0)
G
0

Also sometimes you need the struct to mantain the order you required. In this cases, if you are using gcc, you should use the __attribute__((packed)) statement.

See also this for further info.

Gelasias answered 15/8, 2010 at 9:45 Comment(2)
Is there any way in C (or gcc) to specify that a particular data item may be unaligned? Some processors simply do not support direct access to unaligned data, and so a 32-bit read or write would have to be split into byte operations and shifts. Code to do that would be wasteful if applied to every 32-bit pointer dereference, but being able to specify that certain pointers must be dereferenced with such code would be helpful.Poach
@supercat: why don't you simply use memcpy? memcpy ((void *)&place, (const void *)&my_word, sizeof(my_word));Gelasias
P
0

$9.2/12 states - "Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (11.1). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1)."

So just like the sizeof(double) and sizeof(int), the offsets at which structure members would be aligned is unspecified, except that members that are declared later are at higher addresses.

Pablo answered 15/8, 2010 at 9:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.