深入理解指针

发布于:2023-01-15 ⋅ 阅读:(596) ⋅ 点赞:(0)

《C语言入门》

                热情篇  — — 热情像一对翅膀,带我飞过山岭。

目录

 1.指针数组

 2.数组指针

 3.字符指针

4. 数组传参和指针传参

 5.函数指针

 6.函数指针数组

 7.回调函数


 1.指针数组

是数组,元素为指针类型的数组。数组中的每个元素都是一个指针。

    int arr[10] = { 0 };//整型数组
	char arr1[5] = { 0 };//字符数组
	//指针数组
	int* arr3[5] = { 0 };//整型指针数组
	char* arr4[5] = { 0 };//字符指针数组

 小测试:

 2.数组指针

是指针,指向数组的指针。

int (*p)[10];

注意:和指针数组区分开。[]的优先级要高于*号,(*p)保证p先和*结合,说明p是一个指针变量。p是一个指针,指向一个数组,叫数组指针。


应用: 数组指针指向的是数组,所以数组指针中存放的是数组的地址。
int main()
{
	int arr[3][3] = { 1,2,3,4,5,6,7,8,9};
	print1(arr, 3, 3);
	printf("--------------------------\n");
	print12(arr, 3, 3);
	return 0;
}
要求:编写两个函数,用不同的形参接收。
方法1:
void print1(int arr[3][3], int row, int col) {
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
			printf("\n");
	}
}

方法2:二维数组可以看成元素是一维数组的一维数组,arr是数组首元素的地址,相当于第一行的地址,是第一个(一维数组)元素的地址,可以用数组指针来接收。

void print2(int(*arr)[3], int row, int col) {
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

结果:

 3.字符指针

字符指针: char* ;

#include <stdio.h>
 int main()
{
	const char* p = "hello";
	printf("%s\n", p);
	return 0;
}

字符指针变量存放的是常量字符串的首字符地址。

应用:下面输出输出的结果是什么?

#include <stdio.h>
int main()
{
	char str1[] = "hello bit.";
	char str2[] = "hello bit.";
	const char* str3 = "hello bit.";
	const char* str4 = "hello bit.";
	if (str1 == str2)
		printf("str1 和 str2 相同\n");
	else
		printf("str1 和 str2 不相同\n");

	if (str3 == str4)
		printf("str3 和 str4 相同\n");
	else
		printf("str3 和 str4 不相同\n");

	return 0;
}

 分析:1.相同的常量字符串初始化不同的数组,存放字符串地址不同。所以str1和str2不同。

2.C/C++会把常量字符串存储到单独的一个内存区域,当 几个指针指向同一个字符串的时候,他们指向的是同一块内存。反之,如果指向的是不同的字符串,它们指向的内存块就不同。所以字符指针str1和str2指向同一块空间,字符的首地址也就相同。既:str3和str4相同。

4. 数组传参和指针传参

一维数组传参:形参可以是数组也可以是指针

int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
}

根据上面的代码设计test1、text2函数。

void test(int arr[]){} : 形参是数组
void test(int arr[10]){}:形参是数组,10可以省略不写
void test(int *arr){}形参是指针
void test2(int *arr[20]){}形参是数组,指针数组
void test2(int **arr){}形参是指针,二级指针

二维数组传参:形参可以是数组也可以是指针。

int main()
{
 int arr[3][5] = {0};
 test(arr);
}

设计个text函数:

void test(int arr[3][5]){}:形参是数组
void test(int arr[][5]){}:形参是数组,行可以省略,列不可以省略。
void test(int (*arr)[5]){}:形参是指针,数组指针。

一级指针传参:一级指针传参,一级指针接收。

int main()
{
 int arr[10] = {1,2,3,4,5,6,7,8,9};
 int *p = arr;
 print(p);
 return 0; }

设计print函数:void print(int *p){},形参是指针。

当形参是指针时,除了上述代码的传参方式,函数还能接收:

数组名(数组首元素地址)、例如:int a = 100; 可以传&a,一级指针。


二级指针传参:二级指针传参,二级指针接收。

当一个函数的参数为二级指针的时,可以接收二级指针,&(一级指针)、指针数组的数组名。

 5.函数指针

函数指针,是指针。指向的是一个函数

void (*pmax)();
pmax 先和 * 结合,说明 pmax 是指针, 指向的是一个函数 ,指向的函数无参 数,返回值类型为void

代码1:

( * ( void (*a)() )0 )();
分析:( 类型 ) 代表 强制类型转换,  void(*a)(),a先和*结合 说明a 是指针, 指向的是一个函数 ,指向的函数无参数,返回值类型为void。( void(*a)() ) 对0进行强制类型转换,把0当做一个函数的地址。
答:把0转换成一个 void(*a)() 类型的函数指针,调用0地址的处的函数。
代码2:
void (*a(int , void(*)(int)))(int);

分析:代码2是一次函数声明,声明的函数是a。a的第一个参数是int类型的,第二个参数是函数指针类型的,这个函数指针指向的函数参数是int,返回值类型是void。a函数的返回值类型也是函数指针类型,函数指针指向的函数参数是int,返回值类型是void。

画图分析:

代码2简化:

typedef是存储类关键字,定义一种类型的别名 。代码2可以简化如下:

typedef void(*a)(int);
a signal(int, a);

 6.函数指针数组

函数指针数组,是数组。数组中存放的是函数的地址。

int (*parr1[10])();

parr1 先和 [] 结合,说明 parr1是数组,数组的元素是int (*)() 类型的函数指针。

应用:编写个计算器

#include <stdio.h>
void menu()
{
	printf("|-------小张计算器------|\n");
	printf("|***1.sum ----- 2.sub***|\n");
	printf("|***3.mult----- 4.div***|\n");
	printf("|***5. << ----- 6. >>***|\n");
	printf("|***7. |  ----- 8. & ***|\n");
	printf("|***9. ^  ----- 0.exit**|\n");
	printf("|--------匠心品牌-------|\n");
}

//加法
int sum(int x, int y)
{
	return x + y;
}
//减法
int sub(int x, int y)
{
	return x - y;
}
//乘法
int mult(int x,int y)
{
	return x * y;
}
//除法
int dive(int x, int y)
{
	return x / y;
}
//左移
int left_move(int x, int y)
{
	return x << y;
}
//右移
int right_move(int x, int y)
{
	return x >> y;
}
//按位|
int a(int x, int y)
{
	return x | y;
}
//按位或
int b(int x, int y)
{
	return x & y;
}
//按位异或
int c(int x,int y)
{
	return x ^ y;
}
int main()
{
	int input = 0;
	int x = 0;
	int y = 0;
	int ret = 0;
	int (*arr[12])(int, int) = {0,sum,sub,mult,dive,left_move,right_move,a,b,c};
		do{
			
			menu();
			printf("请输入选项:>");
			scanf("%d", &input);
		if (input == 0)
		{
			printf("退出计算器!");
			break;
		}
		
		if (input > 0 && input < 10)
		{
			printf("请输入两操作数:>");
			scanf("%d %d",&x,&y);
			int ret = arr[input](x, y);			
			printf("结果是:%d\n",ret);
		}
		else
		{
			printf("选项错误!\n");
		}
		
	} while (input);
	return 0;
}

 这样通过函数指针数组的每个元素来调用对应函数的过程可以叫做转移表。

 7.回调函数

回调函数:通过函数指针调用的函数。

函数的指针(地址)作为参数传递给另一个 函数 ,当这个指针被用来调用其所指向的函数时,就是回调函数。 回调函数不是由该函数的实现方直接调用 ,而是由另外的一方调用的,用于对该事件或条件进 行响应。


qsort函数:

void qsort (void* base, size_t num, size_t size,int (*compar) (const void*,const void*));

参数1:待排序数据的起始地址。
参数2:元素的个数。
参数3:元素的大小(单位字节)
参数4:函数指针,参数1:void* (可以接收任意类型的地址),参数2:void*。返回值类型为int。
返回值值类型:void。
小练习:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void print(int *arr,int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ",arr[i]);
	}
	printf("\n");
}
//比较int类型的数据
int cmp_int(const void* e1, const void* e2)
{
	return *(int*)e1 - *(int*)e2;
}
int main()
{
	int arr[10] = { 2,4,6,8,10,1,3,5,7,9 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	//使用库函数
	//int类型
	qsort(arr, sz, sizeof(arr[0]), cmp_int);
	printf("\n-------------------整型-----------------\n");
	print(arr, sz);
	return 0;
}


网站公告

今日签到

点亮在社区的每一天
去签到