數組地址,數組首地址與數組首元素地址的區別


理清概念

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)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM