Struct Hack

What will be the size of following structure?

struct employee
{
    int 	emp_id;
    int 	name_len;
    char	name[0];
};

4 + 4 + 0 = 8 bytes.

And what about size of “name[0]”. In gcc, when we create an array of zero length, it is considered as array of incomplete type that’s why gcc reports its size as “0” bytes. This technique is known as “Stuct Hack”. When we create array of zero length inside structure, it must be (and only) last member of structure. Shortly we will see how to use it.
“Struct Hack” technique is used to create variable length member in a structure. In the above structure, string length of “name” is not fixed, so we can use “name” as variable length array.

Let us see below memory allocation.

struct employee *e = malloc(sizeof(*e) + sizeof(char) * 128); 

is equivalent to

struct employee
{
	int 	emp_id;
	int		name_len;
	char	name[128]; /* character array of size 128 */
};

And below memory allocation

struct employee *e = malloc(sizeof(*e) + sizeof(char) * 1024); 

is equivalent to

struct employee
{
	int		emp_id;
	int		name_len;
	char	name[1024]; /* character array of size 1024 */
};

Note: since name is character array, in malloc instead of “sizeof(char) * 128″, we can use “128” directly. sizeof is used to avoid confusion.

Now we can use “name” same as pointer. e.g.

e->emp_id 	= 100;
e->name_len	= strlen("Geeks For Geeks");
strncpy(e->name, "Geeks For Geeks", e->name_len);

When we allocate memory as given above, compiler will allocate memory to store “emp_id” and “name_len” plus contiguous memory to store “name”. When we use this technique, gcc guaranties that, “name” will get contiguous memory.
Obviously there are other ways to solve problem, one is we can use character pointer. But there is no guarantee that character pointer will get contiguous memory, and we can take advantage of this contiguous memory. For example, by using this technique, we can allocate and deallocate memory by using single malloc and free call (because memory is contagious). Other advantage of this is, suppose if we want to write data, we can write whole data by using single “write()” call. e.g.

write(fd, e, sizeof(*e) + name_len); /* write emp_id + name_len + name */ 

If we use character pointer, then we need 2 write calls to write data. e.g.

write(fd, e, sizeof(*e)); 		/* write emp_id + name_len */
write(fd, e->name, e->name_len);	/* write name */

Note: In C99, there is feature called “flexible array members”, which works same as “Struct Hack”

This article is compiled by Narendra Kangralkar. Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above





  • Tamoghna Bhaduri

    this code below is generating SEGMENTATION fault.

     
    #include<stdio.h>
    #include <stdlib.h>
    typedef struct Node
    {
        int data;
    struct Node *next;
    }Node;
    int count=1;
    Node * create_list(int);
    Node * create_list(int value)
    {
    
        Node *temp,*head,*gen;
        temp=(Node *)malloc(sizeof(Node));
        if(count==1)
        {
        head=temp;
        gen=temp;
        temp->data=value;
        temp->next=NULL;
        }
        else
        {
        gen->next=temp;
        temp->next=NULL;
        temp->data=value;
        gen=temp;
        }
    count++;
    return head;
    }
    int main()
    {
        int num,value,i;
        Node *ptr;
    printf("how many nodes::");
    scanf("%d",&num);
    printf("\nplease enter the number of nodes ::\n");
    for(i=1;i<=num;i++)
    {
        scanf("%d",&value);
        ptr=create_list(value);
    }
    while(ptr->next!=NULL)
    {
        printf("\n%d\n",ptr->data);
        ptr=ptr->next;
    }
        return 0;
    }
    
     
    • linuxWorld

      gen->next=temp; line has error due to wild/ uninitialzed pointer (SIGSEGV error means invalid acess of the valid memory address)

      your implementation needs little modification // reason your list is not linking with the other node.

    • linuxWorld

      #include
      #include
      typedef struct Node
      {
      int data;
      struct Node *next;
      }Node;
      int count=1;
      Node *head , *gen ;

      Node * create_list(int value)
      {

      Node *temp;
      temp=(Node *)malloc(sizeof(Node));
      if(count==1)
      {
      head=temp;
      gen=temp;
      temp->data=value;
      temp->next=NULL;
      }
      else
      {
      gen->next=temp;
      temp->next=NULL;
      temp->data=value;
      gen=temp;
      }
      count++;
      return head;
      }
      int main()
      {
      int num,value,i;
      Node *ptr;
      printf(“how many nodes::”);
      scanf(“%d”,&num);
      printf(“\nplease enter the number of nodes ::\n”);
      for(i=1;inext!=NULL) // last data will not be printed // you should write while(ptr)
      {
      printf(“\n%d\n”,ptr->data);
      ptr=ptr->next;
      }
      return 0;
      }

      // check it i think it will run …. rest is ok

      • Tamoghna Bhaduri

        Thanks a lot :)

         
        /* Paste your code here (You may delete these lines if not writing code) */
         
  • Ashish

    Do you mean to say instead of fixing size in structure definition name[0](struct hack) can be used to make structure size variable and still all the memory of structure as contiguous?

     
    /* Paste your code here (You may delete these lines if not writing code) */
     
  • Venki

    This technique is new to me. But as long as portability is concerned, we will stick to standard.

    Can we define an array of such structures? If not, it breaks the guarantee made by normal structure definition.

  • Ashish

    Sorry experts:
    This article looks strange to me, not sure what am I missing.

    But I could not understand above article. I tried below code:

     
    struct employee {
    int a;
    int b;
    char carr[10];
    };
    
    int main()
    {
    struct employee e;
    struct employee *e1=NULL;
    printf("size:%d:%d\n",sizeof(e), sizeof(*(&e)));
    
    printf("size before alocation:%d\n",sizeof(*e1));
    e1 = malloc(sizeof(struct employee));
    
    strcpy(e1->carr,"ashish");
    
    printf("size:%d,str:%s\n",sizeof(*e1),e1->carr);
    
    }
    
     

    Output is:
    —-
    size:20:20
    size before alocation:20
    size:20:str:ashish
    —-

    So why do we need to allocate memory for char array in separate, while sizeof(*e) should returns the complete size of structure itself.
    i.e.
    why do we need to allocate memory like: malloc(sizeof(*e) + sizeof(char) * 128); while malloc(sizeof(*e)); should be sufficient.

    • owais

      In you example you have defined carr as an array of 10 therefore you dont need to allocate memory for it however if you would have declared carr as a pointer to a char array i.e char *carr; then in that case you have to dynamically allocate memory as sizeof(*e) is dependent on amount of memory carr has .

  • Sharath

    Good article. I never knew about ‘struct hack’. Is it a new concept. Where did you get to know about it ? book/generals/man pages ??

  • Syed Yaser
     
    Consider, 
    struct employee *e =malloc(sizeof(*e) + sizeof(char) * 128);
    it is equivalent to 
    struct employee
    {
    int emp_id;
    int name_len;
    char name[128]; /* character array of size 128 */
    };
    then if I want to write following statement 
    e->name[129]='Y'; 
    what will b the case.Pls Explain
    
    
     
  • atul

    shouldn’t it be :-

    struct employee *e = malloc(sizeof(struct employee) + sizeof(char) * 128);

    instead of sizeof(*e)

     
    /* Paste your code here (You may delete these lines if not writing code) */
     
    • Narendra Kangralkar

      sizeof(*e) is equivalent to sizeof(struct employee)
      We can write both the way.

      • Venki

        The definition of ‘e’ is complete just before assignment sign, so we can use e there after. Not sure, whether any compiler flags it as an error. Perhaps old compilers may throw error.

  • Chris

    Hi,
    Nice post. However, there is a slight error:
    Instead of

     
    struct employee *e = malloc(sizeof(*e) + sizeof(char) * 128); 
     

    it should be

     
    struct employee *e = (employee*) malloc(sizeof(*e) + sizeof(char) * 128);
     

    as you can’t assign

     void 

    , which is returned by

     malloc 

    , to

     employee * 

    (And also in the rest of the

     malloc 

    statements)

    • kartik

      As far as I know, we can assign without typecasting in C, but not in C++.

    • Arpit Jain

      Malloc should not be casted in c.

  • siva krishna

    struct employee *e = malloc(sizeof(*e) + sizeof(char) * 128);
    is equivalent to

    struct employee
    {
    int emp_id;
    int name_len;
    char name[128]; /* character array of size 128 */
    };

    After that dynamic allocation What will be the value of
    sizeof(*e) will be 136 but it is giving 8 only why?

     
    /* Paste your code here (You may delete these lines if not writing code) */
     
    • Narendra Kangralkar

      @siva
      If you checked size by using “sizeof”, then it will show you size of static members(which is 8 bytes). To check size of dynamically allocated members, use “mallinfo” structure and related function.

  • krit

    when we say contiguous memory….then memory allocated for name is contiguous to memory for other members of the structure?isn’t it?
    then why can’t we use char pointer as
    char *name;
    as one of the members of the structure…. then also memory is contiguous?
    please make this point clear… as i may not be getting this right.

     
    /* Paste your code here (You may delete these lines if not writing code) */
     
    • kartik

      I think the author wants to say that all members of employee will be contiguous, the members emp_id, name_len and name[] will be contiguous. When we use pointer, all elements of name[] will be contiguous, but emp_id and name_len will be at different locations

    • Narendra Kangralkar

      @krit

      When we allocate memory using “struct hack” then only contiguous memory will be allocate to store “emp_id + name_len + contents of name”

      And if we use character pointer then emp_id and name_len will be stored at contiguous memory and “name” will store only pointer to allocated memory (which is returned by malloc() ) which may or may not be contiguous memory as there is no guarantee that character pointer will get contiguous memory.