【C/C++】数组指针:array 地址 &array *parray 两次解引用 **parray 值相同的原因解析

一、提出问题

#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;
}
  1. array 为数组名,长度为 16,可理解为数组对象。当 array+1 时,其会隐式转换为数组的首元素的地址;
  2. &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;
}
  1. &*array为指针变量,长度为 4。array 为数组名,*array 为数组首元素,故 &*array 为数组首元素的地址,并且其长度为 4,类型为指针变量。
  2. *&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;
}
  1. parray 为 char int (*)[16] 类型的指针变量,长度为 4。从声明中可以看出,该指针等价于 &array,即指向的是数组对象 array。
  2. *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页

扫描二维码关注公众号,回复: 15624676 查看本文章

  1. 2 ↩︎

  2. 4 ↩︎

猜你喜欢

转载自blog.csdn.net/Alpherkin/article/details/131482910