3

Consider I have structure as below.

struct st
{
  char c;
  double d;
  int i;
};

As per memory alignment size of struct st will be (1+7(padded)+8+4+4(padded))=24bytes

When I do malloc as below

struct st *p = malloc(sizeof(*p));

it will be same as

struct st *p = malloc(13);

How does malloc allocates 24 bytes to meet memory alignment since we are just passing the size of structure(13bytes)?

15
  • Can you show that malloc(13) does allocate 24 bytes? Commented Sep 3, 2018 at 19:28
  • 2
    It doesn't, it only allocates 13 bytes, but some systems allocate more than you asked for because they deal with memory in minimum sized chunks. Don't rely on this. Commented Sep 3, 2018 at 19:28
  • 2
    The alignment will depend on the implementation. For example, on 32-bit architectures it is likely that double only requires 4 byte alignment, in which case your sizeof is incorrect. Commented Sep 3, 2018 at 19:28
  • @WeatherVane In which case doing malloc is wrong for structure since it does not handle memory allignment? Commented Sep 3, 2018 at 19:30
  • 4
    @KBlr on my system sizeof(struct st) is 24 and consequently sizeof(*p), so I don't know why you would even malloc(13) since you already know that 24 bytes are needed. Commented Sep 3, 2018 at 19:43

2 Answers 2

4

The question erroneously assumes that

struct st *p = malloc(sizeof(*p));

is the same as

struct st *p = malloc(13);

It is not. To test,

printf ("Size of st is %d\n", sizeof (*p));

which prints 24, not 13.

The proper way to allocate and manage structures is with sizeof(X), and not by assuming anything about how the elements are packed or aligned.

Sign up to request clarification or add additional context in comments.

9 Comments

In this case there is nothing left for malloc to handle memory alignment and data packing, since everything will be handled by compiler
@KBlr: malloc does play a role in alignment. It is required to return memory suitably aligned for any supported object that might be placed in it. So, if you ask for 24 bytes, and the C implementation has objects less than or equal to 24 bytes in size that require 8-byte alignment, then the memory malloc returns must be at least 8-byte aligned. If you ask for 4 bytes, and the C implementation does not have any objects that size or smaller that require 8-byte alignment but does have 4-byte objects that require 4-byte alignment, then malloc only needs to return 4-byte-aligned memory.
@KBlr : What memory alignment? The only necessary alignment is the start of the allocated block - the members are then aligned relative to that as determined by the padding applied by the compiler. malloc() is required ot return a pointer with suitable alignment for any object that will fit within the allocated space.
@EricPostpischil: Using C11 and having included the appropriate header, I create the type (and type alias) typedef struct {alignas(double) char ch;} Char;. Obviously, sizeof(Char) == sizeof(double). But is it UB to access the ch member of a Char in a memory allocation returned by malloc(1)? I've always just assumed that malloc must conform to the same alignment regardless of its argument -- and, as far as I know, common implementations do -- but now you've got me wondering if it would really be legal for malloc(1) to return an unaligned pointer. TBH, the thought makes me nervous
@rici: alignas is not standard C. Supposing you use _Alignas, then you should not attempt to access a Char (or any part of it) through a pointer cast from the result of malloc(1). A Char will be at least as large as the alignment requirement (not size) of a double, and, if that is greater than one, cannot be constructed in the space of malloc(1). The wording for what alignment malloc must conform to in C 2018 7.22.3 is ambiguous to me, but the fact that a Char is (say) eight bytes means accessing it in a one-byte space is not proper.
|
0

How does malloc allocate 24 bytes to meet memory alignment since we are just passing the size of structure (13 bytes)?

It doesn't. If malloc(13) happens to return at least 24 bytes it's a quirk of the malloc implementation. malloc is allowed to allocate more space than necessary and often must for byte alignment and various other implementation reasons.

We can see this with a simple program.

struct st *a = malloc(13);
struct st *b = malloc(13);
struct st *c = malloc(13);
struct st *d = malloc(13);

printf("%p\n%p\n%p\n%p\n", a, b, c, d);

0x602000003210
0x602000003230
0x602000003250
0x602000003270

As we can see from the addresses, the pointers returned by malloc(13) have 32 bytes between them. Plenty to fit your 24 bytes and the program "works". Even malloc(1) returns the same result.

But if we make your struct a little bigger...

struct st {
  char c;
  double b;
  double a;
  double d;
  int i;
};

That's 40 bytes, aligned. Now it doesn't fit into 32 bytes and we see corruption because the struct's memory overlap with each other.

#include <stdlib.h>
#include <stdio.h>

struct st {
  char c;
  double b;
  double a;
  double d;
  int i;
};

void print_struct(struct st* st) {
    printf("%c %lf %d\n", st->c, st->d, st->i);
}

int main() {
    const size_t struct_size = sizeof(char) + (sizeof(double) * 3) + sizeof(int);

    printf("sizeof(struct st): %zu\n", sizeof(struct st));
    printf("sizeof fields added together: %zu\n", struct_size);

    struct st *a = malloc(13);
    struct st *b = malloc(13);
    struct st *c = malloc(13);
    struct st *d = malloc(13);

    printf("%p\n%p\n%p\n%p\n", a, b, c, d);

    a->c = 'a';
    a->d = 1.0;
    a->i = 1;

    b->c = 'b';
    b->d = 2.0;
    b->i = 2;

    c->c = 'c';
    c->d = 3.0;
    c->i = 3;

    d->c = 'd';
    d->d = 4.0;
    d->i = 4;

    print_struct(a);
    print_struct(b);
    print_struct(c);
    print_struct(d);
}

sizeof(struct st): 40
sizeof fields added together: 29
0x602000003210
0x602000003230
0x602000003250
0x602000003270
a 1.000000 98
b 2.000000 99
c 3.000000 100
d 4.000000 4

98 is ascii b. 99 is ascii c. 100 is ascii d. This indicates that a->i overlaps with b->c, b->i overlaps with c->c, and so on.

2 Comments

This answer accepts the question’s false premise that sizeof *p produces 13 and fails to correct it.
@EricPostpischil OIC, the OP thinks sizeof(*p) is 13. Well, it answers the other half of the question; why malloc(13) happens to work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.