数组地址,数组首地址与数组首元素地址的区别
理清概念
C++中的int*、int**、int&、int*&、int *a[]、int(*a)[]:
int a; //a是一个int型【变量】 int *a; //a是一个指向int型变量的【指针】 int **a; //a是一个指向一个指向int型变量指针的指针,也就是【二级指针】 int &a; //a是一个【普通变量型引用】,若int &a = i;则a是变量i的一个别名,&a=&i,即a和i的地址一样 int *&a; //a是一个【指针变量型引用】,若int *&a = i;则a是指针i的一个引用 int a[2]; //a是一个含有两个int型变量的【数组】 int *a[2]; //a是一个【指针数组】,数组a里存放的是两个int型指针 int (*a)[2]; //a是一个【数组指针】,a指向一个含有两个int型变量的数组
尤其是最后两个。
代码测试
测试平台:win10 Eclipse IDE, Version: 2020-03 (4.15.0)
静态数组
静态数组在程序编译阶段即确定数组的大小并完成固定大小的内存分配,所以数组长度必须是常量,而不能是不确定值的变量。
#include<stdio.h> int main () { int arr[5]={1,2,3,4,5}; int *p1; int (*p2)[5]; p1 = arr; p2 = &arr; printf("sizeof(arr) = %d\n", sizeof(arr)); //sizeof(arr) = 20 printf("&arr = %d\n", &arr); //&arr = 6422276 printf("arr = %d\n", arr); //arr = 6422276 printf("&arr[0] = %d\n", &arr[0]); //&arr[0] = 6422276 printf("sizeof(p1) = %d\n", sizeof(p1)); //sizeof(p1) = 4 printf("p1 = %d\n", p1); //p1 = 6422276 printf("*p1 = %d\n",*p1); //*p1 = 1 printf("sizeof(p2) = %d\n", sizeof(p2)); //sizeof(p2) = 4 printf("p2 = %d\n", p2); //p2 = 6422276 printf("*p2 = %d\n", *p2); //*p2 = 6422276 printf("**p2 = %d\n", **p2); //**p2 = 1 printf("&arr + 1 = %d\n", &arr + 1); //&arr + 1 = 6422296 printf("arr + 1 = %d\n", arr + 1); //arr + 1 = 6422280 printf("&arr[0] + 1= %d\n", &arr[0] + 1); //&arr[0] + 1= 6422280 printf("p1+ 1 = %d\n", p1 + 1); //p1+ 1 = 6422280 printf("*(p1 + 1) = %d\n", *(p1 + 1)); //*(p1 + 1) = 2 printf("p2 + 1 = %d\n", p2 + 1); //p2 + 1 = 6422296 printf("*(p2 + 1) = %d\n", *(p2 + 1)); //*(p2 + 1) = 6422296 printf("*p2 + 1 = %d\n", *p2 + 1); //*p2 + 1 = 6422280 printf("**(p2 + 1) = %d\n", **(p2 + 1)); //**(p2 + 1) = 6422276 printf("*(*p2 + 1) = %d\n", *(*p2 + 1)); //*(*p2 + 1) = 2 return 0; }
分析
【0】数组arr长度为5,类型是int;在测试系统中,1个int为4个字节,1个字节8位对应1个内存地址编号,起始地址为6422276(包含),结束地址为6422276 + 4 * 5 = 6422296(不包含)。
【1】从数值上看:数组地址&arr = 数组首地址arr = 数组首元素地址&arr[0] = 6422276
【2】p1指针变量存储的是数组首地址 p1 = arr; ;p2指针变量存储的是数组地址 p2 = &arr; 。所以p1指向数组首元素:*p1 = 1 ,p2指向数组首地址:*p2 = 6422276 。
【3】数组地址&arr加一,直接移动到下一个数组地址:&arr + 1 = 6422296 。
【4】数组首地址arr加一,直接移动到数组下一个元素地址: arr + 1 = 6422280 。数组首元素地址加一结果同。
【5】p1指针加一,同数组首地址(首元素地址),直接移动到数组下一个元素地址: p1+ 1 = 6422280 。
【6】p2指针加一,同数组地址,直接移动到下一个数组地址: p2 + 1 = 6422296 。
【7】静态数组中,数组名在进行地址操作时,&arr和arr值虽相同,但意义不同:&arr移动的单位是整个数组,而arr移动的单位是数组元素!!!
动态内存分配
所谓动态分配,即程序运行前并不确定数组的大小,也不实际分配内存。在程序运行后,根据实际情况确定大小并动态分配内存。所以数组定义改成根据输入值n确定大小,并进行内存分配:
int n; scanf("%d", &n); // 5 int *arr = new int[n]; arr[0] = 1 ,arr[1] = 2, arr[2] = 3, arr[4], arr[5];
存储区域内存中的栈区(编译完成即分配好且大小固定)变成了堆区(运行时动态分配,大小不固定)。
所以后续代码不随之改动的话,直接后果是报错:
int (*p2)[5]; p2 = &arr; //error: cannot convert 'int**' to 'int (*)[5]' in assignment
显然,此时的p2与&arr并不匹配。所以此时删掉p1和p2,直接测试arr指针即可:
#include<stdio.h> int main () { int n; scanf("%d", &n); int *arr = new int[n]; arr[0] = 1 ,arr[1] = 2, arr[2] = 3, arr[4], arr[5]; printf("sizeof(&arr) = %d\n", sizeof(&arr)); //sizeof(&arr) = 4 printf("sizeof(arr) = %d\n", sizeof(arr)); //sizeof(arr) = 4 printf("&arr = %d\n", &arr); //&arr = 6422296 printf("arr = %d\n", arr); //arr = 15605496 printf("&arr[0] = %d\n", &arr[0]); //&arr[0] = 15605496 printf("*&arr = %d\n", *&arr); //*&arr = 15605496 printf("*arr = %d\n", *arr); //*arr = 1 printf("*&arr[0] = %d\n", *&arr[0]); //*&arr[0] = 1 printf("arr[0] = %d\n", arr[0]); //arr[0] = 1 printf("&arr + 1 = %d\n", &arr + 1); //&arr + 1 = 6422300 printf("arr + 1 = %d\n", arr + 1); //arr + 1 = 15605500 printf("&arr[0] + 1= %d\n", &arr[0] + 1); //&arr[0] + 1= 15605500 printf("*(&arr + 1) = %d\n", *(&arr + 1)); //*(&arr + 1) = 5 printf("*(arr + 1) = %d\n", *(arr + 1)); //*(arr + 1) = 2 printf("*(&arr[0] + 1) = %d\n", *(&arr[0] + 1)); //*(&arr[0] + 1) = 2 printf("*&arr + 1 = %d\n", *&arr + 1); //*&arr + 1 = 15605500 printf("*arr + 1 = %d\n", *arr + 1); //*arr + 1 = 2 printf("*&arr[0] + 1 = %d\n", *&arr[0] + 1); //*&arr[0] + 1 = 2 return 0; }
分析
【1】与静态数组的结果相比,此时最大的区别出现了:数组地址&arr 不等于 数组首地址arr。可以这么理解,arr本身是一个指针,指向堆区中该数组分配的内存首地址,即arr的值是该数组首地址。而存储这个地址的指针变量arr,它本身所在的内存地址即&arr。通俗地讲,我们根据&arr找到arr这个指针,再根据arr的值找到arr这个数组的存储位置(内存地址)。
【2】数组首地址与数组首元素的地址仍然一致:arr = &arr[0]。而*&arr = arr,*arr = arr[0]。
【3】arr加一,移动到数组下一个元素,arr[0]结果同。
【4】&arr加一,移动到下一个变量地址(上例为6422296->6422300)。