指针(四)

一.二级指针.

#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	int i = 0;
	int* pi = &i;
	int** pa = π
	

	return 0;
}

指针(四)_第1张图片

二级指针解读:
int a=10;
int* p=&a;
int* *pi=&p;

int*:说明指针指向的对象类型是int*         

*pi:说明pi是指针变量         

注意:指针变量也是变量!!!

⼆级指针的运算:

一级指针时的解引用:

int a =10;
int b =20;
int* pi=&a;
printf("%d",*pi);//等价为a

二级指针的解引用:

int a = 10;
int* p=&a;
int* *pi=&p;
**pi=30;//等价为a=30

二.指针数组.

类比:

整型数组为存放整型的数组,字符数组是存放字符的数组,那么指针数组即为存放指针的数组

画图对比:

指针(四)_第2张图片

指针(四)_第3张图片

发现:指针数组的每个元素都是⽤来存放地址(指针)的,指针数组的每个元素是地址,⼜可以指向⼀块区域

例题一:

指针模拟二维数组

编程实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	int arr1[5] = { 1,2,3,4,5 };
	int arr2[5] = { 2,3,4,5,6 };
	int arr3[5] = { 3,4,5,6,7 };
	int* pi[3] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
			printf("%d ", pi[i][j]);
		}
		printf("\n");
	}
	return 0;
}

结果:

指针(四)_第4张图片

虽然这个代码模仿了二维数组,但实际上并⾮完全是⼆维数组,因为每⼀⾏并⾮是连续的

三.数组指针变量.

指针数组是⼀种数组,数组中存放的是地址(指针),那么数组指针变量又是啥?

还是类比:

整形指针变量: int * pi; 存放的是整形变量的地址,能够指向整形数据的指针。
浮点型指针变量:  float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。
数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量

对比指针数组和数组指针:

int* pi[5];//数组指针
int(*pi)[5];//指针数组

why???

这时候就要好好分析结和性了,我们知道[]的优先级要⾼于*号的,对于一式,pi与[]先结合,所以是数组指针,对于二式,pi先和*结合,说明p是⼀个指针变量,然后指着指向的是⼀个⼤⼩为5个整型的数组。所以p是⼀个指针,指向⼀个数组,叫数组指针。

注意必须加上()来保证p先和*结合。

对数组指针初始化:

int(*pi)[]=&arr;//&arr是整个数组
int (*p) [ 10 ] = &arr;
[10]: p 指向数组的元素个数
(*p):p 是数组指针变量名
int:p 指向的数组的元素类型

四.字符指针变量

char* pc="hello world.";

请问这个字符指针是将整个字符串放入指针变量里面了吗?

运行后会发现,实际上还是将第一个字符(可以把字符串想象为一个字符数组,但是这个数组是不能修改的)放进指针变量了

看到这,我们来看一道《剑指offer》中的题目:

例题二:

请判断下面的代码的打印结果:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
int main()
{
	char arr1[] = "hello world.";
	char arr2[] = "hello world.";
	char* pc1 = "hello world.";
	char* pc2 = "hello world.";
	if (arr1 == arr2)
	{
		printf("arr1 and arr2 are same\n");
	}
	else 
	{
		printf("arr1 are arr2 are not same\n");
	}
	if (pc1 == pc2)
	{
		printf("pc1 and pc2 are same\n");
	}
	else
	{
		printf("pc1 and pc2 are not same\n");
	}

	return 0;
}

结果如下:

指针(四)_第5张图片

哈哈哈,猜对了吗?奥,不,答对了吗?

下面解释为啥?

C/C++会把常量字符串存储到单独的⼀个内存区域, 当⼏个指针指向同⼀个字符串的时候,他们实际会指向同⼀块内存。但是⽤相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以arr1 和arr2不同,pc1 和pc2相同

五.解释二维数组的本质.

当我们还没学指针是我们在函数中传数组时,我们会写以下代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
void Print(int arr[3][3], int m, int n)
{
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < n; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
		

}

int main()
{
	int arr[3][3] = { {1,2,3},{2,3,4},{3,4,5} };
	Print(arr, 3, 3);
	return 0;

}

其实,二维数组也可以看成每个元素是⼀个⼀维数组,那么⼆维数组的⾸元素就是第⼀⾏,是个⼀维数组

根据数组名是数组⾸元素的地址这个规则,⼆维数组的数组名表⽰的就是第⼀⾏的地址,是⼀
维数组的地址, 那么第⼀⾏的⼀维数组的类型就是 int [3] ,所以第⼀⾏的地址的类 型就是数组指针类型 int(*)[3] 。那就意味着 ⼆维数组传参本质上也是传递了地址 ,传递的是第⼀ ⾏这个⼀维数组的地址,那么形参也是可以写成 指针形 式的

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
void Print(int(* arr)[3], int m, int n)
{
	for (int i = 0; i < m; i++)
	{
		for (int j = 0; j < n; j++)
		{
			printf("%d ", *((*arr+i)+j));
			//*arr+i代表行的解引用
			//*(()+j)代表列的解引用
		}
		printf("\n");
	}
		

}
int main()
{
	int arr[3][3] = { {1,2,3},{2,3,4},{3,4,5} };
	Print(arr, 3, 3);
	return 0;

}
总结:⼆维数组传参,形参的部分可以写成数组,也可以写成指针形式
好处:方便初学者在未学指针时简单明白二维数组传参

六.函数指针变量

还是类比即可:

得出:函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的

那么问题来了,函数有地址吗?

试试就知道了,看代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include 
void test()
{
	//printf("haha\n");
}
int main()
{
	test();
	printf("%p\n", &test);
	printf("%p\n", test);

	return 0;
}

结果:

指针(四)_第6张图片

发现,确实打印出来了地址,所以函数是有地址的,函数名就是函数的地址,当然也可以通过 &函数名 的⽅式获得函数的地址。

int (*pf) (int x, int y)

( int x, int y): pf指向函数的参数类型和个数的交代
*pf:函数指针变量名
int:pf3 指向函数的返回类型
int Add(int x, int y)
{
 return x+y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y写上或者省略都是可以的

六.typedef关键字

typedef 是⽤来类型重命名的,可以将复杂的类型,简单化

typedef unsigned int uint;
//将unsigned int 重命名为uint
typedef int* prt_i;
//将int*指针重命名为prt_i

但是对于数组指针和函数指针稍微有点区别:

typedef void(*Add)(int, int);
typedef int(*arr)[5];

新的类型名必须在*的右边

你可能感兴趣的:(c++,算法,数据结构)