二級指針與二級數組彙總

一個函數形如:

void f(float **p){

/* 想要在函數體中按二維數組的方式訪問*/

    p[1][1] = 0;//c++用vc編譯ok,運行出錯(非法訪問)

}

float **p; //其實這裏的p並不是一個二位數組的指針,只不過是一個指向指針的指針

像你這樣訪問肯定是會出問題的。

例如:

float a[2][2]={0,1,2,3};

float **p=(float**)a;//強制將二維數組指針轉爲指向指針的指針

則此時

p[0]=0;

p[1]=1;

p[2]=2;

p[3]=3;

p[0][0]=*(*(p+0)+0)=**p;

p[0][1]=*(*(p+0)+1);

對於p[0][0]:由於*p=0; ====> **p=*(0);引用地址爲零的內存,必然是錯誤的。

對於p[0][1]=*(*p+1)====>*(4),引用了非法內存

同樣,對於p[1][0]=*(1),p[1][1]=*(5),均引用了非法內存

所以說,二位數組並不能簡單的轉換成指向指針的指針。

正確的指向二維數組的指針應該是:

float a[5][10];

float (*p)[10];//只需要定義爲指向第二維的指針,忽略第一維

p=a;

p[0][1]=a[0][1];

 

二級指針和二維數組並不等價。

二級指針是一個指向指針的指針

而二維數組其實就是一個指針,char a[3][4]; a是指向整個二維數組的首地址。它相當於(char *)[n],並不是char **;

所以不能直接:t=a;

要這樣:t = (char **)a;

我們知道char array[]=”abcdef”; array是數組的首地址,

那麼在二維數組中array當然也是數組的首地址,

看看這個定義char Array[][3] ={“ab“,“cd“,“ef“};

怎麼知道的呢?定義這樣一個數組,在vc調試窗口中

我們看到:

Array ---------0x64324234

|------Array[0]---0x64324234 “ab“

|------Array[1]---0x64324337 “cd“

|------Array[2]---0x6432433A “ef”

已經很明白了,實際編譯器是這樣實現二維數組的,實際上Array是“一維指針數組“的首地址,其中每一個元素指針都對應一個字符串,那麼好我們來看看是否可以這樣來使用Array二維數組.

char **pArray = Array;編譯器提示出錯,怎麼辦呢?加個(char **)試試,仍然出錯,設斷看一下pArray的值和Array的值是相等的,但我們是否可以象使用Array那樣來同樣輸出字符串呢?很明顯是不行的,編譯器不會把pArray+i處理成pArray+i*3尋找到第i個指針的地址,而只是簡單的加了一個i.這說明編譯器只做了很簡單的將地址值賦給了pArray,而它實際沒有任何意義.我們不能用它來訪問任何數據.很奇怪嗎?

再來看看這樣定義char *p[] = {“ab“, “cd“, “ef“};定義了一個指針數組.char **sp = p;這樣的用法經常看到,爲什麼這樣就可以使用sp來訪問字符串了呢,的確編譯器在編譯的時候識別出了sp是一個指向一維數組的指針的指針,那麼我們就可以把它做爲數組名來操縱整個數組了.

 

-----------------------------------------------------------------------------------------------------------------------------------

看如下例子:

#include <stdio.h>

int main(void)

{

       int array[2][2] = {12, 15, 51, 54};

       int **ptr = array; //錯誤

       int **ptr = &array; //錯誤

       int **ptr = &array[0][0]; //錯誤

       ....

       return 0;

}

錯誤在哪呢?

關於指針的說明:
0) 指針只能指向一維數組。
1) C/C++標準中並沒有 array[m][n] 這樣的表達//array爲指針變量;
2) 不能用一維指針或二維指針直接指向二維數組。如果要讓一維指針指向二維數組,則我們必需告訴指針所指向的數組的最後一維包含多少個元素,即告訴指針我們將傳遞一個二維數組的首地址給指針。如上面的例子,應該這樣來定義指針:int (*p)[4];這樣就可以用p=mat了;這是不是和我們上面說的"指針只能指向一維數組"相矛盾呢?不是的。其實指針指向的還是一維數組:這時我們是將每行當成一個元素!!指針即所謂的行指針。
3) 如果沒有告訴指針所指向的數組是二維的(即定義行指針),顯然指針不能接收二維數組名傳來的地址,即p=mat的兩邊地址的類型不同,左邊是一維的,右邊是二維的。但是如果我們將二維數組看成是一維數組,那麼這個一維數組的首地址是:mat[0],所以我們可以用p=mat[0];這樣指針對二維數組的引用將完全按照一維數組的引用方式來調用。

來自:http://wenda.tianya.cn/wenda/thread?tid=441b3214f83b083f

我們知道char array[]=”abcdef”; array是數組的首地址,
  那麼在二維數組中array當然也是數組的首地址,
  看看這個定義char Array[][3] ={“ab“,“cd“,“ef“};
  怎麼知道的呢?定義這樣一個數組,在vc調試窗口中
  我們看到:
  Array ---------0x64324234
   |------Array[0]---0x64324234 “ab“
   |------Array[1]---0x64324337 “cd“
   |------Array[2]---0x6432433A “ef”
  已經很明白了,實際編譯器是這樣實現二維數組的,實際上Array是“一維指針數組“的首地址,其中每一個元素指針都
  對應一個字符串,那麼好我們來看看是否可以這樣來使用Array二維數組.
  char **pArray = Array;編譯器提示出錯,怎麼辦呢?加個(char **)試試,仍然出錯,設斷看一下pArray的值和Array
  的值是相等的,但我們是否可以象使用Array[i]那樣來同樣輸出字符串呢?很明顯是不行的,編譯器不會把
  pArray+i處理成pArray+i*3尋找到第i個指針的地址,而只是簡單的加了一個i.這說明編譯器只做了很簡單的將地址值賦給
  了pArray,而它實際沒有任何意義.我們不能用它來訪問任何數據.很奇怪嗎?
  再來看看這樣定義char *p[] = {“ab“, “cd“, “ef“};定義了一個指針數組.char **sp = p;這樣的用法經常看到,爲什麼這樣
  就可以使用sp[i]來訪問字符串了呢,的確編譯器在編譯的時候識別出了sp是一個指向一維數組的指針的
  指針,那麼我們就可以把它做爲數組名來操縱整個數組了,c神奇的地方或者說精華的地方就在這裏了,希望
  這篇文章對那些對指針或二級指針有疑惑的朋友能夠有所幫助,這也是我blog裏的第一篇文章,呵呵.

來自:http://www.wangchao.net.cn/bbsdetail_59198.html

 

---------------------------------------------------------------------------------------------------------------

本來不想寫什麼的,但是看看,我覺得都沒有我想要的答案,於是我像樓主推薦我的拙見。其實這個數組與指針的問題,要寫的話,一句話,兩句話是將不清楚的。

首先數組和指針的概念你沒分清楚,數組的本質你沒搞清楚。這是導致問題出現的根源。

int x[5]; 這個定義裏面,我們說定義了一個數組x,此數組有5個數組元素,元素的類型爲int類型。首先要問的是,x到底爲什麼東西? 我知道,在譚浩強的書上面說x是數組名,x代表了數組第一個元素的首地址。沒錯,x確實是數組的名字,x的值也確實是第一個數組元素的地址值。注意這裏我們說x代表的值與數組第一個元素的地址值相等,但是並不是說他們的類型是一樣的。那麼x的類型到底是什麼呢? 有人說就是int * 類型。有如下語句可以做證:

int *p=x; //這句話是正確的。

x的類型真是int *嗎,我們說不是,因爲下面的語句是不正確的:

int a=10;
x=&a; // int *類型的變量時可以接受值的。所以x不是int*

那麼我們可以猜測x的類型是不是 int *const呢。也就是說x是一個地址值不可以改變的指針。這句話貌似有點正確。但是請大家看看下面的例子:

int x[5]={0};
int a=sizeof(x); // a的值到底是多少?實際上這裏a的值是5*4=20
我這裏使用的編譯器是VC++ 6.0 int類型數據佔用4個字節空間,所以這裏的道的是整個數組佔用的字節數。 我們不是說x的類型是iint * const類型的嗎,也就是x應該是一個指針類型,應該是4個字節的啊,爲什麼sizeof出來時整個數組佔用的字節數呢。例如

sizeof(int *)這個的結果就是4。所以有此可以看出,x的類型並不是int*,也不是int * const。

int x[5];中的x到底是什麼呢,我們說x是數組,此數組有5個元素,並且每個元素都是int類型。 我們有一個識別數據類型的規律例如:

int x; //x類型爲int
int *x;//x類型爲int *
int **x;//x類型爲int **
int (*x)[10];//x類型爲int(*)[10]實際上是指向數組的指針
int (*x)(int ,int);//x的類型爲int(*)(int,int)實際上是指向函數的指針

由此可以看出,一個符號是什麼數據類型,我們只要在其定義的表達式中去掉符號本身,剩下的就是符號的類型了。照此推斷,int x[5];中x的類型應該是 int [5]這個類型,可以看出此類型並不是int *類型。

那麼int x[5];中的x可以這樣賦值: int *p=x; 爲什麼呢,只能說這裏面將x的類型隱式轉換爲了int *類型。所以這裏是可以賦值的,因爲進行了類型轉換。 再請看下面的例子:

void function(int x[5])
{
  cout<<sizeof(x)<<endl; //這裏輸出4
}

爲什麼會輸出4,而不是4*5呢,可以看出上面的函數形參實際上類型是int*,並不是數組類型,所以我們在定義函數的時候,下面的都是與上面等價的:

void function(int x[])//元素個數是多少可以省略
{
  cout<<sizeof(x)<<endl; //這裏輸出4
}

void function(int *x) //直接寫成指針變量也沒錯
{
  cout<<sizeof(x)<<endl; //這裏輸出4
}

他們都是等價的。

那麼我們看一個類似的問題:
int x[5];
int **p=&x; //爲什麼會報錯? 因爲類型不匹配。

p的類型是int **,而&x的類型卻不是int **。 &x的類型實際上是int(*)[5],因爲去的是x的地址,也就是說這個地址是數組的地址,並不是指向數組第一個元素的指針的指針(也就是二維指針),而是整個數組的地址。所以我們可以改成下面的:
int (*p)[5]=&x;//這就對了。

指向數組的指針,和指向數組元素的指針有什麼不同?

我們說對於一個指針變量,要幾點是我們必須注意的,例如int *p;我們要注意的是,p的類型是int*,p佔用的空間4個字節,p指向的數據類型是int。p指向的數據類型佔用4個字節。所以對於指針變量,我們要明白指針變量本身是佔用空間的,本身是有類型的,其次指針變量所指向的空間是有類型的,是有空間的。

那麼int *p; char *p1; 對於指針變量來說p,p1裏面都放的是地址值,說白了就是一個數值,他們都佔用4個字節的空間,但是他們的類型不一樣,p裏面的地址指向的是int類型的數據,p1指向的是char類型的數據,這主要體現在p++與p1++中他們在內存中移動的字節數是不一樣的,我們假設int佔4個字節,char佔1個字節。那麼對於p來說向前移動了4個字節,p1來說移動了一個字節。這就是他們的類型不同,導致運算過程中的不同。

int x[5];
int (*p3)[5]; 此時p3指向數組x,那麼p3++實際上向前移動了多少呢,可以算出移動了4*5個字節。也就是p3指向的是一個數組,是整個數組,所以p3移動的時候是將一個數組當做一個整體來看待的。所以向前移動了一整個數組的距離。

再看你的問題之前,我們來看一個類似的問題:

int a[2][3];
int**p=&a; //這裏我用&a來賦值行不行呢。是不行的。

這裏爲什麼是錯誤的,原因就是因爲&a的類型不是int**類型。所以類型不兼容,導致不能賦值,同時這兩種類型是不可以相互轉換的。 那麼&a到底是一個什麼樣的類型呢。 我們說&a去的是整個數組的地址,那麼&a自然就是指向整個數組的指針了。 int (*p)[2][3]=&a; 此時這樣賦值纔是正確的。如果我們要用a直接賦值,那該定義一個什麼樣的變量來接受它呢,首先要明白,數組名代表的地址類型是指向數組的第一個元素的指針,例如:

int a[10];
int *p=a; 實際上這裏與 int *p=&a[0];是等價的。因爲指向a[0]的指針類型就是int*類型。  那麼&a的是去數組的地址,其類型是指向數組的指針,而不是指向數組第一個元素的指針,整個是要區別的,他們的類型就不一樣。 int(*p)[10]=&a;

所以說這裏的a和&a絕對不是同一個東西,雖然本質上他們的地址值是一樣的,但是他們的類型不一樣。就決定他們代表不同的意義。

那麼剛剛說了對於下面的例子:
int a[2][3];
int (*p)[2][3]=&a;//我們可以定義這樣的一個變量p來接受&a的值。

那麼我們要接受a應該定義一個什麼樣的變量呢。a[2][3]是一個二維數組,可以看成是這樣的a是一個數組,具有兩個元素,分別爲a[0],a[1]其中這兩個元素的值a[0],a[1]他們的值又是一個具有3個元素的數組。此時我們可以將a[0],a[1]看成是數組名,那麼a[0][0]就是數組a[0]的第0個元素了。對應關係如下:
a[0] ---->  a[0][0],a[0][1],a[0][2]
a[1] ---->  a[1][0],a[1][1],a[1][2]

那麼a到底是什麼,其實a數組有兩個元素,a[0],a[1],那麼a的值自然就是其第一個元素的地址了,也就是&a[0]了。這是一個什麼類型?  我們知道如果我們將a[0]看成一個整體,例如我們用A來代替a[0],那麼A[0],A[1]就相當於a[0][0],a[0][1] 。 此時A就是一個int類型的數組,&A,的類型實際上就是 int(*p)[3]這個類型。

所以下面的代碼也是正確的:
 
int a[2][3];
int(*p)[3]=a; //所以對於你的問題,可以這樣子。。


明白了嗎?

-----------------------------------------------------------------------------

簡單地說說,數組與指針的區別吧  
  int   a[5];//組數a,a代表數組首地址,記住,是地址  
  int   *p;//p,有一個四字節的空間,其中的內容是地址  
   
  a   =   5;//錯誤,因爲a沒有存儲空間,它就是一個地址而已  
  p   =   0x1234;//OK,p有存儲空間  
   
  當數組與指針聲明於函數參數時,  
  void   fun(int   a[]);  
  void   fun(int*   p);//p和a一個意思  


--------------------------------------------------
無論是二維,還是一維在內存中都是一樣的,都是從低到高的地址序列  
我認爲函數應該這樣定義  
void fun(char   *p,   int   rows,int   lines)//p是二維數組的地址(也就是第一個元素的地址)  
                                             //rows是行,lines是列

  在   fun()中,要自己編算法把它還原成二維數組  
   
  調用時可以這樣  
  char   a[2][2];  
  void   main()  
  {  
        fun((char   *)a,2,2);  
        //fun(&a[0][0],2,2);  
  }  
   
如果fun()函數傳遞的二維數組大小是固定的,可以這樣聲名  
  char fun(char a[5][5])
  {
      return a[3][3];  
  }  
   
   
  void main()  
  {  
     char   a[5][5];  
     fun(a);  
  }  
   
  原則是:靜態的申請數組時,必須給定數組的大小

----------------------------------------------------------
轉爲1維的 雙參數

void fnct ( double *p,int row, int col )
{
   for(int i=0;i <row*col;i++)
   {
      cout < <p[i] < <endl;
   }
}

int main()
{
  double A[3][2] = { {1},{2},{3} };
  fnct((double *)A,3,2);

  return 0;
}
------------------------------------------------------------
1維通用接口
void a(int *, int, int);

int main(void)
{
        int c[2][5], b[6][8];
        ......
        a((int*)c, 2, 5);       //使用數組c
        .........
        a((int*)b, 6, 8);       //使用數組b
        ....
        return 0;
}

void a(int *cpSource, int iRow, int iColumn)
{
       int **cpTemp, i;
       cpTemp = (int**)malloc(iRow*sizeof(int*));
       for(i=0; i<iRow; ++i) cpTemp[i] = cpSource+i*iColumn;
       cpTemp[i][j] = ......       //然後就可以cpTemp[i][j]這種形式使用了
       /*釋放*/
       for(i=0; i<iRow; ++i) free(cpTemp[i]);
       free(cpTemp);
       return;
}
================================================
1維 單參數
const   int   N   =   4;  
   
  void   Fun1(int   (*a)[N])  
  {  
  }  
   
  void   Fun2(int   a[][N])  
  {  
  }  
   
  int   main()  
  {  
  int   a[N][N];  
  Fun1(a);  
  Fun2(a);  
  }

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章