1.
Limitations of arrays in C
Arrays in C have several limitations:
Fixed size that must be declared at compile time (unless using dynamic allocation)
No bounds checking (can lead to buffer overflows)
Cannot be resized without creating a new array
Cannot return entire arrays from functions (only pointers to them)
No built-in methods to find length or perform operations like sorting
Example of fixed size limitation:
int numbers[5]; // Can only store 5 integers
numbers[5] = 10; // This is out of bounds but C won't detect it
2. Dangling and null pointers
Null pointer: A pointer that doesn't point to any memory location. It's explicitly assigned
NULL.
int *ptr = NULL; // Null pointer
Dangling pointer: A pointer that points to memory that has been freed or is no longer valid.
int *ptr = (int *)malloc(sizeof(int));
free(ptr); // ptr is now a dangling pointer
// Using ptr here is dangerous
3. Far, near, and huge pointers
These are memory model concepts from older C compilers for 16-bit systems:
Near pointer: 16-bit pointer that can access only the current segment (64KB)
Far pointer: 32-bit pointer with 16 bits for segment and 16 bits for offset
Huge pointer: Similar to far pointers but with normalized segment:offset
Modern C on 32/64-bit systems typically doesn't use these distinctions.
4. Use of #define in C
#define is a preprocessor directive used to define constants and macros:
// Define a constant
#define PI 3.14159
// Define a macro
#define SQUARE(x) ((x) * (x))
int main() {
float radius = 5.0;
float area = PI * SQUARE(radius);
printf("Area: %f\n", area);
return 0;
}
5. Static function in C
A static function is only visible within the file it's defined in. It can't be called from other
files.
// This function is only visible in this file
static int privateFunction() {
return 42;
}
// This function can be called from other files
int publicFunction() {
return privateFunction() + 10;
}
6. Difference between " " and < > for include
#include "file.h" - Searches for the header file first in the current directory, then
in standard directories
#include <file.h> - Searches only in standard system directories
Use double quotes for your own header files and angle brackets for system headers:
#include <stdio.h> // System header
#include "myheader.h" // User-defined header
7. Nested loops
Nested loops are loops inside other loops. The inner loop completes all its iterations for each
iteration of the outer loop.
// Print a 5x5 multiplication table
for (int i = 1; i <= 5; i++) {
for (int j = 1; j <= 5; j++) {
printf("%d\t", i * j);
}
printf("\n");
}
8. Difference between object file and executable file
Object file (.o or .obj): Contains machine code but isn't directly executable. It has
unresolved references to external functions.
Executable file (.exe or no extension): Fully linked program that can be executed
directly. All references are resolved.
Compilation process:
1. Source code (.c) → Compiler → Object file (.o)
2. Object file(s) → Linker → Executable file
9. Program using dynamic memory allocation
#include <stdio.h>
#include <stdlib.h>
int main() {
int size, i, sum = 0;
int *numbers;
printf("How many numbers? ");
scanf("%d", &size);
// Allocate memory
numbers = (int *)malloc(size * sizeof(int));
if (numbers == NULL) {
printf("Memory allocation failed\n");
return 1;
}
// Get values
for (i = 0; i < size; i++) {
printf("Enter number %d: ", i+1);
scanf("%d", &numbers[i]);
sum += numbers[i];
}
printf("Sum: %d\n", sum);
// Release memory
free(numbers);
return 0;
}
10. Difference between 'I' and 'II'
In C:
'I' is a character constant with ASCII value 73
"I" is a string literal (character array) containing 'I' and '\0'
char c = 'I'; // Single character
char *s = "I"; // String (array of characters)
11. Converting a character to ASCII
In C, characters are already stored as their ASCII values, so you can:
#include <stdio.h>
int main() {
char ch = 'A';
int ascii = (int)ch; // Explicit conversion (optional)
printf("The ASCII value of %c is %d\n", ch, ascii);
// Or simply:
printf("The ASCII value of %c is %d\n", ch, ch);
return 0;
}
12. Integer addition/subtraction with pointers
When you add or subtract an integer from a pointer, the pointer moves by that many elements
of the pointer's type:
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // Points to first element
printf("*ptr = %d\n", *ptr); // 10
ptr = ptr + 2; // Move forward 2 integers (8 bytes on most systems)
printf("*ptr after +2 = %d\n", *ptr); // 30
ptr = ptr - 1; // Move back 1 integer
printf("*ptr after -1 = %d\n", *ptr); // 20
return 0;
}
13. Using typedef for structures
typedef creates a new name (alias) for a data type, making structure declarations cleaner:
#include <stdio.h>
#include <string.h>
// Without typedef
struct Person {
char name[50];
int age;
};
// With typedef
typedef struct {
char name[50];
int age;
} Student;
int main() {
// Without typedef
struct Person person1;
strcpy(person1.name, "John");
person1.age = 25;
// With typedef
Student student1;
strcpy(student1.name, "Alice");
student1.age = 20;
printf("%s is %d years old\n", person1.name, person1.age);
printf("%s is %d years old\n", student1.name, student1.age);
return 0;
}
14. Block scope for an identifier
In C, block scope means a variable is only accessible within the block (code between { })
where it's declared:
#include <stdio.h>
int main() {
int outer = 10;
{ // Start of a new block
int inner = 20;
printf("Inside block: outer = %d, inner = %d\n", outer, inner);
} // End of block
printf("Outside block: outer = %d\n", outer);
// printf("inner = %d\n"); // Error: inner is not accessible here
return 0;
}
15. How nested blocks affect accessibility
Variables in outer blocks are accessible in inner blocks, but not vice versa:
#include <stdio.h>
int main() {
int a = 10;
{
int b = 20;
printf("Inner block can access a: %d\n", a);
{
int c = 30;
printf("Innermost block can access a: %d, b: %d\n", a, b);
}
// Cannot access c here
}
// Cannot access b or c here
return 0;
}
16. When is the return statement mandatory in a
function?
The return statement is mandatory in functions with non-void return types:
// Must have return statement
int add(int a, int b) {
return a + b; // Required
}
// Return statement optional
void greet() {
printf("Hello!\n");
// return; is optional here
}
17. Why void pointers need explicit type casting
Void pointers have no type information, so the compiler doesn't know how many bytes to
dereference:
#include <stdio.h>
int main() {
int num = 42;
void *vptr = #
// This won't work: printf("%d\n", *vptr);
// Need explicit casting
printf("%d\n", *(int *)vptr);
return 0;
}
18. Pointer to a pointer
A pointer to a pointer is a variable that stores the address of another pointer:
#include <stdio.h>
int main() {
int value = 42;
int *ptr = &value; // Pointer to value
int **ptrToPtr = &ptr; // Pointer to pointer
printf("value: %d\n", value);
printf("*ptr: %d\n", *ptr);
printf("**ptrToPtr: %d\n", **ptrToPtr);
return 0;
}
19. Extending pointers to multiple levels
Yes, pointers can be extended to any level by adding more asterisks:
#include <stdio.h>
int main() {
int value = 42;
int *ptr1 = &value; // Level 1
int **ptr2 = &ptr1; // Level 2
int ***ptr3 = &ptr2; // Level 3
int ****ptr4 = &ptr3; // Level 4
// Access the value through all levels
printf("value: %d\n", value);
printf("*ptr1: %d\n", *ptr1);
printf("**ptr2: %d\n", **ptr2);
printf("***ptr3: %d\n", ***ptr3);
printf("****ptr4: %d\n", ****ptr4);
// Modify the value through the deepest pointer
****ptr4 = 100;
printf("After modification, value = %d\n", value);
return 0;
}
This verifies that pointers can be extended to any level, limited only by practical
considerations.