指针的进阶 - Pointer

指针的进阶

1.字符指针

字符指针常常指向的是常量字符串,存放的字符串首元素的地址,因为指向的是常量,所以常加const修饰

重点:因为数组中存放的字符串是可以修改的,所以是开辟了两块空间,两块空间都可以修改,所以数组名(数组首元素的地址)不同,const char*常量字符串不相同,常量字符串是不能修改的,所以两个地址可以指向同一个地址,即出现same / not same的不同

int main()
{
	char strArr1[] = "Hello bit";
	char strArr2[] = "Hello bit";
	const char* str1 = "Hello bit";
	const char* str2 = "Hello bit";

	// Not same
	printf("%p\n", strArr1);  
	printf("%p\n", strArr2);
	// same
	printf("%p\n", str1);
	printf("%p\n", str2);
}

2.指针数组

了解指针数组前,先区分指针数组和数组指针,可以通过类比的方法来解析,整型数组中存放的是整型(int),那么指针数组就是存放指针的数组,去除表示地址的变量对应的就是类型

// int* pf[] -> 去除pf -> int* [] ->对应类型就是指针数组

pf先和[ ]结合,表明这是一个指针数组,其中存放的是int*类型

// 利用指针数组实现二维数组
// arr[i]指向每一行,[j]遍历每行的每个元素
int main()
{
	int arr1[] = { 1, 2, 3, 4, 5 };
	int arr2[] = { 2, 3, 4, 5, 6 };
	int arr3[] = { 3, 4, 5, 6, 7 };
// arr[] -> 类型是int*
	int* arr[] = { arr1, arr2, arr3 };

	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < sizeof(arr1) / sizeof(int); ++j)
		{
			//printf("%d  ", arr[i][j]);
			printf("%d ", *((arr[i]) + j));
		}

		printf("\n");
	}
}

3.数组指针

区别:arr的含义:sizeof(arr) / &arr - 此时arr指的是整个给数组的地址
用一级指针接收的时候,arr表示是一个首元素的地址,当为&arr是则表示整个数组的地址,不能再用一级指针接收,要用数组指针接收(数组指针的[ ]表示数组中的元素个数)

区分:int arr[] = {0}; int* pf = arr; int(*pf)[] = &arr;

数组指针:int(*pf)[10]
[ ]的优先级大于*,所以要加()保证和p先结合
p先和
结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针

重点:arr的特殊含义:1. 当sizeof时表示是整个数组的大小 / 2.当为&arr是整个数组的数组名

重点:区分指针数组和数组指针,指针数组的重点是数组,所以pf先和[ ]结合,数组指针的重点是指针,所以pf要先和*结合

// 简易应用
int main()
{
	int arr[5] = { 1, 2, 3, 4, 5 };
	int(*pf)[5] = &arr;

	for (int i = 0; i < 5; ++i)
	{
		// *&arr -> *&arr[i]
		//printf("%d ", (*pf)[i]);
		// *(*&arr + i)
		printf("%d ", *((*pf + i)));
	}
}

实际应用:利用数组指针实现二维数组

  1. 二维数组传arr数组名传的是首元素的地址,对于二维数组来说首元素的地址就是首行数组的地址
  2. 等价于一个一维数组int arr的&arr,所以可以用数组指针来接收
  3. 接收后p就是首行数组的地址 -> (p + i)就是对应三行数组分别的地址 - *解引用取出元素

原理:取二维数组的数组名,就是首元素的地址,而二维数组首元素的地址正好是一整行数组的地址,即相当于一维数组的&arr,正好可以用数组指针来接收([ ]表示二维数组每一行的元素个数)

// 数组指针的应用
void Print(int(*p)[5], int r, int c)
{
	for (int i = 0; i < r; ++i)
	{
		for (int j = 0; j < c; ++j)
		{
			//printf("%d ", *(*(p + i) + j));
			printf("%d ", (*(p + i))[j]);
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][5] = { 1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7 };
	// 二维数组传arr数组名传的是首元素的地址
	// 对于二维数组来说首元素的地址就是首行数组的地址
	// 等价于一个一维数组int arr的&arr,所以可以用数组指针来接收
	// 接收后p就是首行数组的地址 - (p + i)就是对应三行数组分别的地址 
	// *解引用取出元素
	Print(arr, 3, 5);
	// 传二维数组的数组名(首元素地址)
}

4.开辟一个二维数组(重点)

LeetCode中常用二维数组解决一些问题,在C++中可以采用vector<vector<>>的方式,在C语言中则需要malloc动态开辟(事先并不知道需要开辟多上空间)

类比开辟一维数组的方式,开辟一个二维数组

概念区分:一维指针数组中存放的是变量的地址,或者指针(存放变量的地址)int* pf = &a; -> pf是可以存放的,二维指针数组中存放的是一级指针的地址int *pf = &a -> &f是可以存放的,在动态开辟malloc时(误区:malloc开辟的不是指针数组,因为指针数组存放地址,int*并不是表示地址)

开辟一个一维数组 int* pi = (int*)malloc(sizeof(int) * 10);可以理解为pi指向了首元素的地址,因为数组空间连续,所以可以靠*(pi + i)赋值或获取数据,这就是一维数组的开辟

误区:二维数组的开辟不能理解成开辟了一个二维指针数组,内部存放一维数组的地址,因为int*无法表示地址

正确理解开辟一个二维数组:int** ppi = (int**)malloc(sizeof(int*) * 10);,ppi指向的元素是一级指针的地址,因为数组是连续的,所以ppi以列方向向下运动,指向第一个一级指针的地址,ppi[i]对应的是每一行,每一行都可以开辟一维数组,所以每一行 ppi[i] = (int*)malloc(sizeof(int) * i);,因为ppi指向第一个一级指针的地址,所以[ ]解引用就可以遍历每个数组的起始,再通过[ j ]遍历每行数组的元素。(注:不能理解成二维是开辟了一个二维指针数组)

int main()
{
   // 开辟一个一维的数组
   int* pi = (int*)malloc(sizeof(int) * 10);
   // pi指向元素为地址
   // 数组中存放的是数据(int)

   // 开辟一个二维数组
   int** ppi = (int**)malloc(sizeof(int*) * 10);
   // ppi指向元素为一级指针的地址
   // 数组中存放的是一级指针(int*)
   for(int i = 0; i < 10; ++i)
   {
      ppi[i] = (int*)malloc(sizeof(int) * i);
// 一维数组(int**)中开辟的指针指向另一个一维数组(int*),实现开辟一个二维数组
   }
	
	// 验证程序
	int arr[] = { 1, 2, 3, 4, 5 };
	// 二维数组的第二行插入一个数组
	ppi[2] = arr;
	pi[2] = 10;

	for (int j = 0; j < 5; ++j)
	{
		printf("%d ", ppi[2][j]);
	}
	printf("\n");
	printf("%d ", pi[2]);
}

5.本章代码汇总

总结:字符指针 / 指针数组(数组) / 数组指针(指针) / 一二维数组的开辟

// 指针数组 - int* []
void Pointer_array()
{
	int arr1[] = { 1, 2, 3, 4, 5 };
	int arr2[] = { 2, 3, 4, 5, 6 };
	int arr3[] = { 3, 4, 5, 6, 7 };

	int* arr[] = { arr1, arr2, arr3 };

	for (int i = 0; i < 3; ++i)
	{
		for (int j = 0; j < sizeof(arr1) / sizeof(int); ++j)
		{
			//printf("%d  ", arr[i][j]);
			printf("%d ", *((arr[i]) + j));
		}

		printf("\n");
	}
}

void Print(int(*p)[5], int r, int c)
{
	for (int i = 0; i < r; ++i)
	{
		for (int j = 0; j < c; ++j)
		{
			//printf("%d ", *(*(p + i) + j));
			printf("%d ", (*(p + i))[j]);
		}
		printf("\n");
	}
}

/int main()
{
	int arr[3][5] = { 1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7 };
	// 二维数组传arr数组名传的是首元素的地址,对于二维数组来说首元素的地址就是首行数组的地址
	// 等价于一个一维数组int arr的&arr,所以可以用数组指针来接收
	// 接收后p就是首行数组的地址 - (p + i)就是对应三行数组分别的地址 - *解引用取出元素
	Print(arr, 3, 5);
}

//数组指针 - int(*) []
void Array_pointer()
{	
	int arr[5] = { 1, 2, 3, 4, 5 };
	int(*pf)[5] = &arr;
	
	for (int i = 0; i < 5; ++i)
	{
		// *&arr
		//printf("%d ", (*pf)[i]);
		printf("%d ", *((*pf + i)));
	}
}

void Open_up()
{
	// 开辟一个一维的数组
	int* pi = (int*)malloc(sizeof(int) * 10);
	// pi指向元素为地址
	// 数组中存放的是数据(int)

	// 开辟一个二维数组
	// 一级指针数组能存放变量的地址&a,二级指针数组能存放一级指针的地址&pf
	// 开辟一个二级指针数组,数组中存放一级指针int*
	int** ppi = (int**)malloc(sizeof(int*) * 10);
	// ppi指向元素为一级指针的地址
	// 数组中存放的是一级指针(int*)
	for (int i = 0; i < 10; ++i)
	{
		ppi[i] = (int*)malloc(sizeof(int) * i);
// 一维数组(int**)中开辟的指针指向另一个一维数组(int*),实现开辟一个二维数组
	}

	int arr[] = { 1, 2, 3, 4, 5 };
	ppi[2] = arr;
	pi[2] = 10;

	for (int j = 0; j < 5; ++j)
	{
		printf("%d ", ppi[2][j]);
	}

	printf("\n");
	printf("%d ", pi[2]);
}

End!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值