指针篇(4) -- 指针数组和数组指针

续 > C 语言返璞归真: 指针篇(3)

前言

前面一篇说到了复杂指针的第一种指针——二重指针, 本篇博文接着来聊聊更加复杂的指针。

指针数组和数组指针

首先需要明确的是指针数组是数组,而数组指针是指针。 之所以把它们放在一起, 是因为它们在形式上非常相似, 但实际上它们的用法和意义完全不同。 譬如定义一个指针数组: int *a[10], 表示一个有 10 个 int* 类型指针的数组; 定义一个数组指针: int (*pa)[10], 可以表示一个指向 int 类型、长度为 10 的一维数组的指针。 

注意: 上面提到的都是我们比较常见的指针数组和数组指针, 实际上还有许多指针数组和数组指针, 譬如: int **a[10]、 int (*pa)[10][10] 等等, 但是这些出现的频率实在太低, 所以一般说到指针数组和数组指针时都特指上述两类比较常见的。

指针数组和数组指针在内存中的表示

1.指针数组

从图上看到内存中开辟了两块空间, 一块用于存储二重指针 pa, 一块空间用于存储指针数组。 并且二重指针 pa 指向了指针数组名 a。指针数组本质上和一维数组没有区别, 唯一区别的是它们存储的东西不同, 指针数组存储的元素是指针。 图中, 我特意在指针数组的下面标明了数组元素的假定地址, 这里使用的模型是 LP64, 所以该指针数组里面的元素都是 8 byte 大小的指针。 注意指针的大小与指针类型无关。 

在指针篇(3)一文中, 我们提到了二重指针可以指向一重指针。 那么为什么二重指针可以指向指针数组名呢? 我们知道一维数组的数组名在作右值时可以隐式转换为指针类型, 等效于数组首元素首地址。 同理指针数组名在作右值时也可以隐式转换为指针类型, 等效于数组首元素首地址。所以「pa = a 」等效于「pa = &a[0] 」。 指针数组的元素都是指针, 所以「pa = a 」是成立的。

2. 数组指针

顾名思义, 数组指针是指针, 而且是专门用于指向数组的指针。 它可以指向一维数组、 二维数组甚至是三维数组。 但指向一维数组和三维数组时意义不大。 下面说明一下数组指针指向二维数组的内存分布情况:
仔细观察你会发现, 实际上二维数组和一维数组在存储方式上并没有不同。 上面的二维数组 a[2][10] 与 a[20] 在内存中的存储方式完全相同,是线性存储, 而不是二维方式存储。所以其实一维数组完全可以替代二维乃至于更高维的数组。 不过对于编程者来说, 二维数组在很多时候更加容易处理, 也可以增加程序的可读性。 
再观察上面的图, 会发现二维数组的 20 个元素被分为两组, 一组是 a[0], 另外一组是 a[1]。 因为二维数组的这种特性类似于数学中的行列式(矩阵), 所以经常二维数组的第一维称为数组的行, 第二维称为数组的列。 这种称法更有助于理解二维数组。 在定义时需要初始化一个二维数组时, 也建议写成行列式(矩阵)的形式, 比如:
这种分行初始化的写法更加不容易出错, 也利于编程者阅读代码。 
既然二维数组和一维数组在存储方式没有什么区别, 那么一重指针也完全可以指向二维数组, 只要指向二维数组的首元素即可。 代码如下:
int a[2][10] = 
{
	{ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,},
	{10, 11, 12, 13, 14, 15, 16, 17, 18, 19,},
};

int *pa = NULL;
pa = &a[0][0];
换作数组指针呢? 数组指针指向二维数组的第一维, 同时它限定了指向的数组的第二维大小。 譬如上述的 int (*pa)[10]只能指向第二维大小是 10 的二维数组。 代码如下:
int a[2][10] = 
{
	{ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9,},
	{10, 11, 12, 13, 14, 15, 16, 17, 18, 19,},
};

int (*pa)[10] = NULL;
pa = a;

区分指针数组和数组指针的技巧

很多人弄不清楚指针数组和数组指针这两者到底哪个是指针, 哪个是数组。 所以我们首先说说如何在字面上区分这两者。 其实在中文表达当中, 通常会把名词(主语)放在后面, 修饰词(定语)放在前面。 所以指针数组是数组在后, 指针在前, 重点表示的是数组。 反之, 数组指针它重点要表达的是一个指针。

那么又如何区分具体的 C 语言代码呢?这里涉及到一个运算符优先级的问题。 C 语言中有一个运算符优先级表, 大多数情况下不需要用到这个优先级表。 不过作为 C 语言的一个知识点, 也有必要会看懂优先级表和记住几个比较重要运算符的优先级。 关于这一点会在下一篇博文中讲解。 这里我们只涉及到「*」(解引用运算符)、「()」、「[]」这三个运算符。

在优先级表中, 上述的几个运算符的优先级顺序为:「[]」>  「()」 > 「*」。 所以当「*」和「[]」同时作用于 a 的时候, a 先与「[]」结合构成一个数组, 然后再与「*」结合, 结果构成一个指针数组, 其实指针数组也可以写成 int *(a[10]),不过这里的括号没有起到任何作用, 因为「[]」的优先级高于「*」; 同理数组指针中「()」就起到了一个隔离的作用, 这时候「[]」就不直接作用于 a, 所以 a 要先与「*」结合构成指针,然后再与「[]」结合。

2017 年 7 月 13 日

Kilento

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值