Understanding and using c pointers 第四章讀書筆記

tonybai在他的網站上寫了一個本書的知識點綱要

An array name is not a pointer. Although an array name can be treated as a pointer at
times, and array notation can be used with pointers, they are distinct and cannot always
be used in place of each other

一維數組
對於數組使用sizeof會返回數組所佔用內存的大小,如:

int vector[5];
printf("%d ",sizeof(vector) / sizeof(int));	/*輸出5*/


指針記號法與數組(Pointer Notation and Arrays)
數組名只能做右值,在在做右值時返回數組首元素的首地址

int vector[5] = {1,2,3,4,5};
int *pv = vector;	//變量pv是指向數組首元素的指針,而不是指向數組的指針


三種表達方式,vector,&vector[0],&vector數值上是相同的,但是含義有差別:
vector和&vector[0]意思相同,指向數組首元素的首地址,
&vector指向數組的地址
所以如果進行指針運算,那麼vector + 1,指向數組的第二個元素,&vector + 1指向下一個同樣長度的數組,
如果將vector和&vector看做指針的話,vector是指向數組元素的指針,&vector是指向數組的指針也就是數組指針
在上邊的例子中,如果我們修改程序,在gcc中會如下:

int vector[5] = {1,2,3,4,5};
int *pv = &vector;	//Gcc警告:warning: initialization from incompatible pointer type,這樣做不恰當


既然&vector是數組指針,如果不想看到警告,則需要如下修改程序

int vector[5] = {1,2,3,4,5};
int (*pv)[5] = &vector;	//pv聲明爲數組指針,再編譯就不會報錯,(*pv)的括號是必不可少的,去掉就變成另外的意思了

 

Array notation can be thought of as a “shift and dereference” operation. The expression
vector[2] means start with vector, which is a pointer to the beginning of the array, shift
two positions to the right, and then dereference that location to fetch its value. Using
the address-of operator in conjunction with array notation, as in &vector[2], essentially
cancels out the dereferencing. It can be interpreted as go left two positions and then
return that address.

 

數組和指針的差異

int vector[5] = {1,2,3,4,5};
int *pv = vector;


1.產生的機器碼不同
The code generated by vector[i] is different from the code generated by vector+i.
The notation vector[i] generates machine code that starts at location vector, moves
i positions from this location, and uses its content. The notation vector+i generates
machine code that starts at location vector, adds i to the address, and then uses the
contents at that address. While the result is the same, the generated machine code is
different. This difference is rarely of significance to most programmers.
2.sizeof不同,
sizeof(vector)返回20,sizeof(pv)返回4(32位機器上)
3.vector只能做右值,pv可以做左值,也可以做右值

使用malloc創建一維數組

int *pv = (int *) malloc(sizeof(int) * 5);
int i;
for(i = 0; i < 5; i++)
{
	pv[i] = i + 1;
}
或者
for(i = 0; i < 5; i++)
{
	*(pv + i) = i + 1;
}


使用realloc函數調整數組大小
可以使用realloc函數調整由malloc函數創建的數組大小.

 

傳遞一維數組
C語言中傳遞數組必須傳遞長度,不同於java和as中的數組,這些語言中的數組更多是一種容器

void displayArray(int arr[],int size)
{
	int i;
	for(i = 0; i < size; i++)
	{
		printf("%d\n",arr[i]);
	}
}

//此處常見的錯誤是傳遞sizeof(vector),因爲這裏sizeof(vector)返回是數組所佔用的字節數,而不是數組元素的個數
//所以應該傳遞的是sizeof(vector) / sizeof(int)
displayArray(vector,sizeof(vector));

使用指針記號法(Using Pointer Notation)
void displayArray(int* arr, int size)
{
	for (int i = 0; i < size; i++)
	{
		printf("%d\n", *(arr+i));
	}
}

 

使用一維指針數組

//注意這裏的聲明,作者很巧妙的將*放在int後避免混亂 其實可以這樣int *arr[5],要把指針數組與數組指針分開(上面提到的)
//其實下面兩句可以直接這樣生聲明int* arr[5],i;效果與下面兩句聲明一樣,不過容易被搞混
int* arr[5];
int i;
for(i = 0; i < 5; i++)
{
	arr[i] = (int*) malloc(sizeof(int));	//等效語句*(arr + i) = (int*) malloc(sizeof(int));
	*arr[i] = i;	//等效語句**(arr + i) = i;
}

 

指針與多維數組

int matrix[2][5] = {{1,2,3,4,5},{6,7,8,9,10}};
int (*pmatrix)[5] = matrix;	//數組指針

二維數組表示法可以解釋如下:
附圖:


傳遞多維數組
傳遞matrix多維數組,可以如下兩種方式:

void display2DArray(int arr[][5],int rows)
{
	int i,j;
	for(i = 0; i < rows; i++)
	{
		for(j = 0; j < 5; j++)
		{
			printf("%d",arr[i][j]);
		}
		printf("\n");
	}
}

void display2DArray(int (*arr)[5],int rows)
{
	...
}

void display2DArray(int *arr[5],int rows){}	//這樣就不行,因爲*arr[5]是指針數組,而非數組指針

main()
{
	int matrix[2][5] = {{1, 2, 3, 4, 5},{6, 7, 8, 9, 10}};
	display2DArray(matrix, 2);
}


也可能會遇到如下的聲明方式,傳遞一維指針,並傳遞列數與行數

void display2DArrayUnknowSize(int *arr,int rows,int cols)
{
	int i,j;
	for(i = 0; i < rows; i++)
	{
		for(j = 0; j < cols; j++)
		{
			printf("%d",*(arr + i * cols + j));
		}
	}
}
//上述函數中不能使用arr[i][j],因爲arr沒有被聲明爲二維數組
//但可以使用(arr + i)[j]的方式
display2DArrayUnknowSize(&matrix[0][0],2,5);

 

傳遞二維以上的數組

void display3DArray(int (*arr)[2][5],int rows)
{
	int i,j,k;
	for(i = 0; i < rows; i++)
	{
		for(j = 0; j < 2; j++)
		{
			printf("{");
			for(k = 0; j < 5; k++)
			{
				printf("%d",arr[i][j][k]);
			}
			printf("}");
		}
	}
}

int arr3d[3][2][4] = {
		{{1, 2, 3, 4}, {5, 6, 7, 8}},
		{{9, 10, 11, 12}, {13, 14, 15, 16}},
		{{17, 18, 19, 20}, {21, 22, 23, 24}}
	};
display3DArray(arr3d,3);


 

動態分配二維數組
爲二維數組動態分配內存涉及到一些問題:
1.數組元素是否需要連續
2.數組是否是參差不齊的(whether the array is jagged)

當二維數組按如下方式分配,其內存肯定是連續的

int matrix[2][5] = {{1,2,3,4,5},{6,7,8,9,10}};


然而當使用類似malloc這樣的函數創建二維數組時,內存是如何分配的就會有變化.因爲二維數組可以被看做數組的數組,
所以沒有理由要保證"內部數組"是內存連續的,當這種在這種數組中使用數組下標時,數組的不連續本質就可以被透明地處理
(When array subscripts are used with such an array, the array’s noncontiguous nature is handled transparently)

 

分配可能不連續的內存
下面展示了一種不保證二維數組分配的內存是連續的方法,首先分配"外部數組",然後使用單獨的malloc分配每一行的內存

int rows = 2;
int columns = 5,i;
int **matrix = (int **)malloc(sizeof(int *) * rows);
for(i = 0; i < rows; i++)
{
	matrix[i] = (int *) malloc(sizeof(int) * columns);
}


實際分配取決於堆管理器和堆狀態,有可能是連續的

分配連續的內存方法
我們展示兩種爲二維數組分配連續內存的方法
第一種技術先分配"外部數組"然後爲每一行分配內存,第二種技術一次分配所有內存
第一種技術如下:

int rows = 2;
int columns = 5,i;
int **matrix = (int **)malloc(sizeof(int *) * rows);
matrix[0] = (int *)malloc(sizeof(int) * columns * rows);
for(i = 1; i < rows; i++)
{
	matrix[i] = matrix[0] + columns;
}


技術上來講,爲第一個數組所分配的內存matrix可能與數組主體不連續,但是確實爲二維數組內部的數據分配了連續的內存

第二種方法如下,一次分配所有內存

int *matrix = (int *) malloc(sizeof(int) * rows * columns);


後續代碼要引用數組時不能使用數組下標,相反需要手動計算數組下標如下:

int i,j;
for(i = 0; i < rows; i++)
{
	for(j = 0; j < columns; j++)
	{
		*(matrix + i * columns + j) = j * i;
	}
}


真實的開發限制使用這種方法,但其確實展示了一維數組與二維數組在內存上的本質.

 

指針與參差不齊的數組
參差不齊的數組是一個每行元素列數不相同的二維數組.如圖:



使用compound literals創建參差不齊的數組,compound literals是C99加進來的
A compound literal is a C construct that consists of what appears to be a cast operator followed by an initializer list enclosed in braces. An
example of a compound literal follows for both a constant integer and an array of integers.These would be used as part of a declaration:
主要意思是compound literal類似於強轉操作符後面用大括號跟着要初始化的值
(const int) {100}
(int[3]) {10, 20, 30}

int (*(arr1[])) = {
	(int[]) {0,1,2},
	(int[]) {3,4,5},
	(int[]) {6,7,8}
};
int i,j;
for(j = 0; j < 3; j++)
{
	for(i = 0; i < 3; i++)
	{
		printf("arr1[%d][%d] Address: %p, value: %d\n",j,i,&arr1[j][i],arr1[j][i]);
	}
	printf("\n");
}


創建參差不齊的數組

int (*(arr2[])) = {
	(int[]) {0,1,2,3},
	(int[]) {4,5},
	(int[]) {6,7,8}
}

int row = 0;
for(int i=0; i<4; i++)
{
	printf("layer1[%d][%d] Address: %p Value: %d\n",
	row, i, &arr2[row][i], arr2[row][i]);
}
printf("\n");
row = 1;
for(int i=0; i<2; i++)
{
	printf("layer1[%d][%d] Address: %p Value: %d\n",
	row, i, &arr2[row][i], arr2[row][i]);
}
printf("\n");
row = 2;
for(int i=0; i<3; i++)
{
	printf("layer1[%d][%d] Address: %p Value: %d\n",
	row, i, &arr2[row][i], arr2[row][i]);
}
printf("\n");


Compound literals are useful in creating jagged arrays.然而訪問這種數組很彆扭,所以作者不推薦

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