Category: 13. User-Defined Data Types

https://cdn3d.iconscout.com/3d/premium/thumb/data-collection-3d-icon-png-download-4522770.png

  • Structures vs Unions

    Structures and unions in C programming are user-defined data types that are used for storing different types of data together in a single block defined with curly braces {}. The main difference between a structure and a union is in the way they store and manage memory for their members.

    In this chapter, we will see how structures and unions work in C and how their memory is allocated. Let’s move forward and learn about −

    • Structure in C
    • Union in C
    • Memory Allocation Difference
    • Key Differences between Structure and Union

    Structure in C

    structure in C is a user-defined data type that groups variables of different data types under one name. A structure is defined using the struct keyword, and when defining a structure no memory is allocated. Memory is allocated only when we declare variables of that structure type, and each member gets its own separate memory location.

    Members of a structure are accessed using the dot (.) operator when working with structure variables, and the arrow (->) operator when working with pointers to a structure. Structures are useful for storing records like a student’s details, an address, etc.

    Syntax of Structure

    Following is the syntax for declaring a structure in C −

    structstructure_name{
    
    data_type member1;
    data_type member2;...
    data_type memberN;};</code></pre>

    Here, struct is the keyword used to define a structure. structure_name is the name of the structure and member1member2, etc., are the data members of different data types.

    Example of a Structure

    Below is a C program where we define a structure Student with three members: roll_noname, and marks. We create a variable s1, assign values to its members, and then print them. Each member has its own memory, so changing one does not affect the others.

    #include <stdio.h>#include <string.h>// Define a structure to store student detailsstructStudent{int roll_no;char name[50];float marks;};intmain(){structStudent s1;// Create a structure variable
    
    
    s1.roll_no =101;// Assign roll numberstrcpy(s1.name,"Amit");// Assign name
    s1.marks =89.5;// Assign marks// Print student detailsprintf("Roll No: %d\n", s1.roll_no);printf("Name: %s\n", s1.name);printf("Marks: %.2f\n", s1.marks);return0;}</code></pre>

    Following is the output of the program. It displays the student details stored in the structure −

    Roll No: 101
    Name: Amit
    Marks: 89.50
    

    Example of a Structure Holding Pointers to Another Structure

    In this example, we have two structures: Student and Address. The Student structure has a pointer addr to an Address structure. First, we create an Address variable and assign its address to a Student variable s1. Then, we display the student's details along with the city and state from Address structure.

    #include <stdio.h>#include <stdlib.h>#include <string.h>// Structure for AddressstructAddress{char city[50];char state[50];};// Structure for StudentstructStudent{char name[50];int roll_no;structAddress*addr;// Pointer to Address structure};intmain(){// Create an Address dynamicallystructAddress*a1 =malloc(sizeof(structAddress));strcpy(a1->city,"Mumbai");strcpy(a1->state,"Maharashtra");// Create a Student and assign Address pointerstructStudent s1;strcpy(s1.name,"jony");
    
    s1.roll_no =101;
    s1.addr = a1;// Assign address pointer// Display Student information along with Addressprintf("Name: %s\n", s1.name);printf("Roll No: %d\n", s1.roll_no);printf("City: %s\n", s1.addr-&gt;city);printf("State: %s\n", s1.addr-&gt;state);// Free dynamically allocated memoryfree(a1);return0;}</code></pre>

    The output displays the student's details along with the city and state from the associated Address structure using the pointer.

    Name: Jony
    Roll No: 101
    City: Mumbai
    State: Maharashtra
    

    Union in C

    union in C is a user-defined data type that stores different types of data in the same memory location. It is defined using the union keyword, and all members share the same memory, so updating one member changes the content of the others.

    The size of a union equals the size of its largest member. At any given time, only one member contains valid data because all members share the same memory, so initializing another member will overwrite the current value.

    We access union members using the dot (.) operator for union variables or the arrow (->) operator for union pointers. Unions are useful when memory is limited and only one value is needed at a time.

    Syntax of a Union

    Following is the syntax for declaring a union in C −

    union union_name {
       data_type member1;
       data_type member2;...
       data_type memberN;};

    Here, union is the keyword to define the union. union_name is the name of union and and member1member2etc., are the data members of different data types.

    Example of a Union

    Below is an example where we created a union Data with three members: an integer, a float, and a character, and then a variable d. Since all members share the same memory, assigning a new value replaces the previous one. So assigning d.f overwrites d.i, and assigning d.c overwrites d.f, as shown below.

    #include <stdio.h>union Data {int i;float f;char c;};intmain(){union Data d;// create a union variable
    
    
    d.i =10;// assign an integerprintf("i = %d\n", d.i);
    d.f =22.5;// assign a float (replaces integer)printf("f = %.2f\n", d.f);
    d.c ='X';// assign a character (replaces float)printf("c = %c\n", d.c);return0;}</code></pre>

    Following is the output of the program, which shows that each new assignment overwrites the earlier one.

    i = 10
    f = 22.50
    c = X    
    

    Memory Allocation Difference

    This is the most important difference between a structure and a union. The way memory is allocated to their members is different, and this affects the total size they take.

    • In a structure, every member has its own memory, so the total size is the sum of all members plus any padding added by the compiler for alignment.
    • In a union, all members share the same memory, so the total size is equal to the largest member only.

    Example of Memory Allocation

    In the example below, we define both a structure and a union with the same members, then calculate their sizes and display them. This clearly shows how memory is allocated differently for each.

    #include <stdio.h>structExampleStruct{int a;// 4 bytesfloat b;// 4 byteschar c;// 1 byte (plus padding)};union ExampleUnion {int a;// 4 bytesfloat b;// 4 byteschar c;// 1 byte};intmain(){printf("Size of Structure: %lu\n",sizeof(structExampleStruct));printf("Size of Union: %lu\n",sizeof(union ExampleUnion));return0;}

    The program prints the memory used by the structure and the union. The output may vary slightly on different systems or compilers.

    Size of Structure: 12
    Size of Union: 4
    

    Key Differences between Structures and Unions

    The following table compares and contrasts the most important features structures and unions in C −

    FeatureStructureUnion
    Memory allocationEach member gets its own memory, so all members exist independentlyAll members share the same memory, so only one member holds a valid value at a time
    SizeTotal size = sum of all members (plus padding if needed)Total size = size of the largest member only
    Member accessYou can access all members simultaneouslyOnly one member can be reliably used at a time
    InitializationYou can initialize all members at the time of declarationOnly the first member can be initialized during declaration
    Data storageCan hold multiple values at the same timeCan hold only one value at a time
    Best used whenAll properties are needed togetherMemory saving is important and only one value is needed at a time

    Conclusion

    In this chapter, we learned about structures and unions in C. Structures help us store and access multiple values together, while unions are useful when memory is limited and only one value is needed at a time.

  • Flexible Array Members in Structures

    Flexible Array Members is used to handle arrays inside structures without defining their size. These arrays get their size at runtime. A structure in C is a user-defined data type where we define multiple members of different data types together under one name.

    Below is the syntax for declaring a structure in C −

    structStructName{
    
    data_type member1;
    data_type member2;// ...};</code></pre>

    Here, each member can have a different data type.

    Flexible array members extend structures by holding a dynamically sized array at the end of their fixed-size members and all together stored in one block of memory. In this chapter, we'll see how they work inside the structures.

    Flexible Array Members in Structure

    flexible array member is an array inside a structure without a fixed size, and its memory is allocated dynamically at runtime using malloc()calloc(), or similar functions and it is declared using empty square brackets [].

    The flexible array member must be declared at the end of the structure, and there must be at least one other member before declaring it.

    Following is the syntax for declaring a flexible array member inside a structure −

    structStructName{
    
    data_type member1;
    data_type flexible_array&#91;];// flexible array member};</code></pre>

    Here, data_type is the data type of the array, and arrayName[] is the flexible array with no fixed size.

    Memory Allocation for Flexible Array Member

    flexible array member does not have a fixed size, so the compiler does not allocate any memory for it inside the structure. The sizeof operator only calculates the size of the fixed members of the structure without including the flexible array member. That's why we need to allocate memory manually when creating such structures.

    The total memory required is calculated as −

    Total Memory =sizeof(structure)+(number of elements x sizeof(element type))

    Here, sizeof(structure) gives the memory for the fixed members, and (number of elements x sizeof(element type)) gives the memory for the flexible array. Adding them gives the total memory to allocate.

    Example 1: Allocating Memory for a Flexible Array Member

    Below is an example where we define a structure with one fixed member and a flexible array, and allocate memory for the flexible array.

    #include <stdio.h>#include <stdlib.h>structExample{int id;int arr[];// flexible array member };intmain(){int size =5;// Allocate memory for structure + flexible arraystructExample*e =malloc(sizeof(structExample)+ size *sizeof(int));if(e !=NULL){printf("Total allocated memory: %zu bytes\n",sizeof(structExample)+ size *sizeof(int));free(e);}return0;}

    Here, sizeof(struct Example) gives 4 bytes for the id member. Then, we calculate the memory for the flexible array: 5 x sizeof(int) = 20 bytes. The total memory allocated is 4 + 20 = 24 bytes. The output is −

    Total allocated memory: 24 bytes
    

    Example 2: Accessing a Flexible Array in a Structure

    In this example, we define a Student structure with one fixed member (id) and a flexible array member (marks). We allocate memory dynamically for the flexible array and access its elements. If we access elements beyond the allocated size it will cause undefined behavior, so we only access marks[0] to marks[2].

    #include <stdio.h>#include <stdlib.h>structStudent{int id;int marks[];// flexible array member};intmain(){int subjects =3;// Allocate memory for structure + flexible arraystructStudent*s =malloc(sizeof(structStudent)+ subjects *sizeof(int));if(s !=NULL){
    
        s-&gt;id =102;
        s-&gt;marks&#91;0]=80;
        s-&gt;marks&#91;1]=75;
        s-&gt;marks&#91;2]=88;printf("Student ID: %d\n", s-&gt;id);printf("Marks: %d, %d, %d\n", s-&gt;marks&#91;0], s-&gt;marks&#91;1], s-&gt;marks&#91;2]);printf("Total allocated memory: %zu bytes\n",sizeof(structStudent)+ subjects *sizeof(int));free(s);}return0;}</code></pre>

    Following is the output of the above program −

    Student ID: 102
    Marks: 80, 75, 88
    Total allocated memory: 16 bytes
    

    Dynamic Resizing of Flexible Array Members

    Flexible arrays can be resized using the realloc() function. This function either expands the existing memory block or allocates a new block and copies the existing data automatically.

    To resize a flexible array, we call realloc() function with the new total memory size using the formula −

    sizeof(structure)+(new_number_of_elements x sizeof(element_type))

    Note: Always store the result of realloc() function in a temporary pointer. If it fails, your original pointer remains safe. Also, update the size counter after resizing and initialize only the newly added elements.

    Example 3: Dynamic Resizing of Flexible Array Members

    In this example, we create a Student structure to hold 3 marks initially. Later, we resize the flexible array to hold 6 marks using realloc(). The marks we already stored in remain unchanged, so we don't need to copy them manually.

    #include <stdio.h>#include <stdlib.h>structStudent{int id;int count;// This field helps track current array sizeint marks[];// Flexible array member};intmain(){// Step 1: Initial allocation for 3 marksint initial_elements =3;structStudent*s =malloc(sizeof(structStudent)+ initial_elements *sizeof(int));if(s !=NULL){
    
        s-&gt;id =101;
        s-&gt;count = initial_elements;// Store current array size// Display initial sizesprintf("Structure size: %zu bytes\n",sizeof(structStudent));// Output: 8 bytes (id + count)printf("Initial array elements: %d\n", s-&gt;count);// Output: 3printf("Initial total memory: %zu bytes\n",sizeof(structStudent)+ initial_elements *sizeof(int));// Output: 20 bytes// Step 2: Resize to hold 6 marksint new_elements =6;structStudent*temp =realloc(s,sizeof(structStudent)+ new_elements *sizeof(int));if(temp !=NULL){
            s = temp;
            s-&gt;count = new_elements;// Update array size tracker// Display new sizesprintf("\nAfter resizing:\n");printf("Structure size: %zu bytes\n",sizeof(structStudent));// Still 8 bytesprintf("New array elements: %d\n", s-&gt;count);// Output: 6printf("New total memory: %zu bytes\n",sizeof(structStudent)+ new_elements *sizeof(int));// Output: 32 bytes}free(s);}return0;}</code></pre>

    Below is the output showing the structure size and the total memory for both the initial and resized flexible array.

    Structure size: 8 bytes
    Initial array elements: 3
    Initial total memory: 20 bytes
    
    After resizing:
    Structure size: 8 bytes
    New array elements: 6
    New total memory: 32 bytes
    

    In this chapter, we learned about flexible array members in C structures. They are declared at the end of a structure and handle variable-length data, save memory, and adapt to different data sizes easily. We also saw how to allocate, access, and resize them.

  • Typedef

    C typedef

    The C programming language provides a keyword called typedef to set an alternate name to an existing data type. The typedef keyword in C is very useful in assigning a convenient alias to a built-in data type as well as any derived data type such as a struct, a union or a pointer.

    Sometimes it becomes clumsy to use a data type with a longer name (such as “struct structname” or “unsigned int“) every time a variable is declared. In such cases, we can assign a handy shortcut to make the code more readable.

    typedef Syntax

    In general, the typedef keyword is used as follows −

    typedef existing_type new_type;

    typedef Examples

    In this chapter, let us have a look at some of the use-cases of typedef.

    Example 1

    In C language, the keyword “unsigned” is used to declare unsigned integer variables that can store only non-negative values.

    C also has a keyword called “short” that declares an integer data type that occupies 2 bytes of memory. If you want to declare a variable that is short and can have only non-negative values, then you can combine both these keywords (unsigned and short):

    shortunsignedint x;

    If there are going to be many variables to be declared of this type, it will not be very convenient to use these three keywords every time. Instead, you can define an alias or a shortcut with the typedef keyword as follows −

    typedefshortunsignedint USHORT;

    This tells the compiler that the identifier USHORT corresponds to “short unsigned int” type. Hereafter, you can use USHORT in the variable declaration statement −

    USHORT x;

    Example 2

    C also has the keyword static to indicate that such a variable is initialized only once. The keyword “long” allocates 8 bytes to store an integer on a 64-bit system. We can declare a variable of this type as follows −

    staticunsignedlong x;

    However, we cant use the keyword “static” in a “typedef” statement, however we can use typedef to assign a shortcut alias to this type of declaration −

    typedefsignedlong SLONG;static SLONG x;

    Note: As a convention, the alias names are expressed in uppercase, just to differentiate between the built-in type and the aliases used.

    Example 3

    The following example demonstrates how you can use alias names in a C program −

    #include <stdio.h>intmain(){typedefshortunsignedint USHORT;typedefsignedlongint SLONG;static SLONG x =100;
       USHORT y =200;printf("Size of SLONG: %d \nSize of USHORT: %d",sizeof(SLONG),sizeof(USHORT));return0;}

    Output

    When you run this code, it will produce the following ouput −

    Size of SLONG: 8 
    Size of USHORT: 2
    

    Defining a Structure using Typedef

    Normally, we need to declare a struct variable by prefixing the name of struct_type in the declaration statement as −

    structstruct_type var;

    If writing the type name in this manner feels cumbersome, then you can use typedef to assign an alias −

    typedefstructstruct_type ALIAS;

    Example

    In this example, we define a structure type and then use the typedef keyword to set an alias for it −

    #include <stdio.h>intmain(){typedefunsignedlongint ULONG;typedefshortint SHORT;structmystruct{
    
      ULONG a;
      SHORT b;};typedefstructmystruct STR;
    STR s1 ={10,20};printf("%ld %u", s1.a, s1.b);return0;}

    Output

    Run the code and check its output −

    10 20
    

    There is an alternate approach to use the typedef keyword. We can combine it in the structure definition itself, as given below −

    typedefstructmystruct{
       ULONG a;
       SHORT b;} STR;
    
    STR s1 ={10,20};

    Typedef for Struct Pointer

    The typedef keyword may also be used to assign a new identifier to any pointer type. Normally, we declare a pointer variable as follows −

    structmystruct* x;

    Instead, we can use the typedef keyword as follows −

    typedefstructmystruct{
       ULONG a;
       SHORT b;} STR;typedef STR * strptr;

    It allows you to declare a pointer of this type in a much more concise way −

    strptr ptr;

    We can then assign the address of a corresponding struct variable to the pointer.

    Example

    The following example shows how you can use typedef to create a struct prointer −

    #include <stdio.h>intmain(){typedefunsignedlongint ULONG;typedefshortint SHORT;typedefstructmystruct{
    
      ULONG a;
      SHORT b;} STR;
    STR s1 ={10,20};typedef STR * strptr; strptr ptr =&s1;printf("%d %d \n", s1.a, s1.b);printf("%d %d", ptr->a, ptr->b);return0;}

    Output

    When you run this code, it will produce the following output −

    10 20 
    10 20
    

    Typedef for Union

    We can use the typedef keyword to assign a shortcut alias to any union type.

    Example

    The following example illustrates how you can use typedef in creating unions −

    #include <stdio.h>intmain(){typedefunsignedlongint ULONG;typedefshortint SHORT;typedefunion myunion {char a;int b;double c;} UNTYPE;
    
       UNTYPE u1;
       u1.c =65.50;typedef UNTYPE * UNPTR;
       UNPTR ptr =&u1;printf("a:%c b: %d c: %lf \n", u1.a, u1.b, u1.c);printf("a:%c b: %d c: %lf \n", ptr->a, ptr->b, ptr->c);return0;}

    Output

    Run the code and check its output −

    a: b: 0 c: 65.500000
    a: b: 0 c: 65.500000
    

    typedef vs #define in C

    The typedef keyword is often confused with the #define directive. In C language, #define is a preprocessor directive. It is an effective method to define a constant.

    The syntax of using #define is as follows −

    #define name value

    For example −

    #define PI 3.14159265359

    The #define statement can also be used to define a macro −

    #define SQUARE(x) x*x

    A macro works like a function. However, the value is substituted at the preprocessor level when called.

    printf("%d",SQUARE(5));

    #define is a preprocessor directive, while typedef is evaluated at the time of compilation.

    • typedef is limited to giving symbolic names to types only. #define can be used to define alias for values as well. For example, you can define “1” as “ONE”.
    • typedef interpretation is performed by the compiler, whereas #define statements are processed by the pre-processor.

    Example

    In the following code, we use both these features (typedef and #define) −

    #include <stdio.h>#define MAX 10intmain(){typedefunsignedlongint ULONG;typedefshortint SHORT;typedefstructemployee{char name[MAX];int age;} EMP;
    
       EMP e1 ={"Krishna",25};printf("Name: %s \nAge: %d", e1.name, e1.age);return0;}

    Output

    When you run this code, it will produce the following output −

    Name: Krishna 
    Age: 25
    
  • Bit Fields

    When we declare a struct or a union type, the size of the struct/union type variable depends on the individual size of its elements. Instead of the default memory size, you can set the size the bits to restrict the size. The specified size is called bit fields.

    This is the syntax you would use to declare a bit field −

    struct{
       data_type elem : width;};

    Let’s suppose your C program contains a number of TRUE/FALSE variables grouped in a structure called status, as follows −

    struct{unsignedint widthValidated;unsignedint heightValidated;} status;

    This structure requires 8 bytes of memory space but in actual, we are going to store either “0” or “1” in each of the variables. The C programming language offers a better way to utilize the memory space in such situations.

    If you are using such variables inside a structure, then you can define the width of the variables which tells the C compiler that you are going to use only those many number of bytes. For example, the above structure can be re-written as follows −

    struct{unsignedint widthValidated :1;unsignedint heightValidated :1;} status;

    The above structure requires 4 bytes of memory space for the status variable, but only 2 bits will be used to store the values.

    Example

    If you will use up to 32 variables, each one with a width of 1 bit, then also the status structure will use 4 bytes. However, as soon as you have 33 variables, it will allocate the next slot of the memory and it will start using 8 bytes.

    Let us check the following example to understand the concept −

    #include <stdio.h>/* define simple structure */struct{unsignedint widthValidated;unsignedint heightValidated;} status1;/* define a structure with bit fields */struct{unsignedint widthValidated :1;unsignedint heightValidated :1;} status2;intmain(){printf("Memory size occupied by status1: %d\n",sizeof(status1));printf("Memory size occupied by status2: %d\n",sizeof(status2));return0;}

    Output

    When the above code is compiled and executed, it produces the following Output −

    Memory size occupied by status1: 8
    Memory size occupied by status2: 4
    

    Bit Field Declaration

    The declaration of a bit-field has the following form inside a structure −

    struct{
       type [member_name]: width ;};

    The following table describes the variable elements of a bit field −

    ElementDescription
    typeAn integer type that determines how a bit-field’s value is interpreted.The type may be int, signed int, or unsigned int.
    member_nameThe name of the bit-field.
    widthNumber of bits in the bit-field. “width” must be less than or equal to bit width of specified type.

    The variables defined with a predefined width are called bit fields. A bit field can hold more than a single bit; for example, if you need a variable to store a value from 0 to 7, then you can define a bit field with a width of 3 bits, as follows −

    struct{unsignedint age :3;} Age;

    The above structure definition instructs the C compiler that the variable “Age” is going to use only 3 bits to store the value. If you try to use more than 3 bits, then it will not allow you to do so.

    Example

    Let us try the following example −

    #include <stdio.h>struct{unsignedint age :3;} Age;intmain(){
    
       Age.age =4;printf("Sizeof(Age): %d\n",sizeof(Age));printf("Age.age: %d\n", Age.age);
    
       Age.age =7;printf("Age.age : %d\n", Age.age);
    
       Age.age =8;printf("Age.age : %d\n", Age.age);return0;}

    Output

    When the above code is compiled, it will compile with a warning −

    warning: unsigned conversion from 'int' to 'unsigned char:3' changes value from '8' to '0' [-Woverflow]|
    

    And, when executed, it will produce the following output −

    Sizeof(Age): 4
    Age.age: 4
    Age.age: 7
    Age.age: 0
    

    You can use Bit Fields in situations where the available storage is limited. Bit Fields also prove efficient when devices transmit status or information encoded into multiple bits. Whenever certain encryption programs need to access the bits within a byte, bit fields are used to define the data structure.

  • Unions

    Unions in C

    union is a special data type available in C that allows to store different data types in the same memory location. You can define a union with many members, but only one member can contain a value at any given time. Unions provide an efficient way of using the same memory location for multiple purpose.

    All the members of a union share the same memory location. Therefore, if we need to use the same memory location for two or more members, then union is the best data type for that. The largest union member defines the size of the union.

    Defining a Union

    Union variables are created in same manner as structure variables. The keyword union is used to define unions in C language.

    Syntax

    Here is the syntax to define a union in C language −

    union[union tag]{
       member definition;
       member definition;...
       member definition;}[one or more union variables];

    The “union tag” is optional and each member definition is a normal variable definition, such as “int i;” or “float f;” or any other valid variable definition.

    At the end of the union’s definition, before the final semicolon, you can specify one or more union variables.

    Accessing the Union Members

    To access any member of a union, we use the member access operator (.). The member access operator is coded as a period between the union variable name and the union member that we wish to access. You would use the keyword union to define variables of union type.

    Syntax

    Here is the syntax to access the members of a union in C language −

    union_name.member_name;

    Initialization of Union Members

    You can initialize the members of the union by assigning the value to them using the assignment (=) operator.

    Syntax

    Here is the syntax to initialize members of union −

    union_variable.member_name = value;

    Example

    The following code statement shows to initialization of the member “i” of union “data” −

    data.i =10;

    Examples of Union

    Example 1

    The following example shows how to use unions in a program −

    #include <stdio.h>#include <string.h>union Data{int i;float f;char str[20];};intmain(){union Data data;        
       
       data.i =10;
       data.f =220.5;strcpy(data.str,"C Programming");printf("data.i: %d \n", data.i);printf("data.f: %f \n", data.f);printf("data.str: %s \n", data.str);return0;}

    Output

    When the above code is compiled and executed, it produces the following result −

    data.i: 1917853763 
    data.f: 4122360580327794860452759994368.000000 
    data.str: C Programming
    

    Here, we can see that the values of i and f (members of the union) show garbage values because the final value assigned to the variable has occupied the memory location and this is the reason that the value of str member is getting printed very well.

    Example 2

    Now let’s look at the same example once again where we will use one variable at a time which is the main purpose of having unions −

    #include <stdio.h>#include <string.h>union Data{int i;float f;char str[20];};intmain(){union Data data;        
       
       data.i =10;printf("data.i: %d \n", data.i);
       
       data.f =220.5;printf("data.f: %f \n", data.f);strcpy(data.str,"C Programming");printf("data.str: %s \n", data.str);return0;}

    Output

    When the above code is compiled and executed, it produces the following result −

    data.i: 10 
    data.f: 220.500000 
    data.str: C Programming
    

    Here, the values of all the Union members are getting printed very well because one member is being used at a time.

    Size of a Union

    The size of a union is the size of its largest member. For example, if a union contains two members of char and int types. In that case, the size of the union will be the size of int because int is the largest member.

    You can use the sizeof() operator to get the size of a union.

    Example

    In the following example, we are printing the size of a union −

    #include <stdio.h>// Define a unionunion Data {int a;float b;char c[20];};intmain(){union Data data;// Printing the size of the each member of unionprintf("Size of a: %lu bytes\n",sizeof(data.a));printf("Size of b: %lu bytes\n",sizeof(data.b));printf("Size of c: %lu bytes\n",sizeof(data.c));// Printing the size of the unionprintf("Size of union: %lu bytes\n",sizeof(data));return0;}

    Output

    When you compile and execute the code, it will produce the following output −

    Size of a: 4 bytes
    Size of b: 4 bytes
    Size of c: 20 bytes
    Size of union: 20 bytes
    

    Difference between Structure and Union

    Both structures and unions are composite data types in C programming. The most significant difference between a structure and a union is the way they store their data. A structure stores each member in separate memory locations, whereas a union stores all its members in the same memory location.

    Here is a definition of union type called myunion −

    union myunion{int a;double b;char c;};

    The definition of a union is similar to the definition of a structure. A definition of “struct type mystruct” with the same elements looks like this −

    structmystruct{int a;double b;char c;};

    The main difference between a struct and a union is the size of the variables. The compiler allocates the memory to a struct variable, to be able to store the values for all the elements. In mystruct, there are three elements − an int, a double, and char, requiring 13 bytes (4 + 8 + 1). Hence, sizeof(struct mystruct) returns 13.

    On the other hand, for a union type variable, the compiler allocates a chunk of memory of the size enough to accommodate the element of the largest byte size. The myunion type has an int, a double and a char element. Out of the three elements, the size of the double variable is the largest, i.e., 8. Hence, sizeof(union myunion) returns 8.

    Another point to take into consideration is that a union variable can hold the value of only one its elements. When you assign value to one element, the other elements are undefined. If you try to use the other elements, it will result in some garbage.

    Example 1: Memory Occupied by a Union

    In the code below, we define a union type called Data having three members if, and str. A variable of type Data can store an integer, a floating point number, or a string of characters. It means a single variable, i.e., the same memory location, can be used to store multiple types of data. You can use any built-in or user-defined data types inside a union, as per your requirement.

    The memory occupied by a union will be large enough to hold the largest member of the union. For example, in the above example, Data will occupy 20 bytes of memory space because this is the maximum space which can be occupied by a character string.

    The following example displays the total memory size occupied by the above union −

    #include <stdio.h>#include <string.h>union Data{int i;float f;char str[20];};intmain(){union Data data;printf("Memory occupied by Union Data: %d \n",sizeof(data));return0;}

    Output

    When you compile and execute the code, it will produce the following output −

    Memory occupied by Union Data: 20
    

    Example 2: Memory Occupied by a Structure

    Now, let’s create a structure with the same elements and check how much space it occupies in the memory.

    #include <stdio.h>#include <string.h>structData{int i;float f;char str[20];};intmain(){structData data;printf("Memory occupied by Struct Data: %d \n",sizeof(data));return0;}

    Output

    This stucture will occupy 28 bytes (4 + 4 + 20). Run the code and check its output −

    Memory occupied by Struct Data: 28
    
  • Anonymous Structures and Unions

    Anonymous Structures and Unions in C

    The feature of anonymous structures and unions was introduced in C11 standard. The aim was to enhance the flexibility of C and also to discard the need of superfluous naming in certain cases.

    The feature of defining struct and union anonymously has been found to be extremely useful, especially in applications involving the creation of complex data structures, hardware register mappings, etc. This allows for more straightforward and readable code.

    Anonymous Structure

    An anonymous structure is a structure definition without a tag or a typedef. It is usually nested within another struct.

    Syntax for Anonymous Struct

    Here is the syntax of defining an anonymous structure −

    structtype{
       elem1;
       elem2;struct{
    
      elem3;
      elem4;};};</pre>

    Access Members of Anonymous Structure

    Members of an anonymous struct/union can access the parent struct directly, simplifying the notation.

    Example 1

    In the code below, we have defined a structure with the name as employee. Inside it, an anonymous struct is intended to hold date, month and year of birth of the employee. In the example on nested structure, we had an internal dob structure. Now well use anonymous struct.

    #include <stdio.h>structemployee{char name[10];float salary;struct{int d, m, y;};};intmain(){structemployee e1;strcpy(e1.name,"Kiran");
       e1.salary=25000;
       e1.d =12;
       e1.m =5;
       e1.y =1990;printf("Name: %s\n", e1.name);printf("Salary: %f\n", e1.salary);printf("Date of Birth: %d-%d-%d\n", e1.d, e1.m, e1.y);return0;}

    Output

    When you run this code, it will produce the following output −

    Name: Kiran
    Salary: 25000.000000
    Date of Birth: 12-5-1990
    

    Note that the "d", "m" and "y" elements of inner anonymous struct are accessed directly.

    Example 2

    The outer struct type in the following example is student, which has a nested anonymous struct to store the title and ID of the course.

    #include <stdio.h>structstudent{char name[10];int age;struct{char coursettl[20];int courseid;};};intmain(){structstudent s1;strcpy(s1.name,"Kiran");
       s1.age =27;strcpy(s1.coursettl,"C Programming");
       s1.courseid=1;printf("Name: %s\n", s1.name);printf("age: %d\n", s1.age);printf("Course Title: %s Course ID: %d\n", s1.coursettl, s1.courseid);return0;}

    Output

    When you run this code, it will produce the following output −

    Name: Kiran
    age: 27
    Course Title: C Programming Course ID: 1
    

    Anonymous Union

    An anonymous union is a special type of union that doesn't have a name. Unlike regular unions, anonymous unions are primarily used to create unnamed members that can be accessed directly without qualifying them with a union name.

    Syntax for Anonymous Union

    Here is the syntax of defining an anonymous union −

    structtype{
       elem1;
       elem2;union{
    
      elem3;
      elem4;};};</pre>

    Access Members of Anonymous Union

    The members of the anonymous union can be accessed directly without using a union name.

    Example

    Anonymous unions do not have a name. The elements share the same memory location.

    Take a look at the following example −

    #include <stdio.h>structmystruct{int var;union{int var1;float var2;char var3;};};intmain(){structmystruct data;
    
       data.var =10; 
       data.var2 =5.55;printf("mystruct.var: %d\n", data.var);printf("anonymous union elements: %d %f %c", data.var1, data.var2, data.var3);return0;}

    Output

    Run the code and check its output −

    mystruct.var: 10
    anonymous union elements: 1085381018 5.550000 
    

    Note: Like a regular union, the uninitialized members of an anonymous union variable also show garbage value.

    Advantages of Anonymous Struct and Union

    One of the main advantages is the ability to access the members directly without any inner struct or union name. This can make the code more readable. Here is a list of some other advantages of using anonymous structures and unions −

    • Memory Efficiency − Like regular unions, anonymous unions allow different data types to share the same memory space, leading to more memory-efficient code, especially useful in low-memory environments.
    • Flexibility − Anonymous structures provide flexibility in how data is represented and accessed, allowing for more dynamic and versatile data structures.
    • Convenience − This feature allows for a compact representation of a variable that can hold different data types.
    • Ease of Initialization − They can be easier to initialize and use, as they do not require the declaration of a union variable.

    Note that anonymous struct or union types were not part of the C standard before C11. Hence, their use in the code may lead to compatibility problems, if the target system uses a compiler compliant with earlier standards.

  • Nested Structures

    What is a Nested Structure in C?

    A structure within a structure is known as nested structure in C. When one of the elements in the definition of a struct type is of another struct type, then we call it a nested structure in C. Nested structures are defined when one of the elements of a struct type is itself a composite representation of one or more types.

    Nested Structure Declaration

    A nested structure can be declared by using a structure as a member of another structure.

    Syntax

    A general syntax of a nested structure is as follows −

    structstruct1{
       type var1;
       type var2;structstruct2 strvar;}

    Example

    We can think of nested structures in the following situation. If we want to define a struct type representing a “student” with “name” and “age” as its elements and another struct type called “course” that is characterized by “course ID”, “title”, and “credit points”. Here, the “student” structure has an inner course structure.

    structstudent{char*name;int age;structcourse c1;};

    The “student” variable will be stored in the memory as follows −

    NameAgeCourse
    Kiran25001C Programming6

    Accessing Members of a Nested Structure

    The structure’s members can be accessed by using the dot (.) operator. In the case of nested structures, there can be multiple levels of structures. So, you need to use the dot (.) operator for each level of the structure to access the members of the nested structure.

    Syntax

    Below is the syntax to access members of nested structure –

    level1.level2.member_name;

    Here, level1 represents the structure variable of the outer (parent) structure, and level2 represents the structure variable of the inner (child) structure.

    Example

    Consider the following nested structure definition –

    structemployee{char name[10];float salary;structdob{int d, m, y;} d1;}e1;

    Here, e1 is the structure variable of the outer (level 1) structure “employee” and d1 is the structure variable of the inner (level 2) structure “dob”.

    To access the members of the employee structure, use e1.member_name.

    printf("Name: %s\n", e1.name);printf("Salary: %f\n", e1.salary);

    To access the members of the dob structure, use e1.d1.member_name.

    printf("Date of Birth: %d-%d-%d\n", e1.d1.d, e1.d1.m, e1.d1.y);

    The nesting of structures can be performed in two different ways −

    • Defining an inline structure
    • Including the element of a structure already defined

    Let us learn these methods using suitable examples.

    Nested Structure by Defining Inline Structure

    In this method, we shall define an “employee” data type with one of its elements being “date of birth”. C doesnt have a built-in type for “date”. We shall declare the “dob” struct with three “int” types “d”, “m” and “y” inside the “employee” structure and its variable “d1” is one of the elements of the outer type.

    Example

    Take a look at the following example −

    #include <stdio.h>#include <string.h>structemployee{char name[10];float salary;structdob{int d, m, y;} d1;};intmain(){structemployee e1 ={"Kiran",25000,{12,5,1990}};printf("Name: %s\n", e1.name);printf("Salary: %f\n", e1.salary);printf("Date of Birth: %d-%d-%d\n", e1.d1.d, e1.d1.m, e1.d1.y);return0;}

    Output

    When you run this code, it will produce the following output −

    Name: Kiran
    Salary: 25000.000000
    Date of Birth: 12-5-1990
    

    You can see that the variable of “employee” type is initialized with its “date” element having another pair of curly brackets.

    Nested Structure by Defining Separate Structure

    The other approach for using nested structures is to define the inner struct type first, and then use its variable as one of the elements in the outer struct type, which is defined afterwards.

    Here, the “dob” type is defined in the beginning; it has three “int” elements − dm and y. The “employee” struct type is defined afterwards.

    Example 1

    Since “dob” is already defined, we can have an element of its type inside “employee”.

    #include <stdio.h>#include <string.h>structdob{int d, m, y;};structemployee{char name[10];float salary;structdob d1;};intmain(){structemployee e1 ={"Kiran",25000,{12,5,1990}};printf("Name: %s\n", e1.name);printf("Salary: %f\n", e1.salary);printf("Date of Birth: %d-%d-%d\n", e1.d1.d, e1.d1.m, e1.d1.y);return0;}

    Output

    Run the code and check its output −

    Name: Kiran
    Salary: 25000.000000
    Date of Birth: 12-5-1990
    

    Note that the inner struct type should be defined before the outer type. We can also declare a variable of “dob” type and then include it in the initialization of “employee” type variable, as shown below −

    structdob d1 ={12,5,1990};structemployee e1 ={"Kiran",25000, d1};

    Example 2

    In the following code, the nesting of structure goes upto two levels. In other words, the outer struct type “employee” has one element that is a variable of experience struct type. In turn, the experience structure has two elements of another struct type called “date”.

    Hence, the memory allocation for “employee” variable can be understood with the following illustration −

    NameSalaryDesignationfromto
    Kiran25000Clerk12519903132021

    Here is the complete code −

    #include <stdio.h>#include <string.h>structdate{int d, m, y;};structexperience{char designation[10];structdate from;structdate to;};structemployee{char name[10];float salary;structexperience exp;};intmain(){structdate d1 ={12,5,1990};structdate d2 ={31,3,2021};structexperience exp ={"Clerk", d1, d2};structemployee e1 ={"Kiran",25000, exp};printf("Name: %s\n", e1.name);printf("Salary: %f\n", e1.salary);printf("Experience: Designation: %s\n", e1.exp.designation);printf("From:  %d-%d-%d\n", e1.exp.from.d,e1.exp.from.m, e1.exp.from.y);printf("To: %d-%d-%d\n", e1.exp.to.d, e1.exp.to.m, e1.exp.to.y );return0;}

    Output

    When you run this code, it will produce the following output −

    Name: Kiran
    Salary: 25000.000000
    Experience: Designation: Clerk
    From:  12-5-1990
    To: 31-3-2021
    

    Pointer to Nested Structure

    We know that the address of a struct variable can be stored in a pointer variable. Further, C uses the indirection operator (→) to access the elements of a variable that is referenced by a pointer.

    In case of a nested structure, the elements of the inner struct elements are accessed by “ptr → inner_struct_var.element;”

    Example

    In this example, we have declared a pointer ptr to an employee struct variable. the date, month and year elements of inner dob struct variable are accessed as “ptr -> d1.d”, “ptr -> d1.m” and “ptr -> d1.y” expressions.

    #include <stdio.h>#include <string.h>structemployee{char name[10];float salary;structdob{int d, m, y;} d1;};intmain(){structemployee e1 ={"Kiran",25000,{12,5,1990}};structemployee*ptr =&e1;printf("Name: %s\n", ptr -> name);printf("Salary: %f\n", ptr -> salary);printf("Date of Birth: %d-%d-%d\n", ptr -> d1.d, ptr -> d1.m, ptr -> d1.y);return0;}

    Output

    When you run this code, it will produce the following output −

    Name: Kiran
    Salary: 25000.000000
    Date of Birth: 12-5-1990
    
  • Structure Padding and Packing

    What is Structure Padding in C?

    Structure padding in C is the process that is handled by the CPU architecture. Structure Padding adds a certain number of empty bytes within a structure so that the data members are naturally aligned in memory. The alignment requirements are determined by the processor architecture rather than the language itself. Naturally, the alignment requirements change as per the data bus size or other architectural considerations of a certain CPU architecture.

    Understanding Structure Padding with Examples

    Let us define a struct type as follows −

    structstruct1{char x;int y;char z;};

    Example 1

    Let us check the size in bytes required for a variable of this type −

    #include <stdio.h>structstruct1{char a;char b;int c;};intmain(){printf("Size: %d",sizeof(structstruct1));return0;}

    Output

    When you run this code, it will produce the following output −

    Size: 8
    

    The result is contrary to the expectation.

    Considering that a char type needs 1 byte, and an int type needs 4 bytes, one might assume the output should be “1 + 1 + 4 = 6 bytes”.

    Structure Padding

    However, the CPU architecture necessitates altering this structure. Considering that we are using a CPU with 32-bit processor, it reads 4 bytes at a time, which means that 1 word is equal to 4 bytes.

    In one CPU cycle, it accesses the char “a”, then char “b” and the first two bytes of int “c”. In the second cycle, the other two bytes are accessed.

    Even if we want to read only “c”, two CPU cycles are required. For this purpose, the CPU adds two empty bytes before the bytes in which the value of “c” is stored. This mechanism is called as padding.

    Structure Padding

    This now explains the result we obtained above, that of the size of the struct type to be 8 bytes.

    Example 2

    Let us change the sequence of members in the above struct type, and set the type of “b” and the type of “c“.

    #include <stdio.h>structstruct1{char a;int b;char c;};intmain(){printf("size: %d",sizeof(structstruct1));return0;}

    Output

    Run the code and check its output −

    size: 12
    

    Out of the 4 bytes in the first word, the first one is allocated to char “a” followed by three empty bytes.

    The next 4 bytes that form the next word are used to store int “b”. Subsequently, out of the next group of 4, only one is utilized for “c”. However, the struct size is 12.

    What is Structure Packing in C?

    Structure packing, on the other hand, is a mechanism for minimizing the effect of padding, thereby trying and reduce wasted memory space. We can use certain pragma directives and attributes to achieve packing.

    Understanding Structure Packing with Examples

    The padding, forced by the CPU architecture is unavoidable, however there are ways to minimize padding. It can be done with the use of −

    • Using #pragma pack(1) directive
    • Using packed attribute

    Using #pragma pack(1) Directive

    The #pragma pack(1) preprocessor directive forces the compiler to disregard the padding, and align the structure members end to end during the memory allocation process.

    Example

    Let’s add this directive at the top of the code used previously, and see the result −

    #include <stdio.h>#pragma pack(1)structstruct1{char a;int b;char c;};intmain(){printf("size: %d",sizeof(structstruct1));return0;}
    Output

    Run the code and check its output −

    size: 6
    

    We can see the structure padding has been avoided and reduced memory wastage.

    Using __attribute__((packed))

    With GCC, we can use attributes to specify various special properties of structure and union types. The attributes are: aligned, deprecated, packed, transparent_union, unused, and visibility. The syntax of applying these attributes is “__attribute__ ((…))“.

    Example

    Here, we are going to use packed attribute in the definition of our struct type.

    #include <stdio.h>struct__attribute__((packed))struct1{char a;int b;char c;};intmain(){printf("size: %d",sizeof(structstruct1));return0;}
    Output

    Run the code and check its output −

    size: 6
    

    This method also avoids the effect of padding.

  • Enumeration

    Enumeration (or enum) in C

    C enumeration (enum) is an enumerated data type that consists of a group of integral constants. Enums are useful when you want to assign user-defined names to integral constants. The enum keyword is used to define enums.

    Defining and Declaring an Enum Type

    To declare and define an enumeration (enum) data type, use the enum keyword followed by the enum name and assign all values inside the curly braces.

    Syntax

    This is the syntax you would use to define an enum type −

    enumenum_name{const1, const2,...};

    Enum Variable Declaration

    After declaring an enumeration type, you can also declare its variable to access enumeration members (constants). To declare an enum variable, write the enum keyword followed by the enaum name (enum_name) and then the variable name (var).

    Syntax

    Below is the syntax to declare a variable of enum type −

    enumenum_name var;

    Example

    Let us define an enum type with the name myenum −

    enummyenum{val1, val2, val3, val4};

    Identifier values are unsigned integers and they start from “0”. val1 refers 0, val2 refers 1, and so on.

    A variable of myenum type is declared as follows −

    enummyenum var;

    The possible constant values of myenum type are enumerated inside the curly brackets.

    Change Enum Constants Values

    When we declare an enum type, the first constant of the enum is initialized with 0 by default, the second constant is initialized with 1, and so on. We can also assign any integer value to any constant of enum.

    Example

    In the following example, we are declaring an enum type and assigning different values to the enum constants.

    #include <stdio.h>enumstatus_codes{ OKAY =1, CANCEL =0, ALERT =2};intmain(){// Printing valuesprintf("OKAY = %d\n", OKAY);printf("CANCEL = %d\n", CANCEL);printf("ALERT = %d\n", ALERT);return0;}

    Output

    It will produce the following output −

    OKAY = 1
    CANCEL = 0
    ALERT = 2
    

    Enum in Switch Statements

    C language switch case statement works with integral values. We can use enum type to define constants with (or, without) integral type values to use them in switch case statements.

    Example

    In the following example, the colors in the rainbow are enumerated.

    #include <stdio.h>// Enum declarationenumcolors{ VIOLET, INDIGO, BLUE, GREEN, YELLOW, ORANGE, RED };intmain(){// Enum variable declarationenumcolors color = YELLOW;// switch statement using enumswitch(color){case BLUE:printf("Blue color");break;case GREEN:printf("Green color");break;case RED:printf("Red color");break;default:printf("Color other than RGB");}return0;}

    Output

    Run the code and check its output −

    Color other than RGB
    

    Examples of Enumeration (enum) Type

    Practice the following examples to understand the concept of enumeration (or, enum) type in C programming language.

    Example 1: Enum Constants Get Incrementing Integer Values

    C assigns incrementing integer values to each constant, starting with “0”. When we assign val2 to the above variable,

    var = val2;

    The integer value assigned to val2 is 1. Take a look at the following example −

    #include <stdio.h>enummyenum{val1, val2, val3, val4};intmain(){enummyenum var;
       var = val2;printf("var = %d", var);return0;}

    Output

    It will produce the following output −

    var = 1
    

    Example 2: Enumerating the Weekdays

    Let us declare an enum type weekdays to enumerate the names of the days and assign its value to the enum type variable −

    #include <stdio.h>intmain(){enumweekdays{Sun, Mon, Tue, Wed, Thu, Fri, Sat};printf("Monday = %d\n", Mon);printf("Thursday = %d\n", Thu);printf("Sunday = %d\n", Sun);}

    Output

    It will produce the following output −

    Monday = 1
    Thursday = 4
    Sunday = 0
    

    Example 3: Declaring a Variable of Enum Type

    A typical application of enumerated data types is to assign weekdays to the integer values. In this code, we have declared a variable of weekdays enum type −

    #include <stdio.h>enumweekdays{Sun, Mon, Tue, Wed, Thu, Fri, Sat};intmain(){enumweekdays day;
       day = Wed;printf("Day number of Wed is = %d", day);return0;}

    Output

    When you run this code, it will produce the following output −

    Day number of Wed is = 3
    

    Example 4: Enum Values By Default Start from “0”

    In this code, we define an enum type for names of the month in a calendar year. By default, C assigns the values starting from “0”. For example, in the following C program, Jan gets the value “0”, Feb gets “1”, and so on.

    #include <stdio.h>enummonths{Jan, Feb, Mar, Apr, May, Jun, Jul, 
       Aug, Sep, Oct, Nov, Dec};intmain(){for(int i = Jan; i <= Dec; i++)printf("Month No: %d\n", i);return0;}

    Output

    Run the code and check its output −

    Month No: 0
    Month No: 1
    Month No: 2
    Month No: 3
    Month No: 4
    Month No: 5
    Month No: 6
    Month No: 7
    Month No: 8
    Month No: 9
    Month No: 10
    Month No: 11
    

    Example 5: Starting an Enum from 1

    To start enumeration from 1, assign 1 explicitly to the first value, the compiler will assign incrementing numbers to the subsequent values.

    #include <stdio.h>enummonths{Jan=1, Feb, Mar, Apr, May, Jun, Jul, 
       Aug, Sep, Oct, Nov, Dec};intmain(){for(int i = Jan; i <= Dec; i++)printf("Month No: %d\n", i);return0;}

    Output

    Run the code and check its output −

    Month No: 1
    Month No: 2
    Month No: 3
    Month No: 4
    Month No: 5
    Month No: 6
    Month No: 7
    Month No: 8
    Month No: 9
    Month No: 10
    Month No: 11
    Month No: 12
    

    Example 6: Enumerating HTTP Status Codes

    It is not necessary that the constants in the enum type should have incremental integer values. You can assign each of the constants a different and unique value, which may be in any sequence.

    In the following code, the enum type enumerates HTTP status codes.

    #include <stdio.h>enumstatus{
       OK =200,
       BadRequest =400,
       Unauthorized =401,
       Forbidden =403,
       NotFound =404,
       InternalServerError =500,};intmain(){enumstatus code =  InternalServerError;if(code ==500)printf("Internal Server Error has been encountered");return0;}

    Output

    Run the code and check its output −

    Internal Server Error has been encountered
    

    Example 7: Assigning a Fixed Number to Enum Constants

    You can assign a fixed integer to some of the constants, leaving the compiler to assign values to others. All unassigned names get a value that is “value of previous name plus one”.

    #include <stdio.h>enummyenum{a, b =5, c, d, e =10, f};intmain(){printf("a: %d\n", a);printf("b: %d\n", b);printf("c: %d\n", c);printf("d: %d\n", d);printf("e: %d\n", e);printf("f: %d\n", f);return0;}

    Output

    Run the code and check its output −

    a: 0
    b: 5
    c: 6
    d: 7
    e: 10
    f: 11
    

    Applications of Enums

    We can use enums in C in different cases, some of which are listed below −

    • To store constant values, for example, weekdays, months, directions, colors, etc.
    • Enums are used for using flags, status codes, etc.
    • Enums can be used while using switch-case statements in C.
  • Lookup Tables

    Lookup tables (popularly known by the abbreviation LUT) in C are arrays populated with certain pre-computed values. Lookup tables help avoid performing a lot of calculations in a program. Instead of lengthy nested if-else statements or switch statements, one can use Lookup tables to enhance the efficiency of a C program.

    Example 1

    Let us have a look at a simple application of a lookup table. In the following code, we compute the square of a given integer.

    #include <stdio.h>intsquare(int x){return x*x;}intmain(){int num[5]={1,2,3,4,5};for(int i =0; i <=4; i++){printf("No: %d \tSquare(%d): %d\n", i+1, i+1,square(i+1));}return0;}

    Output

    When you run this code, it will produce the following output −

    No: 1 	Square(1): 1
    No: 2 	Square(2): 4
    No: 3 	Square(3): 9
    No: 4 	Square(4): 16
    No: 5 	Square(5): 25
    

    Example 2

    While the above program works satisfactorily, it involves frequent calls to the square() function, for each value of the array index.

    Instead, we can declare an array to store the squares of numbers and access the computed square directly from the index.

    #include <stdio.h>intmain(){int squares[5]={1,4,9,16,25};for(int i =0; i <=4; i++){printf("No: %d \tSquare(%d): %d\n", i+1, i+1, squares[i]);}return0;}

    Output

    Run the code and check its output −

    No: 1 	Square(1): 1
    No: 2 	Square(2): 4
    No: 3 	Square(3): 9
    No: 4 	Square(4): 16
    No: 5 	Square(5): 25
    

    Example 3

    In the example below, we fetch the name of the element corresponding to its atomic number.

    # include <stdio.h>intmain(){int num =3;switch(num){case1:puts("Hydrogen");break;case2:puts("Helium");break;case3:puts("Lithium");break;case4:puts("Beryllium");break;case5:puts("Boron");break;default:puts("error: unknown element!");}return0;}

    Output

    It will produce the following output −

    Lithium
    

    Example 4

    Instead of a lengthy switch statements with a case for each element, we use a lookup table (an array populated with the names of all the elements) to simplify the program −

    #include <stdio.h>staticconstchar*table[]={"Hydrogen","Helium","Lithium","Beryllium","Boron"};intmain(){int num =3;if(num >=1&& num <=5){printf("Name of the element with atomic number %d is %s", num, table[num-1]);}else{puts("error: Atomic number not in the lookup table!");}return0;}

    Output

    Run the code and check its output −

    Name of the element with atomic number 3 is Lithium
    

    Lookup Tables in 7-Segment LED Display

    Lookup tables are extensively used in the design of embedded systems, as they enhance the performance of the application.

    In many devices, the seven-segment LED display is used to show visual output. Eight segments of the unit are illuminated based on a sequence of binary digits. We use a lookup table to convert numbers ranging from 0 to 9 to seven-segment signals to drive a display.

    Example

    The 7-segment binary code for the number is stored as an array element. The hexadecimal code is then converted to binary code that will drive the 7-segment display.

    #include <stdio.h>intmain(){// Array to represent numbers 0-9 in 7-segment display binary encodingintconst nums[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};staticint bin[8];int i =0, num =7,  rem;printf("The binary equivalent of 7 is: ");for(i =7; i >=0; i--){
    
      rem = num %2;
      bin&#91;i]= rem;
      num /=2;}for(i =0; i &lt;=7; i++){printf("%d", bin&#91;i]);if(i ==3)printf(" ");}return0;}</code></pre>

    Output

    Run the code and check its output −

    The binary equivalent of 7 is: 0000 0111
    

    The least significant bits represent segments "a", "b", "c" and "d". The most significant bits are "e", "f", "g" and "h". The segments "a", "b" and "c" illuminate to display 7, leaving the other segments off.