C 指针数组

在 C 语言中,指针数组是一个 “指向数组的指针”。使用指针数组,可以让你通过指针运算间接访问或操作数组元素。

C 指针数组的语法

刚学完 “数组指针” ,又来一个指针数组?估计很多小伙伴已经一脸懵逼了。不用担心,其实这两个很好理解的。 “数组指针” 和 “指针数组”,只要在名词中间加上 “的” 这个字,它们的意思就很直观了。

  • 数组的指针:是一个指针,指向数组的指针。
  • 指针的数组:是一个数组,装着指针的数组。

在 C 语言中,对于数组指针来说,它本质上是一个 “指针” 。对于指针数组来说,它本质上是一个 “数组” ,然后数组每一个元素都是一个指针。

语法:

int *p[n] = [元素1, 元素2, …, 元素n];

说明:

从 “C 运算符优先级” 一节可以知道:() = [] > *,然后可以总结出以下 2 点。

1. (*p)[n] 定义的是一个数组指针

因为 “()” 和 “[]” 优先级相同,然后结合方向是从左到右,所以我们先看 “(*p)”,后看 “[n]”。所以这里 (*p)[n] 强调的是 “(*p)”,也就是一个指针。

2. *p[n] 定义的是一个指针数组

因为 “[]” 的优先级比 “*” 的要高,所以我们先看 “p[n]”,后看前面的 “*”。所以 *p[n] 强调的是一个数组。

根据优先级强调部分的不同,我们可以很直观的区分这是一个数组指针,还是一个指针数组。

示例 1:定义指针数组

#include <stdio.h>

int main(void)
{
    int a = 10;
    int b = 20;
    int c = 30;

    // 定义 3 个指针
    int* p1 = &a;
    int* p2 = &b;
    int* p3 = &c;

    // 定义指针数组
    int* pArr[3];
    pArr[0] = p1;
    pArr[1] = p2;
    pArr[2] = p3;
    for (int i = 0; i < 3; i++)
    {
        printf("%p\n", pArr[i]);
    }

    return 0;
}

运行结果如下。

61FDFC
61FDF8
61FDF4

分析:

在这个例子中,我们定义了一个名为 pArr 的指针数组。它有 3 个元素,每一个元素都是一个指针(即地址)。然后使用一个 for 循环 把 pArr 中每一个元素遍历出来。

如果想要把每一个指针对应的值遍历出来,我们可以使用下面代码来实现。首先 pArr[i] 本质上是一个指针,然后指针前面加上 “*” 就表示拿到该地址对应的变量。对于这一点,小伙伴们应该没有忘记吧?

for(int i = 0; i < 3; i++)
{
    printf("%p\n", *pArr[i]);
}

C 指针数组的应用

那么 C 语言的指针数组到底有什么用呢?总不能搞了一个数组指针出来,又无缘无故搞一个指针数组出来吧?实际上,指针数组最重要的作用就是:定义一个字符串数组

提示: 字符串数组,指的是有一个数组,它的每个元素都是一个字符串。

示例 2:“常规方式” 定义字符串数组

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

int main(void)
{
    char country[5][20] = {"China", "Japan", "Australia", "Netherlands", "Russia"};

    for (int i = 0; i < 5; i++)
    {
        puts(country[i]);
    }
    return 0;
}

运行结果如下。

China
Japan
Australia
Netherlands
Russia

分析:

字符串数组本质上是一个二维数组,上面采用的是传统方式来定义一个字符串数组。country[5][20] 表示该数组有 5 个字符串,每一个字符串的存储长度都是 20。也就是说,就算字符串实际长度小于 20,也会分配 20 个字节来存储。

像 "China"、"Japan" 等字符串的实际长度仅仅是 5 个字节,即使把结束符算进去也只有 6 个字节,但实际却分配了 20 个字节来存储,这样就浪费了大量的内存空间。那么有没有一种好的解决办法呢?其实是有的,那就是使用指针数组来实现。

示例 3:“指针数组” 定义字符串数组

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

int main(void)
{
    char* country[5] = {"China", "Japan", "Australia", "Netherlands", "Russia"};

    for (int i = 0; i < 5; i++)
    {
        puts(country[i]);
    }
    return 0;
}

运行结果如下。

China
Japan
Australia
Netherlands
Russia

分析:

char* country[5] 表示定义了一个名为 country 的指针数组,该数组有 5 个元素,然后每个元素都是一个字符指针(char* 类型)。对于每一个指针来说,它指向的其实就是每一个字符串首元素的地址,因为字符串本质上也是一个数组嘛。

使用指针数组有一个非常大的优点,那就是 “按需分配”。比如 "China" 只有 5 个字符,那么就只会分配 5 个字节。而 "Australia" 有 9 个字节,那么就只会分配 9 个字节。这种方式比常规的方式更能节省内存空间。

实际上,我们还可以把下标省略掉,因为 C 语言会自动识别有多少个元素(字符串)。下面 2 种方式是等价的。

// 方式 1
char* country[5] = {"China", "Japan", "Australia", "Netherlands", "Russia"};

// 方式 2
char* country[] = {"China", "Japan", "Australia", "Netherlands", "Russia"};

注意: 使用指针数组定义字符串时(如 char* s = "Hello"),字符串通常存储在只读内存区,因此不能修改字符内容,否则会导致程序崩溃。而使用二维数组定义的字符串,则是可以修改的。

示例 4:获取字符串数组的长度

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

int main(void)
{
    char* country[] = {"China", "Japan", "Australia", "Netherlands", "Russia"};

    int length = sizeof(country) / sizeof(country[0]);
    printf("%d", length);

    return 0;
}

运行结果如下。

5

分析:

指针数组本质上是一个一维数组,所以我们也可以 sizeof() 函数来计算,从而获取它的实际长度(即有多少个字符串)。

示例 5:C 指针数组的更多应用

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

int main(void)
{
    char* months[] = 
    {
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
    };

    int length = sizeof(months) / sizeof(months[0]);
    for (int i = 0; i < length; i++)
    {
        puts(months[i]);
    }

    return 0;
}

运行结果如下。

January
February 
March
April
May
June
July
August
September
October
November
December

分析:

指针数组这种方式大多数情况下都是用于定义一个字符串数组的,小伙伴们只需要关注这一点就可以了。对于字符串数组来说,很多书只是浅尝辄止地简单介绍一遍就算了。实际上它是一种非常有用的数据结构,在真实的项目开发中会大量用到。所以小伙伴们还是要认真掌握一下。

上一篇: C 数组指针

下一篇: C 函数指针

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号