C中的二維指針問題總結:
概括的說,指針其實就是可變數組的首地址,說是可變數組,是
指其包含內容的數量的可變的,並且是可動態申請和釋放的,從而充
分節約寶貴的內存資源。我一向喜歡一維數組,除非萬不得已,我一
般是不用二維數組的,多維的則更是很少涉足了。因爲一維簡單,容
易理解,而用指針指向的多維數組就具有相當的複雜性了,也因此更
具有討論的必要。
閒話少說,這裏我就以三個二維數組的比較來展開討論:
(1)、int **Ptr;
(2)、int *Ptr[ 5 ];
(3)、int ( *Ptr )[ 5 ];
以上三例都是整數的二維數組,都可以用形如 Ptr[ 1 ][ 1 ]的
方式訪問其內容;但它們的差別卻是很大的。下面我從四個方面對它們
進行討論:
一、內容:
它們本身都是指針,它們的最終內容都是整數。注意我這裏說
的是最終內容,而不是中間內容,比如你寫 Ptr[ 0 ],對於三者來說,
其內容都是一個整數指針,即 int *;Ptr[ 1 ][ 1 ]這樣的形式才
是其最終內容。
二、意義:
(1)、int **Ptr表示指向"一羣"指向整數的指針的指針。
(2)、int *Ptr[ 5 ]表示指向 5個指向整數的指針的指針。
(3)、int ( *Ptr )[ 5 ]表示指向"一羣"指向
5 個整數數
組的指針的指針。
三、所佔空間:
(1)、int **Ptr和 (3)、int ( *Ptr )[ 5 ]一樣,在32位平
臺裏,都是4字節,即一個指針。
但 (2)、int *Ptr[ 5 ]不同,它是 5個指針,它佔5
* 4 = 20
個字節的內存空間。
四、用法:
(1)、int **Ptr
因爲是指針的指針,需要兩次內存分配才能使用其最終內容。首
先,Ptr = ( int ** )new int*[ 5 ];這樣分配好了以後,它和(2)的
意義相同了;然後要分別對 5個指針進行內存分配,例如:
Ptr[ 0 ] = new int[ 20 ];
它表示爲第 0
個指針分配 20 個整數,分配好以後,Ptr[0 ]爲指
向 20個整數的數組。這時可以使用下標用法 Ptr[ 0 ][ 0 ]到
Ptr[ 0 ][ 19 ] 了。
如果沒有第一次內存分配,該 Ptr是個"野"指針,是不能使用
的,如果沒有第二次內存分配,則 Ptr[ 0 ]等也是個"野"指針,也
是不能用的。當然,用它指向某個已經定義的地址則是允許的,那是另外
的用法(類似於"借雞生蛋"的做法),這裏不作討論(下同)。
(2)、int *Ptr[ 5 ]
這樣定義的話,編譯器已經爲它分配了 5個指針的空間,這相當
於(1)中的第一次內存分配。根據對(1)的討論可知,顯然要對其進行一次
內存分配的。否則就是"野"指針。
(3)、int ( *Ptr )[ 5 ]
這種定義我覺得很費解,不是不懂,而是覺得理解起來特別吃力,
也許是我不太習慣這樣的定義吧。怎麼描述它呢?它的意義是"一羣"
指針,每個指針都是指向一個 5個整數的數組。如果想分配 k個指針,
這樣寫: Ptr = ( int ( * )[ 5 ] ) new int[ sizeof( int ) * 5 * k ]。
這是一次性的內存分配。分配好以後,Ptr指向一片連續的地址空間,
其中 Ptr[ 0 ]指向第 0個 5
個整數數組的首地址,Ptr[ 1 ]指向第
1 個 5
個整數數組的首地址。
綜上所述,我覺得可以這樣理解它們:
int ** Ptr <==> int Ptr[ x ][ y ];
int *Ptr[ 5 ] <==> int Ptr[ 5 ][ x ];
int ( *Ptr )[ 5 ] <==> int Ptr[ x ][ 5 ];
這裏 x和 y是表示若干的意思。
二、二維數組 *****和二維指針的關係
1.代碼
1. <span style="white-space: pre;"> </span>//test2
2. cout<<"test2"<<endl;
3. int b[10][10], i, j;
4. for(i = 0; i < 10; i++)
5. {
6. for(j = 0; j < 10; j++)
7. b[i][j] = i * 100 + j *10;
8. }
9. int (*pb)[10] = b;
10. cout<<pb<<' '<<pb+1<<endl;
11. cout<<(*pb)<<' '<<(*pb+1)<<endl;
12. cout<<*pb<<' '<<*(pb+1)<<' '<<*(pb+1)+1<<endl;
13. cout<<**pb<<' '<<**pb+1<<endl;
14. cout<<(*(*pb))<<' '<<(*(*pb+1))<<' '<<(*(*pb+1)+1)<<endl;
15. cout<<**(pb)<<' '<<**(pb+1)<<' '<<**(pb+1)+1<<endl;
//test2
cout<<"test2"<<endl;
int b[10][10], i, j;
for(i = 0; i < 10; i++)
{
for(j = 0; j < 10; j++)
b[i][j] = i * 100 + j * 10;
}
int (*pb)[10] = b;
cout<<pb<<' '<<pb+1<<endl;
cout<<(*pb)<<' '<<(*pb+1)<<endl;
cout<<*pb<<' '<<*(pb+1)<<' '<<*(pb+1)+1<<endl;
cout<<**pb<<' '<<**pb+1<<endl;
cout<<(*(*pb))<<' '<<(*(*pb+1))<<' '<<(*(*pb+1)+1)<<endl;
cout<<**(pb)<<' '<<**(pb+1)<<' '<<**(pb+1)+1<<endl;
2.輸出結果
3.解釋
第一行:
pb:b[0]的地址。pb是指向b[0]的指針,b[0]是一個int[10]數組。
pb+1:b[1]的地址。pb指向的對象是一個數組,所以pb和pb+1相差0x28
第二行:
*pb:b[0][0]的地址。*pb是指向b[0][0]的指針,b[0][0]是一個int。雖然b[0]和b[0][0]的地址是一樣的,但是兩個指針指向的對象是不一樣的。
*pb+1:b[0][1]的地址。pb是指向b[0][1]的指針,b[0][1]是一個int。因此*pb和*pb+1相關4
第三行:
*pb:b[0][0]的地址。
*(pb+1):b[1][0]的地址。pb+1指向b[1]。
*(pb+1)+1:b[1][1]的地址。
第四行:
**pb:pb[0][0]。pb指向pb[0]。*pb指向pb[0][0]。**pb是pb[0][0]的內容。
**pb+1:b[0][0]+1。
第五行:
*(*pb):pb[0][0]
(*(*pb+1)):pb[1][0]
(*(*pb+1)+1):pb[1][1]
第六行:
**(pb):pb[0][0]
**(pb+1):pb[1][0]
**(pb+1)+1:pb[1][0]+1
二維指針對字符串的操作:
1. #include<string>
2. using namespace std;
3. int main(){
4. char **mychr;
5. char chr[]={"I love cplusplus"};
6. char chrs[]={"I love cplusplus"};
7. char *p=chr;
8. mychr=&p;
9.
10.if(strcmp(*mychr,chrs)==0)
11.{
12.cout<<"equal"<<endl;
13.}
14.else
15.{
16.cout<<"not equal"<<endl;
17.}
18.
19.cout<<&chr[0]<<endl;
20.cout<<&p<<endl;
21.cout<<&(*mychr)<<endl;
22.cout<<**mychr<<endl;
23.system("pause");
24.return 0;
25.}
二維數組指針指向一維數組
#include<stdio.h>
int main()
{
int array[5]={1,2,3,4,5};
int (*p)[5]=&array;
for(int i=0;i<5;i++)
{
printf("%d ",*(*p+i));
}
printf("\n");
return 0;
}
在這裏面我們定義了一個指針變量p,這個指針變量指向的是一個含有5個元素的一維數組,它指向的是一個一維數組的整體,所以將用&array賦值給它,*p其實是*(p+0),按照我們一維數組的理解,*(p+0)應該是指向我們數組的第一個元素,即p[0],也即我們所指數組的第一個元素&array[0],這個地址再後移i個元素後得到我們想要的地址,然後再加上*就得到本地址裏面所存儲的整形數據的內容。
博客:http://blog.csdn.net/zwb8848happy/article/details/7389622
對二維指針和二維數組講解的非常詳細
(二)二維數組和指針
(1)地址代表的意義
首先理解,二維數組,例如
int arry[2][3]={{2,3,1},
{23,45,6}};
這樣寫是最好理解的,也就是說2行3列。其中也可以看成是2個一維數組的整合,也就是
arry[0] = {2,3,1};
arry[1] = {23,45,6};
簡單測試一下一些符號究竟代表的是什麼意思
printf("n%x",arry);
printf("n%x",arry[0]);
printf("n%x",&arry[0][0]);
printf("n%x",arry+1);
printf("n%x",arry[1]+1);
printf("n%x",&arry[1][1]+1);
從編譯運行的結果來看
12ff68
12ff68
12ff68
12ff74
12ff78
12ff7c
arry 是這個數組的名字,也就是代表着入口地址,剛纔說了,二維數組實際可以看成N個
一維數組,例如本例中,arry[0] 代表着其中的第一個一維數組,那麼 arry[1] 就代表着
第二個一維數組,而且這裏是地址,注意 arry[0] 本身就代表着地址了。
arry[0][0]本身代表着這個元素,那麼取地址之後 &arry[0][0] 就變成了,這個元素的地址
然而將每個地址加一後發現,這些地址代表的語義並不相同。
arry+1 之後地址變成 12ff68 到 12ff74 也就是說,指向了第二行第一個元素 23
arry[1]本身就代表着第二個一維數組的入口地址,加一之後就只想這個數組的第二個元素
那麼就應該指向 45,從地址可以看出。
&arry[1][1]+1 這個是以元素爲單位,首先 arry[1][1]這個元素爲基準,也就是 45 ,然後
再後移一個元素,所以應該指向 6.
參考
(2)二維數組怎麼傳遞參數
void disparry(int a[2][3])
{
printf("n%d,%d,%d",a[0][0],a[0][1],a[0][2]);
}
void main(void)
{
int arry[2][3]={{2,3,1},
{23,45,6}};
int (*c)[3]=arry;
int *p;
p =(int *)arry;
disparry((int (*)[3])p);
disparry(c);
}
可以通過兩種辦法,第一是通過 int * 類型的指針 *p 來處理。首先因爲 p 是 int* 類型,
而 arry是二維數組,不等價不能直接賦值,那麼就簡單的將二維數組指針轉換爲 int* 類型
這樣就匹配了,產生的效果是 p 指向了 arry 的入口。
然後 disparry 函數需要的參數的類型是 inta[2][3],很顯然,這個時候 p又不能作爲參數
了,當然直接傳遞的話,運行結果不錯,但是會有 warning 的。於是再一次將 int * 轉換爲
需要的 int (*)[3] 類型,說白了,就是 數組指針。
那麼第二種辦法就很乾脆,定義一個數組指針c指向 arry,那麼 c就可以直接傳遞了。
(3)使用 typedef 定義二維數組
typedef int A[2];
A b[3]={32,11,23,45,6,7};
printf("n%d",b[1][0]);
可以看出,先定義一種新的數據類型 A,他是一個 2個元素的一維數組。然後再定義一個含有
3個 A 類型的一維數組b,這樣b其實就是一個二維數組了,b[3]代表的是有3行這樣的一維數組
所以這樣下來實際上等效於 b[3][2]。
C/C++中的二維指針問題--轉載 (內存分配)
指針是可變數組的首地址。正因爲是可變數組,所以一般使用指針時都是採用動態內存分配和釋放的方式。一維指針形式簡單,容易理解。平時應用較多。二維數組和二維指針比較複雜,並且在動態內存分配與釋放方面比較複雜且難以理解。但是二維數組和二維指針是非常有用的。
考慮以下應用:對一幅圖像進行模板運算。這必然會牽涉到對圖像的各象素點的操作。此時使用一維指針進行圖像傳遞時,不可避免的會使用形如*(p+i*width+j)的方式完成對圖像象素點的訪問。這種方式很不直觀並且編寫程序時容易出錯。如果使用二維指針進行圖像數據的傳遞,則會收到很好的效果。可以採用p[i][j]的方式直接操作象素點,直觀又便於維護。因此掌握二維數組和二維指針是必要的。
使用http://www.wangchao.net.cn/bbsdetail_59038.html中的示例。
定義如下3個二維數組和二維指針進行說明:
1. int** ptr;
2. int *ptr[M];
3. int (*ptr)[M];
以上都是存放整數的二維數組,並且都可以通過ptr[i][j]的形式訪問內容。但是它們之間有很大的差別。以下依照文中提到的方面進行分析。
l 內容
三個ptr本身都是指針,並且是二維指針,但是它們的最終內容總是整數。但中間內容,形如ptr[i]並不是整數,而是指針int *.
l 意義
1. int** ptr 表示指向(一組指向整數數據指針)的指針;
2. int *ptr[M] 是指針數組,表示指向(M個指向整數指針)的指針;
3. int (*ptr)[M]表示指向一組(指向包含M個整型數據的指針)的指針。
l 所佔空間
1和3佔用一個內存空間,在32位平臺上爲4個字節,也就是一個指針。
2是M個指針,在其定義過程中編譯器即對其進行了分配,佔用4*M共4M個字節。
l 用法
1. int** ptr 表示指向(一組指向整數數據指針)的指針,是一個二維指針。在其定義過程中,編譯器並不對其進行內存的分配,因此必須自己管理其內存的分配與釋放。典型使用如下:
使用上述方法分配內存,最終ptr耗費的內存空間爲M*sizeof(int*)+M*N*sizeof(int)
2. int *ptr[M] 是指針數組,表示指向(M個指向整數指針)的指針。是一個二維指針。但是在定義的時候,編譯器已經爲ptr指向的M個指向整數的指針ptr[0:M-1]分配了內存。也就是說,定義之後即可得到ptr的地址以及用於存放ptr[0:M-1]的內存空間4MB。要使用ptr必須對ptr[i]分配內存。分配內存後,ptr地址相應內存空間填入ptr[i]指向內存地址。使用如下操作:
使用上述方法分配內存,最終ptr耗費的內存空間爲M*sizeof(int*)+M*N*sizeof(int),其中M*sizeof(int*)爲編譯器分配,M*N*sizeof(int)爲程序員自己分配。
3. int (*ptr)[M]表示指向一組(指向包含M個整型數據的指針)的指針。該定義限定了ptr[i][0:M-1],所有指針ptr[i]必須指向長度爲M的數組。使用方式如下:
使用上述方法分配內存後,ptr所佔內存空間爲M*N*sizeof(int)(不考慮不同操作系統用於管理的內存)。ptr內存空間中保存的爲最終內容而非ptr[i]地址。
最後回到開始考慮的應用,使用以下解決方案:
- // Function Prototype: int test( char* data,
- // const int N_H, const int N_V);
- // Function name: test
- // Return type: int
- // Argument: char* data 輸入圖像數據
- // Argument: const int N_H, const int N_V 圖像尺寸
- int test( char* data, const int N_H, const int N_V);
- {
- char** ppData = NULL;
- int i = 0;
- ppData = (char**)malloc(sizeof(char*)*N_V);
- if (ppData == NULL)
- {
- printf("Error in mem location!/n");
- return 0;
- }
- for (i=0;i<N_V;i++)
- {
- ppData[i] = data + i * N_H;
- }
- //其他操作
- free(ppData);
- return 1;
- }
C++中用new可以通過以下方法爲二維指針分配內存,例如分配整型5*4的空間:
1、化二維爲一維
int *p;
p=new int[5*4];
2、可以直接定義二維指針,通過兩步申請內存,但是從二維數組的角度看內存不連續。
int **p;
int x;
p=new int*[5];
for (int i=0;i<5;i++)
{
p[i]=new int[4];
}
使用時:
for(int i=0;i<5;i++)
for(int j=0;j<4;j++)
p[i][j]=i+j;
for(int i=0;i<5;i++)
for(int j=0;j<4;j++)
{
std::cout<<p[i][j];
if(j==3)
std::cout<<std::endl;
}
釋放內存時應該注意釋放方法:
for(int i=0;i<5;i++)
delete[] p[i];
delete[] p;
3、可以先定義一維指針並分配夠需要的空間,然後自己根據情況映射到二維指針。
int *p1=new int[5*4];
int **p;
int x;
p=new int*[5];
for (int i=0;i<5;i++)
{
p[i]=p1+i*4;
}
使用時仍然是:
for(int i=0;i<5;i++)
for(int j=0;j<4;j++)
p[i][j]=i+j;
for(int i=0;i<5;i++)
for(int j=0;j<4;j++)
{
std::cout<<p[i][j];
if(j==3)
std::cout<<std::endl;
}
此時釋放空間:
delete[] p;