c语言指针深入学习

70 阅读3分钟

指针的进阶

1.指针就是个变量,用来存放地址,地址唯一标识一块内存空间。

2.指针的大小是固定的4/8个字节(32位平台/64位平台)。

3.指针是有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。

4.指针的运算。

字符指针

int main()
{
	char str1[] = "hello bit";
	char str2[] = "hello bit";
	const char* str3 = "hello bit";// 常量字符串
	const char* str4 = "hello bit";// 内存中只会存一份

	if (str1 == str2)
		printf("1 and 2 \n");
	else
		printf("1and2are not sasme \n");// 输出
	if (str3 == str4)
		printf("3 and 4 \n");// 输出
	else
		printf("str3 and str4 not sasme \n");

	return 0;
}

数组指针

int arr[10] = { 0 };

printf("%p",arr);
printf("%p",&arr);
  • 数组名代表首元素地址 arr[0]
  • &arr 是取出的数组的地址,数组的起始位置地址也是 arr[0]
  • 地址打印出来一样,但是意义不同,类型不同
  • 数组名是数组首元素的地址
  • 但是有两个例外
  • sizeof(数组名)数组名表示整个数组 计算的是整个数组的大小,单位是byte
  • &数组名 数组名表示整个数组,取出的是整个数组的地址
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	//int* par = &arr; 这样只存储数组的第一个元素

	// 取出数组的地址 和数组一个地址 一样元素
	// par 就是数组指针
	int (*par)[5] = &arr;
	for (int i = 0; i < 5; i++)
	{
		printf("%d \n", (*par)[i]);
		// 1 2 3 4 5
	}

	double* d[5];
	double* (*pd)[5] = &d;// pd 就是一个数组指针
	return 0;
}

// ---------------------------------------------------------------------

void print1(int (*par)[5],int r,int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf("%d", (*par +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} };
	print1(arr, 3, 5);
	return 0;
}


数组参数指针参数

在写代码的时候难免要把【数组】或者【指针】传给函数,函数的参数该如何设计?

  • 一维数组传参

ww99.png

  • 二维数组传参

89111ww.png

  • 一级指针传参
 // int * p 接收到传过来数组的第一个地址
void test(int * p,int qz)
{
	for (int i = 0; i < qz;i++)
	{
		printf("%d", *(p + i));
	}
}

// char* pc 传过来字符的第一个地址
void test1(char* pc)
{
    
}

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

	int qz = sizeof(arr) / sizeof(arr[0]);
	// p 是一级指针
	int* p = &arr;
       
	test(p, qz);

	char ch = 'w';
	char* pc = &ch;
	test1(pc);
	return 0;
}
  • 二级指针传参
void test(int ** p)
{
	**p = 20;// 改变 a
}

int main()
{
	int a = 10;
	int * pa = &a;// pa 是一级指针
	// 二级指针是专门来存放一级指针变量的地址
	int** ppa = &pa;// ppa 是二级指针

	// 把二级指针进行传参
	test(ppa);
	test(&pa);// 传一级指针变量地址没问题

	int* arr[10] = { 0 };
	test(arr);// 也是可以的

	printf("%d", a);// a = 20
	return 0;
}

11155dd.png

6666ddd.png

函数指针

函数指针:指向函数的指针!存放函数地址的指针

int Add(int x,int y)
{
	return x + y;
}

void test(char *str)
{

}
// 函数指针存储
int main()
{
	int arr[10] = { 0 };
	int(*pa)[10] = &arr;// 取出数组的地址
	// pa 是指向数组的指针 存放的是数组的地址
	// 函数指针 存放函数地址的指针
	// &函数名 取到的就是函数的地址

	// padd 就是一个函数指针变量
 	int (*padd)(int ,int) = &Add;

	void (*pt)(char*) = &test;

	return 0;
}

// -----------------------------------------------------------

int Add(int x,int y)
{
	return x + y;
}
// 函数指针调用
int main()
{
	// padd 就是一个函数指针变量
 	int (*padd)(int ,int) = &Add;
	//  &Add == Add
	// 用函数指针调用 他所指向的那个函数
	int res = (*padd)(10, 10);

	int res1 = padd(10, 10);

	// padd == (*padd)

	printf("%d", res1);

	return 0;
}

函数指针数组

函数指针数组:存放函数指针的数组。

整型指针 int*

整型指针数组 int * arr[5];

函数指针数组 int (*arr[2]) (int,int) = {Add,Sub};


int Add(int x,int y)
{
	return x + y;
}

int Sub(int x,int y)
{
	return x - y;
}

int main()
{
	// parr 就是函数指针数组
	int (*parr[2])(int, int) = { Add,Sub };

	printf("%d" , parr[0](10, 10));// 20
	
	return 0;
}

// -----------------------------------------------------------------------

int Add(int x,int y)
{
	return x + y;
}

int Sub(int x,int y)
{
	return x - y;
}

int Mul(int x, int y)
{
	return x * y;
}

int Dev(int x, int y)
{
	return x / y;
}

void muon()
{
	printf("************************************ \n");
	printf("***** 1.add ************ 2.sub ******* \n");
	printf("****** 3. mul************ 4.dev ******* \n");
	printf("************** 0.exit ************** \n");
	printf("************************************ \n");
}

int main()
{
	int input = 0;
	int x = 0; int y = 0;
	// parr 函数指针数组
	int (*parr[5])(int, int) = {NULL, Add,Sub,Mul ,Dev };
	do {
		muon();
		printf("请选择 1-4: >  \n");
		scanf("%d", &input);
		if (!input) break;
		printf("请输入计算数: >  \n");
		scanf("%d %d", &x,&y);
		int ret = (parr[input])(x,y);
		printf("%d \n", ret);
	} while (input);
	return 0;
}

ddd99988.png

回调函数

回调函数:

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

int main()
{
	int(*parr[4])(int,int) = {Add,Sub,Mul,Dev};
	int x = 10; int y = 20;
	// 回调函数
	int ret = Carc(parr[0],x,y);

	printf("%d", ret);
	return 0;
}

dd11.png

指针和数组面试题的解析

1122.png

222d.png

3dd33.png

dd111.png

dd222.png

dd111.png

w996.png

001.png 002.png

总结:数组名的意义:

  • sizeof(数组名),在这里的数组名表示整个数组,计算的整个数组的大小。
  • &数组名,这个理的数组名表示整个数组,取出整个数组的地址。
  • 初此之外所有的数组名都表示首元素的地址。
int main()
{
	int a[5] = { 1,2,3,4,5 };
	int* prt = (int*)(&a +1);
	printf("%d , %d", *(a + 1), *(prt - 1));// 2 5
	/*
		解释: 声明int a[5];数组
		(&a +1) &a相当于取出整个数组 整个数组 +1 相当于跳过了整个 a数组
		并强制类型转换 int(*)[5]

		*(a + 1) 取出 a地址 1 + 1 为 2
		*(prt - 1) 当前的指针在 数组的最后面 让它往后 -1 为 5
	*/
	return 0;
}

// ----------------------------------------------------------------------

int main()
{
	// 这里是 逗号表达式 (0,1) 有坑
	int a[3][2] = { (0,1),(2,3),(4,5) };
	// 放的是 { {1,3},{5,0},{0 ,0} }
	int* p;
	p = a[0];// 1
	printf("%d", p[0]);// 1
	return 0;
}

// ---------------------------------------------------------------------


dd12.png