Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C memory allocation for struct with malloc

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?
like image 832
Seb Avatar asked Aug 16 '15 08:08

Seb


3 Answers

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 ) );
like image 141
Vlad from Moscow Avatar answered Sep 20 '22 15:09

Vlad from Moscow


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.

like image 43
mort Avatar answered Sep 24 '22 15:09

mort


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.

like image 25
Bathsheba Avatar answered Sep 21 '22 15:09

Bathsheba