天天看點

c語言數組名和指針的差別&&多元數組

之前就知道數組名和指針不同,因為運算符sizeof對數組名和指向數組的指針的運算結果是不同的---sizeof數組名得到的是數組空間的大小,而sizeof指向數組的指針獲得的是指針自身占據空間大小。而如果是對指針進行解引用,即對象是指針指向的,那麼sizeof得到的是這個對象的大小。

舉例:

char  aszFoo_Name[4];
char *pcFoo_Name = aszFoo_Name;
sizeof(aszFoo_Name) = 4;
sizeof(pcFoo_Name) = 8;
sizeof(*pcFoo_Name) = 1;
           

今天恰好看到關于linux(注意可移植性)裡面對于0數量成員的數組的一些解釋,舉例:

typedef struct tagFoo_xxx
{
    char cT1;
    char acT2[0];
}Foo_xxx;
           

acT2就是我說的這種成員數量為0的數組,sizeof(Foo_xxx)得到的大小是1,可以驗證acT2這種數組是不占空間的,引用别人部落格的一段話:

char a[1]裡面的a和char *b的b相同嗎?《 Programming Abstractions in C》(Roberts,

E. S.,機械工業出版社,2004.6)82頁裡面說:“arr is defined to be identical to

&arr[0]”。也就是說,char a[1]裡面的a實際是一個常量,等于&a[0]。而char *b是有一個實實在在的指針變量b存在。

是以,a=b是不允許的,而b=a是允許的。

上面這種東西的用法大緻如下,也是從别人的部落格搞來的:

#include <stdlib.h>
#include <string.h>
struct line {
   int length;   
   char contents[0]; // C99的玩法是:char contents[]; 沒有指定數組長度     
};    
int main(){
   int this_length=10;
   struct line *thisline = (struct line *)malloc (sizeof (struct line) + this_length);
   thisline->length = this_length;
   memset(thisline->contents, 'a', this_length); 	
   return 0;
}
           

上面這段代碼的意思是:我想配置設定一個不定長的數組,于是我有一個結構體,其中有兩個成員,一個是

length,代表數組的長度,一個是contents,代碼數組的内容。後面代碼裡的this_length(長度是10)代表是我想配置設定的資料的長度。(這看上去是不是像一個C++的類?)這種玩法英文叫:FlexibleArray,中文翻譯叫:柔性數組。

需要注意的是後面根的緩沖區不能自己釋放,是以要手動釋放,防止記憶體洩露。。。

我感覺可以結合一下了解下數組名是什麼東西。

而且對數組名取位址和數組名的值是相同的,那麼 數組名指向的是首個元素的位址,數組名取位址指向的是數組的位址,是以這兩者的值是相同的。結合數組指針來了解一下:

char acTest[3]={1,2,3};
    char (*pacTest1)[3]=&acTest;
    char *pacTest2= acTest;
    printf("%d",acTest); 
    printf("%d",&acTest); 
    printf("%d",*acTest); 
    printf("%d",*(&acTest)); 
           

數組指針pacTest1是指向數組的一個位址,是以将數組名的取位址的值賦給它,同樣char指針代表指向一個char的位址,這裡用數組名指派。而四個printf的輸出結果,一二四是相同的,都是同一位址,而第三輸出為1.這樣就可以佐證上面的說法是正确的。

那麼二維數組又是怎樣的呢:

char acTest[2][3]={1,2,3,4,5,6};
        char (*pacTest1)[3]=acTest;
        char (*pacTest2)[2][3]=&acTest;
        printf("%d",acTest); 
        printf("%d",&acTest); 
        printf("%d",*acTest); 
        printf("%d",*(&acTest)); 
           

這次四個表達式的值是相同的。二維數組可分解為多個一維數組,即以一維數組為成員,如二維數組acTest可分解為擁有三個成員:acTest[0],acTest[1],acTest[2]的數組,是以數組名直接指向的是其成員:一維數組,而對其取位址得到的才是二維數組位址,這和上面的結論相同。那麼四個值相同,acTest指向一維數組acTest[0],其為一維數組的位址。&acTest指向二維數組本身。*acTest即acTest[0],也就是第一個一維數組的第一個成員的位址。最後一個與第一個相同。

數組名取位址的類型是一個數組指針類型,但它不是數組指針;而數組名的類型為這個數組的成員的類型的指針的類型,它同樣不是指針(好繞。。)因為指針的sizeof都是指針自身空間的大小~~~這個也可以結合數組下标通路的文法糖來了解,a[i]=*(a+i),a[i][j]=*(a+i)[j]=*(*(a+i)+j)是以a也必定是指向其成員的指針的類型。這裡gdb裡看一下對應的類型:

char a[2][3]={1,2,3,4,5,6};
(gdb) ptype &&a
A syntax error in expression,near '&&a'.
(gdb) ptype &a
char (*)[2][3]
(gdb) ptype a
char [2][3]
(gdb) ptype &a[0]
char (*)[3]
(gdb) ptype &a[0][0]
char *
           

&a是二維數組指針類型,a是一維數組指針類型,&a[0]是一維數組指針類型,&a[0][0]是指向基本數組元素的指針類型。還有一點,在第一行取值的時候,對數組名取兩次位址,顯示原型錯誤,這應該是代表數組名取兩次位址沒有意義,是以不給列印。假設有一個n維數組,a[b1][b2]…[bn],其中bi為第i維數組的長度(i=1,2,…n),那麼元素a[j1][j2]…[jn]的數值為一遞歸函數:

f(n)=*(f(n-1)+jn)
其中f(0)=a,n>=1
           

或者另一中方式, a[j1][j2]…[jn]的位置為:

LOC(a[j1][j2]...[jn]) = LOC(a)+(b2*b3*...bn*j1+b3*b4*...bn*j2+...+jn)L
其中LOC(x)代表x的位置,L為數組基本元素的長度
           

今天又看到兩種模拟二維數組的方法,也可以拿過來了解一下:

一、利用一個二級指針來實作

#include<stdio.h>
#include<malloc.h>
 
int main()
{
	//5行2列的數組
	int **p = (int **)malloc(sizeof(int *) * 5);
	for (int i = 0; i < 5; ++i)
	{
		p[i] = (int *)malloc(sizeof(int) * 2);
	}
	for (int i = 0; i < 5; ++i)
	{
		for (int j = 0; j < 2; ++j)
		{
			//輸出數組每個元素位址
			printf("%p\n", &p[i][j]);
		}
	}
	for (int i = 0; i < 5; ++i)
		free(p[i]);
	free(p);
    return 0;
}
           

二級指針指向了一塊五個int的空間,每個int空間裡面儲存了兩個Int空間的位址。

當使用文法糖通路時,p[i][j]先通過i定位到第幾個int空間,p[i]就是一個Int,而後j則可以在指向的空間内移動,每次移動sizeof(int)大小。

二、利用數組指針來實作

#include<stdio.h>
#include<malloc.h>
int main()
{
 
	//申請一個5行2列的整型數組
	int(*p)[2] = (int(*)[2])malloc(sizeof(int) * 5 * 2);
	for (int i = 0; i < 5; ++i)
	{
		for (int j = 0; j < 2; ++j)
		{
			//輸出數組每個元素位址
			printf("%p\n", &p[i][j]);
		}
	}
	free(p);
    return 0;
           

一維數組指針是一個指向一維數組的指針,當p+i,每次移動整個數組所占空間大小,p[i]即相當于一維數組位址,也是指向第一個成員的位址,而j可以确定在數組中成員的位置

繼續閱讀