L19: Pointer Quirks & Practice Problems

Pointer Quirks #

  • Pointer to Pointers
  • Pointers and Multidim Arrays
  • Array of Pointers
  • Deep/Shallow Copy of Structures
  • Advanced Problems

Pointer to Pointer #


🟢 Level 1 — Basic Double Pointer #

#include <stdio.h>

int main() {
    int a = 10;
    int *p = &a;
    int **q = &p;

    printf("%d %d %d\n", a, *p, **q);
    return 0;
}

🟢 Level 2 — Changing Value Through Double Pointer #

#include <stdio.h>

int main() {
    int a = 5;
    int *p = &a;
    int **q = &p;

    **q = 20;
    printf("%d\n", a);
    return 0;
}

🟡 Level 3 — Pointer Reassignment #

#include <stdio.h>

int main() {
    int x = 5, y = 10;
    int *p = &x;
    int **q = &p;

    *q = &y;
    **q = 25;

    printf("%d %d\n", x, y);
    return 0;
}

Multidimensional Arrays #


🧩 Pointer to Pointer Confusion (Predict the Output) #

Problem:

Predict the output of the following code:

#include <stdio.h>
int main() {
    int a[2][2] = {{1, 2}, {3, 4}};
    int *p[2] = {a[0], a[1]};
    int **q = p;
    printf("%d ", **q);
    printf("%d ", *(*(q + 1) + 1));
    return 0;
}

Output: ?


Mix of 2D Arrays and Double Pointers (Hard) #

Problem:

Consider the code:

#include <stdio.h>
#include <stdlib.h>

int main() {
    int a[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
    int *p[3];
    for (int i = 0; i < 3; i++) p[i] = a[i];
    int **q = p;

    printf("%d ", *(*(q + 1) + 2));
    printf("%d ", *(*(a + 2) + 0));
    return 0;
}

Predict the output.


Array of Pointers #


🟡 Level 4 — Array of Pointers and Pointer to Pointer #

#include <stdio.h>

int main() {
    int a = 1, b = 2, c = 3;
    int *arr[] = {&a, &b, &c};
    int **p = arr;

    printf("%d ", **p);
    p++;
    printf("%d ", **p);
    p++;
    printf("%d\n", **p);
    return 0;
}

🟠 Level 5 — Pointer Arithmetic on Double Pointer #

#include <stdio.h>

int main() {
    int a = 10, b = 20, c = 30;
    int *arr[] = {&a, &b, &c};
    int **ptr = arr;

    *(*ptr + 0) = 15;
    ptr++;
    **ptr = 25;

    printf("%d %d %d\n", a, b, c);
    return 0;
}

Structure Assignments and Shallow Copy #


📘 What is a Shallow Copy? #

A shallow copy of a structure means copying all fields bit by bit (a member-wise copy).
If a structure contains pointer members, only the addresses (not the data they point to) are copied.


🧱 Example 1: Simple Structure Copy #

#include <stdio.h>
#include <string.h>

typedef struct {
    int id;
    char name[20];
} Student;

int main() {
    Student s1 = {1, "Alice"};
    Student s2 = s1;  // Shallow copy

    strcpy(s2.name, "Bob");

    printf("s1: %d %s\n", s1.id, s1.name);
    printf("s2: %d %s\n", s2.id, s2.name);
    return 0;
}

🧩 Output #

s1: 1 Alice
s2: 1 Bob

👉 Each struct has its own name array, so this copy is safe.


⚠️ Example 2: Shallow Copy with Pointers #

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int id;
    char *name;
} Student;

int main() {
    Student s1;
    s1.id = 1;
    s1.name = malloc(20);
    strcpy(s1.name, "Alice");

    Student s2 = s1;   // Shallow copy

    strcpy(s2.name, "Bob");

    printf("s1: %d %s\n", s1.id, s1.name);
    printf("s2: %d %s\n", s2.id, s2.name);

    free(s1.name);
    // free(s2.name);  <-- ⚠️ Double free error!
    return 0;
}

💥 Output #

s1: 1 Bob
s2: 1 Bob

Both s1.name and s2.name point to the same memory — changing one changes both.


🧠 Concept Recap #

OperationWhat happensSafe?
Shallow CopyCopies memory addresses of pointers❌ Unsafe if structs contain pointers
Deep CopyAllocates new memory and copies data recursively✅ Safe

🧩 Example 3: Correct Deep Copy #

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    int id;
    char *name;
} Student;

void deepCopy(Student *dest, const Student *src) {
    dest->id = src->id;
    dest->name = malloc(strlen(src->name) + 1);
    strcpy(dest->name, src->name);
}

int main() {
    Student s1 = {1, malloc(20)};
    strcpy(s1.name, "Alice");

    Student s2;
    deepCopy(&s2, &s1);

    strcpy(s2.name, "Bob");

    printf("s1: %d %s\n", s1.id, s1.name);
    printf("s2: %d %s\n", s2.id, s2.name);

    free(s1.name);
    free(s2.name);
    return 0;
}

✅ Output #

s1: 1 Alice
s2: 1 Bob

Now s1 and s2 have separate copies of the string.


💡 When Shallow Copy is Fine #

✅ When all fields are value types (e.g., int, float, fixed arrays).
❌ Avoid when structs contain pointers or dynamically allocated data.


🧩 Example 4 — Nested Structures #

typedef struct {
    char *street;
    int number;
} Address;

typedef struct {
    char *name;
    Address *addr;
} Employee;

Copying Employee e2 = e1; → shallow copy of addr and name.

Requires deep copy of both pointers to fully clone the structure.

Exercise: Write a function to do a deep copy of Employee structure.


🧩 Summary Slide #

TypeHow it WorksExample
Shallow CopyBitwise copy of fieldsstruct2 = struct1;
Deep CopyField-by-field duplicationUse malloc() and strcpy()

Advanced Problems #


🔴 Level 6 — Triple Pointer #

#include <stdio.h>

int main() {
    int a = 50;
    int *p = &a;
    int **q = &p;
    int ***r = &q;

    ***r = ***r + 10;
    printf("%d\n", a);
    return 0;
}

🔴 Level 7 — Triple Pointer with Reassignment #

#include <stdio.h>

int main() {
    int x = 1, y = 2;
    int *p = &x;
    int **q = &p;
    int ***r = &q;

    **q = y;
    y = 10;
    printf("%d %d\n", x, y);
    return 0;
}

🔴 Level 8 — Complex Pointer Indirection #

#include <stdio.h>

int main() {
    int a = 3, b = 4;
    int *p = &a;
    int *q = &b;
    int **r = &p;
    *r = q;
    **r = 10;

    printf("%d %d\n", a, b);
    return 0;
}

🔴 Level 9 — Triple Pointer with Arrays #

#include <stdio.h>

int main() {
    int a[] = {10, 20, 30};
    int *p = a;
    int **q = &p;
    int ***r = &q;

    **q = *p + 1;
    ***r = **q + 1;

    printf("%d %d %d\n", a[0], a[1], a[2]);
    return 0;
}

🔴 Level 10 — Memory Graph Puzzle #

#include <stdio.h>

int main() {
    int a = 5, b = 6, c = 7;
    int *p1 = &a, *p2 = &b;
    int **q1 = &p1, **q2 = &p2;
    int ***r = &q1;

    **q2 = **q1 + 2;
    *r = q2;
    ***r = ***r + 3;

    printf("%d %d %d\n", a, b, c);
    return 0;
}

Efficient storage for Lower Triangular Matrices #

The bellow code store lower triangular matrices with only n(n-1)/2 memory. Fill in the code for multiplying and deleting lower triangular matrices.

#include <stdio.h>
#include <stdlib.h>

// structure for storing a lower triangular
// matrix with only n(n-1)/2 memory
typedef struct LTMatrix {
    int** mat;
    int num_rows;
    int num_cols;    
} LTMatrix;

// Explain why this function only allocates n(n-1)/2 memory
// for storing lower triangular matrices
LTMatrix* create_LTMatrix(int n, int m, int* init) {
    LTMatrix* M = malloc(sizeof(LTMatrix));
    M->num_rows = n;
    M->num_cols = m;
    M->mat = malloc(n*sizeof(int*));
    int k = 0;
    for (int i =0; i < n; i++) {
        if (i < m) {
            M->mat[i] = malloc(i*sizeof(int));
            if (init != NULL)
                for (int j = 0; j <= i; j++) {
                    M->mat[i][j] = init[k++];
                }
        } else { 
            M->mat[i] = malloc(m*sizeof(int));
            if (init != NULL)
                for (int j = 0; j < m; j++) {
                    M->mat[i][j] = init[k++];
                }
        }
    }
    return M;
}

void print_LTMatrix(LTMatrix* M) {
    printf("-- matrix --\n");
    for (int i = 0; i < M->num_rows; i++) {
        for (int j = 0; j < M->num_cols; j++) {
            printf("%d ", M->mat[i][j]);
        }
        printf("\n");
    }
    printf("-- end --\n");
}

// This function should multiply the
// lower triangular matrices A, B (if the dimensions are approriate)
// and return a newly created LTMatrix which has the result
LTMatrix* multiply_LTMatrix(LTMatrix* A, LTMatrix* B) {
	// TODO
}

// This function should free up all memory corresponding to 
// a LTMatrix
void delete_LTMatrix(LTMatrix* A) {
	// TODO
}

int main() {
    int matrix[6] = {1,
                     2,3,
                     4,5,6};
    LTMatrix* M = create_LTMatrix(3, 3, matrix);
    print_LTMatrix(M);

    return 0;
}

C Memory & Pointer Puzzle Set #

Predict the output of the programs


Level 1: Single Pointer Basics (Easy) #

Problem 1: Basic pointer #

#include <stdio.h>
int main() {
    int x = 10;
    int *p = &x;
    printf("%d %d\n", x, *p);
    return 0;
}

Problem 2: Pointer modification #

#include <stdio.h>
int main() {
    int x = 5;
    int *p = &x;
    *p = 20;
    printf("%d\n", x);
    return 0;
}

Problem 3: Pointer arithmetic #

#include <stdio.h>
int main() {
    int arr[3] = {1,2,3};
    int *p = arr;
    *(p+1) = 10;
    printf("%d %d %d\n", arr[0], arr[1], arr[2]);
    return 0;
}

Level 2: Pointer to Pointer (Medium) #

Problem 4: Double pointer assignment #

#include <stdio.h>
int main() {
    int x = 7;
    int *p = &x;
    int **q = &p;
    **q = 15;
    printf("%d %d\n", x, *p);
    return 0;
}

Problem 5: Pointer redirection #

#include <stdio.h>
int main() {
    int a=1, b=2;
    int *p=&a;
    int **q=&p;
    *q = &b;
    **q = 5;
    printf("%d %d\n", a, b);
    return 0;
}

Problem 6: Pointer swap #

#include <stdio.h>
void swap(int **x, int **y){
    int *temp = *x;
    *x = *y;
    *y = temp;
}

int main(){
    int a=1, b=2;
    int *p1=&a, *p2=&b;
    swap(&p1,&p2);
    *p1 = 10;
    *p2 = 20;
    printf("%d %d\n", a, b);
    return 0;
}

Level 3: Triple Pointer (Challenging) #

Problem 7: Triple pointer basics #

#include <stdio.h>
int main(){
    int x=3;
    int *p=&x;
    int **q=&p;
    int ***r=&q;
    ***r=10;
    printf("%d %d %d\n", x, *p, **q);
    return 0;
}

Problem 8: Modifying pointers with triple pointer #

#include <stdio.h>
int main(){
    int a=1, b=2;
    int *p=&a, *q=&b;
    int **pp=&p;
    int ***r=&pp;
    
    **r=&b;
    ***r=5;
    printf("%d %d\n", a, b);
    return 0;
}

Problem 9: Triple pointer swap #

#include <stdio.h>
void swap(int ***x,int ***y){
    int **temp = *x;
    *x = *y;
    *y = temp;
}
int main(){
    int a=1,b=2;
    int *p1=&a,*p2=&b;
    int **pp1=&p1, **pp2=&p2;
    swap(&pp1,&pp2);
    **pp1=10;
    **pp2=20;
    printf("%d %d\n", a,b);
    return 0;
}

Level 4: Pointer Arrays (Advanced) #

Problem 10: Array of pointers #

#include <stdio.h>
int main(){
    int x=1,y=2,z=3;
    int *arr[3]={&x,&y,&z};
    int **p=arr;
    **p=10;
    p++;
    **p=20;
    printf("%d %d %d\n", x,y,z);
    return 0;
}

Problem 11: Array of pointer-to-pointer #

#include <stdio.h>
int main(){
    int a=1,b=2;
    int *p=&a,*q=&b;
    int **arr[2]={&p,&q};
    **arr[0]=10;
    **arr[1]=20;
    printf("%d %d\n", a,b);
    return 0;
}

Problem 12: Pointer array with triple pointer #

#include <stdio.h>
int main(){
    int a=1,b=2,c=3;
    int *p1=&a,*p2=&b,*p3=&c;
    int **arr[3]={&p1,&p2,&p3};
    int ***r=arr;
    ***r=10;
    r++;
    ***r=20;
    printf("%d %d %d\n", a,b,c);
    return 0;
}

Problem 13: Pointer array arithmetic #

#include <stdio.h>
int main(){
    int x=5,y=6,z=7;
    int *arr[]={&x,&y,&z};
    int **p=arr+1;
    **p=10;
    printf("%d %d %d\n", x,y,z);
    return 0;
}

Level 5: Dynamic Memory & Tricky Pointers (Hard) #

Problem 14: Pointer to malloc #

#include <stdio.h>
#include <stdlib.h>
int main(){
    int *p = (int*)malloc(sizeof(int));
    *p=10;
    int **q=&p;
    **q=20;
    printf("%d\n", *p);
    free(p);
    return 0;
}

Problem 15: Double pointer malloc swap #

#include <stdio.h>
#include <stdlib.h>
void swap(int **x,int **y){
    int *temp=*x;
    *x=*y;
    *y=temp;
}
int main(){
    int *a=(int*)malloc(sizeof(int));
    int *b=(int*)malloc(sizeof(int));
    *a=1;*b=2;
    swap(&a,&b);
    printf("%d %d\n", *a,*b);
    free(a); free(b);
    return 0;
}

Problem 16: Pointer to pointer array with malloc #

#include <stdio.h>
#include <stdlib.h>
int main(){
    int **arr = (int**)malloc(3*sizeof(int*));
    for(int i=0;i<3;i++){
        arr[i]=(int*)malloc(sizeof(int));
        *arr[i]=i+1;
    }
    **arr=10;
    printf("%d %d %d\n", **arr, *arr[1], *arr[2]);
    for(int i=0;i<3;i++) free(arr[i]);
    free(arr);
    return 0;
}

Problem 17: Pointer to pointer to malloc pointer #

#include <stdio.h>
#include <stdlib.h>
int main(){
    int *p = (int*)malloc(sizeof(int));
    *p=5;
    int **q=&p;
    int ***r=&q;
    ***r= (int*)malloc(sizeof(int));
    ****r=10;
    printf("%d\n", *p);
    free(*q);
    return 0;
}

Problem 18: Function modification via triple pointer #

#include <stdio.h>
void f(int ***p){
    ***p = 100;
}
int main(){
    int x=10;
    int *p=&x;
    int **q=&p;
    f(&q);
    printf("%d\n", x);
    return 0;
}

Problem 19: Pointer swapping in array #

#include <stdio.h>
int main(){
    int a=1,b=2,c=3;
    int *arr[3]={&a,&b,&c};
    int **p=arr;
    int *temp=*p;
    *p=*(p+2);
    *(p+2)=temp;
    printf("%d %d %d\n", a,b,c);
    return 0;
}

Problem 20: Complex triple pointer + array #

#include <stdio.h>
int main(){
    int x=1,y=2,z=3;
    int *p1=&x,*p2=&y,*p3=&z;
    int **arr[3]={&p1,&p2,&p3};
    int ***r=arr+1;
    ***r= &x;
    **arr[0]= 10;
    **r = 20;
    printf("%d %d %d\n", x,y,z);
    return 0;
}