“数组指针”和“指针数组”表达的是两种不同的概念,可以理解为:
1)数组的指针:是一个指针,什么样的指针呢?指向数组的指针,占有内存中一个指针的存储空间。
2)指针的数组:是一个数组,什么样的数组呢?存着指针的数组,占有多个指针的存储空间。
根据优先级顺序:()>[]>*,可以总结出:
1)int (*p)[n]:()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。所以数组指针也称指向一维数组的指针,即称为行指针。
2)int *p[n]:[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
假设定义int (*p2)[5],()的优先级比[]高,*号和 p2 构成一个指针的定义,指针变量名为 p2,而 int 修饰的是数组的内容,即数组的每个元素。也就是说,p2 是一个指针,它指向一个包含 5 个 int 类型数据的数组,如图 1 所示。很显然,它是一个数组指针,数组在这里并没有名字,是个匿名数组。
图1 数组指针内存分配
对于语句int *p1[5],因为[]的优先级要比*要高,所以 p1 先与[]结合,构成一个数组的定义,数组名为 p1,而int*修饰的是数组的内容,即数组的每个元素。也就是说,该数组包含 5 个指向 int 类型数据的指针,如图 2 所示,因此,它是一个指针数组。
图2 指针数组内存分配
指向一维数组例子:
#include <iostream>
int main()
{
//一维数组
int a[5] = { 1, 2, 3, 4, 5 };
//步长为5的数组指针,即数组里有5个元素
int(*p)[5];
//把数组a的地址赋给p,则p为数组a的地址,则*p表示数组a本身
p = &a;
//在C中,在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址,它的类型取决于数组元素的类型。
printf("%p\n", a); //输出数组名,一般用数组的首元素地址来标识一个数组,则输出数组首元素地址
printf("%p\n", p); //根据上面,p为数组a的地址,输出数组a的地址
printf("%p\n", *p); //*p表示数组a本身,一般用数组的首元素地址来标识一个数组
printf("%p\n", &a[0]); //a[0]的地址
printf("%p\n", &a[1]); //a[1]的地址
printf("%p\n", p[0]); //数组首元素的地址
printf("%d\n", **p); //*p为数组a本身,即为数组a首元素地址,则*(*p)为值,当*p为数组首元素地址时,**p表示首元素的值1
printf("%d\n", *p[0]); //根据优先级,p[0] 表示首元素地址,则*p[0]表示首元素本身,即首元素的值1
printf("%d\n", *p[1]); //随机数,不表示a[1]
std::cout << sizeof(p) << std::endl; //占用一个指针的内存空间,4个字节
return 0;
}
指向二维数组例子:
#include <iostream>
int main()
{
//一维数组
int a[3][3] = { { 1, 2, 3 }, { 6, 7, 8 } , { 9, 10, 11 } };
//步长为3的数组指针,即数组里有9个元素
int(*p)[3];
//把数组a的第一行首地址赋给p,则p为数组a[0]的地址
p = &a[0];
printf("%p\n", a); //输出数组名,一般用数组的首元素地址来标识一个数组,则输出数组首元素地址
printf("%p\n", p); //根据上面,p为数组a的地址,输出数组a的地址
printf("%p\n", *p); //*p表示数组a本身,一般用数组的首元素地址来标识一个数组
printf("%p\n", &a[0][0]); //a[0][0]的地址,第一行的首地址
printf("%p\n", &a[0]); //a[0]的地址,第一行的首地址
printf("%p\n", &a[1]); //a[1]的地址,第二行的首地址,比第一行多12字节
printf("%p\n", p[0]); //数组首元素的地址,即第一行的首地址
printf("%d\n", a[0][0]); //a[0][0]的值1
printf("%d\n", a[1][0]); //a[1][0]的值6
printf("%d\n", *p[0]); //p[0]表示第一行首地址,则*p[0]表示首元素本身,即首元素的值1
printf("%d\n", *p[1]); //p[1]表示第二行首地址,则*p[1]表示a[1][0]的值6
printf("%d\n", **p); //*p为数组a本身,即为数组a首元素地址,则*(*p)为值,当*p为数组首元素地址时,**p表示首元素的值1
*p++;//*p是第一行的首地址,*p++的步长是12字节,指向下一行首地址
printf("%d\n", **p);//表示a[1][0]的值6
std::cout << sizeof(p) << std::endl; //占用一个指针的内存空间,4个字节
return 0;
}
举个例子:
#include <iostream>
int main()
{
int a = 1;
int b = 2;
int* p[2];
p[0] = &a;
p[1] = &b;
printf("%p\n", p[0]); //a的地址
printf("%p\n", &a); //a的地址
printf("%p\n", p[1]); //b的地址
printf("%p\n", &b); //b的地址
printf("%d\n", *p[0]); //p[0]表示a的地址,则*p[0]表示a的值
printf("%d\n", *p[1]); //p[1]表示b的地址,则*p[1]表示b的值
//将二维数组赋给指针数组
int* pp[3]; //一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2],所以要分别赋值
int c[3][4];
for (int i = 0; i < 3; i++)
{
pp[i] = c[i];
}
const char* str[3] = { "好学的梦想家best", "非常喜欢", "今日头条" };//一个数组存放了3个const char*指针
std::cout << str[0] << std::endl;//输出 "好学的梦想家best"
std::cout << str[1] << std::endl;//输出 "非常喜欢"
std::cout << str[2] << std::endl; //输出 "今日头条"
return 0;
}