Advance C-Programming
UNIT-I: Arrays
Arrays: Definition, declaration and initialization of one-dimensional array; Accessing array elements; Displaying array elements; Sorting arrays; Arrays and functions; Two-Dimensional array: Declaration and Initialization, Accessing and Displaying, Memory representation of array (Row Major, Column Major); Multidimensional array.
UNIT-II: Pointers
Pointers: Definition and declaration, Initialization; Indirection operator, address of operator; pointer arithmetic; dynamic memory allocation; arrays and pointers; functions and pointers.
UNIT-III: Strings
Strings: Definition, declaration and initialization of strings; Standard library functions: strlen(), strcpy(), strcat(), strcmp(); Implementation without using standard library functions.
UNIT-IV: Structures and Unions
Structures: Definition and declaration; Variable initialization; Accessing fields and structure operations; Nested structures. Union: Definition and declaration; Differences between Union and Structure.
UNIT-V: C Preprocessor & Bitwise Operators
Introduction to C Preprocessor: Definition of Pre-processor; Macro substitution directives; File inclusion directives; Conditional compilation.
Bitwise Operators: Bitwise operators; Shift operators; Masks; Bit fields.
UNIT-VI: File Handling
File Handling: Definition of Files, Opening modes of files; Standard functions: fopen(), fclose(), feof(), fseek(), ftell(); Using text files: fgetc(), fputc(), fscanf(); Command line arguments.

UNIT-I: Arrays in C (Advanced Programming)

1. One-Dimensional Arrays

An array is a collection of variables of the same type, stored at contiguous memory locations. A one-dimensional array stores elements in a linear form.

        // Declaration and Initialization
        int numbers[5] = {10, 20, 30, 40, 50};
    
        // Accessing array elements
        printf("%d", numbers[2]);  // Output: 30
            

2. Displaying Array Elements

You can use a loop to print all the array elements.

        int i;
        for(i = 0; i < 5; i++) {
            printf("%d ", numbers[i]);
        }
        // Output: 10 20 30 40 50
            

3. Sorting Arrays

Here is an example of sorting an array using the bubble sort algorithm:

        int arr[5] = {5, 2, 4, 3, 1};
        int i, j, temp;
    
        for(i = 0; i < 5; i++) {
            for(j = 0; j < 4 - i; j++) {
                if(arr[j] > arr[j+1]) {
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    
        // Output after sorting: 1 2 3 4 5
            

4. Arrays and Functions

You can pass arrays to functions either by reference (as a pointer) or directly.

        void display(int a[], int size) {
            for(int i = 0; i < size; i++)
                printf("%d ", a[i]);
        }
    
        int main() {
            int arr[] = {1, 2, 3, 4, 5};
            display(arr, 5);
            return 0;
        }
        // Output: 1 2 3 4 5
            

5. Two-Dimensional Arrays

A 2D array is like a matrix with rows and columns. It is used for tabular data.

        int matrix[2][3] = {
            {1, 2, 3},
            {4, 5, 6}
        };
    
        printf("%d", matrix[1][2]); // Output: 6
            

6. Displaying 2D Arrays

Nested loops are used to access all the elements in a 2D array.

        for(int i = 0; i < 2; i++) {
            for(int j = 0; j < 3; j++) {
                printf("%d ", matrix[i][j]);
            }
            printf("\n");
        }
        // Output:
        // 1 2 3
        // 4 5 6
            

7. Memory Representation

2D arrays in C are stored in memory in row-major order (by default):

        // Example: int mat[2][3] = { {1, 2, 3}, {4, 5, 6} };
    
        // Row-major memory layout:
        [1][2][3][4][5][6]
            

8. Multidimensional Arrays

These are arrays with more than 2 dimensions (e.g., 3D arrays).

        int cube[2][2][2] = {
            { {1, 2}, {3, 4} },
            { {5, 6}, {7, 8} }
        };
    
        printf("%d", cube[1][1][1]); // Output: 8
            

UNIT-II: Pointers in C (Advanced Programming)

1. Pointers: Definition and Declaration

A pointer is a variable that stores the memory address of another variable. It points to the location in memory where the value of a variable is stored.

        int x = 10;
        int *ptr;  // pointer to integer
        ptr = &x;  // stores the address of x
    
        printf("Address of x: %p\n", ptr);  // Output: Address of x
            

2. Indirection Operator and Address of Operator

The indirection operator (*) is used to access the value at the address the pointer is pointing to. The address of operator (&) gives the memory address of a variable.

        int x = 10;
        int *ptr = &x;  // pointer to x
    
        printf("Value of x: %d\n", *ptr);  // Output: 10 (indirection)
        printf("Address of x: %p\n", &x);  // Output: Address of x
            

3. Pointer Arithmetic

Pointer arithmetic refers to operations on pointers like addition or subtraction, allowing us to navigate through arrays or memory addresses.

        int arr[] = {10, 20, 30, 40, 50};
        int *ptr = arr;  // points to the first element of the array
    
        printf("First element: %d\n", *ptr);  // Output: 10
        ptr++;  // move to the next element
        printf("Second element: %d\n", *ptr);  // Output: 20
            

4. Dynamic Memory Allocation

Dynamic memory allocation in C is done using functions like malloc, calloc, realloc, and free for memory management at runtime.

        int *ptr;
        ptr = (int *)malloc(5 * sizeof(int));  // dynamically allocate memory for 5 integers
    
        if(ptr != NULL) {
            for(int i = 0; i < 5; i++) {
                ptr[i] = i * 10;
            }
    
            for(int i = 0; i < 5; i++) {
                printf("%d ", ptr[i]);  // Output: 0 10 20 30 40
            }
    
            free(ptr);  // free dynamically allocated memory
        }
            

5. Arrays and Pointers

Arrays and pointers are closely related in C. The name of an array is essentially a pointer to the first element. You can use pointers to access array elements and manipulate arrays.

        int arr[] = {1, 2, 3, 4, 5};
        int *ptr = arr;
    
        // Accessing array elements using pointer arithmetic
        printf("%d\n", *(ptr + 2));  // Output: 3 (pointer arithmetic to access the 3rd element)
            

6. Functions and Pointers

Pointers can be passed to functions, allowing functions to modify the actual value of variables.

        void modifyValue(int *p) {
            *p = 100;  // modifies the value at the address
        }
    
        int main() {
            int x = 10;
            printf("Before: %d\n", x);  // Output: 10
    
            modifyValue(&x);
            printf("After: %d\n", x);  // Output: 100
            return 0;
        }
            

UNIT-III: Strings in C (Advanced Programming)

1. Strings: Definition, Declaration, and Initialization

A string is an array of characters terminated by a null character '\0'. In C, strings are handled as arrays of characters.

        char str1[] = "Hello, World!";  // String initialized with a literal
        char str2[20];  // Declaration of a string with enough space
        strcpy(str2, "Hello, C!");  // Copying a string into str2
            

2. Standard Library Functions for Strings

Here are some commonly used standard library functions for working with strings:

        char str1[] = "Hello";
        char str2[] = "World";
        char str3[20];
    
        // Using strlen()
        int len = strlen(str1);  // Output: 5
    
        // Using strcpy()
        strcpy(str3, str1);  // str3 will hold "Hello"
    
        // Using strcat()
        strcat(str1, str2);  // str1 will hold "HelloWorld"
    
        // Using strcmp()
        int result = strcmp(str1, str2);  // Result will be negative since "Hello" < "World"
            

3. Implementation Without Using Standard Library Functions

Below are implementations of the above functions without using standard library functions:

a. Implementing strlen()

        int my_strlen(char str[]) {
            int i = 0;
            while (str[i] != '\0') {  // Traverse until the null character
                i++;
            }
            return i;  // Return the length of the string
        }
    
        char my_str[] = "Hello";
        printf("Length of string: %d\n", my_strlen(my_str));  // Output: 5
            

b. Implementing strcpy()

        void my_strcpy(char *dest, const char *src) {
            while (*src != '\0') {  // Traverse until the null character
                *dest = *src;  // Copy each character
                dest++;
                src++;
            }
            *dest = '\0';  // Null terminate the destination string
        }
    
        char src[] = "Hello";
        char dest[20];
        my_strcpy(dest, src);
        printf("Copied string: %s\n", dest);  // Output: Hello
            

c. Implementing strcat()

        void my_strcat(char *dest, const char *src) {
            while (*dest != '\0') {  // Move to the end of the destination string
                dest++;
            }
            while (*src != '\0') {  // Append characters from source to destination
                *dest = *src;
                dest++;
                src++;
            }
            *dest = '\0';  // Null terminate the concatenated string
        }
    
        char str1[50] = "Hello";
        char str2[] = " World";
        my_strcat(str1, str2);
        printf("Concatenated string: %s\n", str1);  // Output: Hello World
            

d. Implementing strcmp()

        int my_strcmp(const char *str1, const char *str2) {
            while (*str1 != '\0' && *str2 != '\0') {
                if (*str1 != *str2) {
                    return *str1 - *str2;  // Return the difference between the characters
                }
                str1++;
                str2++;
            }
            return *str1 - *str2;  // Return the difference between the null characters (0 if equal)
        }
    
        char str1[] = "Hello";
        char str2[] = "World";
        int result = my_strcmp(str1, str2);
        printf("Comparison result: %d\n", result);  // Output: Negative number because "Hello" < "World"
            

UNIT-IV: Structures and Unions in C (Advanced Programming)

1. Structures: Definition and Declaration

A structure is a user-defined data type in C that allows grouping of different data types into a single unit. Each member of the structure is called a field or member.

        struct Person {
            char name[50];
            int age;
            float salary;
        };
    
        // Declaration of structure variable
        struct Person person1;
        

In the above example, a structure named Person is defined with three fields: name, age, and salary. The structure variable person1 is then declared.

2. Variables Initialization

Structure variables can be initialized at the time of declaration. You can also initialize them later in the program.

        struct Person person2 = {"John", 25, 50000.50};  // Initialization at the time of declaration
    
        // Alternatively, structure members can be initialized later:
        person1.age = 30;
        strcpy(person1.name, "Alice");
        person1.salary = 75000.00;
            

In this example, we initialize the structure person2 at the time of declaration with values. The fields of person1 are initialized later in the program.

3. Accessing Fields and Structure Operations

To access or manipulate the members of a structure, the dot (.) operator is used.

        // Accessing structure members
        printf("Name: %s\n", person1.name);
        printf("Age: %d\n", person1.age);
        printf("Salary: %.2f\n", person1.salary);
            

In the above example, we access and print the values of the fields of the structure person1 using the dot operator.

4. Nested Structures

A nested structure is a structure that contains another structure as one of its members.

        struct Address {
            char city[50];
            char state[50];
            int pin_code;
        };
    
        struct Employee {
            char name[50];
            struct Address address;  // Nested structure
            float salary;
        };
    
        struct Employee emp1 = {"David", {"New York", "NY", 10001}, 60000.00};
            

Here, the structure Employee contains another structure Address as a member. The field address is initialized with an address when creating emp1.

5. Union: Definition and Declaration

A union is a special data type that allows storing different data types in the same memory location. However, only one of the union's members can hold a value at any given time. The size of a union is the size of its largest member.

        union Data {
            int i;
            float f;
            char str[20];
        };
    
        union Data data1;  // Declaration of union variable
            

In this example, a union named Data is defined with three members: an integer, a float, and a string. The union variable data1 is then declared.

6. Differentiating Between Union and Structure

Although both structures and unions allow grouping of different data types, there are key differences:

Example of Structure:

        struct Student {
            int roll_no;
            char name[50];
        };
            

Example of Union:

        union Value {
            int i;
            float f;
            char c;
        };
            

UNIT-V: Introduction to C Preprocessor and Bitwise Operators

1. Introduction to C Preprocessor

The C Preprocessor is a tool that processes the source code before the actual compilation begins. It handles tasks like macro substitution, file inclusion, and conditional compilation.

1.1 Macro Substitution Directives

Macro substitution is done using the #define directive. It allows us to define constants or functions that are replaced in the code before compilation.

        #define PI 3.14  // Macro definition for a constant
    
        int main() {
            float area = PI * 5 * 5;  // Macro PI gets substituted with 3.14
            printf("Area: %.2f\n", area);
            return 0;
        }
            

In the above example, the preprocessor replaces PI with 3.14 before the program is compiled.

1.2 File Inclusion Directives

The #include directive is used to include the contents of a file in the source code. It can be used to include header files containing declarations and function prototypes.

        #include   // Standard library header file
        #include "myheader.h"  // Custom header file
    
        int main() {
            printf("Hello, World!\n");
            return 0;
        }
            

The #include directive allows us to include standard library files or custom header files containing function prototypes.

1.3 Conditional Compilation

The #if, #else, #elif, and #endif directives are used for conditional compilation. These directives allow different parts of the program to be compiled based on conditions.

        #define DEBUG
    
        int main() {
        #ifdef DEBUG
            printf("Debugging is enabled.\n");
        #else
            printf("Debugging is disabled.\n");
        #endif
            return 0;
        }
            

In this example, the code block inside #ifdef DEBUG is included if the macro DEBUG is defined. If it's not defined, the code inside #else will be compiled.

2. Bitwise Operators

Bitwise operators are used to perform operations on individual bits of integers. They are particularly useful in low-level programming, such as system programming or hardware interfacing.

2.1 Bitwise Operators

Bitwise operators perform operations on corresponding bits of two operands. The common bitwise operators are:

        int a = 5, b = 3;
        int result;
    
        result = a & b;  // Bitwise AND: 0101 & 0011 = 0001 (1 in decimal)
        result = a | b;  // Bitwise OR: 0101 | 0011 = 0111 (7 in decimal)
        result = a ^ b;  // Bitwise XOR: 0101 ^ 0011 = 0110 (6 in decimal)
        result = ~a;     // Bitwise NOT: 0101 becomes 1010 (complement in binary)
            

In the above example, we perform different bitwise operations on two integer variables a and b.

2.2 Shift Operators

Shift operators shift the bits of a number to the left or right.

        int num = 5;   // 0101 in binary
        num = num << 1;  // Left shift: 1010 (10 in decimal)
        num = num >> 2;  // Right shift: 0010 (2 in decimal)
            

In the example, we first shift the bits of num to the left by 1 position, then shift it to the right by 2 positions.

2.3 Masks

A mask is a bit pattern used to isolate or modify certain bits of a number. It is often used in conjunction with bitwise operators to modify specific bits.

        int num = 5;   // 0101 in binary
        int mask = 1;  // 0001 in binary
        int result = num & mask;  // Bitwise AND to isolate the least significant bit
            

In this example, we create a mask with the least significant bit set to 1, and use the bitwise AND operator to isolate the least significant bit of num.

2.4 Bit Fields

A bit field allows the packing of data in a structure. It provides an efficient way to store boolean or small integer values by using a specific number of bits.

        struct Flags {
            unsigned int isActive : 1;    // 1 bit for isActive
            unsigned int isVisible : 1;   // 1 bit for isVisible
            unsigned int isEnabled : 1;   // 1 bit for isEnabled
        };
    
        struct Flags status;
        status.isActive = 1;  // Set the isActive bit to 1
        status.isVisible = 0; // Set the isVisible bit to 0
            

In the example, we define a structure with bit fields. Each field uses only 1 bit of memory.

UNIT-VI: File Handling

1. Definition of Files

A file in C is a collection of data stored on a permanent storage medium such as a hard disk. Files allow programs to store data and retrieve it even after the program has ended.

C provides standard functions to handle files, allowing operations such as reading from or writing to files, as well as closing and opening them.

2. Opening Modes of Files

The fopen() function is used to open a file. The file opening mode determines how the file is accessed, and there are several modes available:

2.1 Example of File Opening

        FILE *file;
        file = fopen("example.txt", "w");  // Open file for writing
        if (file == NULL) {
            printf("Error opening file.\n");
            return 1;
        }
            

In the above code, we attempt to open a file named example.txt in write mode. If the file cannot be opened (e.g., due to permissions), the function returns NULL.

3. Standard Functions for File Handling

3.1 fopen()

The fopen() function opens a file with the specified mode. It returns a pointer to the file, which can be used to read or write to the file.

3.2 fclose()

The fclose() function is used to close a file once it is no longer needed. It releases the resources associated with the file.

        fclose(file);  // Close the file after operations
            

3.3 feof()

The feof() function checks if the end-of-file (EOF) has been reached. It returns a non-zero value if EOF is reached.

        while (!feof(file)) {
            // Read data from file
        }
            

3.4 fseek()

The fseek() function sets the file position to a specified location within the file. It allows for moving to specific parts of a file (useful for random access).

        fseek(file, 0, SEEK_SET);  // Move to the beginning of the file
        fseek(file, 10, SEEK_CUR); // Move 10 bytes forward from the current position
        fseek(file, -10, SEEK_END); // Move 10 bytes backward from the end
            

3.5 fread(), fwrite()

The fread() and fwrite() functions are used for reading and writing binary data to and from a file.

        char buffer[20];
        fread(buffer, sizeof(char), 20, file);  // Read 20 characters from file
        fwrite(buffer, sizeof(char), 20, file); // Write 20 characters to file
            

4. Working with Text Files

For working with text files, we use functions like fgetc(), fputc(), and fscanf() for reading and writing single characters or formatted input.

4.1 fgetc()

The fgetc() function reads a single character from the file.

        char ch;
        ch = fgetc(file);  // Read a character from the file
        printf("%c", ch);  // Print the character
            

4.2 fputc()

The fputc() function writes a single character to the file.

        fputc('A', file);  // Write character 'A' to the file
            

4.3 fscanf()

The fscanf() function reads formatted input from the file, similar to scanf().

        int x, y;
        fscanf(file, "%d %d", &x, &y);  // Read two integers from the file
        printf("x = %d, y = %d", x, y);  // Print the values
            

5. Command Line Arguments

Command-line arguments are passed to a program when it is executed. They provide a way to pass data to the program without needing user interaction.

5.1 Example of Command Line Arguments

Command-line arguments are passed as parameters to the main() function, as an array of strings.

        int main(int argc, char *argv[]) {
            if (argc < 2) {
                printf("Usage: %s \n", argv[0]);
                return 1;
            }
            FILE *file = fopen(argv[1], "r");  // Open file passed as argument
            if (file == NULL) {
                printf("Error opening file.\n");
                return 1;
            }
            // Process file
            fclose(file);
            return 0;
        }
            

In this example, argc stores the number of command-line arguments, and argv is an array of strings holding the arguments. The first argument is always the name of the program itself, and the remaining arguments are the ones passed by the user.