当前位置: 首页 > news >正文

C语言中易混淆问题【数组指针与指针数组详解】

数组指针与指针数组

核心定义与语法的区别

数组指针(指向数组的指针)
  • 定义:数组指针是一个指针变量,它指向一个固定长度的数组.
  • 语法:类型(*指针名)[数组长度]
  • 示例:int (*ptr)[5]; 表示 ptr 是一个指针,指向包含 5 个 int 的数组。
  • 关键特征:
    • 指针指向数组的首地址解引用后得到整个数组
    • 指针的步长为 数组长度 × 单个元素大小。
指针数组(数组存储指针)
  • 定义:指针数组是一个数组,其元素均为指针变量。
  • 语法:类型* 数组名[数组长度]
  • 示例:int* ptr_arr[5]表示:ptr_arr是一个包含5个int*类型元素的数组。
  • 关键特征:
    • 数组元素是指针,可指向不同的内存地址(如不同的变量、数组或动态内存)。
    • 本质是普通数组,遵循数组的访问规则(通过下标访问指针元素)。

内存布局对比

1.数组指针:(以int (*ptr)[3]为例)
  • 指向对象:一维数组:int arr[3] = {1,2,3};
  • 内存示意图:
arr:  ┌───┬───┬───┐│ 123 │└───┴───┴───┘地址:0x1000    0x1004    0x1008  ptr: ────────────────► arr (0x1000)
  • 指针运算:

    ptr + 1偏移 3 * sizeof(int) = 12 字节(指向 arr 之后的下一个数组)。

    2.指针数组(以 int* ptr_arr[2] 为例)
  • 存储的内容:指针数组存储多个指针,每个指针指向独立的内存区域。

  • 内存示意图:

    ptr_arr:  ┌─────────┬─────────┐│ 0x20000x3000 │ (假设分别指向 arr1 和 arr2)└─────────┴─────────┘地址:0x4000    0x4004  arr1: ┌───┐          arr2:┌───┐│ 1 │          │ 4 │└───┘          └───┘0x2000         0x3000
    
  • 访问方式:ptr_arr[0] 得到 0x2000(指向 arr1),*ptr_arr[0] 解引用得到 1。

典型示例对比:

1.数组指针示例:处理二维数组
#include <stdio.h>int main() 
{// 二维数组(2行3列)int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};// 数组指针指向二维数组的行(列数必须与数组一致)int (*row_ptr)[3] = arr; // 等价于 int (*row_ptr)[3] = &arr[0];// 访问第一行第二列元素printf("第一行第二列:%d\n", (*row_ptr)[1]); // 输出 2// 指针移动到第二行row_ptr++;printf("第二行第一列:%d\n", (*row_ptr)[0]); // 输出 4return 0;
}
  • 关键点:二维数组名arr隐式转换为数组指针 int (*)[3],指向首行。
2.指针数组示例:管理多个动态数组
#include<stdio.h>
int main()
{//指针数组:存储3个指向int 数组的指针int* ptr_arr[3];//动态分布三个一维数组ptr_arr[0] = (int*)malloc(sizeof(int));ptr_arr[1] = (int*)malloc(2 * sizeof(int));ptr_arr[2] = (int*)malloc(3 * sizeof(int));// 赋值*ptr_arr[0] = 10;ptr_arr[1][0] = 20; ptr_arr[1][1] = 21;ptr_arr[2][0] = 30; ptr_arr[2][1] = 31; ptr_arr[2][2] = 32;  // 访问printf("第一个数组元素:%d\n", *ptr_arr[0]);       // 10printf("第二个数组元素:%d\n", ptr_arr[1][1]);    // 21// 释放内存for (int i = 0; i < 3; i++) {free(ptr_arr[i]);}return 0;
}

维度
数组指针
指针数组

本质
指针(指向一个固定大小的数组)
数组(元素为指针类型)

语法核心
(*) 优先(先声明为指针)
[] 优先(先声明为数组)

定义示例
int (ptr)[5];
int
ptr[5];

内存占用
占一个指针的大小(如 8 字节,64 位)
占 n × 指针大小(如 5×8=40 字节)

典型用途
二维数组行操作、固定列数的数组传递
动态数组管理、字符串数组、指针集合

初始化方式
指向已有数组(如 ptr = &arr;)
逐个初始化指针元素(如 ptr[i] = &var;)

访问元素
通过 (*ptr)[i] 或 ptr[i][j] 访问
通过 ptr[i] 访问指针指向的值

常见场景
函数参数传递二维数组(列数固定)
存储多个动态分配的内存地址

经典应用场景

1. 数组指针:
1.处理多维数组:当需要指针遍历或多维数组时
//ptr 直接指向二维数组的行,偏移量为 3*sizeof(int),无需额外转换,可直接通过 ptr[i][j] 访问元素
#include <stdio.h>
int main() 
{int arr[2][3] = {{1,2,3}, {4,5,6}};int (*ptr)[3] = arr; // 指向第一行(类型为int(*)[3])// 遍历数组for (int i = 0; i < 2; i++) {for (int j = 0; j < 3; j++) {printf("%d ", ptr[i][j]); // 等价于*(*(ptr+i)+j)}printf("\n");}return 0;
}
int arr[2][3] = {{1,2,3},{4,5,6}};
int (*ptr)[3] = arr 		//指向第一行
printf("%d\n",ptr[1][2]);	//输出6
2.动态分配二维数组(连续内存)
//优势:内存连续,缓存命中率高释放简单,只需一次 free
#include <stdio.h>
#include <stdlib.h>int main() 
{int rows = 3, cols = 4;int (*matrix)[cols] = malloc(rows * sizeof(*matrix));// 初始化for (int i = 0; i < rows; i++)for (int j = 0; j < cols; j++)matrix[i][j] = i * cols + j;// 释放内存free(matrix); // 一次释放全部内存return 0;
}
3.函数参数传递多维数组
//注意:函数参数中 ,int (*arr)[3] 必须指定列数,等价于 int arr[][3],但指针形式更清晰
#include <stdio.h>// 计算二维数组的和(必须指定列数)
int sum(int (*arr)[3], int rows) 
{int total = 0;for (int i = 0; i < rows; i++)for (int j = 0; j < 3; j++)total += arr[i][j];return total;
}int main() 
{int arr[2][3] = {{1,2,3}, {4,5,6}};printf("Sum: %d\n", sum(arr, 2)); // 输出21return 0;
}
// 函数声明:处理二维数组(列数固定为3)
// 参数说明:
// - int (*arr)[3]:数组指针,指向包含3个int的一维数组(即二维数组的行)
// - rows:二维数组的行数
void process_2d_array(int (*arr)[3], int rows) 
{// 外层循环:遍历每一行(i表示行号)for (int i = 0; i < rows; i++) {// 内层循环:遍历当前行的每一列(j表示列号,列数固定为3)for (int j = 0; j < 3; j++) {// 访问二维数组元素并乘以2// 等价于:(*(arr + i))[j] *= 2;// 解析:// - arr + i:指向第i行(数组指针偏移i行,每行3个int)// - *(arr + i):解引用得到第i行的一维数组(类型为int[3])// - [j]:访问该行的第j个元素arr[i][j] *= 2; }}
}int main() 
{// 定义二维数组:2行3列int arr[2][3] ={{1, 2, 3},  // 第0行{4, 5, 6}   // 第1行};// 调用函数处理二维数组// 传递数组名arr时,会隐式转换为数组指针int (*)[3](指向首行)process_2d_array(arr, 2); // 若需要验证结果,可添加打印代码:// for (int i = 0; i < 2; i++){//     for (int j = 0; j < 3; j++) {//         printf("%d ", arr[i][j]);//     }// }return 0;
}
// 2 4 6 8 10 12       
2.指针数组:
1.动态二维数组(非连续内存)
#include <stdio.h>
#include <stdlib.h>int main()
{int rows = 3, cols = 4;int* matrix[rows]; // 创建指针数组// 为每行分配内存(每行可不同长度)for (int i = 0; i < rows; i++) {matrix[i] = malloc(cols * sizeof(int));}// 初始化for (int i = 0; i < rows; i++)for (int j = 0; j < cols; j++)matrix[i][j] = i * cols + j;// 释放内存(必须逐行释放)for (int i = 0; i < rows; i++) {free(matrix[i]);}return 0;
}
//matrix[0] → [0, 1, 2, 3]
//matrix[1] → [4, 5, 6, 7]
//matrix[2] → [8, 9, 10, 11]
//优势:每行可独立分配不同长度,适合不规则数组
2.指向独立内存块
#include <stdio.h>int main() 
{int a = 10, b = 20, c = 30;int* ptr[3] = {&a, &b, &c}; // 每个指针指向不同变量for (int i = 0; i < 3; i++){printf("%d ", *ptr[i]); // 输出: 10 20 30}return 0;
}
3.字符串数组
#include <stdio.h>int main()
{char* names[3] = {"Alice",  // 指向字符串常量的指针"Bob","Charlie"};for (int i = 0; i < 3; i++) {printf("Name %d: %s\n", i, names[i]);}return 0;
}
//本质:names 是一个包含3个 char* 的数组,每个指针指向一个字符串常量
字符串数组(存储字符串指针)
#include <string.h>  // 提供strcpy等字符串处理函数int main() 
{// 指针数组:存储多个字符串常量的地址// 每个字符串常量存储在只读内存区,str_arr存储它们的首地址char* str_arr[] = {"apple", "banana", "cherry"};// 遍历指针数组,输出每个字符串for (int i = 0; i < 3; i++)  {printf("字符串 %d: %s\n", i+1, str_arr[i]);// str_arr[i] 是第i个字符串的首地址,%s自动解析字符串直到'\0'}// 动态创建指针数组(在堆区分配内存)char** dyn_str_arr = (char**)malloc(2 * sizeof(char*));// 分配2个指针的空间,dyn_str_arr指向该内存块// 为第一个指针分配内存并复制字符串dyn_str_arr[0] = (char*)malloc(5 * sizeof(char));  // 分配5字节(含'\0')strcpy(dyn_str_arr[0], "test");  // 复制"test"到分配的内存中// 注意:代码未释放内存(存在内存泄漏)// 正确做法应在return前添加:free(dyn_str_arr[0]);free(dyn_str_arr);return 0;
}
//字符串 1: apple
//字符串 2: banana
//字符串 3: cherry
  • 静态字符串数组 str_arr

    • str_arr ┌─────────┬─────────┬─────────┐│ 0x10000x10060x1013  │ (字符串常量地址)└─────────┴─────────┴─────────┘↓         ↓         ↓"apple\0" "banana\0" "cherry\0"0x1000    0x1006     0x1013    (只读内存区)
      
    • str_arr 是栈上的指针数组,每个元素指向一个字符串常量。

  • 动态指针数组 dyn_str_arr

    • dyn_str_arr ───► ┌─────────┬─────────┐ (堆区:2个指针)│ 0x2000NULL  │└─────────┴─────────┘↓┌─────────────┐ (堆区:5字节)│ t e s t \0  │└─────────────┘0x2000
      
    • dyn_str_arr 指向堆区的指针数组

    • dyn_str_arr[0] 指向另一段堆内存,存储 “test”

  • 动态内存分配

  • char** ptr = (char**)malloc(n * sizeof(char*));  // 分配指针数组
    ptr[i] = (char*)malloc(len * sizeof(char));      // 为每个指针分配字符串空间
    

总结:

处理固定列数的二维数组 |数组指针 |保持行列逻辑,高效传递二维数组
管理动态或变长数组 |指针数组 |灵活分配内存,每个指针可独立操作
存储字符串集合 | 指针数组 |字符串本质是 char*,适合用指针数组管理
按行操作二维数组 |数组指针 |直接操作行数据,符合内存布局特性

内存布局连续存储整个多维数组存储多个指针,每个指针可能指向不同内存区域
指针类型ptr 是一个指针,类型为 int(*)[n]ptr 是数组,类型为 int*[n]
偏移量ptr + 1 跳过 n*sizeof(int) 字节ptr + 1 跳过 sizeof(int*) 字节
初始化int arr[2][3]; int (*ptr)[3] = arr;int* ptr[3] = {malloc(4), malloc(4), malloc(4)};
访问方式ptr[i][j] 直接访问ptr[i][j] 通过指针间接访问
内存释放一次 free(ptr)需逐个释放每个指针:for(i) free(ptr[i])
典型场景规则矩阵运算、高效遍历不规则数组、动态长度数组、字符串数组

常见错误与注意事项

错误1:混淆指针类型
int arr[2][3];
int (*ptr)[3] = arr;    // 正确:ptr指向第一行
int* ptr2 = arr;        // 错误:类型不匹配(int* 与 int(*)[3])
错误2:错误释放内存
int rows = 3, cols = 4;
int* matrix[rows];
for (int i = 0; i < rows; i++) 
{matrix[i] = malloc(cols * sizeof(int));
}free(matrix); // 错误!只释放了指针数组,未释放每行内存
// 正确做法:
for (int i = 0; i < rows; i++)
{free(matrix[i]);
}
http://www.xdnf.cn/news/867601.html

相关文章:

  • C++内存列传之RAII宇宙:智能指针
  • C#入门学习笔记 #7(传值/引用/输出/数组/具名/可选参数、扩展方法(this参数))
  • WPF可拖拽ListView
  • 质检 LIMS 系统数据防护指南 三级等保认证与金融级加密方案设计
  • 英国2025年战略防御评估报告:网络与电磁域成现代战争核心
  • Axios 取消请求的演进:CancelToken vs. AbortController
  • 【读代码】从预训练到后训练:解锁语言模型推理潜能——Xiaomi MiMo项目深度解析
  • 【android bluetooth 协议分析 12】【A2DP详解 2】【开启ble扫描-蓝牙音乐卡顿分析】
  • 光伏防逆流控制方案
  • .NET Core接口IServiceProvider
  • Spring Boot MVC自动配置与Web应用开发详解
  • Asp.net Core 通过依赖注入的方式获取用户
  • 全志A40i android7.1 调试信息打印串口由uart0改为uart3
  • 六种高阶微分方程的特解(原创:daode3056)
  • Java观察者模式深度解析:构建松耦合事件驱动系统的艺术
  • NC28 最小覆盖子串【牛客网】
  • 基于Axure+墨刀设计的电梯管理系统云台ERP的中保真原型图
  • Apache APISIX
  • CMake入门:3、变量操作 set 和 list
  • 深度学习项目之RT-DETR训练自己数据集
  • 通过模型文件估算模型参数量大小
  • Flask框架详解:轻量高效的Python Web开发利器
  • 深入解析Oracle SQL调优健康检查工具(SQLHC):从原理到实战优化
  • intense-rp-api开源程序是一个具有直观可视化界面的 API,可以将 DeepSeek 非正式地集成到 SillyTavern 中
  • Windows系统工具:WinToolsPlus 之 SQL Server Suspect/质疑/置疑/可疑/单用户等 修复
  • stress 服务器压力测试的工具学习
  • linux操作系统---网络协议
  • LeetCode 3370.仅含置位位的最小整数
  • 二维 根据矩阵变换计算镜像旋转角度
  • 短剧+小说网盘搜索系统(支持全网网盘转存拉新)