6

I am trying to understand the memory allocation in C for struct but I am stuck on it.

struct Person {
    char *name;
    int age;
    int height;
    int weight;
};
struct Person *Person_create(char *name, int age, int height, int weight)
{
    struct Person *who = malloc(sizeof(struct Person));
    assert(who != NULL);

    who->age = age;
    who->height = height;
    who->weight = weight;
    who->name = strdup(name);

    return who;
}
int main(int argc, char *argv[])
{
    struct Person *joe = Person_create("ABC", 10, 170, 60);
    printf("Size of joe: %d\n", sizeof(*joe));
    printf("1. Address of joe \t= %x\n", joe);
    printf("2. Address of Age \t= %x\n", &joe->age);
    printf("3. Address of Height \t= %x\n", &joe->height);
    printf("4. Address of Weight \t= %x\n", &joe->weight);
    printf("5. Address of name \t= %x\n", joe->name);
...

What I don't understand is the memory allocation for this struct. On my printout I see this:

Size of joe: 24
1. Address of joe   = 602010
2. Address of Age   = 602018
3. Address of Height    = 60201c
4. Address of Weight    = 602020
5. Address of name  = 602030

Questions:

  • Why there is a gap between the 1 and 2?
  • Why there is a gap between the 4 and 5?
  • How is the size of *name being calculated as the name points only to first char?
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Seb
  • 105
  • 3
  • 1
    The gap of 4 is the size of the char*. The last gap is the char* not the address of the pointer – Jens Munk Aug 16 '15 at 08:30
  • 7
    To print pointers with [`printf`](http://en.cppreference.com/w/cpp/io/c/fprintf), use the `"%p"` format code. – Some programmer dude Aug 16 '15 at 08:32
  • Also, [this old question and its answers](http://stackoverflow.com/q/119123/440558) should be helpful. – Some programmer dude Aug 16 '15 at 08:34
  • "Why there is a gap between the 1 and 2?" - um because age is not the first member of the struct?! – The Paramagnetic Croissant Aug 16 '15 at 08:39
  • 5
    @joachimpileborg and use `%zu` for `size_t`. – The Paramagnetic Croissant Aug 16 '15 at 08:39
  • 2
    The type of the argument corresponding to `%d` must be `int`; you've provided a `size_t` value which invokes undefined behaviour. Perhaps you meant `%zu`. Similarly, the type of the argument corresponding to `%x` should be `unsigned int`, but you've provided various pointer values... Perhaps you meant to use `%p` with a cast to `(void *)`. `assert` should be used as a debugging aid, not an error handling feature. These misnomers make me concerned about the book which you are reading; what is that book, by the way? – autistic Aug 16 '15 at 08:42
  • [`printf` with the wrong format string invokes undefined behavior](http://stackoverflow.com/q/14504148/995714). Like others said, [use `%zu` to print `size_t`](http://stackoverflow.com/q/2524611/995714) (which is what returned by `sizeof`), and [`%p` for pointers](http://stackoverflow.com/q/9053658/995714) – phuclv Aug 16 '15 at 09:19
  • 3
    By the way, there's no requirement that two consecutive memory allocations should return two consecutive memory blocks.. You also don't print the location of the `name` member, but the pointer returned by the `strdup` call (which itself call `malloc`), to print the location of the `name` member you should use the address-of operator `&` like for the others. This is one of the reasons for the "gap" between 4 and 5. – Some programmer dude Aug 16 '15 at 09:30

5 Answers5

9

There is no gap between the address of the object joe and the address of data member age. This extent is occupied by data member name.

struct Person {
    char *name;
    int age;
    //...

According to the output

1. Address of joe   = 602010
2. Address of Age   = 602018

it occupies 8 bytes that is sizeof( char * ) in your platform is equal to 8. And its address coincides with the address of the object joe itself.

In this statement

printf("5. Address of name \t= %x\n", joe->name);

you did not output the address of name itself. You printed the value stored in this pointer and this value is the address of the first character of a copy of the string literal "ABC" that was gotten by using strdup.

So there is a gap between values in the outputs 4 and 5 because they are different extents of memory. Data member weight belongs to object joe while the copy of the string literal "ABC" is stored outside the object. The object just has data member name that points to the first character of the copy of the literal.

As name is a pointer then its size is calculated like

sizeof( char * )

or

sizeof( joe->name )

and equal to 8 as I explained in the beginning of the post.

If you want to determine the length of the string literal you should use standard function strlen declared in header <string.h>. For example

printf( "%zu\n", strlen( joe->name ) );
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
7

Why there is a gap between the 1 and 2?

A struct's start address is always equal to the address of it's first member. From the C standard:

6.7.2.1-13. A pointer to a structure object, suitably converted, points to its initial member

The first member is not age, but name. So the following two lines should print the same address:

printf("1. Address of joe \t= %x\n", joe);
printf("1. Address of name-pointer \t= %x\n", &joe->name);

In your code,

printf("5. Address of name \t= %x\n", joe->name);

does not print the address of the pointer, but the address of the data the pointer points to.

How the size of *name is being calculated as the name points only to first char?

nameis a pointer, which occupies 8 bytes of memory regardless of the size of data it points to (that may be a string as in your case, a single char, an int or whatever).

Why there is a gap between the 4 and 5?

The memory for storing the actual name string is not within the struct - strdup allocates memory somewhere to duplicate the string into. This happens to be 16 bytes after the last member of your struct. This memory location is then pointed to by your name pointer.

Note that padding and memory alignment are a factor only for the size of the struct (they do not matter for your explicitly stated questions). Since the struct contains one pointer (8 bytes on your machine) and 3 integers (4 bytes each), one would assume that the total size is 20 bytes. On most platforms, memory is 8 byte aligned - which is why the size of your struct is rounded up to 24 bytes. This way, if you declare an array of Persons, each array element starts at an address that is 8 byte aligned, i.e., the address value can be divided evenly by 8.

mort
  • 12,988
  • 14
  • 52
  • 97
4

The only things the c standard guarantees is that the address of the first member is the same as the address of the structure, and that the addresses of subsequent members increases with their position in the structure.

Compilers are allowed to insert spaces between members; this is called padding. Regard it as the compiler optimising the structure for a particular platform.

Arrays must always be contiguous in memory though.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
0

It is due to something called Data alignment. To quote from this website

Every data type in C/C++ will have alignment requirement (in fact it is mandated by processor architecture, not by language).

And then extending this requirement for structures:

Because of the alignment requirements of various data types, every member of structure should be naturally aligned.

You can go through this article for a detailed read..

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Haris
  • 12,120
  • 6
  • 43
  • 70
  • 2
    -1, sorry. Data alignment is not a major factor here. The OP had three questions: "Why there is a gap between the 1 and 2?", "Why there is a gap between the 4 and 5?", and "How the size of \*name is being calculated as the name points only to first char?". Data alignment does play a role in the second question, but it's not the whole story there (and not the important part of the story); and it plays no role in answering the first and third questions. – ruakh Aug 23 '15 at 19:20
  • 5
    The large number of up/downvotes on this answer is likely due [to this Meta question](http://meta.stackoverflow.com/q/303130/472495). – halfer Aug 24 '15 at 13:58
  • 4 years later - yes this is an answer. This answer does answer a question. Or many. But it address absolutely nothing in the OPs one. – Antti Haapala -- Слава Україні Jun 05 '19 at 21:57
-2

The memory layout of the struct is machine dependent, so you should not bother with that unless you are trying to implement a DBMS or a device driver or something like that.

sizeof(*name) would equal to sizeof(char), I do not get what confused you here, can you give further explanation?

user1635881
  • 239
  • 3
  • 11