Basic Dynamic Memory Allocation Programming Questions

Here are 10 C programming exercises that involve dynamic memory allocation:

  1. Dynamic Array: Create a program that dynamically allocates memory for an array of integers. Allow the user to input the size of the array and elements, and then print the array.

  2. Matrix Operations: Implement a program to perform matrix addition and multiplication using dynamic memory allocation. Allow the user to input the dimensions and elements of the matrices.

  3. String Reversal: Write a program that takes a string as input, dynamically allocates memory to store the reversed string, and then prints the reversed string.

  4. Dynamic Linked List: Implement a linked list using dynamic memory allocation. Allow users to insert, delete, and display elements in the linked list.

  5. Dynamic Stack: Create a program that simulates a stack using dynamic memory allocation. Implement push and pop operations and display the contents of the stack.

  6. Dynamic Queue: Implement a queue using dynamic memory allocation. Allow users to enqueue and dequeue elements and display the contents of the queue.

  7. Dynamic String Concatenation: Write a program that concatenates two strings using dynamic memory allocation. Allocate memory only for the resulting string.

  8. Dynamic Structure: Define a structure representing a student with name, roll number, and marks. Dynamically allocate memory for an array of such structures and allow the user to input and display the data.

  9. File Reading and Dynamic Memory: Create a program that reads data from a file, dynamically allocates memory to store the data, and then prints the contents.

  10. Dynamic 2D Array: Implement a program that dynamically allocates memory for a 2D array of integers. Allow the user to input the dimensions and elements, and then print the array.

These exercises cover a range of dynamic memory allocation-related concepts and will help you practice and strengthen your skills in C programming. I have solved all of the above with explanatory comments. You should first make a sincere attempt at solving these problems before looking at the solutions.

  1. Dynamic Array

     #include <stdio.h>
     #include <stdlib.h>
    
     int main() {
         int *arr; // Declare a pointer to int to store the base address of the dynamically allocated array
         int size;
    
         // Get array size from the user
         printf("Enter the size of the array: ");
         scanf("%d", &size);
    
         // Dynamically allocate memory for the array
         arr = (int *)malloc(size * sizeof(int));
    
         // Check if memory allocation was successful
         if (arr == NULL) {
             printf("Memory allocation failed. Exiting...");
             return 1; // Exit with an error code
         }
    
         // Input array elements from the user
         printf("Enter %d elements:\n", size);
         for (int i = 0; i < size; i++) {
             scanf("%d", &arr[i]);
         }
    
         // Display array elements
         printf("Array elements: ");
         for (int i = 0; i < size; i++) {
             printf("%d ", arr[i]);
         }
    
         // Free dynamically allocated memory
         free(arr);
    
         return 0;
     }
    
  1. Matrix Multiplication

     #include <stdio.h>
     #include <stdlib.h>
    
     // Function prototypes
     int** allocateMatrix(int rows, int cols);
     void inputMatrix(int** matrix, int rows, int cols);
     void displayMatrix(int** matrix, int rows, int cols);
     void addMatrices(int** matrix1, int** matrix2, int** result, int rows, int cols);
     void multiplyMatrices(int** matrix1, int** matrix2, int** result, int rows1, int cols1, int rows2, int cols2);
     void freeMatrix(int** matrix, int rows);
    
     int main() {
         int **matrix1, **matrix2, **result;
         int rows1, cols1, rows2, cols2;
    
         // Input dimensions of the first matrix
         printf("Enter dimensions of Matrix 1:\n");
         printf("Rows: ");
         scanf("%d", &rows1);
         printf("Columns: ");
         scanf("%d", &cols1);
    
         // Input dimensions of the second matrix
         printf("Enter dimensions of Matrix 2:\n");
         printf("Rows: ");
         scanf("%d", &rows2);
         printf("Columns: ");
         scanf("%d", &cols2);
    
         // Check if matrix multiplication is possible
         if (cols1 != rows2) {
             printf("Matrix multiplication not possible. Exiting...");
             return 1; // Exit with an error code
         }
    
         // Dynamically allocate memory for matrices
         matrix1 = allocateMatrix(rows1, cols1);
         matrix2 = allocateMatrix(rows2, cols2);
         result = allocateMatrix(rows1, cols2);
    
         // Input elements of matrices
         printf("Enter elements of Matrix 1:\n");
         inputMatrix(matrix1, rows1, cols1);
    
         printf("Enter elements of Matrix 2:\n");
         inputMatrix(matrix2, rows2, cols2);
    
         // Perform matrix addition
         addMatrices(matrix1, matrix2, result, rows1, cols1);
         printf("\nMatrix Addition Result:\n");
         displayMatrix(result, rows1, cols1);
    
         // Perform matrix multiplication
         multiplyMatrices(matrix1, matrix2, result, rows1, cols1, rows2, cols2);
         printf("\nMatrix Multiplication Result:\n");
         displayMatrix(result, rows1, cols2);
    
         // Free dynamically allocated memory
         freeMatrix(matrix1, rows1);
         freeMatrix(matrix2, rows2);
         freeMatrix(result, rows1);
    
         return 0;
     }
    
     // Function to allocate memory for a matrix
     int** allocateMatrix(int rows, int cols) {
         int **matrix = (int **)malloc(rows * sizeof(int *));
         for (int i = 0; i < rows; i++) {
             matrix[i] = (int *)malloc(cols * sizeof(int));
         }
         return matrix;
     }
    
     // Function to input elements of a matrix
     void inputMatrix(int** matrix, int rows, int cols) {
         for (int i = 0; i < rows; i++) {
             for (int j = 0; j < cols; j++) {
                 printf("Enter element at position (%d, %d): ", i + 1, j + 1);
                 scanf("%d", &matrix[i][j]);
             }
         }
     }
    
     // Function to display a matrix
     void displayMatrix(int** matrix, int rows, int cols) {
         for (int i = 0; i < rows; i++) {
             for (int j = 0; j < cols; j++) {
                 printf("%d\t", matrix[i][j]);
             }
             printf("\n");
         }
     }
    
     // Function to add two matrices
     void addMatrices(int** matrix1, int** matrix2, int** result, int rows, int cols) {
         for (int i = 0; i < rows; i++) {
             for (int j = 0; j < cols; j++) {
                 result[i][j] = matrix1[i][j] + matrix2[i][j];
             }
         }
     }
    
     // Function to multiply two matrices
     void multiplyMatrices(int** matrix1, int** matrix2, int** result, int rows1, int cols1, int rows2, int cols2) {
         for (int i = 0; i < rows1; i++) {
             for (int j = 0; j < cols2; j++) {
                 result[i][j] = 0;
                 for (int k = 0; k < cols1; k++) {
                     result[i][j] += matrix1[i][k] * matrix2[k][j];
                 }
             }
         }
     }
    
     // Function to free memory allocated for a matrix
     void freeMatrix(int** matrix, int rows) {
         for (int i = 0; i < rows; i++) {
             free(matrix[i]);
         }
         free(matrix);
     }
    
  2. String Reversal

     #include <stdio.h>
     #include <stdlib.h>
     #include <string.h>
    
     // Function prototypes
     char* reverseString(const char* input);
    
     int main() {
         char inputString[100];  // Assuming a maximum string length of 100
         char *reversedString;
    
         // Input a string
         printf("Enter a string: ");
         scanf("%s", inputString);
    
         // Call the function to reverse the string and allocate memory
         reversedString = reverseString(inputString);
    
         // Display the reversed string
         printf("Reversed string: %s\n", reversedString);
    
         // Free dynamically allocated memory
         free(reversedString);
    
         return 0;
     }
    
     // Function to reverse a string and allocate memory
     char* reverseString(const char* input) {
         int length = strlen(input);
         char *reversed = (char *)malloc((length + 1) * sizeof(char));  // +1 for the null terminator
    
         // Check if memory allocation was successful
         if (reversed == NULL) {
             printf("Memory allocation failed. Exiting...");
             exit(1);  // Exit with an error code
         }
    
         // Reverse the string
         for (int i = 0; i < length; i++) {
             reversed[i] = input[length - 1 - i];
         }
    
         // Add null terminator at the end of the reversed string
         reversed[length] = '\0';
    
         return reversed;
     }
    
  3. Dynamic Linked List

     #include <stdio.h>
     #include <stdlib.h>
    
     // Node structure to represent elements in the linked list
     struct Node {
         int data;
         struct Node* next;
     };
    
     // Function prototypes
     struct Node* createNode(int data);
     void insertNode(struct Node** head, int data);
     void deleteNode(struct Node** head, int data);
     void displayList(struct Node* head);
     void freeList(struct Node* head);
    
     int main() {
         struct Node* head = NULL; // Initialize an empty linked list
    
         // Insert elements into the linked list
         insertNode(&head, 10);
         insertNode(&head, 20);
         insertNode(&head, 30);
    
         // Display the linked list
         printf("Linked List: ");
         displayList(head);
    
         // Delete an element from the linked list
         deleteNode(&head, 20);
    
         // Display the linked list after deletion
         printf("Linked List after deletion: ");
         displayList(head);
    
         // Free the memory allocated for the linked list
         freeList(head);
    
         return 0;
     }
    
     // Function to create a new node with given data
     struct Node* createNode(int data) {
         struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
         if (newNode == NULL) {
             printf("Memory allocation failed. Exiting...");
             exit(1);
         }
         newNode->data = data;
         newNode->next = NULL;
         return newNode;
     }
    
     // Function to insert a new node with given data at the end of the linked list
     void insertNode(struct Node** head, int data) {
         struct Node* newNode = createNode(data);
         if (*head == NULL) {
             *head = newNode; // If the list is empty, the new node becomes the head
         } else {
             struct Node* temp = *head;
             while (temp->next != NULL) {
                 temp = temp->next;
             }
             temp->next = newNode;
         }
     }
    
     // Function to delete a node with given data from the linked list
     void deleteNode(struct Node** head, int data) {
         if (*head == NULL) {
             printf("Linked list is empty. Cannot delete.\n");
             return;
         }
    
         struct Node* temp = *head;
         struct Node* prev = NULL;
    
         // Search for the node with the given data
         while (temp != NULL && temp->data != data) {
             prev = temp;
             temp = temp->next;
         }
    
         // If the node with the data is found
         if (temp != NULL) {
             if (prev == NULL) {
                 *head = temp->next; // If the node to be deleted is the head
             } else {
                 prev->next = temp->next;
             }
             free(temp); // Free the memory of the deleted node
             printf("Node with data %d deleted.\n", data);
         } else {
             printf("Node with data %d not found.\n", data);
         }
     }
    
     // Function to display the elements of the linked list
     void displayList(struct Node* head) {
         struct Node* temp = head;
         while (temp != NULL) {
             printf("%d ", temp->data);
             temp = temp->next;
         }
         printf("\n");
     }
    
     // Function to free the memory allocated for the linked list
     void freeList(struct Node* head) {
         struct Node* temp;
         while (head != NULL) {
             temp = head;
             head = head->next;
             free(temp);
         }
     }
    
  4. Dynamic Stack

     #include <stdio.h>
     #include <stdlib.h>
    
     // Structure to represent a stack node
     struct Node {
         int data;
         struct Node* next;
     };
    
     // Function prototypes
     struct Node* createNode(int data);
     void push(struct Node** top, int data);
     void pop(struct Node** top);
     void displayStack(struct Node* top);
     void freeStack(struct Node* top);
    
     int main() {
         struct Node* stackTop = NULL; // Initialize an empty stack
    
         // Push elements onto the stack
         push(&stackTop, 10);
         push(&stackTop, 20);
         push(&stackTop, 30);
    
         // Display the contents of the stack
         printf("Stack contents: ");
         displayStack(stackTop);
    
         // Pop an element from the stack
         pop(&stackTop);
    
         // Display the contents of the stack after popping
         printf("Stack contents after popping: ");
         displayStack(stackTop);
    
         // Free the memory allocated for the stack
         freeStack(stackTop);
    
         return 0;
     }
    
     // Function to create a new node with given data
     struct Node* createNode(int data) {
         struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
         if (newNode == NULL) {
             printf("Memory allocation failed. Exiting...");
             exit(1);
         }
         newNode->data = data;
         newNode->next = NULL;
         return newNode;
     }
    
     // Function to perform push operation on the stack
     void push(struct Node** top, int data) {
         struct Node* newNode = createNode(data);
         newNode->next = *top;
         *top = newNode;
     }
    
     // Function to perform pop operation on the stack
     void pop(struct Node** top) {
         if (*top == NULL) {
             printf("Stack is empty. Cannot pop.\n");
             return;
         }
    
         struct Node* temp = *top;
         *top = (*top)->next;
         free(temp);
         printf("Popped an element from the stack.\n");
     }
    
     // Function to display the contents of the stack
     void displayStack(struct Node* top) {
         struct Node* temp = top;
         while (temp != NULL) {
             printf("%d ", temp->data);
             temp = temp->next;
         }
         printf("\n");
     }
    
     // Function to free the memory allocated for the stack
     void freeStack(struct Node* top) {
         struct Node* temp;
         while (top != NULL) {
             temp = top;
             top = top->next;
             free(temp);
         }
     }
    
  5. Dynamic Queue

     #include <stdio.h>
     #include <stdlib.h>
    
     // Structure to represent a queue node
     struct Node {
         int data;
         struct Node* next;
     };
    
     // Structure to represent a queue
     struct Queue {
         struct Node* front;
         struct Node* rear;
     };
    
     // Function prototypes
     struct Node* createNode(int data);
     struct Queue* createQueue();
     void enqueue(struct Queue* queue, int data);
     void dequeue(struct Queue* queue);
     void displayQueue(struct Queue* queue);
     void freeQueue(struct Queue* queue);
    
     int main() {
         struct Queue* myQueue = createQueue(); // Initialize an empty queue
    
         // Enqueue elements into the queue
         enqueue(myQueue, 10);
         enqueue(myQueue, 20);
         enqueue(myQueue, 30);
    
         // Display the contents of the queue
         printf("Queue contents: ");
         displayQueue(myQueue);
    
         // Dequeue an element from the queue
         dequeue(myQueue);
    
         // Display the contents of the queue after dequeuing
         printf("Queue contents after dequeue: ");
         displayQueue(myQueue);
    
         // Free the memory allocated for the queue
         freeQueue(myQueue);
    
         return 0;
     }
    
     // Function to create a new node with given data
     struct Node* createNode(int data) {
         struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
         if (newNode == NULL) {
             printf("Memory allocation failed. Exiting...");
             exit(1);
         }
         newNode->data = data;
         newNode->next = NULL;
         return newNode;
     }
    
     // Function to create an empty queue
     struct Queue* createQueue() {
         struct Queue* newQueue = (struct Queue*)malloc(sizeof(struct Queue));
         if (newQueue == NULL) {
             printf("Memory allocation failed. Exiting...");
             exit(1);
         }
         newQueue->front = newQueue->rear = NULL;
         return newQueue;
     }
    
     // Function to perform enqueue operation on the queue
     void enqueue(struct Queue* queue, int data) {
         struct Node* newNode = createNode(data);
         if (queue->rear == NULL) {
             // If the queue is empty
             queue->front = queue->rear = newNode;
         } else {
             // If the queue is not empty
             queue->rear->next = newNode;
             queue->rear = newNode;
         }
     }
    
     // Function to perform dequeue operation on the queue
     void dequeue(struct Queue* queue) {
         if (queue->front == NULL) {
             printf("Queue is empty. Cannot dequeue.\n");
             return;
         }
    
         struct Node* temp = queue->front;
         queue->front = temp->next;
         free(temp);
         printf("Dequeued an element from the queue.\n");
    
         // If the last element is dequeued, update rear to NULL
         if (queue->front == NULL) {
             queue->rear = NULL;
         }
     }
    
     // Function to display the contents of the queue
     void displayQueue(struct Queue* queue) {
         struct Node* temp = queue->front;
         while (temp != NULL) {
             printf("%d ", temp->data);
             temp = temp->next;
         }
         printf("\n");
     }
    
     // Function to free the memory allocated for the queue
     void freeQueue(struct Queue* queue) {
         while (queue->front != NULL) {
             dequeue(queue);
         }
         free(queue);
     }
    
  1. Dynamic String Concatenation

     #include <stdio.h>
     #include <stdlib.h>
     #include <string.h>
    
     // Function prototype
     char* concatenateStrings(const char* str1, const char* str2);
    
     int main() {
         // Input two strings
         char str1[50], str2[50];
    
         printf("Enter the first string: ");
         scanf("%s", str1);
    
         printf("Enter the second string: ");
         scanf("%s", str2);
    
         // Call the function to concatenate strings
         char* result = concatenateStrings(str1, str2);
    
         // Display the concatenated string
         printf("Concatenated string: %s\n", result);
    
         // Free the dynamically allocated memory
         free(result);
    
         return 0;
     }
    
     // Function to concatenate two strings and allocate memory for the result
     char* concatenateStrings(const char* str1, const char* str2) {
         // Calculate the length of the resulting string
         int len1 = strlen(str1);
         int len2 = strlen(str2);
         int resultLen = len1 + len2 + 1; // +1 for the null terminator
    
         // Dynamically allocate memory for the resulting string
         char* result = (char*)malloc(resultLen * sizeof(char));
         if (result == NULL) {
             printf("Memory allocation failed. Exiting...");
             exit(1);
         }
    
         // Copy the contents of the first string into the resulting string
         strcpy(result, str1);
    
         // Concatenate the second string to the resulting string
         strcat(result, str2);
    
         return result;
     }
    
  1. Dynamic Structure

     #include <stdio.h>
     #include <stdlib.h>
    
     // Structure to represent a student
     struct Student {
         char name[50];
         int rollNumber;
         float marks;
     };
    
     // Function prototypes
     struct Student* createStudentArray(int size);
     void inputStudentData(struct Student* students, int size);
     void displayStudentData(struct Student* students, int size);
     void freeStudentArray(struct Student* students);
    
     int main() {
         int size;
    
         // Get the number of students from the user
         printf("Enter the number of students: ");
         scanf("%d", &size);
    
         // Create an array of structures dynamically
         struct Student* studentArray = createStudentArray(size);
    
         // Input data for each student
         inputStudentData(studentArray, size);
    
         // Display data for each student
         displayStudentData(studentArray, size);
    
         // Free the dynamically allocated memory
         freeStudentArray(studentArray);
    
         return 0;
     }
    
     // Function to dynamically create an array of structures
     struct Student* createStudentArray(int size) {
         struct Student* students = (struct Student*)malloc(size * sizeof(struct Student));
         if (students == NULL) {
             printf("Memory allocation failed. Exiting...");
             exit(1);
         }
         return students;
     }
    
     // Function to input data for each student
     void inputStudentData(struct Student* students, int size) {
         for (int i = 0; i < size; i++) {
             printf("\nEnter data for student %d:\n", i + 1);
             printf("Name: ");
             scanf("%s", students[i].name);
             printf("Roll Number: ");
             scanf("%d", &students[i].rollNumber);
             printf("Marks: ");
             scanf("%f", &students[i].marks);
         }
     }
    
     // Function to display data for each student
     void displayStudentData(struct Student* students, int size) {
         printf("\nStudent Data:\n");
         for (int i = 0; i < size; i++) {
             printf("Student %d:\n", i + 1);
             printf("Name: %s\n", students[i].name);
             printf("Roll Number: %d\n", students[i].rollNumber);
             printf("Marks: %.2f\n", students[i].marks);
         }
     }
    
     // Function to free the dynamically allocated memory
     void freeStudentArray(struct Student* students) {
         free(students);
     }
    
  2. File Reading and Dynamic Memory

     #include <stdio.h>
     #include <stdlib.h>
    
     // Function prototypes
     char* readFromFile(const char* filename);
     void printFileContent(const char* content);
    
     int main() {
         const char* filename = "input.txt"; // Replace with the actual file name
    
         // Read data from the file
         char* fileContent = readFromFile(filename);
    
         // Print the contents read from the file
         printf("File Contents:\n");
         printFileContent(fileContent);
    
         // Free the dynamically allocated memory
         free(fileContent);
    
         return 0;
     }
    
     // Function to read data from a file and dynamically allocate memory
     char* readFromFile(const char* filename) {
         FILE* file = fopen(filename, "r");
         if (file == NULL) {
             printf("Error opening file. Exiting...");
             exit(1);
         }
    
         // Determine the size of the file
         fseek(file, 0, SEEK_END);
         long fileSize = ftell(file);
         fseek(file, 0, SEEK_SET);
    
         // Allocate memory to store the file content
         char* content = (char*)malloc((fileSize + 1) * sizeof(char));
         if (content == NULL) {
             printf("Memory allocation failed. Exiting...");
             exit(1);
         }
    
         // Read the file content into the allocated memory
         fread(content, sizeof(char), fileSize, file);
         content[fileSize] = '\0'; // Null-terminate the string
    
         // Close the file
         fclose(file);
    
         return content;
     }
    
     // Function to print the contents read from the file
     void printFileContent(const char* content) {
         printf("%s", content);
     }
    
  3. Dynamic 2D Array

     #include <stdio.h>
     #include <stdlib.h>
    
     // Function prototypes
     int** createDynamicArray(int rows, int cols);
     void inputArrayElements(int** array, int rows, int cols);
     void displayArray(int** array, int rows, int cols);
     void freeDynamicArray(int** array, int rows);
    
     int main() {
         int **dynamicArray;
         int rows, cols;
    
         // Input dimensions of the array
         printf("Enter dimensions of the 2D array:\n");
         printf("Rows: ");
         scanf("%d", &rows);
         printf("Columns: ");
         scanf("%d", &cols);
    
         // Dynamically allocate memory for the 2D array
         dynamicArray = createDynamicArray(rows, cols);
    
         // Input elements of the array
         printf("Enter elements of the 2D array:\n");
         inputArrayElements(dynamicArray, rows, cols);
    
         // Display the 2D array
         printf("\n2D Array:\n");
         displayArray(dynamicArray, rows, cols);
    
         // Free dynamically allocated memory
         freeDynamicArray(dynamicArray, rows);
    
         return 0;
     }
    
     // Function to dynamically create a 2D array
     int** createDynamicArray(int rows, int cols) {
         int **array = (int **)malloc(rows * sizeof(int *));
         if (array == NULL) {
             printf("Memory allocation failed. Exiting...");
             exit(1);
         }
    
         for (int i = 0; i < rows; i++) {
             array[i] = (int *)malloc(cols * sizeof(int));
             if (array[i] == NULL) {
                 printf("Memory allocation failed. Exiting...");
                 exit(1);
             }
         }
    
         return array;
     }
    
     // Function to input elements of the 2D array
     void inputArrayElements(int** array, int rows, int cols) {
         for (int i = 0; i < rows; i++) {
             for (int j = 0; j < cols; j++) {
                 printf("Enter element at position (%d, %d): ", i + 1, j + 1);
                 scanf("%d", &array[i][j]);
             }
         }
     }
    
     // Function to display the 2D array
     void displayArray(int** array, int rows, int cols) {
         for (int i = 0; i < rows; i++) {
             for (int j = 0; j < cols; j++) {
                 printf("%d\t", array[i][j]);
             }
             printf("\n");
         }
     }
    
     // Function to free dynamically allocated memory for the 2D array
     void freeDynamicArray(int** array, int rows) {
         for (int i = 0; i < rows; i++) {
             free(array[i]);
         }
         free(array);
     }
    
0
Subscribe to my newsletter

Read articles from Jyotiprakash Mishra directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jyotiprakash Mishra
Jyotiprakash Mishra

I am Jyotiprakash, a deeply driven computer systems engineer, software developer, teacher, and philosopher. With a decade of professional experience, I have contributed to various cutting-edge software products in network security, mobile apps, and healthcare software at renowned companies like Oracle, Yahoo, and Epic. My academic journey has taken me to prestigious institutions such as the University of Wisconsin-Madison and BITS Pilani in India, where I consistently ranked among the top of my class. At my core, I am a computer enthusiast with a profound interest in understanding the intricacies of computer programming. My skills are not limited to application programming in Java; I have also delved deeply into computer hardware, learning about various architectures, low-level assembly programming, Linux kernel implementation, and writing device drivers. The contributions of Linus Torvalds, Ken Thompson, and Dennis Ritchie—who revolutionized the computer industry—inspire me. I believe that real contributions to computer science are made by mastering all levels of abstraction and understanding systems inside out. In addition to my professional pursuits, I am passionate about teaching and sharing knowledge. I have spent two years as a teaching assistant at UW Madison, where I taught complex concepts in operating systems, computer graphics, and data structures to both graduate and undergraduate students. Currently, I am an assistant professor at KIIT, Bhubaneswar, where I continue to teach computer science to undergraduate and graduate students. I am also working on writing a few free books on systems programming, as I believe in freely sharing knowledge to empower others.