Module 4: Chapter 5 - Pointers Continued

Pointers to Functions

In C, functions occupy memory and have an address, just like variables. A pointer that holds the address of a function is known as a function pointer. Once assigned, the function can be invoked using this pointer. Function pointers are powerful because they let you:

Declaring a Function Pointer

A function pointer must specify the function's return type and parameter types.

int (*p)(const char*, const char*);

Assigning a Function to Pointer

The function name without parentheses gives its address.

p = strcmp;

Calling a Function Using Pointer

Two valid styles:

(*p)(a, b);
p(a, b);

Example Usage

#include <stdio.h>
#include <string.h>
void check(char *a, char *b, int (*cmp)(const char*, const char*));

int main() {
  int (*p)(const char*, const char*);
  p = strcmp;
  check("Hello", "Hello", p);
  return 0;
}

void check(char *a, char *b, int (*cmp)(const char*, const char*)) {
  if((*cmp)(a, b) == 0) printf("Equal");
  else printf("Not Equal");
}

Why Use Function Pointers?

Function pointers are extremely versatile in advanced C programming and help in writing flexible, modular, and scalable applications.

Dynamic Memory Allocation

Definition

Dynamic Memory Allocation (DMA) in C allows a program to request memory at runtime. The allocated memory comes from the heap and remains available until it is explicitly released by the programmer.

Dynamic Memory Allocation Functions

C provides the following standard library functions for dynamic memory management:

1. malloc() - Memory Allocation

malloc() allocates a single block of memory of the specified size in bytes and returns a pointer to the allocated memory.

Syntax

ptr = (type *) malloc(size_in_bytes);

Example

int *p;
p = (int *) malloc(5 * sizeof(int));

if(p == NULL)
  printf("Memory allocation failed");
else
  printf("Memory allocated successfully");

2. calloc() - Contiguous Allocation

calloc() allocates memory for multiple elements and initializes all bytes to zero.

Syntax

ptr = (type *) calloc(n, sizeof(type));

Example

int *p;
p = (int *) calloc(5, sizeof(int));

if(p == NULL)
  printf("Memory allocation failed");
else
  printf("Memory allocated and initialized to zero");

3. realloc() - Reallocation of Memory

realloc() is used to resize a previously allocated memory block without losing existing data.

Syntax

ptr = (type *) realloc(ptr, new_size);

Example

p = (int *) realloc(p, 10 * sizeof(int));

4. free() - Deallocating Memory

free() releases dynamically allocated memory back to the system.

Syntax

free(ptr);

Example

free(p);
p = NULL;

Summary

Dynamic memory allocation is essential for creating flexible and memory-efficient C programs, especially when working with arrays, structures, and large data sets whose size is determined at runtime.

Dynamically Allocated Arrays

Dynamic memory allocation allows creating arrays at runtime using malloc(). A pointer returned by malloc can be indexed like an array, making it possible to form dynamically allocated arrays. This is useful when the required array size is not known before execution.

1D Dynamic Array Example

Allocating memory for a string and printing it in reverse:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(){
  char *s;
  int t;
  s = malloc(80); // Allocate 80 bytes
  if(!s){ // Check memory allocation
    printf("Memory request failed\n");
    exit(1);
  }
  gets(s); // Input string
  for(t=strlen(s)-1; t>=0; t--)
    putchar(s[t]); // Print reversed
  free(s); // Release memory
  return 0;
}

2D Dynamic Array Example

Multi-dimensional dynamic arrays require a pointer that represents all dimensions except the first. The example below allocates memory for a 4×10 integer array and fills it with values 1 to 10 raised to the power 1-4.

#include <stdio.h>
#include <stdlib.h>
int pwr(int a,int b);

int main(){
  int (*p)[10]; // Pointer to array of 10 ints
  int i,j;
  p = malloc(40*sizeof(int)); // Allocate 4×10 ints
  if(!p){ printf("Memory failed"); exit(1);}

  for(j=1;j<11;j++)
    for(i=1;i<5;i++) p[i-1][j-1]=pwr(j,i);

  for(j=1;j<11;j++){
    for(i=1;i<5;i++) printf("%10d",p[i-1][j-1]);
    printf("\n");
  }
  return 0;
}

int pwr(int a,int b){
  int t=1;
  while(b--) t=t*a;
  return t;
}

Declaration insight:

int (*p)[10];

In C++, malloc return values require typecasting:

p = (int (*)[10]) malloc(40*sizeof(int));

Dynamically allocated arrays provide flexibility in memory usage, especially when size is unknown at compile time, making them useful in data structures, file handling, and variable-sized storage.