指针数组和数组指针的区别
一、问题引入
在使用C语言编程时,一旦涉及到指针数组和数组指针就会陷入困惑。
-
如何区分指针数组和数组指针的定义形式
-
在某个场景下,应该使用指针数组,还是使用数组指针
-
指针数组和数组指针如何作为参数传递
二、解决过程
2-1 指针数组
指针数组是一个数组,每个数组元素存放一个指针变量。
例如: char *p[2]; 中的 p 就是一个指针数组,p拥有两个元素,可以存放两个变量的地址。
💡 注意:[ ] 运算符的优先级高于 * 运算符
代码举例-01:
#include <stdio.h>
int main(void)
{
char str_01[] = {"《C程序设计》"}, str_02[] = {"《算法与数据结构》"};
char *p[2]; // p 是一个拥有两个元素的指针数组
p[0] = str_01;
p[1] = str_02;
for(int i = 0; i < 2; i++)
{
printf("第%d本书:%s\n", i+1, *(p+i));
}
return 0;
}
重点语句理解:printf("第%d本书:%s\n", i+1, *(p+i)); ,如何介绍 *(p+i) 的解引用?
(p+i) 表示数组的第i元素,*(p+i) 表示解引用数组的第i元素的地址。但是让我困惑的一点是 (p+0) 不就是 str_01数组的地址吗,为什么还要解引用?
首先应该理解什么是指针,才能理解指针数组。指针是一种特殊的变量,它里面存放的是地址,不是我们普通概念的值,它也有自己的地址值,它的取值是别的变量的地址值。
说回 char *p[2] char类型的指针数组,包含两个指针且指针的地址是连续的(假设 0X00000004 、0X00000008)。那就说明第一个指针(0X00000004)存放 str_01(0X0000FF00) 的地址,第二个指针(0X00000008)存放 str_01(0X0000EE00) 的地址。
(p+0) 表示第一个指针,我们要的不是第一个指针,而是第一个指针的值(即str_01的地址),因此还要对第一个指针进行取值(即 *(p+0))
借助一下代码,理解指针的意义
#include <stdio.h>
int main(int argc, const char *argv[])
{
char str_01[] = { "《C程序设计》" }, str_02[] = { "《算法与数据结构》" };
char *p[2]; // p 是一个拥有两个元素的指针数组
printf("str_01的地址:%p\n", str_01);
printf("str_02的地址:%p\n", str_02);
printf("\n");
p[0] = str_01;
p[1] = str_02;
for (int i = 0; i < 2; i++)
{
printf("(p+i)的地址:%p\n", i, (p + i));
printf("*(p+i)的地址:%p\n", i, *(p + i));
printf("第%d本书:%s\n", i + 1, *(p + i));
printf("\n");
}
return 0;
}

代码举例-02:
#include <stdio.h>
int main(void)
{
char *p[2] =
{
"《C程序设计》",
"《算法与数据结构》"
};
for(int i = 0; i < 2; i++)
{
printf("第%d本书:%s\n",i+1, *(p+i)); //p+i表示数组第i个元素的地址,*(p+i)表示第i个元素的值
}
return 0;
}
代码举例-01 和 代码举例-02 结果是等价
程序运行结果:

2-2 数组指针
数组指针是一个指针,指向拥有n(n>1)个元素的数组,所指向的数组的元素一般是固定的。
例如:int (*p)[5]; 中的 p 就是一个数组指针,指针指向拥有5个元素的数组
代码举例:
#include <stdio.h>
int main()
{
int temp[5] = {1, 2, 3, 4, 5};
int(*p)[5] = &temp;
for (int i = 0; i < 5; i++)
{
printf("%d ", *(*p + i));
}
printf("\n");
return 0;
}
💡 数组指针p指向拥有5个元素的数组的首地址,不能将超过5个元素的数组的地址赋值给数组指针p
运行结果:

三、指针数组作为参数
若想将指针数组作为参数传入函数,应该怎样传递呢?
#include <stdio.h>
void print_book(char **ptr, int num) // 形参是一个二级指针 即指向指针的指针
{
for (int i = 0; i < num; i++)
{
printf("(ptr+%d)的地址:%p\n", i, ptr+i);
printf("*(ptr+%d)的地址:%p\n", i, *(ptr + i));
printf("第%d本书:%s\n", i + 1, *(ptr + i));
}
}
int main(int argc, const char *argv[])
{
char str_01[] = { "《C程序设计》" }, str_02[] = { "《算法与数据结构》" };
char *p[2]; // p 是一个拥有两个元素的指针数组
p[0] = str_01;
p[1] = str_02;
printf("p的地址:%p\n\n", p);
print_book(p, 2); // 实参是指针数组的首地址 或者说是指针数组第一个元素的地址
//print_book(&p[0], 2); // p 和 &p[0] 是等价的
return 0;
}

四、数组指针作为参数
若想将数组指针作为参数传入函数,该如何定义形参呢?
#include <stdio.h>
int print_number(int (*p)[5])
{
int i;
for (i = 0; i < 5; i++)
{
printf("%d ", *(*p + i));
}
printf("\n\n");
return 0;
}
int print_book(char (*p)[50])
{
int i;
for(i = 0; i < 2; i++)
{
printf("%p %p %s\n", p+i, *(p+i), *(p+i));
}
printf("\n");
return 0;
}
int main(void)
{
int temp[5] = {1, 2, 3, 4, 5};
int(*p)[5] = &temp; // 将数组temp的地址赋值给数组指针p
print_number(p);
char book[2][50] = {"C程序设计", "C++程序设计"};
printf("book[0]'s address:%p\nbook[1]'s address:%p\n", book[0], book[1]);
char (*p_book)[50] = book; // 将数组第一个元素的首地址赋值给数组指针p_book
printf("(*p_book)[50]'s address:%p\n", p_book);
print_book(p_book);
return 0;
}

❓ 注意:book[0]、(*p_book)[50]、(p+0)和*(p+0)的地址相同
如何理解:printf("%p %p %s\n", p+i, *(p+i), *(p+i)); 中 p+i, *(p+i)地址相同的问题?
分析过程:首先,0x7ffee0b6a3e0 和 0x7ffee0b6a412 的差值:50,这验证了 char (*p)[50] 数组指针的跨度单位是 50。
其次,p+i 指向一维数组的地址,*(p+i) 指向一维数组第一个元素的地址。对于一维数组而言,数组的首地址和第一个元素的地址值是相同,这就说明了 p+i, *(p+i)地址相同的问题
五、反思总结
重点理解
- 指针数组和数组指针的定义和形式
- 指针数组其属性是一个地址连续的数组。数组元素是指针,指针是指向地址的。
- 数组指针,数组是对指针的约束条件。未明确说明数组长度,不能将其地址赋值给数组指针
2023-08-23 新增内容:
在 四、数组指针作为参数 中,char book[2][50] = {"C程序设计", "C++程序设计"}; 的 sizeof(book) 是多少?
而 int print_book(char (*p)[50]) 的 sizeof(p) 是多少?
答案:sizeof(book)=100、sizeof(p)=8 (在windows10-x64实现)

浙公网安备 33010602011771号