深入理解指针(2)

目录:

1. 字符指针变量

2. 数组指针变量

3. ⼆维数组传参的本质

4. 函数指针变量

5. 函数指针数组

6. 转移表

1.字符指针变量

在指针的类型中我们知道有⼀种指针类型为字符指针 char* 

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

 这里我们有两种指针读取字符串的方式,但是第二种p2指向的是常量字符串不能修改,所以我们可以在前面添加一个const

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

下面我们来看一道题

#define _CRT_SECURE_NO_WARNINGS
#include 
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 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("str3 and str4 are not same\n"); 
	return 0;
}

 那结果是什么呢?

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

我们创建地str1数组存储hello bit,先创建一块空间 str2数组存储hello bit又创建一块空间 ,但是这两块空间是截然不同的两块空间,而str3和str4都是指针变量,指向的是常量字符串,而常量字符串不可以改变,所以没有必要创建两份,所以创建一份还可以节省空间。

另外str1,str2,str3,str4都是创建在栈区的,而指向的hellobit.是在静态区的

2.数组指针变量

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

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

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

#define _CRT_SECURE_NO_WARNINGS
#include 
int main()
{
	int a = 10;
	int* p1 = &a;//整形指针
	int b = 'w';
	char* p2 = &b;//字符指针
	int arr[10] = { 0 };
	int(*p)[10] = &arr;//取出的是整个数组的地址
	arr;//数组首元素的地址
	int* p1[10];//p1是指针数组-存放指针的数组
	int(*p2)[10];//p2是指针变量-指向的是数组,数组指针
	return 0;
}

2.2 数组指针变量怎么初始化

如果要存放个数组的地址,就得存放在数组指针变量中

如果我们要用数组指针来访问数组就显得很麻烦,不建议使用

#define _CRT_SECURE_NO_WARNINGS
#include 
int main()
{
	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++)
	{
		printf("%d ", (*p)[i]);
	}
	return 0;
}

3. ⼆维数组传参的本质

我们很容易能利用for循环打印二维数组

#define _CRT_SECURE_NO_WARNINGS
#include 
print(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 main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);
	return 0;
}

但我们有一个问题,二位数组的数组名也是首元素的地址,但是究竟是谁的地址呢?

首元素就是第一行,第一行的地址就是首元素的地址,第一行的地址就是数组指针的地址

#define _CRT_SECURE_NO_WARNINGS
#include 
void print(int (*arr)[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 main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);
	return 0;
}

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

4. 函数指针变量

4.1 函数指针变量的创建

数组指针是指向数组的,那么函数指针是指向函数的,那么函数有没有地址呢?

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

我们发现函数名的地址和&函数名是一样的。

#define _CRT_SECURE_NO_WARNINGS
#include 
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*pf)(int,int) = &Add;//pf是专门用来存放函数地址的,pf是函数指针变量
	printf("%p\n", &Add);
	printf("%p", Add);
	return 0;
}

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

4.2 函数指针变量的使⽤ 

#define _CRT_SECURE_NO_WARNINGS
#include 
int Add(int x, int y)
{
	return x + y;
}

int main()
{
	int (*pf)(int,int) = &Add;//pf是专门用来存放函数地址的,pf是函数指针变量
	int c = Add(3, 5);
	printf("%d\n", c);
	int d = (*pf)(4, 5);
	printf("%d\n", d);
	int e = pf(5, 5);
	printf("%d\n", e);
	return 0;
}

以上这几种方法都是可以成功调用函数的

我们下面来看两个有趣的代码:

(*(void (*)())0)();

这里void(*)--函数指针类型

(void(*))--强制类型转换

(void(*)0 --将0强制转化为函数指针类型

这就意味着0处存放着无参,函数返回类型为void的类型

void (*signal(int , void(*)(int)))(int)

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

4.3.1 typedef关键字 

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

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

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

深入理解指针(2)_第10张图片 深入理解指针(2)_第11张图片

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

5. 函数指针数组 

把函数的地址存到⼀个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

#define _CRT_SECURE_NO_WARNINGS
#include 
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 Div(int x, int y)
{
	return x / y;
}
int main()
{
	int(*pf1)(int,int) = Add;
	int(*pf2[4])(int, int) = { Add,Sub,Mul,Div };//这里pf2就是函数指针数组
	int i = 0;
	for (i = 0;i < 4;i++)
	{
		int ret = (*pf2[i])(8, 4);
		printf("%d ", ret);
	}
	return 0;
}

完!!!

你可能感兴趣的:(数据结构)