一、提出问题
#include <stdio.h>
int main()
{
char array[16] = {'A', 'B'};
char (*parray)[16] = &array;
printf("========================\n");
printf(" array: \t%#lx\n", array);
printf("& array: \t%#lx\n", &array);
printf("&* array: \t%#lx\n", &*array);
printf("*& array: \t%#lx\n", *&array);
printf(" parray: \t%#lx\n", parray);
printf("* parray: \t%#lx\n", *parray);
printf("*& parray: \t%#lx\n", *&parray);
printf("&* parray: \t%#lx\n", &*parray);
printf("========================\n");
return 0;
}
上述程序中 array &array &*array *&array parray *parray *&parray &*parray 输出都相同 。在测试计算机上均为 0x61ff10 (如下图),这是什么原因呢?下面逐步解析,结论见文末。
二、解决思路
第一组:array 与 &array 的值相同
#include <stdio.h>
int main()
{
char array[16] = {'A', 'B'};
char (*parray)[16] = &array;
printf(" array: \t%#lx\n", array); // 输出 0x61ff10
printf("& array: \t%#lx\n", &array); // 输出 0x61ff10
printf("数组array的第二元素的地址: \t%#lx\n", array+1); // 输出 0x61ff11
printf("数组array的下一数组的地址: \t%#lx\n", &array+1); // 输出 0x61ff20
printf("数组array的第二元素的地址: \t%c\n", *(array+1)); // 输出字符 B
printf("数组array的下一数组的地址: \t%c\n", *(&array+1));// 输出为空
printf(" array: \t%d\n", sizeof(array)); // 输出 16,整个数组array的大小也为16,即array代表整个数组
printf(" array: \t%d\n", sizeof(&array)); // 输出 4,则 &array 应为指针变量
printf(" array: \t%d\n", sizeof(array+1)); // 输出 4,因为 array+1 时,array 隐式转换为指针变量了
return 0;
}
array
为数组名,长度为 16,可理解为数组对象。当 array+1 时,其会隐式转换为数组的首元素的地址;&array
为指针变量,长度为 4,表示 整个 数组 array 的地址,但是值等于数组的首元素的地址。
虽然两者值相同,但是含有不同。在上述代码中 array + 1 表示数组的第二个元素的地址,而 &array + 1 表示 整个 数组 array 的 “下一个数组”,即指向数组 array 尾部的下一地址。
注:本机测试时编译器为32位,故指针变量长度位 4 1
第二组:&*array 与 *&array
#include <stdio.h>
int main()
{
char array[16] = {'A', 'B'};
char (*parray)[16] = &array;
printf("&* array: \t%#lx\n", &*array); // 输出 0x61ff10
printf("*& array: \t%#lx\n", *&array); // 输出 0x61ff10
printf("&* array: \t%d\n", sizeof(&*array)); // 输出 4,数组首元素的地址
printf("*& array: \t%d\n", sizeof(*&array)); // 输出 16,*&array 等价为 array
printf("*&*array: \t%c\n", *&*array); // 输出 A
printf("**&array: \t%c\n", **&array); // 输出 A
return 0;
}
&*array
为指针变量,长度为 4。array 为数组名,*array 为数组首元素,故 &*array 为数组首元素的地址,并且其长度为 4,类型为指针变量。*&array
为数组,长度为 16。&array 为指针变量,指向整个数组。对指向整个数组的指针 &array 进行一次解引用,则得到整个数组的对象。
另一方面,* 与 & 是互逆运算符,在表面上可以理解为相互抵消。但是需要注意 sizeof(&*array) 的值为 4,即 &*array 为指针变量; sizeof(*&array) 为 16,即 *&array 等价于 array,表示数组对象。
第三组:parray 与 *parray
#include <stdio.h>
int main()
{
char array[16] = {'A', 'B'};
char (*parray)[16] = &array;
printf(" parray: \t%#lx\n", parray); // 输出 0x61ff10
printf("* parray: \t%#lx\n", *parray); // 输出 0x61ff10
printf(" parray: \t%#lx\n", parray+1); // 输出 0x61ff20,则 parray 指向整个数组
printf("* parray: \t%#lx\n", *parray+1); // 输出 0x61ff11,则*parray 指向数组首个元素
printf("**parray: \t%c\n", **parray); // 输出 A
printf(" parray: \t%d\n", sizeof(parray)); // 输出 4
printf(" parray: \t%d\n", sizeof(*parray)); // 输出 16
return 0;
}
parray
为 char int (*)[16] 类型的指针变量,长度为 4。从声明中可以看出,该指针等价于 &array,即指向的是数组对象 array。*parray
表示对指针变量 parray 进行了一次解引用,即得到了数组 array 的对象,且其长度为16,与数组名 array 作用等价,比如 (*parray)[0] 与 array[0] 的值均为 A。而 *array,数组名发生隐式转换后,表示为数组首元素,故若再解引用一次,即 **parray 则表示数组 array 的首元素:A。
第四组:*&parray 与 &*parray
#include <stdio.h>
int main()
{
char array[16] = {'A', 'B'};
char (*parray)[16] = &array;
printf("*&parray: \t%#lx\n", parray); // 输出 0x61ff10
printf("*&parray: \t%#lx\n", *&parray); // 输出 0x61ff10
printf("&*parray: \t%#lx\n", &*parray); // 输出 0x61ff10
printf("*&parray: \t%d\n", sizeof(*&parray)); // 输出 4
printf("&*parray: \t%d\n", sizeof(&*parray)); // 输出 4
printf("*&parray的下一单元: %#lx\n", *&parray+1); // 输出 0x61ff20
printf("&*parray的下一单元: %#lx\n", &*parray+1); // 输出 0x61ff20
return 0;
}
此组没什么好讲的,理解成 *& 抵消掉就好了。注意代码中 parray 是 0x61ff10,而 *&parray+1 和 &*parray+1 都是 0x61ff20,再次印证了 parray 是指向整个数组的,作用与 &array 等价。
三、本文结论
char array[16] = {'A', 'B'};
char (*parray)[16] = &array;
在上述代码中,array
为数组名,姑且理解为该数组的对象 ,当对 array 进行运算时,会发生“隐式转换”。通过 sizeof(array) 知其长度为16,即数组的长度。因此,array 本质不应是指针,但是在进行运算,比如 array + 1 时,array 会隐式地转换成指向数组首元素的指针变量,而 array + 1 则指向数组第二个元素。&array
则表示指向整个数组的指针变量,其类型为 int (*)[16] ,而 &array + 1 则会跨过整个数组 array,指向下一个 “数组”(如果存在的话)。
从变量声明中可知 parray
与 &array 作用等价,则 *parray
与 *&array(即 array)等价,为数组名,表示数组对象。在进行运算时,*parray 会像 array 一样,发生隐式转换。
以下节选自书籍《C++ Primer Plus》第 213 页 2:
补充扩展
#include <stdio.h>
int main()
{
char array[16] = {'A', 'B'};
char (*parray)[16] = &array;
printf(" array: \t%c\n", (&array[0])[0]); // 输出 A
printf(" array: \t%c\n", (&array[0])[1]); // 输出 B
printf(" array: \t%c\n", (&array[1])[0]); // 输出 B
printf(" array: \t%c\n", (&array[1])[1]); // 输出 空
return 0;
}
#include <stdio.h>
int main()
{
char array[16] = {'A', 'B'};
char (*parray)[16] = &array;
printf(" parray: \t%#lx\n", parray); // 输出 0x61ff10
printf("* parray: \t%#lx\n", *parray); // 输出 0x61ff10
printf(" parray: \t%c\n", parray[0][0]); // 输出 A
printf(" parray: \t%c\n", parray[0][1]); // 输出 B
// printf(" parray: \t%c\n", parray[1][0]); // 输出 空
// printf(" parray: \t%c\n", parray[1][1]); // 输出 乱码
printf(" parray: \t%c\n", *parray [0]); // 输出 A
printf(" parray: \t%c\n", (*parray)[1]); // 输出 B
printf(" parray: \t%c\n", **parray ); // 输出 A
printf(" parray: \t%c\n", **parray+1); // 输出 B
return 0;
}
以上问题及解决基于测试所得出,由于本人知识有限,欢迎指出文中错误。
参考链接
[1] C语言之指针变量占几个字节
[2] 何时发生隐式转换
[3] 受启:c语言中数组指针取值*(解引用)问题
[4] C++ Primer Plus 中文第六版 第213页
2 ↩︎
4 ↩︎