This is because of padding added to satisfy alignment constraints. Data structure alignment impacts both performance and correctness of programs:
- Mis-aligned access might be a hard error (often
SIGBUS
).
- Mis-aligned access might be a soft error.
- Either corrected in hardware, for a modest performance-degradation.
- Or corrected by emulation in software, for a severe performance-degradation.
- In addition, atomicity and other concurrency-guarantees might be broken, leading to subtle errors.
Here's an example using typical settings for an x86 processor (all used 32 and 64 bit modes):
struct X
{
short s; /* 2 bytes */
/* 2 padding bytes */
int i; /* 4 bytes */
char c; /* 1 byte */
/* 3 padding bytes */
};
struct Y
{
int i; /* 4 bytes */
char c; /* 1 byte */
/* 1 padding byte */
short s; /* 2 bytes */
};
struct Z
{
int i; /* 4 bytes */
short s; /* 2 bytes */
char c; /* 1 byte */
/* 1 padding byte */
};
const int sizeX = sizeof(struct X); /* = 12 */
const int sizeY = sizeof(struct Y); /* = 8 */
const int sizeZ = sizeof(struct Z); /* = 8 */
One can minimize the size of structures by sorting members by alignment (sorting by size suffices for that in basic types) (like structure Z
in the example above).
IMPORTANT NOTE: Both the C and C++ standards state that structure alignment is implementation-defined. Therefore each compiler may choose to align data differently, resulting in different and incompatible data layouts. For this reason, when dealing with libraries that will be used by different compilers, it is important to understand how the compilers align data. Some compilers have command-line settings and/or special #pragma
statements to change the structure alignment settings.
In C++, there is only a subtle difference. It's a holdover from C, in which it makes a difference.
The C language standard (C89 §3.1.2.3, C99 §6.2.3, and C11 §6.2.3) mandates separate namespaces for different categories of identifiers, including tag identifiers (for struct
/union
/enum
) and ordinary identifiers (for typedef
and other identifiers).
If you just said:
struct Foo { ... };
Foo x;
you would get a compiler error, because Foo
is only defined in the tag namespace.
You'd have to declare it as:
struct Foo x;
Any time you want to refer to a Foo
, you'd always have to call it a struct Foo
. This gets annoying fast, so you can add a typedef
:
struct Foo { ... };
typedef struct Foo Foo;
Now struct Foo
(in the tag namespace) and just plain Foo
(in the ordinary identifier namespace) both refer to the same thing, and you can freely declare objects of type Foo
without the struct
keyword.
The construct:
typedef struct Foo { ... } Foo;
is just an abbreviation for the declaration and typedef
.
Finally,
typedef struct { ... } Foo;
declares an anonymous structure and creates a typedef
for it. Thus, with this construct, it doesn't have a name in the tag namespace, only a name in the typedef namespace. This means it also cannot be forward-declared. If you want to make a forward declaration, you have to give it a name in the tag namespace.
In C++, all struct
/union
/enum
/class
declarations act like they are implicitly typedef
'ed, as long as the name is not hidden by another declaration with the same name. See Michael Burr's answer for the full details.
Best Solution
It would have to be
because
name
was allocated withnew[]
while the other two were allocated withnew
. Neitherdelete
nordelete[]
changes the pointer value, though, it just invalidates what the pointer used to point to.However, doing it this way is rather terribad. In the case of the object itself and the
int
, there is no reason to use dynamic allocation at all, and forname
there's a class in the standard library calledstd::string
that does the memory management for you. It would be best to replace the code with