深入理解指针(三)

深入理解指针(三)

目录

深入理解指针(三)

1.字符指针变量

2.数组指针

2.1数组指针是什么?

​编辑

2.2数组指针如何初始化

2.3数组指针简单应用

3.二维数组传参本质

3.1用数组的形式接收

3.2运用指针数组来接收形参

4.函数指针变量

4.1什么是函数指针变量

4.2函数指针的使用

4.3涉及函数指针的两段代码

5.typedef关键字使用

6.函数指针数组

7.数组指针数组的简单应用


1.字符指针变量

//为了严谨,加上const,让其不能进行修改。
const char* p = "abcdef";//把他想象成一个字符数组,指针向首个元素的地址。
//这种称为常量字符数组,是不可以修改里面的内容的
printf("%c ", "abcdef"[2]);
//p[1] = 'a'; //这里是修改不了的

《剑指offer》中收录了⼀道和字符串相关的笔试题,我们⼀起来学习⼀下:

char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
if (str1 == str2)
	printf("str1 and str2 are same\n");
else
	printf("str1 and str2 are not same\n");

if (str3 == str4)
	printf("str3 and str4 are same\n");
else
	printf("str1 and str2 are not same\n");

深入理解指针(三)_第1张图片

这题就考到字符指针变量的用法

深入理解指针(三)_第2张图片深入理解指针(三)_第3张图片

1.首先看str1他是一个字符数组,存放内容是字符串,str2也是,两个是独立的数组,地址是没有任何关系的。

2.其次看str3和str4,他们都是字符指针数组都指向“hello bit”这个字符串的首元素的地址,所以他们地址是一样的。

2.数组指针

2.1数组指针是什么?

之前我们学习了指针数组,指针数组是⼀种数组,数组中存放的是地址(指针)。 数组指针变量是指针变量?还是数组? 答案是:指针变量。

我们已经熟悉:

• 整形指针变量: int * pint; 存放的是整形变量的地址,能够指向整形数据的指针。

• 浮点型指针变量: float * pf; 存放浮点型变量的地址,能够指向浮点型数据的指针。

那数组指针变量应该是:存放的应该是数组的地址,能够指向数组的指针变量。

深入理解指针(三)_第4张图片

//例子
int n = 100;
int* pn = &n;

char ch = 'w';
char* pc = &ch;

float f = 3.14f;
float* pf = &f;

int arr[10] = { 0 };
//指向数组的一个指针
int (*parr)[10] = &arr;//取出的事整个数组的地址,不应该是数组的首元素地址嘛
//两边要同时符合 int (*)[] =int (*)[]
//数组指针
int* p1[10];//p1是指针数组,数组10个元素,每个元素的类型是int * ,所以p1是指针数组
int(*p2)[10];//p2是数组指针,指针指向的是数组,数组有10个元素,每个元素类型是int
int arr[10] = { 0 };
int* pa = arr;//这个是指向数组首元素的地址
//p+1 //是数组的下一个元素
printf("pa    = %p\n", pa);
printf("pa + 1=%p\n", pa + 1);

int(*p)[10] = &arr; //这个指向整个数组
//p +1 //是直接跳过这个数组 
printf("p     =%p \n", p);
printf("p + 1 =%p\n ", p + 1);

深入理解指针(三)_第5张图片

2.2数组指针如何初始化

数组指针变量是⽤来存放数组地址的其实获取数组地址就是初始化了,那怎么获得数组的地址呢?就是我们之前学习的 &数组名 。

int arr[10] = { 0 };
//&arr得到的就是数组的地址
int(*p)[10] = &arr;

数组指针类型解析:

深入理解指针(三)_第6张图片

2.3数组指针简单应用

//数组指针的简单应用(实际场景不应在此应用过于麻烦)
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int (*p)[10] = &arr;
int i = 0;
for (i = 0; i < 10; i++)
{
    //推理
	//p = &arr  -> *p = *&arr=arr
	//-> *p = arr ->(*p)[i] = arr[i];
	printf("%d  ", (*p)[i]);
}

3.二维数组传参本质

首先要明白

1.二维数组的首元素其实是第一行,

2.而二维数组的每一行代表一维数组

3.所以说二维数组的首元素地址就是第一行的地址。

深入理解指针(三)_第7张图片

二维数组接收形参的方式

3.1用数组的形式接收

void swap(int arr[3][5],int r ,int c )//这里行可以省略但是列不行
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
}

int arr[3][5] = { {1,2,3},{4,5,6},{7,8,9} };
swap(arr, 3, 5); 

3.2运用指针数组来接收形参

        因为二维数组每一行都代表代表一个一维数组,用数组指针指向每一行(一维数组),并且二维数组传参传的是首元素的地址,而首元素的是一维数组,所以要用到指向数组的指针,即数组指针。

参考一下模拟二维数组

//用数组指针来接收
void swap(int (*p)[5], int r, int c)//这里行可以省略但是列不行
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			//*(*(p+i)+j) = *(p[i]+j)  //这里跟指针数组实现模拟二维数组那里内容是一样的写法

			printf("%d ", *(*(p+i)+j));//指针的解引用
		}
		printf("\n");
	}
}
int arr[3][5] = { {1,2,3},{4,5,6},{7,8,9} };
swap(arr, 3, 5); 

4.函数指针变量

4.1什么是函数指针变量


 函数指针变量应该是⽤来存放函数地址的,未来通过地址能够调⽤函数的
其实函数也是有地址的,不信下面可以测试一下

int ax()
{
	return 0;
}
printf("%p \n", &ax);
printf("%p \n", ax);

可以看出来函数的数组名和取地址都是函数的地址,那么有地址就可以用到指针了。

深入理解指针(三)_第8张图片

4.2函数指针的使用

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

int (*p)(int,int) = &ax;//p就是函数指针变量
//int (*)(int, int) //这个是函数指针类型
int ret = ax(3, 6);
printf("%d \n", ret);

//函数指针调用函数
int ret2 = (*p)(3, 6);
printf("%d \n", ret2);

//可以去掉*号,因为ax也是地址,而p存放ax的地址。
int ret3 = (*p)(3, 6);
printf("%d \n", ret3);

int (*pf)(int, int) = ax;
int ret4 = (*p)(3, 6);
printf("%d \n", ret4);

char* ax2(char x)
{
	return 0;
}
//当函数是指针函数时
char* (*pf2)(char) = ax2;

4.3涉及函数指针的两段代码

//涉及函数指针的两段代码
(*(void (*)())0)();
//上述代码是一次函数调用
//1.(void (*)()) 是一个函数指针类型,函数参数为空返回类型是void
//2..(*(void (*)())0)把0整数值,强制转换为一个函数的地址,
//3.去调用0地址处的函数。

void (* signal(int, void(x)(int)))(int);
//这是一个函数声明
//1.singal是一个函数,参数为(int, void(x)(int))第一个是int,
// 第二个是函数指针类型,该指针指向函数参数是int,返回类型是void
//2.void (* signal(int, void(x)(int)))(int),去掉里面的函数,得到
//void (*)(int)  其实也是一个函数指针类型
// 该指针指向函数参数是int,返回类型是void

5.typedef关键字使用

关键字typedef用法
typedef 是⽤来类型重命名的,可以将复杂的类型,简单化。
⽐如, unsigned int 写起来不⽅便,如果能写成 uint 就⽅便多了,那么我们可以使⽤

typedef unsigned int ax;

写一个数组指针

typedef int(* ax2)[10];

写一个函数指针

typedef void (*ax3)(int, int);

6.函数指针数组


当要实现一个指针多个函数的地址时候就用到函数指针数组。

#include
int arr(int x, int y)
{
	return x + y;
}
int arr2(int x, int y)
{
	return x + y;
}
int arr3(int x, int y)
{
	return x + y;
}
int arr4(int x, int y)
{
	return x + y;
}

int main()
{
//当要实现一个指针多个函数的地址时候就用到函数指针数组
int (* pa[4])(int, int) = { arr,arr2,arr3,arr4 };  //写法只是加上[]即可,其实还是函数指针类型
for (int i = 0; i < 4; i++)
{
	printf("%d \n", pa[i](2,3));
}
return 0;
}

7.数组指针数组的简单应用

举例:计算器的⼀般实现:

#include 
int add(int a, int b)
{
 return a + b;
}
int sub(int a, int b)
{
 return a - b;
}
int mul(int a, int b)
{
 return a * b;
}
int div(int a, int b)
{
 return a / b;
}
int main()
{
 int x, y;
 int input = 1;
 int ret = 0;
 do
 {
 printf("*************************\n");
 printf(" 1:add 2:sub \n");
 printf(" 3:mul 4:div \n");
 printf(" 0:exit \n");
 printf("*************************\n");
 printf("请选择:");
 scanf("%d", &input);
 switch (input)
 {
 case 1:
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = add(x, y);
 printf("ret = %d\n", ret);
 break;
 case 2:
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = sub(x, y);
 printf("ret = %d\n", ret);
 break;
 case 3:
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = mul(x, y);
 printf("ret = %d\n", ret);
 break;
 case 4:
 printf("输⼊操作数:");
 scanf("%d %d", &x, &y);
 ret = div(x, y);
 printf("ret = %d\n", ret);
 break;
 case 0:
 printf("退出程序\n");
 break;
 default:
 printf("选择错误\n");
 break;
 }
 } while (input);
 return 0;
}

使用函数指针数组来实现

	void men()
{
	printf("*************************\n");
	printf(" 1:add 2:sub \n");
	printf(" 3:mul 4:div \n");
	printf(" 0:exit \n");
	printf("*************************\n");
}
int arr(int x, int y)
{
	return x + y;
}
int arr2(int x, int y)
{
	return x - y;
}
int arr3(int x, int y)
{
	return x * y;
}
int arr4(int x, int y)
{
	return x / y;
}

int main()
{
    int x = 0;
	int (*pa[])(int, int) = { NULL,arr,arr2,arr3,arr4 };//尽量让其函数对应菜单功能,null用来顶替0位置。
	int count = 0, count2 = 0;
	do
	{
		men();
	printf("请选择\n");
	scanf("%d", &x);

	if (x == 0)
	{
		printf("退出计算器\n");
	}
	else if (x >= 1 && x <= 4)
	{
		
		printf("请输入操作数:  \n");
		scanf("%d %d", &count, &count2);
		int ret = pa[x](count, count2);
		printf("%d \n", ret);
	}
	else
	{
		printf("输入错误请重新选择 \n");
	}
	} while (x);
	
	return 0;
}

两者对比可以看出,函数指针的实现更简写了。

你可能感兴趣的:(c语言,算法)