10、指针数组:指针数组的定义、指针数组的应用(命令行参数)、指针数组与二维数组的对比

指针数组,这个名字听起来有点绕。说白了,它是一个数组,但数组里存的不是整数、不是浮点数,而是指针。每个元素都是一个指针变量。

我刚开始学的时候,总把它和数组指针搞混。一个是“指针的数组”,一个是“数组的指针”。嗯,这里要注意区分。今天我们就把它彻底讲透。

10.1 指针数组的定义

定义方式很简单:类型 *数组名[数组长度]。注意,[]的优先级高于*,所以它先是一个数组,然后数组的元素是指针。

// 定义一个指针数组,包含3个元素
// 每个元素都是 char* 类型
char *str_arr[3];

// 初始化
char a[] = "Hello";
char b[] = "World";
char c[] = "C语言";

str_arr[0] = a;
str_arr[1] = b;
str_arr[2] = c;

// 或者直接初始化
char *str_arr2[3] = {"Hello", "World", "C语言"};

我个人习惯用第二种方式,简洁明了。每个字符串字面量其实就是一个char*,直接赋值给指针数组的元素。

核心理解:指针数组的每个元素都是一个指针,指向某个内存地址。你可以通过这个地址访问数据。

10.2 指针数组的应用:命令行参数

指针数组最经典的应用,就是main函数的参数。你想想看,每次写int main(int argc, char *argv[]),这个argv就是指针数组。

#include <stdio.h>

int main(int argc, char *argv[])
{
    printf("参数个数: %d\n", argc);
    
    for (int i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
    
    return 0;
}

我在项目中遇到过一个问题:有个同事写了一个命令行工具,需要解析十几个参数。他一开始用getopt库,但后来发现有些参数是变长的。最后我用指针数组手动解析,反而更灵活。

避坑指南:我曾经在解析命令行参数时,直接修改了argv[i]指向的字符串,结果导致后续参数解析出错。记住,argv指向的是程序启动时系统分配的内存,不要随意修改。

为什么会这样?因为argv是一个指针数组,每个元素指向一个字符串。如果你修改了字符串内容,可能会影响其他参数的解析,甚至导致程序崩溃。

10.3 指针数组与二维数组的对比

这两个东西,初学者特别容易搞混。我们直接上代码对比:

// 二维数组
char arr2d[3][10] = {
    "Hello",
    "World",
    "C语言"
};

// 指针数组
char *arrptr[3] = {
    "Hello",
    "World",
    "C语言"
};

看起来差不多?其实差别大了去了。我画个图你就明白了。

指针数组 vs 二维数组 内存布局对比 二维数组 char arr2d[3][10] H e l l o \0 W o r l d \0 C 语 言 \0 连续内存块,每行固定10字节 指针数组 char *arrptr[3] ptr[0] ptr[1] ptr[2] Hello\0 World\0 C语言\0 关键区别 二维数组 指针数组 内存连续,固定大小 指针连续,数据可分散 每行长度固定(浪费空间) 每行长度可变(节省空间) 访问速度快(连续内存) 访问需间接寻址(稍慢)

从图上可以看得很清楚:

  • 二维数组:内存是连续的,每行固定长度。比如char arr2d[3][10],每行10个字节,不管字符串多短,都占10字节。浪费空间,但访问速度快。
  • 指针数组:数组本身是连续的,但每个指针指向的字符串可以分散在内存各处。字符串长度可以不同,按需分配。节省空间,但访问时需要多一次间接寻址。

注意:二维数组的arr2d[0]arr2d[1]之间是连续的,相差10个字节。而指针数组的arrptr[0]arrptr[1]指向的地址可能相隔很远,没有任何规律。

10.4 实际项目中的选择

我做过一个嵌入式项目,需要存储几十个设备的名字。名字长度从3个字符到20个字符不等。如果用二维数组,每个名字都得按最长的20字节分配,浪费了将近一半的内存。对于嵌入式系统来说,内存就是命啊。

所以我用了指针数组:

// 设备名字列表
const char *device_names[] = {
    "LED",           // 3字节
    "Temperature",   // 11字节
    "Humidity",      // 8字节
    "Pressure",      // 8字节
    "Motor_Control", // 13字节
    NULL             // 结束标记
};

你看,每个字符串按实际长度存储,不浪费一个字节。而且用NULL作为结束标记,遍历起来也很方便。

小技巧:如果你需要频繁修改字符串内容,用二维数组更合适。如果只是读取,或者字符串长度差异很大,指针数组是更好的选择。

10.5 总结

指针数组,说白了就是一个存指针的数组。它和二维数组的区别,本质上就是“指针”和“数组”的区别。指针数组灵活、节省空间,但多了一次间接访问。二维数组规整、访问快,但可能浪费空间。

嗯,这里要记住:char *argv[]就是指针数组,它是C语言命令行参数的标准接口。理解了指针数组,你就能理解为什么main函数可以接收任意数量的参数了。

我在实际项目中,80%的情况下用指针数组,20%的情况下用二维数组。具体选哪个,看你的需求:要速度还是要灵活?


公众号:蓝海资料掘金营,微信deep3321