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
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
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
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
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
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
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]
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
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
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
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
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 }
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)
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; }
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
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"
Below are implementations of the above functions without using standard library functions:
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
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
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
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"
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.
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.
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.
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.
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.
Although both structures and unions allow grouping of different data types, there are key differences:
struct Student { int roll_no; char name[50]; };
union Value { int i; float f; char c; };
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
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.
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.
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
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 }
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
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
For working with text files, we use functions like fgetc(), fputc(), and fscanf() for reading and writing single characters or formatted input.
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
The fputc() function writes a single character to the file.
fputc('A', file); // Write character 'A' to the file
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
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.
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.