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.