Character Pointer Variables
Character pointers (char*) serve two primary purposes: pointing to a single char value and referencing the start of a null-terminated string literal.
Basic Usage (Single Character)
int main() {
char single = 'x';
char* ptr = &single;
*ptr = 'y';
return 0;
}
String Literal Usage
Assigning a string literal to a const char* pointer stores the address of the literal’s first character, not the entire string itself. String literals reside in read-only memory, so const ensures accidental modification attempts trigger compiler errors.
#include <stdio.h>
int main() {
const char* greeting = "hi world";
printf("%s", greeting); // Prints from greeting[0] until '\0'
return 0;
}
Interview Question: String vs Array Comparisons
The following example leverages compiler optimization and memory layout rules:
#include <stdio.h>
int main() {
char buf_a[] = "hi world";
char buf_b[] = "hi world";
const char* lit_c = "hi world";
const char* lit_d = "hi world";
// Compare memory addresses stored in variables
if (buf_a == buf_b)
puts("buf_a and buf_b share memory");
else
puts("buf_a and buf_b occupy separate blocks");
if (lit_c == lit_d)
puts("lit_c and lit_d share memory");
else
puts("lit_c and lit_d occupy separate blocks");
return 0;
}
Compilers often merge identical string literals into a single read-only memory segment, so lit_c and lit_d hold the same address. In contrast, buf_a and buf_b are distinct stack-allocated arrays initialized with copies of the literal, creating unique memory blocks.
Array Pointer Variables
Array pointers are variables that store the address of an entire array, not just its first element. This differs from pointer arrays, which are arrays containing pointer values.
Syntax Breakdown
Because [] has higher precedence than *, parantheses ensure the identifier binds first to *, marking it as a pointer. The remaining syntax describes the array type it points to:
int (*arr_ptr)[8]; // arr_ptr points to an int array of size 8
Initialization
Use the & operator with an array name to retrieve the address of the entire array:
int main() {
int nums[8] = {1, 3, 5, 7, 9, 11, 13, 15};
int (*arr_ptr)[8] = &nums;
return 0;
}
2D Array Parameter Passing
A 2D array is an array of 1D arrays. Its name decays to a pointer to the first 1D subarray, not the first scalar element.
Traditional Array Syntax
#include <stdio.h>
void display_grid(int grid[2][4], int rows, int cols) {
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
printf("%d ", grid[r][c]);
}
putchar('\n');
}
}
int main() {
int matrix[2][4] = {{2,4,6,8}, {10,12,14,16}};
display_grid(matrix, 2, 4);
return 0;
}
Array Pointer Syntax
Since the array name decays to int (*)[4], the function parameter can be rewritten explicitly as an array pointer. The column count is mandatory for pointer arithmetic to work correctly:
#include <stdio.h>
void display_grid(int (*grid)[4], int rows, int cols) {
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
printf("%d ", *(*(grid + r) + c));
}
putchar('\n');
}
}
int main() {
int matrix[2][4] = {{2,4,6,8}, {10,12,14,16}};
display_grid(matrix, 2, 4);
return 0;
}
Function Pointer Variables
Function pointers store the memory address of a function, enabling indirect function calls. Both the function name and &function_name yield the same address.
Syntax and Initialization
#include <stdio.h>
float multiply(float a, float b) {
return a * b;
}
int main() {
float (*calc_ptr)(float, float) = multiply; // Parameter names optional
printf("%p\n", multiply);
printf("%p\n", &multiply);
return 0;
}
Indirect Function Calls
Both dereferencing and direct variable invocation work for function pointers:
#include <stdio.h>
float multiply(float a, float b) {
return a * b;
}
int main() {
float (*calc_ptr)(float, float) = multiply;
printf("%.2f\n", (*calc_ptr)(2.5, 4.0));
printf("%.2f\n", calc_ptr(3.2, 5.0));
return 0;
}
Complex Function Pointer Expressions
Expression 1: Cast and Call
(*(void (*)(void))0x1000)();
This code:
- Casts memory address
0x1000to a function pointer typevoid (*)(void) - Dereferences the pointer (optional for function calls)
- Invokes the function with no arguments
Expression 2: Complex Function Declaration
void (*register_handler(int, void (*)(int)))(int);
This declares a function register_handler that:
- Takes two parameters: an
intand a function pointervoid (*)(int) - Returns a function pointer of type
void (*)(int)
Simplifying Types with typedef
typedef renames complex types for readability. For array/function pointers, the new name sits after *:
// Rename int (*)[6] to int_arr6_ptr
typedef int (*int_arr6_ptr)[6];
// Rename void (*)(int) to void_int_handler
typedef void (*void_int_handler)(int);
// Simplify the complex declaration above
void_int_handler register_handler(int, void_int_handler);
Functon Pointer Arrays
A function pointer array stores multiple function pointers of compatible signature.
Practical Use: Jump/Transfer Table
Instead of long switch/if-else chains, use a function pointer array to map input values directly to function calls:
#include <stdio.h>
#include <stdlib.h>
void show_menu() {
puts("\n=== Simple Calculator ===");
puts("1. Add");
puts("2. Subtract");
puts("3. Multiply");
puts("4. Divide");
puts("0. Exit");
puts("========================");
printf("Choice: ");
}
float add_ops(float left, float right) { return left + right; }
float sub_ops(float left, float right) { return left - right; }
float mul_ops(float left, float right) { return left * right; }
float div_ops(float left, float right) {
if (right == 0.0f) {
puts("Error: Division by zero");
return NAN;
}
return left / right;
}
int main() {
float (*calc_table[5])(float, float) = {NULL, add_ops, sub_ops, mul_ops, div_ops};
int selection;
float op1, op2, result;
do {
show_menu();
scanf("%d", &selection);
if (selection == 0) {
puts("Exiting...");
} else if (selection >= 1 && selection <= 4) {
printf("Enter two numbers: ");
scanf("%f %f", &op1, &op2);
result = calc_table[selection](op1, op2);
if (!isnan(result)) {
printf("Result: %.2f\n", result);
}
} else {
puts("Invalid choice, try again");
}
} while (selection != 0);
return 0;
}