C&C++快速入门

12、指针与内存管理

1、课程目标

  • 掌握指针的概念和使用
  • 理解指针与数组的关系
  • 掌握动态内存分配与释放
  • 了解常见的内存错误

2、名词解释

术语 说明
指针 存储内存地址的变量
解引用 通过指针访问指向的数据,使用*
取地址 获取变量的内存地址,使用&
空指针 值为NULL的指针
野指针 未初始化或指向已释放内存的指针
悬空指针 指向已释放内存的指针
内存泄漏 分配的内存未释放
动态分配内存的区域(Heap)
局部变量存储区域(Stack)

3、代码实现

1. 指针基础

#include <stdio.h>

int main() {
    printf("=== 指针基础 ===\n\n");
    
    int value = 42;
    int* ptr = &value;  // ptr存储value的地址
    
    printf("【基本操作】\n");
    printf("value = %d\n", value);
    printf("&value = %p\n", (void*)&value);
    printf("ptr = %p\n", (void*)ptr);
    printf("*ptr = %d\n", *ptr);  // 解引用
    
    // 通过指针修改值
    *ptr = 100;
    printf("\n修改后 value = %d\n", value);
    
    // 指针的指针
    int** pptr = &ptr;
    printf("\n【二级指针】\n");
    printf("pptr = %p\n", (void*)pptr);
    printf("*pptr = %p\n", (void*)*pptr);
    printf("**pptr = %d\n", **pptr);
    
    return 0;
}

2. 指针运算

#include <stdio.h>

int main() {
    printf("=== 指针运算 ===\n\n");
    
    int arr[] = {10, 20, 30, 40, 50};
    int* p = arr;
    
    // 指针加减
    printf("【指针加减】\n");
    printf("p = %p, *p = %d\n", (void*)p, *p);
    printf("p+1 = %p, *(p+1) = %d\n", (void*)(p+1), *(p+1));
    printf("p+2 = %p, *(p+2) = %d\n", (void*)(p+2), *(p+2));
    
    // 指针相减
    printf("\n【指针相减】\n");
    int* end = &arr[4];
    printf("元素间隔: %td\n", end - p);  // 4
    
    // 数组下标与指针
    printf("\n【等价写法】\n");
    printf("arr[2] = %d\n", arr[2]);
    printf("*(arr+2) = %d\n", *(arr+2));
    printf("*(p+2) = %d\n", *(p+2));
    printf("p[2] = %d\n", p[2]);
    
    return 0;
}

3. 动态内存分配

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

int main() {
    printf("=== 动态内存分配 ===\n\n");
    
    // malloc: 分配未初始化的内存
    printf("【malloc】\n");
    int* arr1 = (int*)malloc(5 * sizeof(int));
    if (arr1 == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }
    printf("分配成功: %p\n", (void*)arr1);
    
    // calloc: 分配并初始化为0
    printf("\n【calloc】\n");
    int* arr2 = (int*)calloc(5, sizeof(int));
    if (arr2) {
        printf("初始化为0: %d %d %d\n", arr2[0], arr2[1], arr2[2]);
    }
    
    // realloc: 重新分配大小
    printf("\n【realloc】\n");
    arr1[0] = 100;
    int* arr3 = (int*)realloc(arr1, 10 * sizeof(int));
    if (arr3) {
        printf("扩展后原数据保留: %d\n", arr3[0]);
        arr1 = arr3;  // 更新指针
    }
    
    // free: 释放内存
    free(arr1);  // arr3和arr1指向同一内存
    free(arr2);
    
    printf("\n内存已释放\n");
    
    return 0;
}

4. Windows内存管理API

#include <stdio.h>
#include <windows.h>

int main() {
    printf("=== Windows内存API ===\n\n");
    
    // VirtualAlloc: 分配虚拟内存
    printf("【VirtualAlloc】\n");
    LPVOID pMem = VirtualAlloc(
        NULL,              // 系统选择地址
        4096,              // 大小
        MEM_COMMIT | MEM_RESERVE,  // 分配类型
        PAGE_READWRITE     // 保护属性
    );
    
    if (pMem) {
        printf("分配地址: %p\n", pMem);
        strcpy((char*)pMem, "Hello VirtualAlloc!");
        printf("内容: %s\n", (char*)pMem);
        
        // VirtualFree: 释放
        VirtualFree(pMem, 0, MEM_RELEASE);
        printf("内存已释放\n");
    }
    
    // HeapAlloc: 堆内存分配
    printf("\n【HeapAlloc】\n");
    HANDLE hHeap = GetProcessHeap();
    LPVOID pHeapMem = HeapAlloc(hHeap, HEAP_ZERO_MEMORY, 256);
    
    if (pHeapMem) {
        printf("堆内存: %p\n", pHeapMem);
        HeapFree(hHeap, 0, pHeapMem);
        printf("堆内存已释放\n");
    }
    
    return 0;
}

5、课后作业

5.1、作业1:实现动态数组

实现可动态增长的数组(简单的vector)。

5.2、作业2:内存分配封装

封装一个安全的内存分配函数,包含分配失败检查。

5.3、作业3:内存拷贝

实现memcpy函数,处理内存重叠情况。