C Program to Create a 2D Array Using malloc()

C Program to Create a 2D Array Using malloc()

In C programming, 2D arrays are widely used to represent matrices, tables, or grids. Static 2D arrays are simple but have a fixed size determined at compile time. To handle variable-sized arrays or large datasets efficiently, dynamic memory allocation using malloc() is preferred.

Dynamic allocation allows programs to create 2D arrays whose dimensions are determined at runtime. In this tutorial, we will show how to allocate memory for a 2D array using pointers and malloc(), input and output values, and safely release memory to avoid leaks.

Understanding the Problem

A 2D array can be visualized as an array of arrays. When allocating memory dynamically, you can either allocate a contiguous block of memory or allocate an array of pointers, each pointing to a row. Both methods allow flexible sizes, but using pointers for rows is often simpler and easier to understand.

For example, to create a matrix of m rows and n columns, memory must be allocated for each row separately, then for each element within that row. This ensures that each element can be accessed using standard 2D array indexing syntax.

Program 1: 2D Array Using Array of Pointers

This program demonstrates creating a 2D array using an array of row pointers. Each row is allocated individually, giving flexibility to handle rows of different lengths if needed. Proper memory management requires freeing each row first, then the array of pointers itself.

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

int main() {

    int rows, cols;
    int **matrix;

    printf("Enter number of rows: ");
    scanf("%d", &rows);

    printf("Enter number of columns: ");
    scanf("%d", &cols);

    // Allocate memory for row pointers
    matrix = (int **) malloc(rows * sizeof(int *));

    if (matrix == NULL) {

        printf("Memory allocation failed.\n");
        return 1;

    }

    // Allocate memory for each row
    for (int i = 0; i < rows; i++) {

        matrix[i] = (int *) malloc(cols * sizeof(int));

        if (matrix[i] == NULL) {

            printf("Memory allocation failed for row %d.\n", i);
            return 1;

        }

    }

    // Input elements
    for (int i = 0; i < rows; i++) {

        for (int j = 0; j < cols; j++) {

            printf("Enter element [%d][%d]: ", i, j);
            scanf("%d", &matrix[i][j]);

        }

    }

    // Print elements
    printf("2D Array Elements:\n");

    for (int i = 0; i < rows; i++) {

        for (int j = 0; j < cols; j++) {
            printf("%d ", matrix[i][j]);
        }

        printf("\n");

    }

    // Free allocated memory
    for (int i = 0; i < rows; i++) {
        free(matrix[i]);
    }

    free(matrix);

    return 0;

}

In this approach, matrix is a pointer to pointers. Each row is allocated separately, allowing flexible row sizes. Memory is freed in reverse order to prevent leaks, ensuring all dynamically allocated memory is properly released.

Program 2: 2D Array Using Single Contiguous Block

An alternative method is to allocate a single contiguous block of memory for all elements. This approach is more memory-efficient and suitable for large arrays because it reduces the overhead of multiple allocations. Elements are accessed using pointer arithmetic.

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

int main() {

    int rows, cols;
    int *matrix;

    printf("Enter number of rows: ");
    scanf("%d", &rows);

    printf("Enter number of columns: ");
    scanf("%d", &cols);

    // Allocate memory for the entire 2D array
    matrix = (int *) malloc(rows * cols * sizeof(int));

    if (matrix == NULL) {

        printf("Memory allocation failed.\n");
        return 1;

    }

    // Input elements
    for (int i = 0; i < rows; i++) {

        for (int j = 0; j < cols; j++) {

            printf("Enter element [%d][%d]: ", i, j);
            scanf("%d", &matrix[i * cols + j]);

        }

    }

    // Print elements
    printf("2D Array Elements:\n");

    for (int i = 0; i < rows; i++) {

        for (int j = 0; j < cols; j++) {
            printf("%d ", matrix[i * cols + j]);
        }

        printf("\n");

    }

    // Free allocated memory
    free(matrix);

    return 0;

}

Here, all elements are stored in a single contiguous block. The element at [i][j] is accessed as matrix[i * cols + j]. This method is simple, efficient, and minimizes allocation overhead, making it ideal for large-scale 2D arrays.

Program 3: Pointer-to-Pointer with Contiguous Data Block

This approach combines the array-of-pointers and contiguous block methods. One block is allocated for the row pointers and another contiguous block for all elements. Each row pointer is then set to the correct offset in the data block.

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

int main() {

    int rows, cols;
    int **matrix;
    int *data;

    printf("Enter number of rows: ");
    scanf("%d", &rows);

    printf("Enter number of columns: ");
    scanf("%d", &cols);

    // Allocate memory for row pointers
    matrix = (int **) malloc(rows * sizeof(int *));

    if (matrix == NULL) {

        printf("Memory allocation failed.\n");
        return 1;

    }

    // Allocate a single contiguous block for all elements
    data = (int *) malloc(rows * cols * sizeof(int));

    if (data == NULL) {

        printf("Memory allocation failed.\n");
        free(matrix);

        return 1;

    }

    // Assign row pointers
    for (int i = 0; i < rows; i++) {
        matrix[i] = data + i * cols;
    }

    // Input elements
    printf("Enter matrix elements:\n");

    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            scanf("%d", &matrix[i][j]);

    // Print elements
    printf("2D Array Elements:\n");

    for (int i = 0; i < rows; i++) {

        for (int j = 0; j < cols; j++)
            printf("%d ", matrix[i][j]);

        printf("\n");

    }

    // Free memory
    free(data);
    free(matrix);

    return 0;

}

This method allows convenient matrix[i][j] syntax while keeping all data in one contiguous block, making it memory-efficient and easy to manage.

Program 4: Flat 1D Array with Helper Macro

A simple method is to treat the 2D array as a 1D array and use a macro to calculate element indices. This avoids multiple allocations entirely.

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

#define IDX(i,j,cols) ((i)*(cols) + (j))

int main() {

    int rows, cols;
    int *matrix;

    printf("Enter number of rows: ");
    scanf("%d", &rows);

    printf("Enter number of columns: ");
    scanf("%d", &cols);

    // Allocate memory for all elements
    matrix = (int *) malloc(rows * cols * sizeof(int));

    if (matrix == NULL) {

        printf("Memory allocation failed.\n");
        return 1;

    }

    // Input elements
    printf("Enter matrix elements:\n");

    for (int i = 0; i < rows; i++)
        for (int j = 0; j < cols; j++)
            scanf("%d", &matrix[IDX(i,j,cols)]);

    // Print elements
    printf("2D Array Elements:\n");

    for (int i = 0; i < rows; i++) {

        for (int j = 0; j < cols; j++)
            printf("%d ", matrix[IDX(i,j,cols)]);

        printf("\n");

    }

    // Free memory
    free(matrix);

    return 0;

}

This method is highly efficient and simple, but accessing elements requires manual index calculation instead of using the natural matrix[i][j] syntax.

Program 5: Struct-Based 2D Array

A struct can encapsulate a dynamic 2D array, storing rows, columns, and a data pointer. This makes passing arrays to functions easier and keeps the code organized.

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

typedef struct {
    int rows, cols;
    int *data;
} Matrix;

int main() {

    Matrix m;

    printf("Enter number of rows: ");
    scanf("%d", &m.rows);

    printf("Enter number of columns: ");
    scanf("%d", &m.cols);

    // Allocate memory for all elements
    m.data = (int *) malloc(m.rows * m.cols * sizeof(int));

    if (m.data == NULL) {

        printf("Memory allocation failed.\n");
        return 1;

    }

    // Input elements
    printf("Enter matrix elements:\n");

    for (int i = 0; i < m.rows; i++)
        for (int j = 0; j < m.cols; j++)
            scanf("%d", &m.data[i * m.cols + j]);

    // Print elements
    printf("2D Array Elements:\n");

    for (int i = 0; i < m.rows; i++) {

        for (int j = 0; j < m.cols; j++)
            printf("%d ", m.data[i * m.cols + j]);

        printf("\n");

    }

    // Free memory
    free(m.data);

    return 0;

}

This struct-based approach makes the array self-contained, improving code readability and maintainability. You can also pass the Matrix struct to functions without separately passing rows, columns, and pointers.

FAQs

1. How do I create a 2D array dynamically in C?
Dynamic 2D arrays can be created using several methods: an array of pointers, where each row is allocated separately; a single contiguous memory block with pointer arithmetic; a pointer-to-pointer with contiguous data; or using a struct to encapsulate rows, columns, and data. All methods allow array sizes to be determined at runtime, giving flexibility for variable dimensions.

2. Which method is more memory-efficient?
Allocating a single contiguous block of memory is the most memory-efficient method, as it requires only one allocation and avoids multiple row pointers. Hybrid methods, like pointer-to-pointer with a contiguous block, balance convenience (matrix[i][j] syntax) with efficiency. Array-of-pointers methods have slightly higher overhead but allow rows of different lengths.

3. How do I free dynamically allocated 2D arrays?
Freeing depends on the allocation method. For an array of pointers, free each row individually, then the array of pointers. For a single contiguous block, pointer-to-pointer with contiguous data, or struct-based arrays, a single call to free() on the main data block (and row pointer array if applicable) is sufficient. Always ensure that pointers are set to NULL after freeing to avoid dangling references.

4. Can I resize a dynamically allocated 2D array?
Yes. Resizing is possible using realloc(). For array-of-pointers methods, each row can be resized individually. For a single contiguous block or struct-based arrays, the entire block can be reallocated. Care must be taken to preserve existing data and avoid memory leaks when resizing.

Conclusion

Creating 2D arrays dynamically in C offers flexibility and control over memory usage, allowing programs to handle variable-sized matrices, grids, and tables efficiently. Each method—whether an array of pointers, a contiguous block, a hybrid pointer-to-pointer, or a struct-based array—has its advantages and trade-offs in terms of memory efficiency, convenience, and flexibility.

Mastering these techniques ensures that your programs allocate, access, and free memory safely, preventing leaks and maintaining performance. With careful memory management, you can write robust, scalable, and high-performance C programs that handle complex 2D data structures reliably.

References & Additional Resources

The following resources cover dynamic memory allocation in C, including creating and managing 2D arrays, and proper use of pointers and memory functions.

  1. Kernighan, B. W., & Ritchie, D. M. (1988). The C Programming Language (2nd Edition). Prentice Hall – Classic reference covering pointers, memory allocation, and dynamic arrays.
  2. Thareja, R. (2011). Data Structures Using C. Oxford University Press – Detailed explanations of dynamic memory allocation and multidimensional arrays.
  3. GeeksforGeeks: Dynamic 2D Array in C – Step-by-step guide to allocating and accessing 2D arrays dynamically.
  4. Tutorialspoint: malloc() in C – Explanation of malloc() usage and memory allocation examples.
  5. Cprogramming.com: Pointers in C – Beginner-friendly guide to pointers and dynamic memory handling.
  6. cplusplus.com: malloc() and free() – Official documentation for malloc() and free() functions.
Scroll to Top