數組首地址取地址

1、問題來由

普通指針可被改動致使地址偏移:ios

#include <iostream>
using namespace std;

int main(int argc,char *argv[])
{
    int a = 6;
    int *p = &a;

    //p存放一個地址。pp存放p的地址,上面的代碼可以讓p存放的地址偏移
    cout<<&a<<endl;
    int *pp = (int *)&p;
    cout<<p<<endl;
    (*pp) += 4;
    cout<<p<<endl;

    return 0; 
}

執行結果:c++

這裏寫圖片描寫敘述

但是數組首地址卻不行:數組

#include <iostream>
using namespace std;

int main(int argc,char *argv[])
{
    int b[5]={111,666,3,4,5};
    int *pos = (int *)&b;

    cout<<*pos<<endl;
    (*pos)++;
    cout<<*pos<<endl;

    return 0; 
}

執行結果:markdown

這裏寫圖片描寫敘述

因而…函數

這說明數組首地址取地址有問題…post

打印出來,果真:學習

cout<<b<<endl;
cout<<&b<<endl;

這裏寫圖片描寫敘述

數組首地址是指向地址的指針,但是這個指針取地址跟裏面存的同樣。。。ui

2、數組首地址和數組名取地址

剛開始學習的人應該都知道。數組名至關於指針。指向數組的首地址,而函數名至關於函數指針,指向函數的入口地址。spa

#include<stdio.h> 

int main()   
{   
    int a[10];

    printf("a:\t%p\n", a);
    printf("&a:\t%p\n", &a);
    printf("a+1:\t%p\n", a+1);
    printf("&a+1:\t%p\n", &a+1);

    return 0;
}

輸出:.net

a: 0032FCBC
&a: 0032FCBC
a+1: 0032FCC0
&a+1: 0032FCE4

a和&a指向的是同一塊地址。但他們+1後的效果不一樣。a+1是一個元素的內存大小(添加4),而&a+1添加的是整個數組的內存大小(添加40)。

即a和&a的指向和&a[0]是一樣的,但性質不一樣!

int main()   
{   
    int a[10];   
    printf("%d\n",sizeof(a));   
    return 0;   
}

這段代碼會輸出整個數組的內存大小。而不是首元素的大小,由此咱們是否聯繫到,sizeof(a)這裏的a和
&a有些一樣之處呢?! 是的,沒錯。&a取得的是整個數組的地址!既數組名取地址等價於對數組取地址。

總結一下

事實上a和 &a結果都是數組的首地址。但他們的類型是不同。
a表示&a[0],也即對數組首元素取地址。a+1表示首地址+sizeof(元素類型)。


&a儘管值爲數組首元素地址。但類型爲:類型 (*)[數組元素個數],因此&a+1大小爲:首地址+sizeof(a)。

說明:

應該在瞭解數組名便是數組的首地址的同一時候,也要知道,數組名僅僅是「至關於」指針。而並非真的是指針,數組名是僅僅是個常量(一個值爲數組首元素地址的常量),因此不能進行++或者–運算。而常量更是沒法取地址的,而之因此有&a,事實上這裏的a的意義早已經不是當初那個數組名了,它此時表明了整個數組。

3、補充

註明:例如如下補充摘錄自參考資料裏面出現的還有一段文字,文字內容很是好,但是安排很是亂,特整理例如如下。

先上第一段代碼:

#include<stdio.h>

int main()
{
    int a[5]={0x11121314,0x21222324,0x31323334,0x41424344,0x51525354};
    int *ptr1=(int *)(&a+1);
    int *ptr2=(int *)(a+1);

    printf("%x\n%x\n",ptr1[-1],*ptr2);
}

打印結果例如如下:

這裏寫圖片描寫敘述

這說明 &a+1 跨過了整個數組長度,而 a+1 僅僅是在數組首地址上遞增了一個元素空間大小,同上文所述。

再想一下,假設將第一例第五行改成

int *ptr2=(int *)((int)a+1);

打印結果會是什麼?

這裏寫圖片描寫敘述

這裏要考慮數據在計算機中的存儲模式:大端模式和小端模式。解釋一下:

大端模式(Big_endian):字數據的高字節存儲在低地址中。而字數據的低字節則存放在高地址中。


小端模式(Little_endian):字數據的高字節存儲在高地址中,而字數據的低字節則存放在低地址中。

在大端模式下。a在計算機中存儲例如如下(從低地址到高地址,一個十六進制數表明內存的一個字節,下同):
0x11 0x12 0x13 0x14 0x21 0x22 0x23 0x24 0x31 0x32 0x33 0x34 0x41 0x42 0x43 0x44 0x51 0x52 0x53 0x54

在小端模式下,a在計算機中存儲例如如下:
0x14 0x13 0x12 0x11 0x24 0x23 0x22 0x21 0x34 0x33 0x32 0x31 0x44 0x43 0x42 0x41 0x54 0x53 0x52 0x51

(int)a表示將a的首地址強轉爲整型數據(若原來是0012FF6C,轉換後仍爲0012FF6C,但是這時已經不是表示地址而是一個
十六進制整型數了)。這時+1表明整型數(int)a加1(變爲0012FF6D),再把結果強轉爲整形指針,故指向的數據地址爲0012FF6D。
即上述存儲去的第二個字節開始處。此時輸出*ptr2。即輸出a數組存儲區中第二到第五字節組成的一個整型數,若爲大端模式則輸出
12131421,若爲小端模式則輸出24111213。

#include<stdio.h>
#include <iostream>

using namespace std;

int main()
{
    int a[5]={0x11121314,0x21222324,0x31323334,0x41424344,0x51525354};
    printf("%p\n%p\n",a,&a); //數值相等

    cout<<sizeof(a)<<endl; // a是複合類型 int[5]
    cout<<sizeof(&a)<<endl; // &a是int(*)[5]

    cout<<sizeof(a+1)<<endl; // a+1 變成指向數組第二個元素的指針,其類型是 int *
    cout<<sizeof(&a+1)<<endl; // &a+1 仍是 int(*)[5]。類型仍是指針

    //a+1 因爲 a 指向int(但不是普通指針),加1移動int個大小
    //&a+1 因爲 &a 指向 int[5],加1移動 int[5] 個大小
    printf("%p\n%p\n",a+1,&a+1);
}

執行結果例如如下:

這裏寫圖片描寫敘述

你可以說a+i和&a[i]含義一樣,但是毫不能說a和&a[0]或a+0含義一樣。

補充說明下。在vc6.0上,上面sizeof(&a)纔是20,而在GCC ,以及以後的vc版本號(如vs2005及以後版本號)則是將&a全然視做一個指針,sizeof(&a)爲4 (32位機器)

4、二維數組傳參

三種寫法

(1)int (*p)[5]

(2)int a[][5]

(3)int a[5][5]


參考資料

[1] http://blog.csdn.net/syzobelix/article/details/40054095

相關文章
相關標籤/搜索