C Programming - ZishanManna/interview-prep-wiki GitHub Wiki
Click here to reveal the answer
-
A variable is a memory location that holds a value which can change during the program execution.
int a = 5; // 'a' is a variable that holds the value 5
-
A constant is a value that cannot be altered after it is defined. Constants are defined using the
const
keyword.const int b = 10; // 'b' is a constant that holds the value 10
Click here to reveal the answer
The main()
function is the entry point of every C program. When the program starts, execution begins from the main()
function. It is mandatory in every C program.
Syntax:
int main() {
// code
return 0;
}
Click here to reveal the answer
C provides four storage classes:
-
Automatic (
auto
): Default for local variables. -
Register (
register
): Suggests storing the variable in a CPU register for fast access. -
Static (
static
): Preserves the value of a variable between function calls. -
External (
extern
): Refers to a global variable that is defined elsewhere.
Click here to reveal the answer
A pointer is a variable that stores the memory address of another variable. Pointers are used for dynamic memory allocation, arrays, functions, and data structures like linked lists.
Example:
int a = 10;
int *p = &a; // `p` is a pointer that stores the address of variable `a`
Here, *p
dereferences the pointer, giving access to the value stored in a
.
Click here to reveal the answer
#include <stdio.h>
int main() {
int num, sum = 0;
printf("Enter a number: ");
scanf("%d", &num);
while (num != 0) {
sum += num % 10; // Extract the last digit and add to sum
num = num / 10; // Remove the last digit
}
printf("Sum of digits: %d\n", sum);
return 0;
}
Explanation: The program takes an integer input and calculates the sum of its digits using a while loop.
Click here to reveal the answer
-
struct
: Allocates memory for all members individually. Every member has its own storage.struct example { int a; float b; };
-
union
: Shares the same memory location for all members. The size of a union is determined by its largest member.union example { int a; float b; };
Click here to reveal the answer
Preprocessor directives are instructions given to the compiler before the actual compilation starts. They begin with a #
symbol.
Examples:
-
#include
: Includes the contents of a file. -
#define
: Defines a constant or macro. -
#ifdef
/#endif
: Conditional compilation.
Click here to reveal the answer
-
Call by Value: A copy of the actual parameter is passed. Changes made to the parameter inside the function do not reflect outside.
void increment(int a) { a = a + 1; } // Changes are local to the function
-
Call by Reference: The address of the actual parameter is passed, so changes made inside the function affect the original value.
void increment(int *a) { *a = *a + 1; } // Changes affect the original value
Click here to reveal the answer
The volatile
keyword tells the compiler that the value of a variable can change at any time, outside of the control of the code (e.g., a variable modified by hardware). This prevents the compiler from optimizing code involving volatile
variables.
volatile int flag = 0;
The flag
value will be re-read every time it is accessed.
Click here to reveal the answer
#include <stdio.h>
int main() {
int num, i, flag = 0;
printf("Enter a number: ");
scanf("%d", &num);
if (num <= 1) {
printf("%d is not a prime number.\n", num);
return 0;
}
for (i = 2; i <= num / 2; i++) {
if (num % i == 0) {
flag = 1; // If divisible, then not a prime
break;
}
}
if (flag == 0)
printf("%d is a prime number.\n", num);
else
printf("%d is not a prime number.\n", num);
return 0;
}
Explanation: The program iterates from 2 to num/2
to check divisibility. If no divisors are found, the number is prime.
Click here to reveal the answer
Dynamic memory allocation refers to the process of allocating memory at runtime using functions like malloc()
, calloc()
, realloc()
, and free()
. This allows programs to request memory as needed and release it when no longer in use, helping to manage memory efficiently.
Functions:
-
malloc(size_t size)
: Allocates a specified number of bytes. -
calloc(size_t num, size_t size)
: Allocates memory for an array and initializes it to zero. -
realloc(void *ptr, size_t size)
: Resizes previously allocated memory. -
free(void *ptr)
: Frees previously allocated memory.
Example:
int *arr = (int *)malloc(10 * sizeof(int)); // Allocates memory for an array of 10 integers
Click here to reveal the answer
-
++i
(Pre-increment): Increments the value ofi
first and then returns the incremented value.int i = 5; int a = ++i; // i becomes 6, a is assigned 6
-
i++
(Post-increment): Returns the current value ofi
and then increments it.int i = 5; int a = i++; // a is assigned 5, i becomes 6
In summary, the difference lies in when the increment operation takes place relative to the assignment.
Click here to reveal the answer
C provides three primary types of loops:
-
for
loop: Used when the number of iterations is known.for (int i = 0; i < 10; i++) { printf("%d\n", i); // Prints numbers from 0 to 9 }
-
while
loop: Used when the number of iterations is not known and depends on a condition.int i = 0; while (i < 10) { printf("%d\n", i); // Prints numbers from 0 to 9 i++; }
-
do-while
loop: Similar to thewhile
loop, but guarantees at least one iteration.int i = 0; do { printf("%d\n", i); // Prints numbers from 0 to 9 i++; } while (i < 10);
Click here to reveal the answer
Recursion is a programming technique where a function calls itself to solve a problem. It is often used for tasks that can be broken down into smaller, similar sub-tasks. A recursive function must have a base case to terminate the recursion and prevent infinite loops.
Example:
#include <stdio.h>
int factorial(int n) {
if (n == 0) // Base case
return 1;
else
return n * factorial(n - 1); // Recursive call
}
int main() {
int num = 5;
printf("Factorial of %d is %d\n", num, factorial(num)); // Output: 120
return 0;
}
In this example, the factorial
function calls itself with a decremented value until it reaches the base case of 0
.
Click here to reveal the answer
-
strcmp()
: Compares two strings and returns:-
0
if the strings are equal, - a negative value if the first string is less than the second,
- a positive value if the first string is greater than the second.
Example:
#include <stdio.h> #include <string.h> int main() { char str1[] = "Hello"; char str2[] = "World"; int result = strcmp(str1, str2); printf("Comparison Result: %d\n", result); // Output will depend on the strings return 0; }
-
-
strcpy()
: Copies the contents of one string into another, including the null terminator.Example:
#include <stdio.h> #include <string.h> int main() { char source[] = "Hello"; char destination[20]; strcpy(destination, source); printf("Copied String: %s\n", destination); // Output: Hello return 0; }
Click here to reveal the answer
#include <stdio.h>
int main() {
int n, i, largest;
printf("Enter number of elements: ");
scanf("%d", &n);
int arr[n]; // Declare an array of size n
printf("Enter %d integers:\n", n);
for (i = 0; i < n; i++) {
scanf("%d", &arr[i]); // Input elements into the array
}
largest = arr[0]; // Assume the first element is the largest
for (i = 1; i < n; i++) {
if (arr[i] > largest) {
largest = arr[i]; // Update largest if current element is greater
}
}
printf("Largest element: %d\n", largest);
return 0;
}
Explanation: The program prompts the user to enter the number of elements and the elements themselves. It then iterates through the array to find and display the largest element.
Click here to reveal the answer
The static
keyword in C serves two main purposes:
-
Static Variables: When declared inside a function, a static variable retains its value between function calls. It is initialized only once and maintains its state throughout the program execution.
void counter() { static int count = 0; // Initialized only once count++; printf("%d\n", count); // Prints incremented count on each call }
-
Static Functions: When a function is declared as static, its visibility is limited to the file in which it is defined. This helps in encapsulating functionality and preventing naming conflicts across multiple files.
static void helperFunction() { // This function cannot be called outside this file }
Click here to reveal the answer
A file pointer in C is a pointer that points to a file. It is used for file operations, allowing you to read from and write to files using standard library functions.
File pointer declaration:
FILE *fp;
Common file operations:
-
Opening a file: Use
fopen()
to open a file.fp = fopen("file.txt", "r"); // Opens a file for reading
-
Reading from a file: Use functions like
fscanf()
,fgets()
, orfread()
.char buffer[100]; fgets(buffer, 100, fp); // Reads a line from the file
-
Writing to a file: Use functions like
fprintf()
,fputs()
, orfwrite()
.fprintf(fp, "Hello, World!\n"); // Writes to the file
-
Closing a file: Use
fclose()
to close the file and release resources.fclose(fp); // Closes the file
Example:
#include <stdio.h>
int main() {
FILE *fp;
fp = fopen("file.txt", "w"); // Open for writing
if (fp != NULL) {
fprintf(fp, "Hello, World!\n"); // Write to file
fclose(fp); // Close the file
}
return 0;
}
Click here to reveal the answer
A segmentation fault (often referred to as a "segfault") is a specific kind of error that occurs when a program attempts to access a memory location that it is not allowed to access. This typically happens due to:
-
Dereferencing a NULL pointer: Trying to access memory using a pointer that has not been initialized.
int *ptr = NULL; printf("%d\n", *ptr); // Causes a segmentation fault
-
Accessing out-of-bounds array elements: Attempting to read or write beyond the allocated memory for an array.
int arr[5]; arr[10] = 25; // Causes a segmentation fault
-
Stack overflow: Occurs when too much memory is used on the call stack, often due to deep or infinite recursion.
When a segmentation fault occurs, the operating system terminates the program, and an error message is usually printed, indicating the fault.
Click here to reveal the answer
The enum
data type in C is used to define a variable that can hold a set of named integer constants. It enhances code readability and maintainability by allowing developers to use meaningful names instead of numeric values.
Syntax:
enum enum_name {
constant1,
constant2,
constant3,
// ...
};
By default, the first enumerator has the value 0
, and each subsequent enumerator's value increments by 1
, unless explicitly specified.
Example:
#include <stdio.h>
enum day {SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY};
int main() {
enum day today;
today = WEDNESDAY;
printf("Day number: %d\n", today); // Output: 3
return 0;
}
In this example, the enumeration day
is defined, and today
is set to WEDNESDAY
, which corresponds to the integer value 3
.
Click here to reveal the answer
Pointers in C are variables that store the address of another variable. They are used for dynamic memory allocation, arrays, and efficient array manipulation, among other purposes.
Declaration:
int *ptr; // Pointer to an integer
Initialization:
You can initialize a pointer by assigning it the address of a variable using the address-of operator (&
).
int var = 10;
int *ptr = &var; // ptr now holds the address of var
Dereferencing:
To access the value stored at the address a pointer is pointing to, use the dereference operator (*
).
printf("%d\n", *ptr); // Outputs: 10
Example:
#include <stdio.h>
int main() {
int var = 20;
int *ptr = &var; // Pointer initialization
printf("Value of var: %d\n", var); // Outputs: 20
printf("Value via pointer: %d\n", *ptr); // Outputs: 20
*ptr = 30; // Changing the value of var via pointer
printf("New value of var: %d\n", var); // Outputs: 30
return 0;
}
In this example, changing the value via the pointer also updates the original variable.
Click here to reveal the answer
Both malloc()
and calloc()
are used for dynamic memory allocation in C, but they have key differences:
-
malloc()
:- Stands for "memory allocation".
- Allocates a specified number of bytes of memory but does not initialize it.
- Syntax:
void* malloc(size_t size);
- Returns a pointer to the allocated memory or
NULL
if the allocation fails.
Example:
int *arr = (int *)malloc(5 * sizeof(int)); // Allocates memory for an array of 5 integers
-
calloc()
:- Stands for "contiguous allocation".
- Allocates memory for an array of elements and initializes all bytes to zero.
- Syntax:
void* calloc(size_t num, size_t size);
- Returns a pointer to the allocated memory or
NULL
if the allocation fails.
Example:
int *arr = (int *)calloc(5, sizeof(int)); // Allocates memory for an array of 5 integers and initializes to 0
Key Differences:
-
malloc()
allocates memory without initialization, whilecalloc()
allocates and initializes memory to zero. -
malloc()
takes a single argument (total bytes), whereascalloc()
takes two arguments (number of elements and size of each element).
Click here to reveal the answer
A struct
(short for structure) in C is a user-defined data type that allows grouping of related variables of different data types under a single name. Structures are useful for organizing complex data.
Syntax:
struct struct_name {
data_type member1;
data_type member2;
// ...
};
Example:
#include <stdio.h>
// Define a struct named 'Person'
struct Person {
char name[50];
int age;
};
int main() {
struct Person person1; // Declare a variable of type struct Person
// Assign values to the members
snprintf(person1.name, sizeof(person1.name), "Alice");
person1.age = 30;
// Print the values
printf("Name: %s, Age: %d\n", person1.name, person1.age); // Output: Name: Alice, Age: 30
return 0;
}
Key Points:
- Structures allow the combination of different data types into a single unit.
- You can access the members of a structure using the dot operator (
.
).
Click here to reveal the answer
The typedef
keyword in C is used to create an alias for existing data types, making the code more readable and easier to manage. It can be particularly useful when working with complex data types like structures, pointers, and arrays.
Syntax:
typedef existing_type new_type_name;
Example:
#include <stdio.h>
// Create an alias for the 'unsigned long' data type
typedef unsigned long ulong;
int main() {
ulong num = 100000; // Now 'ulong' can be used as an alias for 'unsigned long'
printf("Number: %lu\n", num); // Output: Number: 100000
return 0;
}
Usage with Structures:
#include <stdio.h>
struct Point {
int x;
int y;
};
// Create an alias for 'struct Point'
typedef struct Point Point;
int main() {
Point p1; // Now 'Point' can be used instead of 'struct Point'
p1.x = 10;
p1.y = 20;
printf("Point: (%d, %d)\n", p1.x, p1.y); // Output: Point: (10, 20)
return 0;
}
Key Points:
-
typedef
does not create a new data type; it simply creates an alias. - It helps improve code readability and maintainability.
Click here to reveal the answer
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 has its own memory space, a union shares the same memory space among its members. This means that a union can only hold one of its members at a time.
Syntax:
union union_name {
data_type member1;
data_type member2;
// ...
};
Example:
#include <stdio.h>
// Define a union named 'Data'
union Data {
int intValue;
float floatValue;
char charValue;
};
int main() {
union Data data; // Declare a variable of type union Data
// Assign an integer value
data.intValue = 5;
printf("Integer: %d\n", data.intValue); // Output: Integer: 5
// Assign a float value (overwrites the previous value)
data.floatValue = 3.14;
printf("Float: %f\n", data.floatValue); // Output: Float: 3.140000
// The intValue is now undefined since we assigned a new value to the union
printf("Integer after float assignment: %d\n", data.intValue); // Output may be undefined
return 0;
}
Key Differences Between struct
and union
:
- Memory Allocation: In a struct, each member has its own memory, while in a union, all members share the same memory location.
- Data Storage: A struct can store values of all its members at the same time, while a union can only store one member value at a time.
Click here to reveal the answer
The const
keyword in C is used to declare variables whose values cannot be modified after initialization. This provides a way to protect variables from being altered unintentionally and can help improve code safety and readability.
Usage:
-
const
can be applied to any data type, including basic types, pointers, and structures.
Example:
#include <stdio.h>
int main() {
const int MAX = 100; // MAX is a constant integer
// MAX = 200; // This would cause a compilation error
printf("Maximum value: %d\n", MAX);
return 0;
}
Const with Pointers:
When using const
with pointers, there are two scenarios:
-
Pointer to a constant value:
const int *ptr; // Pointer to an int that cannot be modified through ptr
-
Constant pointer:
int *const ptr; // Pointer that cannot point to a different address
Example of Pointer to Constant:
#include <stdio.h>
void printValue(const int *p) {
// *p = 10; // This would cause a compilation error
printf("Value: %d\n", *p);
}
int main() {
int num = 5;
printValue(&num);
return 0;
}
Key Points:
-
const
helps prevent accidental modifications to variables. - It can improve the optimization potential of compilers by providing additional information.
Click here to reveal the answer
Recursion in C refers to a function calling itself directly or indirectly to solve a problem. It breaks down a problem into smaller sub-problems, each of which is a smaller instance of the same problem. A recursive function typically has two components:
- Base case: A condition under which the function stops calling itself.
- Recursive case: The part of the function that includes the recursive call.
Example: Calculating the factorial of a number using recursion.
#include <stdio.h>
// Recursive function to calculate factorial
int factorial(int n) {
if (n == 0) // Base case
return 1;
else
return n * factorial(n - 1); // Recursive case
}
int main() {
int num = 5;
printf("Factorial of %d is %d\n", num, factorial(num)); // Output: Factorial of 5 is 120
return 0;
}
In this example, the factorial
function calls itself with a decremented value until it reaches the base case of 0
. Each function call waits for the result of the next call, building the final result as the calls return.
Key Points:
- Recursion can lead to elegant solutions but can also consume more stack space compared to iterative solutions.
- Care must be taken to define a proper base case to avoid infinite recursion.
Click here to reveal the answer
The expressions ++i
and i++
both increment the value of i
by 1
, but they differ in their behavior regarding the timing of the increment operation.
-
++i
(Pre-increment):- Increments the value of
i
before the value is used in an expression. - The expression evaluates to the new value of
i
.
Example:
int i = 5; int result = ++i; // i is incremented to 6, and result is assigned the value 6 printf("i: %d, result: %d\n", i, result); // Output: i: 6, result: 6
- Increments the value of
-
i++
(Post-increment):- Increments the value of
i
after the value is used in an expression. - The expression evaluates to the original value of
i
.
Example:
int i = 5; int result = i++; // result is assigned the original value 5, and then i is incremented to 6 printf("i: %d, result: %d\n", i, result); // Output: i: 6, result: 5
- Increments the value of
Key Points:
- Use
++i
when you need the incremented value immediately. - Use
i++
when you need to use the original value first before the increment.
Click here to reveal the answer
Macros in C are preprocessor directives that define a name for a piece of code. They are used for code substitution and can simplify complex expressions, enable code reuse, and make programs more readable.
Defining Macros:
Macros are defined using the #define
preprocessor directive. The syntax is as follows:
#define MACRO_NAME replacement_code
Example:
#include <stdio.h>
// Define a macro for the square of a number
#define SQUARE(x) ((x) * (x))
int main() {
int num = 5;
int result = SQUARE(num); // Expands to ((5) * (5))
printf("The square of %d is %d\n", num, result); // Output: The square of 5 is 25
return 0;
}
Key Points:
- Macros can take parameters, allowing for more complex substitutions.
- Be cautious with macros as they are expanded inline and do not respect variable scope. This can lead to unexpected results, especially with expressions.
- Parentheses are often used in macro definitions to ensure the proper order of operations.
Click here to reveal the answer
The break
and continue
statements in C are used to control the flow of loops, but they serve different purposes.
-
break
Statement:- Used to exit from a loop or switch statement immediately.
- When a
break
statement is encountered, the control is transferred to the statement following the loop or switch.
Example:
#include <stdio.h> int main() { for (int i = 1; i <= 5; i++) { if (i == 3) { break; // Exit the loop when i equals 3 } printf("%d ", i); // Output: 1 2 } printf("\nLoop exited.\n"); return 0; }
-
continue
Statement:- Used to skip the current iteration of a loop and move to the next iteration.
- When a
continue
statement is encountered, the control jumps to the loop's next iteration.
Example:
#include <stdio.h> int main() { for (int i = 1; i <= 5; i++) { if (i == 3) { continue; // Skip the rest of the loop body when i equals 3 } printf("%d ", i); // Output: 1 2 4 5 } printf("\nLoop completed.\n"); return 0; }
Key Points:
-
break
is used to terminate the loop, whilecontinue
is used to skip to the next iteration. - Both statements can be used within
for
,while
, anddo-while
loops.