Derived data types are built from the basic data types and used for more complex data structures.
Arrays: A collection of variables of the same type stored in contiguous memory locations.
Example: int numbers[5] = {1, 2, 3, 4, 5};
Usage: Accessed using indices, starting from 0.
Pointers: Variables that store the memory address of another variable.
Example: int *ptr = # (pointer to an integer)
Usage: Allows direct memory manipulation, which is powerful but requires careful handling.
Structures (struct): A user-defined data type that groups variables of different types.
Example:-
struct Student {
int id;
char name[50];
float grade;
};
Usage: Useful for representing complex data structures (e.g., a student with ID, name, and grade).
Unions (union): Similar to structures but store only one of the member variables at a time, as all members share the same memory location.
Example:-
union Data {
int id;
float value;
};
Usage: Saves memory when only one member is needed at a time.
enum: A user-defined data type consisting of named integer constants, improving code readability.
Example:
enum Days { MON, TUE, WED, THU, FRI, SAT, SUN };
Usage: Assigns symbolic names to integral values, making code more understandable.
void: Represents the absence of a data type. Commonly used in functions.
Function without a return value: void display() { ... }
Void pointer: void *ptr; (a generic pointer that can point to any data type)
An array in C is a collection of elements of the same data type stored in contiguous memory locations. Arrays are used to store multiple values in a single variable, which is especially useful when dealing with large amounts of data of the same type. Arrays are one of the most commonly used data types in C for organizing data.
Fixed Size:
Once an array is declared with a specific size, it cannot be resized. The size of the array must be specified at the time of declaration.
Contiguous Memory Allocation:
All elements of an array are stored in contiguous memory locations. This makes it efficient for accessing and manipulating array elements using indices.
Same Data Type:
All elements of an array must be of the same data type, whether it's int, char, float, or any other type. This allows for easier processing and manipulation of data.
Zero-based Indexing:
Array indices in C start from 0. The first element of an array is accessed using index 0, the second with index 1, and so on.
Memory Efficiency:
Arrays are memory efficient when you know the exact number of elements in advance, as they allocate a contiguous block of memory based on the size of the array.
Fixed-Type Elements:
Since all elements must be of the same data type, arrays cannot hold elements of different types simultaneously, unlike structures or unions.
Multidimensional Arrays:
C allows for arrays to have more than one dimension (i.e., 2D arrays, 3D arrays, etc.). These can be used for more complex data representations like matrices.
Array Name as Pointer:
The name of an array in C acts as a constant pointer to the first element of the array. This allows array elements to be accessed through pointers.
Data Storage:
Arrays are commonly used for storing collections of data such as integers, characters, and floating-point numbers in a structured way.
Efficient Data Access:
Arrays allow for efficient and random access to their elements using indices. This makes it easy to iterate over and manipulate array data.
Multi-Dimensional Data Representation:
Arrays can represent matrices, grids, and other multi-dimensional data structures, making them useful in fields like image processing, scientific computing, and simulations.
Function Arguments:
Arrays can be passed to functions as arguments, allowing functions to manipulate large datasets without needing to return large values.
Sorting and Searching:
Arrays are used as the base for many common algorithms like sorting (e.g., bubble sort, quicksort) and searching (e.g., binary search).
Buffer Storage:
Arrays are used to create buffers for storing temporary data during program execution, such as in file I/O or network data transfer.
Efficient Memory Usage:
Arrays allocate a fixed amount of memory, which is ideal when you know the number of elements in advance, allowing for efficient memory usage and management.
Declaring an Array
Single-Dimensional Array:
data_type array_name[array_size];
data_type: The type of elements to be stored in the array (e.g., int, char, float).
array_name: The name of the array.
array_size: The number of elements the array can hold (must be a positive integer).
Example:
int numbers[5]; // Declaring an array of 5 integers
Multi-Dimensional Array (2D Array):
data_type array_name[row_size][column_size];
Example:
int matrix[3][3]; // Declaring a 3x3 matrix (2D array)
Initializing an Array
Static Initialization (at the time of declaration):
data_type array_name[] = {value1, value2, value3, ..., valueN};
The array size can be omitted if initializing the array at the time of declaration. The compiler will automatically determine the size.
Example:
int numbers[] = {1, 2, 3, 4, 5}; // Array with 5 elements
Explicit Size Initialization:
data_type array_name[array_size] = {value1, value2, value3, ..., valueN};
Example:
c
Copy code
int numbers[5] = {1, 2, 3, 4, 5}; // Array with size 5, initialized with values
Partial Initialization:
If fewer values are provided than the declared size, the remaining elements are initialized to 0 for numeric types or NULL for pointers.
Example:
int numbers[5] = {1, 2}; // Array will be initialized to {1, 2, 0, 0, 0}
Accessing Array Elements
Array elements can be accessed using indices, starting from 0.
Syntax:
array_name[index]
Example:
int numbers[] = {1, 2, 3, 4, 5};
printf("%d", numbers[2]); // Accessing the 3rd element (index 2), which is 3
Iterating Through an Array
You can use loops to iterate over the array elements.
Example (using a for loop):
int main() {
int numbers[] = {1, 2, 3, 4, 5};
// Iterating through the array using a for loop
for (int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
return 0;
}
Output
1 2 3 4 5
Example 1: One-Dimensional Array
int main() {
int marks[5] = {85, 90, 78, 92, 88};
// Accessing elements using indices
printf("Marks of 1st student: %d\n", marks[0]);
printf("Marks of 2nd student: %d\n", marks[1]);
return 0;
}
Output:
Marks of 1st student: 85
Marks of 2nd student: 90
Example 2: Two-Dimensional Array (Matrix)
int main() {
int matrix[2][2] = {{1, 2}, {3, 4}};
// Accessing 2D array elements
printf("Element at [0][0]: %d\n", matrix[0][0]);
printf("Element at [1][1]: %d\n", matrix[1][1]);
return 0;
}
Output:
Element at [0][0]: 1
Element at [1][1]: 4
Example 3: Array with Pointer
int main() {
int numbers[] = {10, 20, 30, 40, 50};
int *ptr = numbers; // Pointer to the first element of the array
printf("First element: %d\n", *ptr);
printf("Second element: %d\n", *(ptr + 1)); // Using pointer arithmetic
return 0;
}
Output:
First element: 10
Second element: 20
Example 4: Array as Function Argument
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
printArray(arr, 5);
return 0;
}
Output
1 2 3 4 5
Zero-based Indexing:
Array indices start from 0 in C, so the first element is accessed via index 0.
Fixed Size:
Once an array is declared, its size cannot be changed dynamically, and the number of elements is fixed.
Contiguous Memory Allocation:
All elements of an array are stored in adjacent memory locations, making access via indexing fast.
Multidimensional Arrays:
Arrays can have more than one dimension, such as 2D or 3D arrays, useful for representing matrices, grids, etc.
int main() {
int numbers[] = {1, 2, 3, 4, 5};
printf("First element: %d\n", numbers[0]);
printf("Last element: %d\n", numbers[4]);
return 0;
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < 5; i++) {
sum += numbers[i];
}
printf("Sum of array elements: %d\n", sum);
return 0;
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
int size = 5;
printf("Reversed array: ");
for (int i = size - 1; i >= 0; i--) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
int main() {
int numbers[] = {3, 1, 4, 1, 5, 9};
int max = numbers[0];
for (int i = 1; i < 6; i++) {
if (numbers[i] > max) {
max = numbers[i];
}
}
printf("Maximum element: %d\n", max);
return 0;
}
#include <stdio.h>
int main() {
int matrix[2][2] = {{1, 2}, {3, 4}};
printf("Matrix elements:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
return 0;
}
#include <stdio.h>
int main() {
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int transpose[3][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
transpose[j][i] = matrix[i][j];
}
}
printf("Transpose of the matrix:\n");
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 2; j++) {
printf("%d ", transpose[i][j]);
}
printf("\n");
}
return 0;
}
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello";
printf("String: %s\n", str);
// String length
printf("Length of string: %ld\n", strlen(str));
// Reverse string
printf("Reversed string: ");
for (int i = strlen(str) - 1; i >= 0; i--) {
putchar(str[i]);
}
printf("\n");
return 0;
}
int main() {
int numbers[] = {5, 2, 9, 1, 5, 6};
int size = 6;
// Bubble sort
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (numbers[j] > numbers[j + 1]) {
int temp = numbers[j];
numbers[j] = numbers[j + 1];
numbers[j + 1] = temp;
}
}
}
printf("Sorted array: ");
for (int i = 0; i < size; i++) {
printf("%d ", numbers[i]);
}
printf("\n");
return 0;
}
int main() {
int numbers[] = {10, 20, 30, 40, 50};
int target = 30;
int found = 0;
for (int i = 0; i < 5; i++) {
if (numbers[i] == target) {
printf("Element %d found at index %d\n", target, i);
found = 1;
break;
}
}
if (!found) {
printf("Element %d not found\n", target);
}
return 0;
}
int main() {
int source[] = {1, 2, 3, 4, 5};
int destination[5];
for (int i = 0; i < 5; i++) {
destination[i] = source[i];
}
printf("Copied array: ");
for (int i = 0; i < 5; i++) {
printf("%d ", destination[i]);
}
printf("\n");
return 0;
}
A pointer in C is a variable that stores the memory address of another variable. Pointers are fundamental to the C language and allow for dynamic memory management, efficient data manipulation, and more complex data structures like linked lists, trees, etc.
Memory Address Storage:
Pointers hold the memory address of another variable. Instead of directly holding data, they point to a location in memory where the data is stored.
Data Type Association:
A pointer must be associated with a specific data type, which dictates the size of the data the pointer will refer to. For example, an int pointer stores the address of an int, and a char pointer stores the address of a char.
Dereferencing:
Dereferencing a pointer means accessing the data at the memory address stored by the pointer. This is done using the * operator.
Address-of Operator (&):
The address-of operator (&) is used to get the address of a variable, which can be stored in a pointer.
Pointer Arithmetic:
Pointers can be incremented or decremented. When you perform arithmetic on pointers, the pointer moves by the size of the data type it points to.
Null Pointer:
A pointer can be set to NULL to indicate that it is not pointing to any valid memory location.
Dynamic Memory Allocation:
Pointers are essential for dynamic memory management, such as allocating memory at runtime using functions like malloc, calloc, and free.
Dynamic Memory Allocation:
Pointers are used to allocate memory dynamically at runtime, which is essential for handling memory efficiently in large programs (e.g., using malloc, calloc, free).
Efficient Function Arguments:
When you pass a pointer to a function, the function can modify the actual argument passed by reference, rather than making a copy. This is especially useful for large data structures (like arrays or structures).
Building Complex Data Structures:
Pointers are used to build complex data structures like linked lists, trees, and graphs, where each node or element contains a pointer to the next element.
Memory Management:
Pointers allow low-level access to memory, giving you the ability to manage memory directly for tasks such as memory allocation, deallocation, and resizing.
Arrays and Strings:
In C, arrays and strings are closely tied to pointers. An array's name acts as a pointer to the first element, and string manipulation often involves pointer arithmetic.
Function Pointers:
Pointers can also point to functions, which is useful for implementing callback functions or for handling event-driven programming.
Efficient Array Manipulation:
Pointers can be used to iterate over arrays more efficiently, without copying the entire array.
Declaring a Pointer
To declare a pointer, use the * operator to indicate that the variable is a pointer to the specified data type:
data_type *pointer_name;
Example: Declaring a Pointer
int *ptr; // Pointer to an integer
char *str; // Pointer to a character
Assigning a Value to a Pointer
To assign a pointer the address of a variable, use the & operator:
ptr = &variable;
Dereferencing a Pointer
To access the value stored at the memory location the pointer is pointing to, use the * operator (dereferencing):
int value = *ptr;
Pointer Initialization
int a = 5;
int *ptr = &a;
Example 1: Basic Pointer
int main() {
int a = 10;
int *ptr = &a; // pointer ptr holds the address of variable a
printf("Address of a: %p\n", ptr); // Print the address stored in ptr
printf("Value of a: %d\n", *ptr); // Dereference ptr to print the value of a
return 0;
}
Output:
Address of a: 0x7ffee0b6d4bc
Value of a: 10
In this example, ptr holds the memory address of a, and dereferencing ptr (*ptr) gives the value stored at that address.
Example 2: Pointer to Array
int main() {
int arr[3] = {1, 2, 3};
int *ptr = arr; // Array name is the address of the first element
printf("First element: %d\n", *ptr); // Dereference ptr to get the first element
ptr++; // Move pointer to the next element
printf("Second element: %d\n", *ptr);
ptr++; // Move pointer to the third element
printf("Third element: %d\n", *ptr);
return 0;
}
Output:
First element: 1
Second element: 2
Third element: 3
In this example, ptr points to the first element of the array. The pointer arithmetic (ptr++) moves the pointer to the next element in the array.
Example 3: Pointer with Dynamic Memory Allocation
c
Copy code
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
ptr = (int *)malloc(3 * sizeof(int)); // Dynamically allocate memory for 3 integers
if (ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// Assign values to the allocated memory
ptr[0] = 10;
ptr[1] = 20;
ptr[2] = 30;
printf("Values: %d, %d, %d\n", ptr[0], ptr[1], ptr[2]);
free(ptr); // Free the allocated memory
return 0;
}
Output:
makefile
Copy code
Values: 10, 20, 30
Here, we use the malloc() function to dynamically allocate memory for 3 integers, assign values to them, and print them. Finally, we free the allocated memory using free().
Example 4: Function Pointer
#include <stdio.h>
void greet() {
printf("Hello, World!\n");
}
int main() {
void (*func_ptr)(); // Declare a function pointer
func_ptr = greet; // Assign function address to the pointer
func_ptr(); // Call the function through the pointer
return 0;
}
Output:
Copy code
Hello, World!
In this example, we declare a function pointer func_ptr that points to the greet() function. We then call the function through the pointer.
Dereferencing:
Dereferencing a pointer using the * operator gives access to the value stored at the address the pointer points to.
Pointer Arithmetic:
You can perform arithmetic on pointers (e.g., ptr++, ptr--) to navigate through memory locations. Pointer arithmetic depends on the size of the data type the pointer is pointing to.
Null Pointer:
A pointer can be set to NULL to indicate it is not pointing to any valid memory address. Dereferencing a NULL pointer leads to undefined behavior.
Memory Management:
Pointers are essential for dynamic memory allocation, allowing memory to be allocated and deallocated during runtime using malloc, calloc, realloc, and free.
Function Pointers:
Function pointers allow functions to be passed as arguments, enabling more flexible and dynamic behavior in programs.
int main() {
int num = 10;
int *ptr = #
printf("Value of num: %d\n", num);
printf("Address of num: %p\n", &num);
printf("Value stored in ptr: %p\n", ptr);
printf("Value pointed to by ptr: %d\n", *ptr);
return 0;
}
int main() {
int arr[] = {10, 20, 30, 40, 50};
int *ptr = arr;
printf("Pointer arithmetic:\n");
for (int i = 0; i < 5; i++) {
printf("Element %d: %d\n", i, *(ptr + i));
}
return 0;
}
void increment(int *ptr) {
(*ptr)++;
}
int main() {
int num = 5;
printf("Before increment: %d\n", num);
increment(&num);
printf("After increment: %d\n", num);
return 0;
}
int main() {
int arr[] = {1, 2, 3, 4, 5};
int *ptr = arr;
printf("Array elements using pointers:\n");
for (int i = 0; i < 5; i++) {
printf("%d ", *(ptr + i));
}
printf("\n");
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr;
int n = 5;
ptr = (int *)malloc(n * sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed\n");
return 1;
}
for (int i = 0; i < n; i++) {
ptr[i] = i + 1;
}
printf("Dynamically allocated array:\n");
for (int i = 0; i < n; i++) {
printf("%d ", ptr[i]);
}
printf("\n");
free(ptr);
return 0;
}
struct Point {
int x, y;
};
int main() {
struct Point p = {10, 20};
struct Point *ptr = &p;
printf("Point coordinates: (%d, %d)\n", ptr->x, ptr->y);
return 0;
}
int main() {
int num = 10;
int *ptr = #
int **ptr2 = &ptr;
printf("Value of num: %d\n", **ptr2);
printf("Address of num: %p\n", *ptr2);
printf("Value stored in ptr: %p\n", ptr);
printf("Address of ptr: %p\n", &ptr);
return 0;
}
void greet() {
printf("Hello, World!\n");
}
int main() {
void (*funcPtr)() = greet;
funcPtr();
return 0;
}
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
printf("Before swap: x = %d, y = %d\n", x, y);
swap(&x, &y);
printf("After swap: x = %d, y = %d\n", x, y);
return 0;
}
int main() {
char str[] = "Hello, Pointers!";
char *ptr = str;
printf("String: ");
while (*ptr != '\0') {
printf("%c", *ptr);
ptr++;
}
printf("\n");
return 0;
}
A union in C is a user-defined data type that allows storing different data types in the same memory location. Unlike structures, where each member gets its own memory, all members of a union share the same memory space. A union allows the same memory location to hold different data types at different times, depending on which member is being accessed.
Memory Sharing:
All members of a union share the same memory space. The size of a union is the size of its largest member, and the total memory used by a union is enough to hold its largest member.
Single Member at a Time:
At any given time, only one member of the union can hold a value. When a new value is assigned to one member, the value of the previous member is overwritten.
Efficient Memory Usage:
Since all members share the same memory location, unions provide an efficient way to store data when only one of the members is needed at a time, saving memory.
Syntax:
The syntax for defining and using unions is similar to structures, but they are defined using the union keyword instead of struct.
Initialization:
Unions can be initialized only with one member, and this member will occupy the shared memory space.
Memory Alignment:
Just like structures, unions also may have padding between members depending on the compiler and the target platform.
Memory Efficiency:
Unions are useful when different types of data need to be stored but not simultaneously. It saves memory by allowing multiple types to share the same space.
Type Conversion:
Unions are commonly used for type conversion, where you can store different data types in a union and interpret the same memory in different ways.
Handling Different Data Types in a Generic Way:
Unions are often used in low-level programming, such as when working with hardware or network protocols, where data can be interpreted in multiple formats.
Union in Structs:
Unions can be used as members within structures to manage different data types in a flexible way.
Variant Records:
Unions are suitable for representing data structures where only one type of information is valid at a time, like in a "variant" record.
Defining a Union
c
Copy code
union UnionName {
data_type member1;
data_type member2;
...
};
Declaring a Union Variable
c
Copy code
union UnionName variableName;
Accessing Union Members
Use the dot (.) operator for accessing union members in the case of a union variable.
c
Copy code
variableName.member1;
Use the arrow (->) operator for accessing union members when working with union pointers.
c
Copy code
pointerName->member1;
Example of Union Definition and Declaration
c
Copy code
union Data {
int i;
float f;
char str[20];
};
union Data data1;
Example 1: Basic Union Usage
c
Copy code
#include <stdio.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data1;
data1.i = 10; // Assign an integer value
printf("data1.i: %d\n", data1.i);
data1.f = 220.5; // Assign a float value (overwrites the integer)
printf("data1.f: %.2f\n", data1.f);
// Now, assign a string value (overwrites the float)
strcpy(data1.str, "Hello, Union!");
printf("data1.str: %s\n", data1.str);
return 0;
}
Output:
makefile
Copy code
data1.i: 10
data1.f: 220.50
data1.str: Hello, Union!
In this example, we see that the union's members share the same memory space, and as each member is assigned a new value, the previous one is overwritten.
Example 2: Union with Struct
c
Copy code
#include <stdio.h>
struct Employee {
char name[50];
union {
int employeeID;
float salary;
};
};
int main() {
struct Employee e1 = {"John Doe", .employeeID = 12345};
printf("Employee Name: %s\n", e1.name);
printf("Employee ID: %d\n", e1.employeeID);
// Assigning the salary value to the union member (overwrites employeeID)
e1.salary = 50000.50;
printf("Salary: %.2f\n", e1.salary);
return 0;
}
Output:
yaml
Copy code
Employee Name: John Doe
Employee ID: 12345
Salary: 50000.50
This example demonstrates using a union inside a struct. The union allows storing either the employeeID or salary, but not both at the same time.
Example 3: Union for Type Conversion
c
Copy code
#include <stdio.h>
union Data {
int i;
float f;
char c;
};
int main() {
union Data data1;
data1.i = 65; // Storing integer value
printf("data1.i: %d\n", data1.i);
// Reinterpreting the same memory as a float
data1.f = 3.14;
printf("data1.f: %.2f\n", data1.f);
// Reinterpreting the same memory as a char (ASCII value 65 is 'A')
data1.c = 'A';
printf("data1.c: %c\n", data1.c);
return 0;
}
Output:
makefile
Copy code
data1.i: 65
data1.f: 3.14
data1.c: A
Here, the same memory is used to store and interpret different data types: an integer, a float, and a character. The value is overwritten each time a new type is stored in the union.
Memory Allocation:
The size of the union is determined by the largest member. All members share the same memory space, so only one member can be used at a time.
Accessing Members:
You can only access one member of a union at a time, and assigning a value to one member overwrites the previous member.
Efficient Memory Usage:
Unions are useful for saving memory when you know that only one member will be used at any given time.
Default Values:
Unions do not have default values. You need to initialize a member explicitly.
Type Compatibility:
Unions can be used for type conversion by storing data of different types in the same memory location and accessing them as required.
Nested Unions:
Unions can be nested inside structures or other unions, allowing more flexible data representations.
#include <stdio.h>
union Data {
int i;
float f;
char c;
};
int main() {
union Data data;
data.i = 10;
printf("Integer: %d\n", data.i);
data.f = 3.14;
printf("Float: %.2f\n", data.f);
data.c = 'A';
printf("Character: %c\n", data.c);
return 0;
}
c
Copy code
#include <stdio.h>
union Data {
int i;
double d;
char c;
};
int main() {
printf("Size of union: %ld bytes\n", sizeof(union Data));
return 0;
}
#include <stdio.h>
union Data {
int arr[5];
float f;
};
int main() {
union Data data;
for (int i = 0; i < 5; i++) {
data.arr[i] = i + 1;
}
printf("Array values in union:\n");
for (int i = 0; i < 5; i++) {
printf("%d ", data.arr[i]);
}
printf("\n");
return 0;
}
#include <stdio.h>
struct StructData {
int i;
float f;
};
union UnionData {
int i;
float f;
};
int main() {
printf("Size of struct: %ld bytes\n", sizeof(struct StructData));
printf("Size of union: %ld bytes\n", sizeof(union UnionData));
return 0;
}
#include <stdio.h>
struct Point {
int x, y;
};
union Data {
struct Point p;
int value;
};
int main() {
union Data data;
data.p.x = 10;
data.p.y = 20;
printf("Point: (%d, %d)\n", data.p.x, data.p.y);
data.value = 100;
printf("Overwritten value: %d\n", data.value);
return 0;
}
#include <stdio.h>
union Converter {
int i;
float f;
};
int main() {
union Converter conv;
conv.i = 1065353216; // Bit representation of 1.0 in IEEE 754
printf("Integer: %d\n", conv.i);
printf("Float: %f\n", conv.f);
return 0;
}
#include <stdio.h>
union Data {
int i;
float f;
char c;
};
int main() {
union Data data;
data.i = 42;
printf("Integer: %d\n", data.i);
data.f = 3.14;
printf("Float (overwritten): %.2f\n", data.f);
data.c = 'X';
printf("Character (overwritten): %c\n", data.c);
return 0;
}
#include <stdio.h>
union Data {
int i;
float f;
};
int main() {
union Data data;
int choice;
printf("Enter 1 for integer, 2 for float: ");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("Enter an integer: ");
scanf("%d", &data.i);
printf("You entered: %d\n", data.i);
break;
case 2:
printf("Enter a float: ");
scanf("%f", &data.f);
printf("You entered: %.2f\n", data.f);
break;
default:
printf("Invalid choice\n");
}
return 0;
}
#include <stdio.h>
union Flags {
struct {
unsigned int flag1 : 1;
unsigned int flag2 : 1;
unsigned int flag3 : 1;
};
unsigned int allFlags;
};
int main() {
union Flags flags;
flags.allFlags = 0;
flags.flag1 = 1;
flags.flag2 = 1;
printf("Flags: %u\n", flags.allFlags);
return 0;
}
#include <stdio.h>
enum DataType { INTEGER, FLOAT, CHARACTER };
union Data {
int i;
float f;
char c;
};
int main() {
union Data data;
enum DataType type;
type = INTEGER;
data.i = 42;
if (type == INTEGER) {
printf("Integer: %d\n", data.i);
}
type = FLOAT;
data.f = 3.14;
if (type == FLOAT) {
printf("Float: %.2f\n", data.f);
}
return 0;
}
#include <stdio.h>
struct Student {
char name[50];
int age;
float marks;
};
int main() {
struct Student s = {"Alice", 20, 85.5};
printf("Name: %s\n", s.name);
printf("Age: %d\n", s.age);
printf("Marks: %.2f\n", s.marks);
return 0;
}
struct Student {
char name[50];
int age;
float marks;
};
int main() {
struct Student s;
printf("Enter name, age, and marks: ");
scanf("%s %d %f", s.name, &s.age, &s.marks);
printf("Student Details:\n");
printf("Name: %s\n", s.name);
printf("Age: %d\n", s.age);
printf("Marks: %.2f\n", s.marks);
return 0;
}
struct Student {
char name[50];
int age;
float marks;
};
int main() {
struct Student students[3] = {
{"Alice", 20, 85.5},
{"Bob", 21, 90.0},
{"Charlie", 22, 88.0}
};
printf("Student Details:\n");
for (int i = 0; i < 3; i++) {
printf("Name: %s, Age: %d, Marks: %.2f\n", students[i].name, students[i].age, students[i].marks);
}
return 0;
}
struct Address {
char city[50];
int pin;
};
struct Student {
char name[50];
struct Address address;
};
int main() {
struct Student s = {"Alice", {"New York", 10001}};
printf("Name: %s\n", s.name);
printf("City: %s\n", s.address.city);
printf("PIN: %d\n", s.address.pin);
return 0;
}
struct Student {
char name[50];
int age;
float marks;
};
int main() {
struct Student s = {"Alice", 20, 85.5};
struct Student *ptr = &s;
printf("Using pointer:\n");
printf("Name: %s\n", ptr->name);
printf("Age: %d\n", ptr->age);
printf("Marks: %.2f\n", ptr->marks);
return 0;
}
#include <stdio.h>
struct Student {
char name[50];
int age;
float marks;
};
void display(struct Student s) {
printf("Name: %s, Age: %d, Marks: %.2f\n", s.name, s.age, s.marks);
}
int main() {
struct Student s = {"Alice", 20, 85.5};
display(s);
return 0;
}
struct Student {
char name[50];
int age;
float marks;
};
struct Student createStudent(char name[], int age, float marks) {
struct Student s;
strcpy(s.name, name);
s.age = age;
s.marks = marks;
return s;
}
int main() {
struct Student s = createStudent("Bob", 21, 90.0);
printf("Name: %s, Age: %d, Marks: %.2f\n", s.name, s.age, s.marks);
return 0;
}
struct Flags {
unsigned int flag1 : 1;
unsigned int flag2 : 1;
unsigned int flag3 : 1;
};
int main() {
struct Flags flags = {1, 0, 1};
printf("Flag1: %d, Flag2: %d, Flag3: %d\n", flags.flag1, flags.flag2, flags.flag3);
return 0;
}
struct Node {
int data;
struct Node *next;
};
int main() {
struct Node node1 = {10, NULL};
struct Node node2 = {20, NULL};
node1.next = &node2;
printf("Node1 Data: %d\n", node1.data);
printf("Node2 Data via Node1: %d\n", node1.next->data);
return 0;
}
struct Data {
int type;
union {
int intValue;
float floatValue;
char charValue;
};
};
int main() {
struct Data data;
data.type = 1; // Integer
data.intValue = 42;
printf("Integer: %d\n", data.intValue);
data.type = 2; // Float
data.floatValue = 3.14;
printf("Float: %.2f\n", data.floatValue);
data.type = 3; // Character
data.charValue = 'A';
printf("Character: %c\n", data.charValue);
return 0;
}
A structure in C is a user-defined data type that allows the grouping of variables (which may have different data types) under a single name. Structures are useful for organizing complex data in a way that makes it more manageable and understandable.
Group Different Data Types:
A structure can hold variables of different data types, which can include integers, floating-point numbers, arrays, and even other structures.
Memory Allocation:
The size of a structure is the sum of the sizes of its members. It is important to note that the structure may include some padding between its members to align the data types in memory for efficiency.
Accessing Members:
Members of a structure are accessed using the dot operator (.) when dealing with structure variables, or the arrow operator (->) when working with pointers to structures.
No Default Value:
Structures do not assign any default values to their members. All members must be initialized explicitly.
Pass By Reference:
Structures are passed by reference to functions, which means the original structure data can be modified by the function.
Flexible Data Handling:
Structures can contain arrays, pointers, or even other structures, allowing for flexible data organization.
Grouping Related Data:
Structures are ideal for grouping related data that should logically be stored together (e.g., a person's name, age, and address).
Database Records:
Structures can represent records in a database (e.g., employee details, product specifications).
Function Arguments:
Structures can be passed as arguments to functions when you need to handle multiple pieces of data in a single unit.
Memory Management:
Structures can be used for efficient memory management, especially when creating linked lists, trees, and other complex data structures.
Creating Complex Data Models:
Structures help in creating complex data models by organizing data into more understandable and reusable components.
Defining a Structure
c
Copy code
struct StructureName {
data_type member1;
data_type member2;
...
};
Example: Basic Structure Definition
c
Copy code
struct Person {
char name[50];
int age;
float height;
};
Declaring a Structure Variable
c
Copy code
struct StructureName variableName;
Example: Declaring a Structure Variable
c
Copy code
struct Person p1;
Accessing Members
Dot Operator: To access members of a structure using the structure variable.
c
Copy code
variableName.memberName;
Arrow Operator: To access members of a structure when working with pointers.
c
Copy code
pointerName->memberName;
Example 1: Basic Structure
c
Copy code
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
struct Person p1 = {"John Doe", 30, 5.9};
printf("Name: %s\n", p1.name);
printf("Age: %d\n", p1.age);
printf("Height: %.2f\n", p1.height);
return 0;
}
Example 2: Structure Initialization
c
Copy code
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
struct Person p1 = {"Alice", 25, 5.5};
struct Person p2 = {"Bob", 28, 6.1};
printf("Person 1: %s, %d, %.2f\n", p1.name, p1.age, p1.height);
printf("Person 2: %s, %d, %.2f\n", p2.name, p2.age, p2.height);
return 0;
}
Example 3: Structure as Function Argument
c
Copy code
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
};
void displayPerson(struct Person p) {
printf("Name: %s\n", p.name);
printf("Age: %d\n", p.age);
printf("Height: %.2f\n", p.height);
}
int main() {
struct Person p1 = {"John Doe", 30, 5.9};
displayPerson(p1);
return 0;
}
Example 4: Structure with Pointers
c
Copy code
#include <stdio.h>
struct Person {
char name[50];
int age;
float height;
};
int main() {
struct Person p1 = {"John Doe", 30, 5.9};
struct Person *ptr = &p1;
printf("Name: %s\n", ptr->name);
printf("Age: %d\n", ptr->age);
printf("Height: %.2f\n", ptr->height);
return 0;
}
Example 5: Nested Structures
c
Copy code
#include <stdio.h>
struct Address {
char street[100];
char city[50];
int zipCode;
};
struct Person {
char name[50];
int age;
struct Address addr; // Nested structure
};
int main() {
struct Person p1 = {"John Doe", 30, {"123 Main St", "Springfield", 12345}};
printf("Name: %s\n", p1.name);
printf("Age: %d\n", p1.age);
printf("Address: %s, %s, %d\n", p1.addr.street, p1.addr.city, p1.addr.zipCode);
return 0;
}
Memory Size:
The size of a structure depends on the size of its members, and padding may be added by the compiler to align the data types properly in memory.
Accessing Members:
Use the dot (.) operator for structure variables and the arrow (->) operator for structure pointers.
Passing to Functions:
Structures are passed by value to functions by default. To modify the structure inside a function, use pointers.
Initialization:
You can initialize a structure at the time of declaration, or you can assign values to its members later.
Nested Structures:
Structures can contain other structures as members, which is helpful for representing complex data.
Memory Allocation:
Structures are typically allocated on the stack. You can dynamically allocate memory for structures using malloc or calloc.
The enum (short for "enumeration") is a user-defined data type in C that allows you to assign names to integral constants, making the code more readable and manageable. It is particularly useful when working with a set of related constants.
Integral Values:
Each enumerator in an enum is associated with an integer value. By default, it starts from 0 and increments by 1 for each subsequent enumerator, unless explicitly defined.
Improves Readability:
enum provides meaningful names for constants, making the code easier to understand and maintain.
Type-Safe:
Enumerations help reduce the likelihood of invalid values being used in the program as they are restricted to the defined set.
Scalable:
Enumerations allow you to group related constants, making it easier to expand and maintain the codebase.
Shared Scope:
Enumerators are visible within the scope where the enum is defined.
Improves Code Readability:
By replacing numeric constants with descriptive names, it enhances the readability of the code.
State Representation:
Enums are often used to represent states, modes, or other predefined sets of values (e.g., traffic light states, days of the week).
Switch-Case Statements:
Enums are commonly used with switch statements for cleaner and more readable branching logic.
Error Handling:
Enums are used to represent error codes in a structured way.
Flexible Values:
You can define custom values for specific enumerators to map them to meaningful constants (e.g., error codes like 404 for "Not Found").
Defining an Enum
c
Copy code
enum EnumName {
Enumerator1,
Enumerator2,
Enumerator3
};
Using an Enum
c
Copy code
enum EnumName variableName;
Example with Initialization
c
Copy code
enum Color {
RED,
GREEN,
BLUE
};
enum Color myColor = GREEN;
Example 1: Basic Enum
c
Copy code
#include <stdio.h>
enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY };
int main() {
enum Day today = WEDNESDAY;
printf("Day of the week: %d\n", today);
return 0;
}
Example 2: Enum with Explicit Values
c
Copy code
#include <stdio.h>
enum Status { SUCCESS = 0, ERROR = 1, TIMEOUT = 2, NOT_FOUND = 404 };
int main() {
enum Status currentStatus = NOT_FOUND;
if (currentStatus == NOT_FOUND) {
printf("Error: Resource not found (Code %d)\n", NOT_FOUND);
}
return 0;
}
Example 3: Enum in Switch Statement
c
Copy code
#include <stdio.h>
enum TrafficLight { RED, YELLOW, GREEN };
int main() {
enum TrafficLight signal = GREEN;
switch (signal) {
case RED:
printf("Stop!\n");
break;
case YELLOW:
printf("Get ready to stop.\n");
break;
case GREEN:
printf("Go!\n");
break;
default:
printf("Invalid signal.\n");
}
return 0;
}
Example 4: Enum with Custom Increment
c
Copy code
#include <stdio.h>
enum Month { JAN = 1, FEB = 2, MAR = 3, APR = 4, DEC = 12 };
int main() {
enum Month currentMonth = DEC;
printf("Current month is: %d\n", currentMonth);
return 0;
}
Example 5: Enum and Struct Together
c
Copy code
#include <stdio.h>
enum Gender { MALE, FEMALE };
struct Person {
char name[50];
enum Gender gender;
};
int main() {
struct Person p = {"Alice", FEMALE};
printf("Name: %s\n", p.name);
printf("Gender: %s\n", (p.gender == FEMALE) ? "Female" : "Male");
return 0;
}
Enumerator Values:
By default, the first enumerator has a value of 0, and subsequent ones increment by 1. Custom values can be assigned.
Memory:
An enum typically occupies the same amount of memory as an int, but it depends on the compiler and platform.
Scope:
Enumerators are visible only within the scope of the enum definition unless declared globally.
Type Compatibility:
Enumerators are essentially integers in C, so they can be used wherever an integer is valid.
Typecasting:
Explicit typecasting may be necessary when using an enumerator with pointers or arithmetic.
#include <stdio.h>
enum Weekday { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY };
int main() {
enum Weekday today = MONDAY;
printf("Today is day %d of the week.\n", today);
return 0;
}
enum Month { JAN = 1, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };
int main() {
enum Month currentMonth = AUG;
printf("The current month is %d.\n", currentMonth);
return 0;
}
enum Color { RED, GREEN, BLUE };
int main() {
enum Color favoriteColor = GREEN;
switch (favoriteColor) {
case RED:
printf("You like red!\n");
break;
case GREEN:
printf("You like green!\n");
break;
case BLUE:
printf("You like blue!\n");
break;
default:
printf("Unknown color.\n");
}
return 0;
}
enum Level { LOW, MEDIUM, HIGH };
void printLevel(enum Level level) {
switch (level) {
case LOW:
printf("Level is LOW.\n");
break;
case MEDIUM:
printf("Level is MEDIUM.\n");
break;
case HIGH:
printf("Level is HIGH.\n");
break;
}
}
int main() {
enum Level currentLevel = MEDIUM;
printLevel(currentLevel);
return 0;
}
enum Permissions { READ = 1, WRITE = 2, EXECUTE = 4 };
int main() {
int userPermissions = READ | EXECUTE;
if (userPermissions & READ) {
printf("User can read.\n");
}
if (userPermissions & WRITE) {
printf("User can write.\n");
}
if (userPermissions & EXECUTE) {
printf("User can execute.\n");
}
return 0;
}
enum Gender { MALE, FEMALE };
struct Person {
char name[50];
enum Gender gender;
};
int main() {
struct Person p1 = {"Alice", FEMALE};
printf("Name: %s\n", p1.name);
printf("Gender: %s\n", (p1.gender == FEMALE) ? "Female" : "Male");
return 0;
}
enum ErrorCode { SUCCESS = 0, ERROR_NOT_FOUND = 404, ERROR_ACCESS_DENIED = 403 };
int main() {
enum ErrorCode status = ERROR_ACCESS_DENIED;
if (status == SUCCESS) {
printf("Operation succeeded.\n");
} else {
printf("Error occurred. Code: %d\n", status);
}
return 0;
}
enum Season { SPRING, SUMMER, FALL, WINTER };
int main() {
enum Season season1 = SPRING, season2 = WINTER;
printf("Season 1: %d\n", season1);
printf("Season 2: %d\n", season2);
return 0;
}
enum Direction { NORTH, EAST, SOUTH, WEST };
int main() {
enum Direction dir = EAST;
printf("Numeric value of direction: %d\n", (int)dir);
return 0;
}
#include <stdio.h>
enum Status { OK, WARNING, ERROR };
enum Status getStatus(int code) {
if (code == 0) return OK;
else if (code < 100) return WARNING;
else return ERROR;
}
int main() {
int code = 50;
enum Status status = getStatus(code);
printf("Status: %d\n", status);
return 0;
}
The void data type in C is a special type used to specify that a function does not return a value, a function does not take arguments, or to represent a generic pointer. Below are its properties, uses, and syntax in detail:
No Return Type:
When a function is declared with a void return type, it does not return a value to the caller.
No Parameters:
A function can accept void as an argument, indicating that it does not accept parameters.
Generic Pointer:
The void type can be used as a pointer (void *) to represent a pointer to any data type. It needs to be cast to the appropriate type before dereferencing.
Cannot Declare Variables:
You cannot declare a variable of type void. For example, void x; is invalid.
Memory Operations:
The void * pointer is often used in memory-related functions like malloc and free.
Functions That Do Not Return a Value:
A void function is typically used to perform actions rather than compute and return a value.
Functions That Take No Parameters:
The void keyword can indicate that a function takes no arguments.
Generic Pointers:
void * pointers allow handling of different data types dynamically. This is useful in generic programming and memory management.
Callback Functions:
void is commonly used in callback mechanisms where the return type is irrelevant.
Dynamic Memory Allocation:
Functions like malloc return void * to provide a generic pointer that can be cast to any specific type.
1. Function with No Return Type
Example:
2. Function with No Parameters
void functionName(void) {
Example:
3. Void Pointer
Example:
4. Dynamic Memory Allocation
Example:
5. Void Function Pointer
Example:
Example 1: Void Function
1. Function with No Return Type
Example:
2. Function with No Parameters
void functionName(void) {
Example:
3. Void Pointer
Example:
4. Dynamic Memory Allocation
Example:
5. Void Function Pointer
Example:
Example 1: Void Function
Example 2: Using Void Pointer
Example 3: Function with Void Parameters
void cannot be used as a variable type.
void * must be typecast to the appropriate data type before use.
Functions with void return type should not contain a return statement with a value.
Use void for generic and flexible functions when the type is unknown or irrelevant.
---------------------------------------------------------------------------------------------------------------------------------------
#include <stdio.h>
void greet() {
printf("Hello, welcome to programming!\n");
}
int main() {
greet();
return 0;
}
void display(void) {
printf("This function takes no arguments.\n");
}
int main() {
display();
return 0;
}
int main() {
int x = 10;
float y = 20.5;
char z = 'A';
void *ptr;
ptr = &x;
printf("Value of x: %d\n", *(int *)ptr);
ptr = &y;
printf("Value of y: %.2f\n", *(float *)ptr);
ptr = &z;
printf("Value of z: %c\n", *(char *)ptr);
return 0;
}
void printValue(void *ptr, char type) {
if (type == 'i') {
printf("Integer: %d\n", *(int *)ptr);
} else if (type == 'f') {
printf("Float: %.2f\n", *(float *)ptr);
} else if (type == 'c') {
printf("Character: %c\n", *(char *)ptr);
}
}
int main() {
int x = 42;
float y = 3.14;
char z = 'A';
printValue(&x, 'i');
printValue(&y, 'f');
printValue(&z, 'c');
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main() {
void *ptr = malloc(sizeof(int));
if (ptr == NULL) {
printf("Memory allocation failed.\n");
return 1;
}
*(int *)ptr = 100;
printf("Dynamically allocated value: %d\n", *(int *)ptr);
free(ptr);
return 0;
}
void callback(void) {
printf("Callback function executed.\n");
}
void executeCallback(void (*func)(void)) {
func();
}
int main() {
executeCallback(callback);
return 0;
}
void showMessage() {
printf("This function does not return a value.\n");
}
int main() {
showMessage();
return 0;
}
void printNumber(int num) {
printf("Number: %d\n", num);
}
void printMessage(char *msg) {
printf("Message: %s\n", msg);
}
int main() {
printNumber(25);
printMessage("Hello, World!");
return 0;
}
void process(void *data) {
printf("Processing data...\n");
}
int main() {
int x = 10;
process(&x);
return 0;
}
void hello() {
printf("Hello, world!\n");
}
void goodbye() {
printf("Goodbye, world!\n");
}
int main() {
void (*funcPtr)();
funcPtr = hello;
funcPtr();
funcPtr = goodbye;
funcPtr();
return 0;
}