6. 指向数组的指针

这一小节我们来学习 C 语言中指向数组的指针。

关于数组,我们在使用其内部数据时通常使用索引来访问内部的数据,其实 C 语言中万能的指针也可以担此重任。有时使用指针比索引更高效。

提起指针,我们知道指针使用两部分组成的:

  1. 指针的值,表示指针指向的内存地址。
  2. 指针的类型,表示指针指向的地址里的数据是什么样的结构。

那我们用什么类型的指针才能指向数组呢?我们先来讲解一下数组的类型。

在数组的声明中,如果去掉变量名部分,则得到的类型就是数组的类型,如数组定义:double data[10]; 数组 data 的类型就是去掉标识符后的类型 double[10],这个我们可以通过 printf("%ld\n", sizeof(double[10])); 来验证并得到结果。那我们用什么类型的指针来指向这个数组 data 呢?即 &data 返回的类型是什么呢?答案是 double[10] 类型的指针,类型写作 double(*)[10],如果声明这种类型的指针pdata 应当把指针变量名称写在星号的后面,写成: double(*pdata)[10];,你是不是很崩溃呢?但这确实是一维数组类型的指针的定义方式,我们写代码来验证一下。

示例1

指向数组的指针的声明和使用。

// filename: pointer_to_array.c
#include <stdio.h>

int main(int argc, char *argv[]) {
    double data[10] = {3.1415, 2.7182};
    double(*pdata)[10];  // pdata 的类型为:double(*)[10]
    pdata = &data;

    printf("&data: %p\n", &data);
    printf("pdata: %p\n", pdata);
    printf("sizeof(data): %ld\n", sizeof(data));
    printf("sizeof(*pdata): %ld\n", sizeof(*pdata));
    return 0;
}

运行结果如下:

weimingze@mzstudio:~$ gcc -o pointer_to_array pointer_to_array.c
weimingze@mzstudio:~$ ./pointer_to_array
&data: 0x7fffb93e4330
pdata: 0x7fffb93e4330
sizeof(data): 80
sizeof(*pdata): 80

可见 数组 data 的地址 &data 和指针 pdata 的值相同,且数组 data 占用的字节数和 pdata 解引用后占用的字节数完全相同。即 *pdata 就是数组 data 的引用。

上述 指向数组的指针 虽然可用,但实际使用起来比较麻烦。因此我们实际在写程序的时候常用指向数组第一个数据元素的指针来代替指向数组的指针

下面我们来讲解数组的返回值和返回值的类型。

  1. 数组名称取地址的返回值是数组的起始地址,类型是数组类型的指针,即声明 int score[5];&score 的返回值类型是 int(*)[5];
  2. 数组名称表达式的返回值是数组的起始地址,类型是数组中数据元素的类型的指针,即声明 int score[5];score 的返回值类型是 int*;
  3. 数组中索引为0的元素取地址的表达式的返回值是数组的起始地址,类型是数组中数据元素的类型的指针,即声明 int score[5];&score[0] 的返回值类型也是 int*;

综合以上第二、第三条我们总结如下:

因此我们也常使用 上述第二、第三种指针对数组进行操作。

一维数组的名字的类型

示例2

指向数组的指针的声明和使用。

// filename: pointer_to_array2.c
#include <stdio.h>

int main(int argc, char *argv[]) {
    int score[5] = {100, 90, 80};
    int(*parr)[5];  // 指向数组的指针
    int * ps1;
    int * ps2;

    parr = &score;  // 取数组的地址
    ps1 = score;  // 数组名表达式的返回值
    ps2 = &score[0];  // 取第一个数据元素的地址

    // 下面我们使用多种方法对数组的第一个数据元素进行取值
    printf("score[0]: %d\n", score[0]);
    printf("(*parr)[0]: %d\n", (*parr)[0]);
    printf("*ps1: %d\n", *ps1);
    printf("*ps2: %d\n", *ps2);
    return 0;
}

运行结果如下:

weimingze@mzstudio:~$ gcc -o pointer_to_array2 pointer_to_array2.c
weimingze@mzstudio:~$ ./pointer_to_array2
score[0]: 100
(*parr)[0]: 100
*ps1: 100
*ps2: 100

实验: