-------------------------------------------Section 0 前言-------------------------------------------
寫個簡單的yuv讀取的庫,卡在多維陣列動態分配的問題上。唉,還是C基本功不紮實,於是花了一下午時間,算是給自己有了點交代。參考《C專家編程》。水平有限,歡迎看客指正。
-------------------------------------------Section 1 左值與右值-------------------------------------
編譯器為每個變數分配一個地址(左值),該地址在編譯時間可知,且變數在運行時一直存於該地址。存於該地址的變數的值(右值)只有在運行時可知。因此,編譯器如果需要一個地址來執行某種操作,它可以直接進行操作,如果需要一個變數的值,它需要發出指令從指定地址中讀入變數值並存於寄存器中。到這裡,可以理解作為一個指標變數,它本身的地址是左值,它變數的值(即指向的地址值)為右值。所以指標首先需要在運行時取得它的當前值,然後才能對它進行解除引用操作。
數組名是一個左值,即記憶體中的位置。但數組名是一個不可修改的左值,即不可被賦值。
int main()
{
int a[3] = {0};
int b = 1;
a = &b; //ERROR: “=” : 左運算元必須為 l 值。
return 0;
}
-----------------------------------------Section 2 數組與指標的不同---------------------------------
一個例子:
int main()
{
char arr[4] = "abc"; // Note 1
//char arr[4] = {'a', 'b', 'c', '\0'}; // Note 2
char *ptr = "ABC"; // Note 3
//ptr+1 = &arr[2]; // Note 4
printf("arr: %x, %x, %x %x \n", &arr, &arr[0], &arr[1]); //Note 5
printf("ptr: %x, %x, %x %x \n", &ptr, &ptr[0], &ptr[1]);
return 0;
}
Note 1&2等價定義,其結構如下:
a b c \0
[__] [__] [__] [__]
12fed4 +1 +2 +3
Note 3結構如下
42703c A B C \0
[__] [__] [__] [__] [__]
12fec8 42703c +1 +2 +3
Note 4複習一下Section 1。顯然的錯誤,因為p+1首先需要知道p的值(右值),只有在運行時刻才能得到,編譯時間刻就希望對其所在的地址進行賦值顯然錯誤。
Note 5驗證Note1和3,運行結果如下:
arr: 12fed4, 12fed4, 12fed5
ptr: 12fec8, 42703c, 42703d
可以發現,arr的地址(左值)的結果與數組中首元素的地址一致,而ptr的變數值(右值)與數組的首元素地址一致。
因此對一個數組中的元素進行引用,c=arr[i]和c=ptr[i]都能夠取出相應數組中的第i個元素。但要注意這兩個操作的過程完全不同:
c = arr[i]; c = ptr[i];
1:取地址12fec8的內容,即42703c
1 取出i的值與12fed4相加 2:取出i的值與42703c相加
2 取地址(12fed4+ i)的內容 3:取地址(42703c+i)的內容
得到結論:儘管c=arr[i]和c=ptr[i]用同樣的形式完成了同樣的功能,但絕不可以混用。注意數組原始的聲明方式,如果原始聲明為數組式的,那麼對其元素的引用要使用數組形式,反之亦然。
檔案1中:
int array[100];
檔案2中:
extern int *array;
array[50] = 3; //知道這句為什麼錯了吧?
-----------------------------------------Section 3 數組與指標的相同----------------------------------
傳說有三種情況下,數組名會被當作指標。
1 “運算式中的數組名”就是指標
int a[10], *p, i;
p = a; //here
2 數組下標就是指標的位移量
以下語句功能一致,但需注意實現的過程不一樣(Section 2):
a[i] = 0;
p[i] = 0;
*(p+i) = 0;
3 函數形參中的數組名被當作指向第一個元素的指標
以下三種函式宣告的形式是等同的:
my_function(int *p) {...}
my_function(int p[]) {...}
my_function(int p[100]) {...}
對my_function函數的調用,無論實參是數組還是指標,都是合法的。
----------------------------------------------Section 4 多維陣列-------------------------------------
首先理解一個簡單的多維陣列:
int main()
{
int apricot[2][3][5];
int (*p)[3][5] = apricot; // 別忘記“運算式中的數組名”就是指標
int (*r)[5] = apricot[1];
int *t = apricot[1][2];
int u = apricot[1][2][3];
return 0;
}
根據數組下標規則不難理解,apricot[i][j][k]將被編譯器解析為(*(*(apricot+i)+j)+k)。而且多維陣列在記憶體中的布局是線性形式的,所以可以得到apricot[i][j][k]可以通過計算*(&apricot + i*3*5 + j*5 + k)得到。
另一種實現方法是使用指標數組或指標的指標。這種方式的特點是靈活,並且可以實現動態分配多維陣列。
char *pea[4];
char **pea;
仍然可以通過pea[i][j]來引用其中的變數以及上述的記憶體位置計算方法(注意是否滿足連續線性記憶體布局)。但這時需要注意的是對這種變數的初始化工作有一點技巧性,因為需要保證指標在後續的使用過程中都是合法的。常用方法是迴圈malloc
for(j = 0; j < 4; j++)
pea[j] = malloc[6];
或一次性malloc一整塊資料,然後用迴圈將指標指向各個地區:
malloc(row * column * sizeof(char));
最後來兩個我的yuvlib裡的子程式,看懂了指標就過關了。
************************************************************************
* \brief
* Allocate 2D memory array -> unsigned char array2D[rows][columns]
*
* \par Output:
* memory size in bytes
************************************************************************/
int get_mem2D(byte ***array2D, int rows, int columns)
{
int i;
if((*array2D = (byte**)malloc(rows*sizeof(byte*))) == NULL)
exit(2);
if(((*array2D)[0] = (byte* )malloc(columns*rows*sizeof(byte ))) == NULL)
exit(2);
for(i=1;i<rows;i++)
(*array2D)[i] = (*array2D)[i-1] + columns ;
return rows*columns;
}
/*!
************************************************************************
* \brief
* free 2D memory array
* which was alocated with get_mem2D()
************************************************************************
*/
void free_mem2D(byte **array2D)
{
if (array2D)
{
if (array2D[0])
free (array2D[0]);
else exit(6);
free (array2D);
} else
{
exit(6);
}
}