MST

星途 面试题库

面试题:C语言中的指针与动态内存管理

编写一个函数,实现动态分配二维数组,并通过指针操作来访问和修改数组元素。在函数结束时,需要正确释放分配的内存,避免内存泄漏。请阐述这样做的原理以及在大型项目中动态内存管理可能遇到的问题及解决方案。
50.0万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试
#include <stdio.h>
#include <stdlib.h>

// 动态分配二维数组
int** create2DArray(int rows, int cols) {
    int** arr = (int**)malloc(rows * sizeof(int*));
    if (arr == NULL) {
        return NULL;
    }

    for (int i = 0; i < rows; i++) {
        arr[i] = (int*)malloc(cols * sizeof(int));
        if (arr[i] == NULL) {
            // 释放已分配的内存
            for (int j = 0; j < i; j++) {
                free(arr[j]);
            }
            free(arr);
            return NULL;
        }
    }

    return arr;
}

// 通过指针操作访问和修改数组元素
void accessAndModify(int** arr, int row, int col, int value) {
    arr[row][col] = value;
}

// 释放二维数组内存
void free2DArray(int** arr, int rows) {
    for (int i = 0; i < rows; i++) {
        free(arr[i]);
    }
    free(arr);
}

int main() {
    int rows = 3, cols = 4;
    int** myArray = create2DArray(rows, cols);
    if (myArray == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    accessAndModify(myArray, 1, 2, 42);
    printf("修改后的值: %d\n", myArray[1][2]);

    free2DArray(myArray, rows);
    return 0;
}

原理阐述

  1. 动态分配二维数组
    • 首先通过 malloc 分配一个指针数组,这个数组的每个元素都是指向 int 类型数组的指针。
    • 然后为每一行分配 int 类型的数组,这样就构成了一个二维数组。
  2. 指针操作访问和修改数组元素
    • 二维数组名是一个指向指针数组的指针,通过 arr[row][col] 这种形式,实际上是先找到第 row 行的指针,再通过这个指针找到第 col 列的元素进行访问和修改。
  3. 释放内存
    • 先释放每一行分配的内存,再释放指针数组的内存,这样就完成了内存的正确释放,避免内存泄漏。

大型项目中动态内存管理可能遇到的问题及解决方案

  1. 内存泄漏
    • 问题:忘记释放分配的内存,随着程序运行,内存不断被占用,最终导致系统内存不足。
    • 解决方案
      • 使用智能指针(在C++ 中),例如 std::unique_ptrstd::shared_ptr,它们会在对象生命周期结束时自动释放内存。
      • 建立内存管理的规范,在分配内存的地方明确标记需要释放内存的位置,并在代码审查时重点检查。
  2. 悬空指针
    • 问题:指针所指向的内存已经被释放,但指针仍然存在,继续使用这个指针会导致未定义行为。
    • 解决方案
      • 在释放内存后,将指针设置为 NULL,这样可以避免意外使用悬空指针。例如在 free(ptr); ptr = NULL;
      • 使用智能指针,智能指针会自动处理悬空指针的问题,因为当所指向的对象被销毁时,智能指针会自动更新。
  3. 内存碎片
    • 问题:频繁的内存分配和释放操作导致内存空间不连续,产生许多小块的空闲内存,使得较大的内存分配请求无法满足。
    • 解决方案
      • 使用内存池技术,预先分配一块较大的内存,程序中的内存分配请求从这个内存池中获取,释放时再归还到内存池,减少系统级的内存分配和释放次数。
      • 优化内存分配策略,例如尽量按顺序分配和释放内存,减少内存碎片的产生。