Struct C: Packing Data

Structures in C Programming Language

struct cIf you have a collection of data, say a bunch of numbers, you can store them as a array type in C. If they are bunch of strings, you can store them as an array of strings. Lets say you have a series of strings, representing items of a grocery store shelf, and two other arrays of numbers containing inventory of the item and its sale price, then question is how do you keep track of this item ? The solution is not surprising if you consider the complex user defined data-type called structures, in the C language.

In this blog post, we are going to see how you can create composite data types, like item in the above example, which contains the remaining inventory, name of item and cost price. We will also discuss the extensions of structures to build data structures like singly linked list. Languages like C++ and Java have template structures to implement advanced data structures.

Declaring a Structure

If you are a beginning C user, you may benefit from starting with this code sample, which declares a structure called UserInfo and also names a new type ‘UserInfoDef’.

typedef struct UserInfo UserInfoDef; 

struct UserInfo { 
    char *user_name;   
    int age;
}; 

struct GroceryItem {
    char *item_name;
    int remaining_inventory;
    float cost_price;
};

Now this declaration is not very useful, unless you make variables of this type and store some information in them. This is done by the code,

 struct UserInfo { 
    char *user_name;   
    int age;
} user_a, user_b; 

 user_a.user_name = “Apple Sauce”;
 user_a.age = 21;
 user_b.user_name = “Beet Root”;
 user_b.age = 69;

Example – Basic Structures

Now, lets put this data together into a simple program, and print the user data to the terminal. You should see the following output

#include <stdio.h>

struct UserInfo {
  char *user_name;   
  int age;
};

void print_user(  struct UserInfo* ptr ) {
  printf("%s -> %d  \n",ptr->user_name,ptr->age);
}

int main() {       

  struct UserInfo user_a, user_b, q;


  user_a.user_name = "Apple Sauce";
  user_a.age = 21;

  user_b.user_name = "Beet Root";
  user_b.age = 69;

  q = user_b;
  q.age++;

  print_user( &user_a );
  print_user( &user_b );  
  print_user( &q );  

  return 0;
}

Compiling and running the program should give you an output,

Apple Sauce -> 21  
Beet Root -> 69  
Beet Root -> 70  

Operations on Structures

Structures in C language allow the following operations,

  1. create a structure element

  2. create a structure array

  3. delete a structure

  4. copy a structure element

You have seen 1 and 4 structure element declarations on the stack and element copy in previous section. You will see 2, and 3 in the following sections.

Creating Dynamic Structures – Memory Allocation

To create structures on the run time, say from data received over the Internet, or user input, or files read off the disk, we need to know the number of structures/records you want to store on memory. Since this information is not available at runtime, we have to allocate memory and create new space. This is usually the case in web programming, or Internet middleware or backend codes.

In this routine get_new_user_info, you use the function malloc, (for which use the directive #include<stdlib.h>), which retrieves usable memory space from the OS heap memory. In the following example, you request memory for the data structure UserInfoDef, for the number of bytes given by the inbuilt C-operator sizeof().

UserInfoDef* get_new_user_info(const char * user_name) { 
        UserInfoDef* user_curr = (UserInfoDef*) malloc( sizeof(UserInfoDef) ); 
        user_curr->next = NULL; 
        user_curr->user_name = (char *) malloc( sizeof(char)*strlen(user_name) );        
        strcpy( user_curr->user_name, user_name); 
        return user_curr; 
} 

Examples – Linked List Example

In this example, you read the file on UNIX called ‘/etc/passwd’ which is a historical artifact, listing number of users and their encrypted passwords, and group IDs. Once we open the file with the read attribute, “r”, we create a struct for the user information and connect these struct’s in what is called a linked list. Using this data structure we build a list of elements in the function, and pass it back to the caller. Once all

Putting all this together we see, the listing ‘struct_demo.c’

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

typedef struct UserInfo UserInfoDef; 

struct UserInfo { 
    char *user_name;   
    UserInfoDef* next;    
}; 

UserInfoDef* get_new_user_info(const char * user_name) { 
        UserInfoDef* user_curr = (UserInfoDef*) malloc( sizeof(UserInfoDef) ); 
        user_curr->next = NULL; 
        user_curr->user_name = (char *) malloc( sizeof(char)*strlen(user_name) );        
        strcpy( user_curr->user_name, user_name); 
        return user_curr; 
} 



/* extract username from the line of  /etc/passwd & 
   and find the longest user-names 
 */ 
UserInfoDef* get_user_names(int* total_users) { 

    UserInfoDef* user_begin = NULL; 
    UserInfoDef* user_curr = NULL, *user_prev = NULL; 

    const char* fname = "/etc/passwd"; 
    FILE* fp = fopen(fname,"r"); 
    assert( fp != NULL ); 
    char line_data[512] = {0,}, *ptr=NULL; 
    int line_no = 0; 

    while(! feof( fp ) ) { 
        fgets( line_data, 512, fp ); 
        /* remove parts of line after ':' */ 
        ptr = line_data; 
        while(  *ptr !=':' ) { 
            ptr++; 
        } 
        *ptr = ''; 
        /* output username */ 
        printf("%d) %s \n",line_no,line_data); 

        /* create a new user ID  & copy the current username */ 
       user_curr = get_new_user_info( line_data ); 

        /* update the linked list */ 
       if ( user_prev ) { 
             user_prev->next = user_curr; 
       } 
       user_prev = user_curr; 

        /* keep track of the beginning */ 
        if ( user_begin == NULL ) { 
              user_begin = user_curr; 
        } 

        line_no++; 
    } 
    *total_users = line_no; //update rval 
    return user_begin; 
} 

int main() { 
    int total_users; 
    UserInfoDef* user_begin = get_user_names(&total_users);
    UserInfoDef* prev = NULL; 
    int count = 0; 

    printf(" ######## TOTAL USERS  %04d  #############\n",total_users); 
    while( user_begin ) { 
        printf("%03d | name = %s \n",++count,user_begin->user_name); 
        prev = user_begin; 

        //advance 
        user_begin = user_begin->next; 

        //cleanup memory 
        free(prev->user_name); 
        free(prev); 
    } 
} 
/** gcc struct_demo.c -o struct_demo 
   ./struct_demo */ 

Running the Program

You can download the C package for your platform from the source website, http://gcc.gnu.org, and run the tests and programs as, $gcc struct_demo.c -o struct_demo && ./struct_demo, is the command to interpret the code and then run the program

001 | name = apache
002 | name = gdm 

.. <snip> ..

034 | name = mu2
035 | name = lightdm 
036 | name = colord 
037 | name = whoopsie 
038 | name = mysql 
039 | name = halla 
040 | name = guest-r7Ir02# 
041 | name = guest-r7Ir02 

Summary

Software developers use structures as agglomerative data structures to hold heterogeneous pieces of data. Structures are first class elements in C language, you can use to store multiple pieces of data, and build arrays from and use malloc/free system. Advanced algorithms and data structures like trees, graphs, linked lists, and maps are built off using the struct in C. Key elements of operating systems like Linux are written on these basic data structures. You can learn more advanced data structures to get most of your programming, and take it to the next level.