Understanding and using c pointers 第四章读书笔记

本文详细介绍了C语言中数组和指针的区别,包括它们的表示法、内存分配、机器码生成、sizeof运算符的应用,以及如何动态分配和调整数组大小。特别提到了一维和多维数组的传递方式,以及参差不齐数组的概念和创建方法。
摘要由CSDN通过智能技术生成

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.然而访问这种数组很别扭,所以作者不推荐

以下是使用C语言编写的程序,可以使用函数的call by value和call by reference两种方式来计算一个数的平方: ```c #include <stdio.h> // Call by value function to find square of a number int squareByValue(int num) { return num * num; } // Call by reference function to find square of a number void squareByReference(int *numPtr) { *numPtr = (*numPtr) * (*numPtr); } int main() { int num = 5; // Call by value example int square1 = squareByValue(num); printf("Square of %d is %d (Call by value)\n", num, square1); // Call by reference example squareByReference(&num); printf("Square of %d is %d (Call by reference)\n", num, num); return 0; } ``` 该程序定义了两个函数,一个是使用call by value方式计算平方的函数,另一个是使用call by reference方式计算平方的函数。在主函数中,我们首先定义一个整数num,并将其设置为5。然后,我们使用call by value方式调用squareByValue函数来计算num的平方,并将结果存储在square1变量中。接下来,我们使用call by reference方式调用squareByReference函数来计算num的平方,这里我们传递了num的指针作为参数。最后,我们打印出计算结果。 运行该程序,输出结果为: ``` Square of 5 is 25 (Call by value) Square of 25 is 25 (Call by reference) ``` 从输出结果可以看出,两种方式都可以正确地计算num的平方。使用call by value方式时,我们将num的值作为参数传递给函数,并在函数中进行计算。使用call by reference方式时,我们将num的地址作为参数传递给函数,并在函数中使用指针来访问num的值并进行计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值