Author: saqibkhan

  • Dynamic Array Resizing

    Dynamic arrays are arrays whose size can change during runtime. To manage dynamic arrays in C, memory is allocated at runtime using pointers and functions from the <stdlib.h> header file. Specifically, malloc() and calloc() are the two primary functions used to allocate memory for dynamic arrays.

    • malloc() (memory allocation) allocates a specified number of bytes of memory but does not initialize them.
    • calloc() (contiguous allocation) allocates memory for an array of elements and initializes all the bytes to zero.

    In this chapter, we will see how we can resize this allocated memory for dynamic arrays.

    Dynamic Array Resizing

    We can resize dynamic arrays by increasing or decreasing the dynamically allocated memory using the realloc() function. If more elements need to be stored, we can increase the size or if fewer elements are required, we can decrease it.

    We will look at how to resize memory using the realloc() function, and then how to free the allocated memory in C using −

    • realloc() function
    • free() function

    We will also look at −

    • Variable-Length Arrays (VLAs)
    • Array Members in Structures

    Next, we will go through each function in detail and see how it allocates and frees memory with examples.

    Dynamic Memory Using realloc() Function

    The realloc() funciton in C is used to resize a previously allocated memory block. It can increase or decrease the size of a dynamic array while preserving the existing data.

    Following is the syntax for reallocating memory using the realloc() function in C −

    ptr =(castType*)realloc(pointer, new_size_in_bytes);

    Here, ptr is the pointer to the previously allocated memory, castType is the type of the pointer (like int* or float*), and new_size_in_bytes is the new size of the memory block.

    Example of realloc() Function

    In this example, we first allocate memory for an array of three integers using malloc() funciton. Then, we resize the array to five integers using realloc() function, assign values, and print the array elements.

    #include <stdio.h>#include <stdlib.h>intmain(){int*numbersArray;int index;// Allocate memory for 3 integers
    
    numbersArray =(int*)malloc(3*sizeof(int));// Check if memory allocation was successfulif(numbersArray ==NULL){printf("Memory allocation failed\n");return1;}else{printf("Memory successfully allocated for 3 elements.\n");}// Assign values to the arrayfor(index =0; index &lt;3; index++){
        numbersArray&#91;index]=(index +1)*10;// Store 10, 20, 30}// Resize memory to hold 5 integers
    numbersArray =(int*)realloc(numbersArray,5*sizeof(int));// Check if memory reallocation was successfulif(numbersArray ==NULL){printf("Memory reallocation failed\n");return1;}else{printf("Memory successfully reallocated for 5 elements.\n");}// Assign values to the new elementsfor(index =3; index &lt;5; index++){
        numbersArray&#91;index]=(index +1)*10;// Store 40, 50}// Print all valuesprintf("Array elements after realloc: ");for(index =0; index &lt;5; index++){printf("%d ", numbersArray&#91;index]);}return0;}</code></pre>

    Following is the output of the above program where we reallocate the previously allocated array and display its elements.

    Memory successfully allocated for 3 elements.
    Memory successfully reallocated for 5 elements.
    Array elements after realloc: 10 20 30 40 50 
    

    free() Function

    The free() function in C is used to deallocate/release memory that was previously allocated dynamically using malloc()calloc(), or realloc(). This gives the memory back to the system and helps prevent memory leaks. After freeing, the pointer still exists, but the memory it points to is no longer valid, so don't use the pointer after freeing it.

    The syntax for using free() function is −

    free(pointer);

    Here, pointer is the pointer to the memory block we want to deallocate.

    Example of free() Function

    In this example, we use malloc() fucntion to allocate memory for an array of five integers. We store values in the array, display them, and then free the allocated memory using the free() funciton.

    #include <stdio.h>#include <stdlib.h>intmain(){int*numbersArray;// Pointer for dynamic arrayint index;// Allocate memory for 5 integers
    
    numbersArray =(int*)malloc(5*sizeof(int));// Check if memory allocation was successfulif(numbersArray ==NULL){printf("Memory allocation failed\n");return1;}else{printf("Memory successfully allocated for the array.\n");}// Assign values to the arrayfor(index =0; index &lt;5; index++){
        numbersArray&#91;index]=(index +1)*10;// Store 10, 20, 30, 40, 50}// Print the valuesprintf("Array elements: ");for(index =0; index &lt;5; index++){printf("%d ", numbersArray&#91;index]);}printf("\n");// Free the allocated memoryfree(numbersArray);printf("Memory has been freed.\n");return0;}</code></pre>

    Following is the output of the program showing the array allocation, its elements, and successful memory deallocation.

    Memory successfully allocated for the array.
    Array elements: 10 20 30 40 50 
    Memory has been freed.
    

    Variable-Length Arrays (VLAs)

    Variable-Length Arrays (VLAs) in C are arrays whose size is decided at runtime. Unlike dynamic arrays that are created on the heap using functions like malloc(), variable-length arrays are created on the stack, which means they are automatically freed when the function ends.

    Variable-Length Arrays are supported only in C99 and later standards, and their size must always be a positive value. It cannot be zero.

    Following is the syntax for declaring a variable length array in C −

    data_type arrayName[size];

    Here, data_type is the type of elements (like int or float), arrayName is the name of the array, and size is a value that is decided at runtime.

    Example of Variable-Length Array

    In this example, we decide the array size at runtime using a variable. The VLA numbers[size] is created on the stack. We store values in a loop and print them. The memory is automatically freed when the program ends.

    #include <stdio.h>intmain(){int size =5;// Size decided at runtimeint index;// Create a Variable-Length Arrayint numbers[size];// Store values in the arrayfor(index =0; index < size; index++){
    
        numbers&#91;index]=(index +1)*10;// Store 10, 20, 30, 40, 50}// Print the arrayprintf("Array elements: ");for(index =0; index &lt; size; index++){printf("%d ", numbers&#91;index]);}return0;}</code></pre>

    Following is the output of the above program that displays the elements of the variable length array.

    Array elements: 10 20 30 40 50
    

    Array Members in Structures

    Arrays can be members of structures in C. They can have a fixed size, which is allocated along with the structure, or be pointer-based, which requires dynamic memory allocation using malloc() or calloc() functions.

    Following is the syntax for a fixed-size array inside a structure in C −

    structStructName{
    
    data_type arrayName&#91;size];// other members};</code></pre>

    Here, data_type is the type of elements, arrayName is the name of the array, and size is determined at compile time.

    Following is the syntax for a pointer-based dynamic array inside a structure in C −

    structStructName{
    
    data_type *arrayName;int size;// store the array size// other members};</code></pre>

    Here, data_type *arrayName is a pointer to the array elements, and size stores the number of elements.

    Some important points to know −

    • Fixed-size arrays inside structures have memory allocated along with the structure.
    • Pointer-based arrays require dynamic memory allocation using malloc() or calloc() functions.
    • Always free dynamically allocated memory after use to prevent memory leaks.

    Example of Array as Structure Member

    In this example, we create a structure Student with a pointer to an array of marks. Memory is dynamically allocated for 3 subjects. Then, we store the values in the array and print them. Finally, we free the allocated memory.

    #include <stdio.h>#include <stdlib.h>structStudent{int*marks;// Pointer for dynamic arrayint subjects;};intmain(){structStudent s1;int i;
    
    s1.subjects =3;// Allocate memory for marks
    s1.marks =(int*)malloc(s1.subjects *sizeof(int));if(s1.marks ==NULL){printf("Memory allocation failed\n");return1;}// Store values in the arrayfor(i =0; i &lt; s1.subjects; i++){
        s1.marks&#91;i]=(i +1)*10;// 10, 20, 30}// Print the marksprintf("Marks of student: ");for(i =0; i &lt; s1.subjects; i++){printf("%d ", s1.marks&#91;i]);}// Free allocated memoryfree(s1.marks);return0;}</code></pre>

    Following is the output that displays the marks of the student −

    Marks of student: 10 20 30
    

    In this chapter, we learned how to resize arrays in C using realloc() function and free memory using free() function. We also covered variable-length arrays, arrays in structures, and overall memory management in C. We can also create a two-dimensional dynamic array in C, and there are different functions for this.

  • Storage Classes

    C storage classes define the scope (visibility) and lifetime of variables and/or functions within a C Program. They precede the type that they modify.

    We have four different storage classes in a C program −

    • auto
    • register
    • static
    • extern

    The auto Storage Class

    The auto is a default storage class for all variables that are declared inside a function or a block. The keyword “auto“, which is optional, can be used to define local variables.

    The scope and lifetime of auto variables are within the same block in which they are declared.

    Example of auto Storage Class

    The following code statements demonstrate the declaration of an automatic (auto) variable −

    {int mount;autoint month;}

    The example above defines two variables with in the same storage class. ‘auto’ can only be used within functions, i.e., local variables.

    The register Storage Class

    The register storage class is used to define local variables that should be stored in a register instead of RAM. This means that the variable has a maximum size equal to the register size (usually one word) and can’t have the unary ‘&’ operator applied to it (as it does not have a memory location).

    The register should only be used for variables that require quick access such as counters. It should also be noted that defining ‘register’ does not mean that the variable will be stored in a register. It means that it MIGHT be stored in a register depending on hardware and implementation restrictions.

    Example of register Storage Class

    The following code statement demonstrates the declaration of a register variable −

    {registerint  miles;}

    The static Storage Class

    The static storage class instructs the compiler to keep a local variable in existence during the life-time of the program instead of creating and destroying it each time it comes into and goes out of scope. Therefore, making local variables static allows them to maintain their values between function calls.

    The static modifier may also be applied to global variables. When this is done, it causes that variable’s scope to be restricted to the file in which it is declared.

    In C programming, when static is used on a global variable, it causes only one copy of that member to be shared by all the objects of its class.

    Example of static Storage Class

    The following example demonstrates the use of a static storage class in a C program −

    #include <stdio.h>/* function declaration */voidfunc(void);staticint count =5;/* global variable */main(){while(count--){func();}return0;}/* function definition */voidfunc(void){staticint i =5;/* local static variable */
       i++;printf("i is %d and count is %d\n", i, count);}

    Output

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

    i is 6 and count is 4
    i is 7 and count is 3
    i is 8 and count is 2
    i is 9 and count is 1
    i is 10 and count is 0
    

    The extern Storage Class

    The extern storage class is used to give a reference of a global variable that is visible to ALL the program files. When you use ‘extern’, the variable cannot be initialized however, it points the variable name at a storage location that has been previously defined.

    When you have multiple files and you define a global variable or function, which will also be used in other files, then extern will be used in another file to provide the reference of defined variable or function. Just for understanding, extern is used to declare a global variable or function in another file.

    The extern modifier is most commonly used when there are two or more files sharing the same global variables or functions as explained below.

    Example of extern Storage Class

    The example of an extern storage class may contain two or more files. Here is an example demonstrating the use of an extern storage class in C language −

    First File: main.c

    #include <stdio.h>int count;externvoidwrite_extern();main(){
       count =5;write_extern();}

    Second File: support.c

    #include <stdio.h>externint count;voidwrite_extern(void){printf("Count is %d\n", count);}

    Here, extern is being used to declare count in the second file, whereas it has its definition in the first file (main.c). Now, compile these two files as follows −

    $gcc main.c support.c
    

    It will produce the executable program a.out. When this program is executed, it will produce the following output −

    Count is 5
    

    Use of storage classes

    Storage classes are used to define the scope, visibility, lifetime, and initial (default) value of a variable.

    Summary of Storage Classes

    The following table provides a summary of the scope, default value, and lifetime of variables having different storage classes −

    Storage ClassNameMemoryScope, Default ValueLifetime
    autoAutomaticInternal MemoryLocal Scope, Garbage ValueWithin the same function or block in which they are declared.
    registerRegisterRegisterLocal Scope, 0Within the same function or block in which they are declared.
    staticStaticInternal MemoryLocal Scope, 0Within the program i.e., as long as program is running.
    externExternalInternal MemoryGlobal Scope, 0Within the program i.e., as long as program is running.
  • Memory Address

    Memory Address in C

    The memory address is assigned to a variable when a variable is declared in C language. C compiler stores the value of the variable in the different segments of the memory.

    Segments of Memory

    Different elements of a C program are stored in different segments of the computers memory, which has the following segments −

    • Text segment − A text segment, also known as a code segment or simply as text, is one of the sections of a progris used to store the object version of the C program.
    • Initialized data segment − The global variables and static variables that are initialized by the programmer are allocated the memory in the initialized data segment.
    • Uninitialized data segment − An uninitialized data segment also called as bss (stands for block started by symbol). The program allocates memory for this segment when it loads. Every data in bss is initialized to arithmetic “0” and pointers to null pointer by the kernel before the C program executes.
    • Stack − Stack is a LIFO (last in first out) data structure. Stack segment stores the value of local variables and values of parameters passed to a function. It also maintains the pointer to which a function call returns.
    • Heap − Heap is used for allocating memory during the runtime. All the functions that perform dynamic memory allocation deal with heap.

    Accessing Memory Address

    The memory addresses in C can be accessed or specified through the Address of (&) operator. To print a memory address using the printf() function, you need to use %p format specifier.

    Syntax

    Below is the syntax to access memory address −

    &variable_name
    

    Example

    In the following example, we are declaring two variables and printing their memory addresses −

    #include <stdio.h>intmain(){// Declaring two variablesint a;int b;// Accessing their memory// addresses and print themprintf("Memory address of a is %p\n",&a);printf("Memory address of b is %p\n",&b);return0;}

    How Does C Compiler Allocate Memory?

    Memory can be considered of as an array of bytes where each address is on index in the array and holds 1 byte.

    When you declare a variable in a C program, the C compiler allocates a random memory location to it, depending on the size requirement, which depends on the type.

    When an int variable is declared −

    int x =10;

    The compiler assigns the value in a random byte address. Since an int type needs 4 bytes, the next four addresses are earmarked for it.

    C allows you to find out which address has been allocated to a variable. You can use the %p format specifier to print the hexadecimal address of the memory location.

    char x ='A';printf("address of x: %p\n",&x);

    This prints the address of “x” in hexadecimal format −

    Address of x:000000000061FE1F
    

    Example

    Arrays in C are contiguous memory areas that hold a number of values of the same data type (int, long, *char, etc.).

    #include <stdio.h>intmain(){// initialize an array of intsint numbers[5]={1,2,3,4,5};int i =0;// print the address of the array variableprintf("numbers = %p\n", numbers);// print addresses of each array indexdo{printf("numbers[%u] = %p\n", i,(void*)(&numbers[i]));
    
      i++;}while(i &lt;5);// print the size of the arrayprintf("sizeof(numbers) = %lu\n",sizeof(numbers));}</code></pre>

    Output

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

    numbers = 0x7fff0815c0e0
    numbers[0] = 0x7fff0815c0e0
    numbers[1] = 0x7fff0815c0e4
    numbers[2] = 0x7fff0815c0e8
    numbers[3] = 0x7fff0815c0ec
    numbers[4] = 0x7fff0815c0f0
    sizeof(numbers) = 20
    
  • Memory Management

    One of the important characteristics of C is that the compiler manages how the memory is allocated to the variables declared in the code. Once the compiler allocates the required bytes of memory, it cannot be changed during the runtime.

    The compiler employs static memory allocation approach. However, there are times where you may need to allocate the memory on demand, during the runtime. Read this chapter to understand how dynamic memory management works in C.

    Functions for Dynamic Memory Management in C

    The C programming language provides several functions for dynamic memory allocation and management. These functions can be found in the <stdlib.h> header file.

    FunctionDescription
    void *calloc(int num, int size);This function allocates an array of num elements each of which size in bytes will be size.
    void free(void *address);This function releases a block of memory block specified by address.
    void *malloc(size_t size);This function allocates an array of num bytes and leave them uninitialized.
    void *realloc(void *address, int newsize);This function re-allocates memory extending it up to newsize.

    Allocating Memory Dynamically

    If you are aware of the size of an array, then it is easy and you can define it as an array. For example, if you need to store the name of a person, then you can safely define an array to hold a maximum of 100 characters (assuming that a name wouldn’t contain more than 100 characters). So, you can define an array as follows −

    char name[100];

    This is an example of static memory allocation. Now let us consider a situation where you have no idea about the length of the text you need to store, for example, you want to store a detailed description about a topic. In such a case, if the content is less than the allocated size, the allocated memory is wasted during the programs execution.

    On the other hand, if the size required is more than the allocated memory size, it may lead to unpredictable behaviour, including causing the data to be corrupted, as the size of the array cannot be dynamically altered.

    It is in these kind of situations you need to use the dynamic memory allocation methods as described in this chapter.

    The malloc() Function

    This function is defined in the stdlib.h header file. It allocates a block memory of the required size and returns a void pointer.

    void*malloc(size)

    The size parameter refers to the block of memory in bytes. To allocate a memory required for a specified data type, you need to use the typecasting operator.

    For example, the following snippet allocates the memory required to store an int type −

    int*ptr;
    ptr =(int*)malloc(sizeof(int));

    Here we need to define a pointer to character without defining how much memory is required and later, based on requirement, we can allocate memory.

    Example

    The following example uses the malloc() function to allocate the required memory to store a string (instead of declaring a char array of a fixed size) −

    #include <stdio.h>#include <stdlib.h>#include <string.h>intmain(){char*name;
       name =(char*)malloc(strlen("TutorialsPoint"));strcpy(name,"TutorialsPoint");if(name  ==NULL){fprintf(stderr,"Error - unable to allocate required memory\n");}else{printf("Name = %s\n", name);}}

    Output

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

    Name = TutorialsPoint
    

    The calloc() Function

    The calloc() function (stands for contiguous allocation) allocates the requested memory and returns a pointer to it.

    void*calloc(n, size);

    Here, “n” is the number of elements to be allocated and “size” is the byte-size of each element.

    The following snippet allocates the memory required to store 10 int types −

    int*ptr;
    ptr =(int*)calloc(25,sizeof(int));

    Example

    Let’s rewrite the above program using the calloc() function. All that you need to do is replace malloc with calloc −

    #include <stdio.h>#include <stdlib.h>#include <string.h>intmain(){char*name;
       name =(char*)calloc(strlen("TutorialsPoint"),sizeof(char));strcpy(name,"TutorialsPoint");if(name  ==NULL){fprintf(stderr,"Error - unable to allocate required memory\n");}else{printf("Name = %s\n", name);}}

    So you have complete control and you can pass any size value while allocating memory, unlike arrays where once the size is defined, you cannot change it.

    Resizing and Releasing the Memory

    When your program comes out, the operating system automatically releases all the memory allocated by your program. However, it is a good practice to release the allocated memory explicitly by calling the free() function, when you are not in need of using the allocated memory anymore.

    In this section, we will highlight the use of two functions, realloc() and free(), that you can use to resize and release the allocated memory.

    The realloc() Function

    The realloc() (re-allocation) function in C is used to dynamically change the memory allocation of a previously allocated memory. You can increase or decrease the size of an allocated memory block by calling the realloc() function.

    The prototype of using the realloc() function is like this −

    void*realloc(*ptr, size);

    Here, the first parameter “ptr” is the pointer to a memory block previously allocated with malloc, calloc or realloc to be reallocated. If this is NULL, a new block is allocated and a pointer to it is returned by the function.

    The second parameter “size” is the new size for the memory block, in bytes. If it is “0” and ptr points to an existing block of memory, the memory block pointed by ptr is deallocated and a NULL pointer is returned.

    Example

    The following example demonstrates how you can use the realloc() function in a C program −

    #include <stdio.h>#include <stdlib.h>#include <string.h>intmain(){char*name;
       name =(char*)calloc(strlen("TutorialsPoint"),sizeof(char));strcpy(name,"TutorialsPoint");
    
       name =(char*)realloc(name,strlen(" India Private Limited"));strcat(name," India Private Limited");if(name ==NULL){fprintf(stderr,"Error - unable to allocate required memory\n");}else{printf("Name = %s\n", name);}}

    Output

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

    Name = TutorialsPoint India Private Limited
    

    The free() Function

    The free() function in C is used to dynamically de-allocate the memory allocated using functions such as malloc() and calloc(), since it is not freed on their own.

    In C programming, any reference to unused memory creates a garbage deposition, which may lead to problems like program crashing. Hence, it is wise to use the free() function to perform a manual cleaning operation of the allocated memory.

    Here is the prototype to use the free() function −

    voidfree(void*ptr);

    Where ptr is the pointer to the block of memory previously allocated.

    Example

    The following example demonstrates how you can use the free() function in a C program −

    #include <stdio.h>#include <stdlib.h>#include <string.h>intmain(){char*name;
       name =(char*)calloc(strlen("TutorialsPoint"),sizeof(char));strcpy(name,"TutorialsPoint");if(name  ==NULL){fprintf(stderr,"Error - unable to allocate required memory\n");}else{printf("Name = %s\n", name);free(name);}}

    Output

    At the end of the code, the memory allocated to the char * pointer is de-allocated.

    Name = TutorialsPoint India Private Limited
    
  • Memory Layout

    The memory layout of a C program refers to how the program’s memory is organized during its execution. Understanding the memory layout helps developers manage memory more effectively, debug programs, and avoid common memory-related errors.

    The memory is typically divided into the following distinct memory segments −

    • Text segment
    • Initialized data segment
    • Uninitialized data segment
    • Heap
    • Stack

    Efficiently managing these memory segments in RAM, which is faster but limited in capacity as compared to the secondary storage, is crucial for preventing segmentation faults and optimizing C program execution.

    The following illustration shows how the memory layout is organized, and also depicts how the RAM loads a C program into its different memory segments −

    Memory Layout in C

    Let us discuss each of these memory segments in detail.

    Text Segment

    The text segment is also known as the code segment, which generates a binary file after compiling the program. This binary file is then used to execute the program by loading it into the RAM. This binary file contains instructions that get stored in the text segment of the memory.

    • The text segment is usually read-only and stored in the lower part of the memory to prevent accidental modification of the code while the program is running.
    • The size of the text segment determines the number of instructions and the complexity of the program.

    Initialized Data Segment

    The initialized data segment is a type of data segment that stores the global and static variables created by the programmer. This segment is placed just above the text segment of the program.

    The initialized data segment contains global and static variables that have been explicitly initialized by the programmer. For example,

    // Global variableint a =10;// Static variablestaticint b =20;

    This memory segment has read-write permission because the value of a variable can change during program execution.

    Example: Initialized Data Segment

    The following C program shows how the initialized data segment works −

    #include<stdio.h>int globalVar1 =50;char* greet ="Hello World";constint globalVar2 =30;intmain(){// static variable stored in initialized data segmentstaticint n =10;// ...printf("Global variables are stored in Initialize Data Segment");return0;}

    In this code, the variables globalVar1 and the pointer greet are declared outside the scope of the main() function, and therefore they are stored in the read-write section of the initialized data segment. However, the global variable globalVar2 is declared with the keyword const, and hence it is stored in the readonly section of the initialized data segment. Static variables like a are also stored in this part of the memory.

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

    Global variables are stored in Initialize Data Segment
    

    Uninitialized Data Segment

    The uninitialized data segment, also known as the BSS (Block Started by Symbol) segment, is a part of a C program’s memory layout. When a program is loaded into the memory, space for the BSS segment is allocated by the operating system. Before the execution of the C program begins, the kernel automatically initializes all variables in the BSS segment: arithmetic data types are set to 0, and pointers are set to a null pointer.

    The BSS segment contains all the global and static variables that are not explicitly initialized by the programmer (or initialized with 0). Since the values of these variables can be modified during program execution, the BSS segment has read-write permission.

    Example: Uninitialized Data Segment

    Let’s understand the role of uninitialized data segment through the following C program −

    #include <stdio.h>// Uninitialized global variable stored in the bss segmentint globalVaraible;intmain(){// Uninitialized static variable stored in bssstaticint staticVariable;printf("Global Variable = %d\n", globalVaraible);printf("Static Variable = %d\n", staticVariable);return0;}

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

    Global Variable = 0
    Static Variable = 0
    

    In this C program, both the static and global variables are uninitialized, so they are stored in the BSS segment of the memory layout. Before the program execution begins, the kernel initializes these variables with the value 0.

    Heap Segment

    The heap area begins at the end of the BSS segment and grows upward toward higher memory addresses. It is the memory segment used for dynamic memory allocation during program execution. Whenever additional memory is required, functions like malloc() and calloc() allocate space from the heap, causing it to grow upward.

    • The heap is managed by functions such as malloc(), calloc(), and free(), which internally may use system calls like brk and sbrk to adjust its size.
    • Since the heap is a shared region, it is also used by all shared libraries and dynamically loaded modules within a process.

    Example: Heap Segment

    In this C program, we have created a variable of data type char, which allocates 1 byte of memory (the size of a char in C) at the time of program execution. Since this variable is created dynamically, it is allocated in the heap segment of the memory.

    #include <stdio.h>#include <stdlib.h>intmain(){// Allocate memory for a single charchar*var =(char*)malloc(sizeof(char));*var ='A';// Print the value and the size of the allocated memoryprintf("Value of dynamically allocated char: %c\n",*var);printf("Size of dynamically allocated char: %zu bytes\n",sizeof(*var));// Free the dynamically allocated memoryfree(var);return0;}

    Run the code and check its output −

    Value of dynamically allocated char: A
    Size of dynamically allocated char: 1 bytes
    

    Stack Segment

    The stack segment follows a LIFO (Last In, First Out) structure and usually grows downward toward lower memory addresses (though the exact behavior depends on the computer architecture). It grows in the direction opposite to the heap.

    The stack is used to manage function calls and local variables. Each time a function is called, a stack frame is created, which stores the function’s local variables, parameters, and return address. When the function finishes, its stack frame is removed, following the LIFO principle.

    Example: Stack Segment

    The following example shows how the variables are stored in the stack memory segment −

    #include <stdio.h>voiddisplay(int x){int y =20;printf("Parameter x = %d\n", x);printf("Local variable y = %d\n", y);}intmain(){int mainVar =10;// function call creates new stack framedisplay(mainVar);return0;}

    Run the code and check its output −

    Parameter x = 10
    Local variable y = 20
    

    When the main function starts, its stack frame is created storing mainVar and the return address.
    When the display function is called, a new stack frame is pushed storing x and y, and removed once the function ends (LIFO order).

    Command-line Arguments

    When a C program is executed, any command-line arguments passed to it are also stored in the memory. These arguments are placed in the special memory segment, typically above the stack in the process memory layout.

    The command-line arguments are passed to the main() function in the form of −

    intmain(int argc,char*argv[])

    Here,

    • argc (argument count): Stores the total number of argument passed, includes the program name.
    • argv (argument vector): It is an array of character pointer (strings), where each element point to a command-line arguments.

    Example: argc and argv

    Let’s understand both arguments (argc and argv) through a C program:

    #include <stdio.h>intmain(int argc,char*argv[]){printf("Total arguments: %d\n", argc);for(int i =0; i < argc; i++){printf("Argument %d: %s\n", i, argv[i]);}return0;}

    Following is the output of the above code −

    Total arguments: 1
    Argument 0: /tmp/HqDVg7xJye/main.o
    

    Example: Program to get the Size of Memory Segment

    In this example, we create a simple C program layout and use the command below to get the size of each memory segment. To run this, you need a Linux environment. On Windows, you can download and install MinGW to use GCC and related commands.

    #include<stdio.h>intmain(){return0;}

    Use the following command to get the size −

    gcc file_name.c -o file_name
    size file_name
    

    Output: Following is the size −

    ~$ gcc program.c -o program
    ~$ size program
       text    data     bss     dec     hex filename
       1418     544      8    1970     7b0 program
    

    Example: Inserting an Uninitialized Global Variable

    Inserting an uninitialized global variable increases the size of the Data segment −

    #include <stdio.h>int global;intmain(){return0;}

    Run the code and check its output −

    ~$ gcc program.c -o program
    ~$ size program
       text    data     bss     dec     hex filename
       1418     548      8    1970     7b0 program
    

    Example: Inserting an Uninitialized Static Variable

    If you insert an uninitialized static variable, it increases the occupied space in the BSS segment.

    #include <stdio.h>int globalVar =10;intmain(){staticint staticVar;return0;}

    Run the code and check its output −

    ~$ gcc program.c -o program
    ~$ size program
       text    data     bss     dec     hex filename
       1418     548      12    1970     7b0 program
    

    Example: Inserting a Static Variable with Initialized Value

    If you insert a static variable with an initialized value, it will be stored in the data segment.

    #include <stdio.h>int globalVar =10;intmain(){staticint staticVar;staticint a =5;return0;}

    Run the code and check its output −

    ~$ gcc program.c -o program
    ~$ size program
       text    data     bss     dec     hex filename
       1418     552      8    1970     7b0 program
    

    Example: Inserting an Uninitialized Global Variable

    As we saw in the above programs, if we insert a global variable without initialization, it will be stored in the BSS segment.

    #include <stdio.h>int globalVar =10;int x;intmain(){staticint staticVar;staticint a =5;return0;}

    Run the code and check its output −

    ~$ gcc program.c -o program
    ~$ size program
       text    data     bss     dec     hex filename
       1418     552      16    1970     7b0 program
    

    Conclusion

    The memory layout of a C program is divided into distinct segments: text segment, data segment, BSS, heap, and stack. Each segment has a specific role in program execution. The text segment stores code, while the data and BSS segments handle global and static variables. The heap manages dynamic memory, and the stack is used for function calls and local variables.

  • 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