C | 指针的相关知识(二)

目录

  • 前言
  • 无类型指针
  • 结构体指针
  • 枚举变量指针
  • 函数指针
    • 函数指针的创建
    • 函数指针的使用
    • 回调函数
  • 数组指针
    • 数组指针的创建
    • 数组指针的使用
  • 指针数组
  • 结语

前言

上次我们学习了指针的相关知识,那么今天就让我们进一步的深入去学习指针吧!

没有看过上一篇关于与指针的相关知识是小伙伴可以点击下面的链接跳转哦~

指针的相关知识(一)

C | 指针的相关知识(二)_第1张图片

无类型指针

不知道小伙伴们有没有在查询函数的时候,看到函数的形参列表或者返回类型中,出现了void* 这种类型?

例如:

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

上面最后面就出现了void*,那么这个是什么呢?

这个不是空指针!

空指针的表示方法为:NULL

这种类型叫做无类型指针!

无类型指针意思就是没有具体类型的指针,也就是说,你在函数传参的时候,传入什么指针类型都可以。

这种指针类型并不能直接对其进行解引用和运算,需要强制类型转化为具体类型的指针才可以!

int main() {
     

	void* p;//定义了一个无类型指针变量p
	int n = 10;
	p = &n;//把整型变量n的地址赋值给p

	//printf("%d\n", *p);//这是错误的用法!
	//我们需要在解引用前把p强制类型转化为int*类型才可以进引用!

	printf("%d\n", *(int*)p);

	return 0;
}

结构体指针

在C语言中,除了内置的数据类型,我们还可以自定义数据类型。

结构体就是自定义类型的典型代表!下面就让我们一起来学习结构体的指针吧!

代码演示如下:

#include

//简单的定义了一个结构体类型
struct Book {
     

	char name[20];
	double price;

};


int main() {
     


	//用定义的结构体类型创建了一个变量book,并初始化
	struct Book book = {
      "sanguoyanyi",25.5 };

	//定义了一个结构体的指针变量,并把book的地址赋值给该结构体指针变量
	struct Book* p = &book;

	//利用指针找到结构体变量
	printf("%s\n", (*p).name);
	printf("%.2lf\n", p->price);

	return 0;
}

上面演示了结构体指针变量的基本使用方式,关于结构体的相关知识,我们在这里就不展开啦,以后我们在去了解结构体。

我们还演示了结构体指针变量的两种用法

  1. 解引用后再找到成员变量
  2. 直接指向成员变量

关于结构体指针变量的用法,有一个特别重要的应用场景哦~

那就是再调用函数需要再传参时传入结构体变量的话,我们一般采用的是传入结构体变量的地址哦!

枚举变量指针

在C语言中,自定义类型除了结构体还有枚举。

枚举类型也有属于自己的指针哦。

看下面代码演示:

#include

//定义了一个枚举类型
enum Day//星期
{
     
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};

int main() {
     

	//创建了一个枚举变量
	enum Day day = Mon;

	//创建了一个枚举类型的指针
	enum Day* p;

	//把枚举变量的地址复制给枚举类型的指针
	p = &day;

	printf("%d\n", *p);


	return 0;
}

上面的代码就演示了枚举类型的指针,但是由于我知识储备不够,没有想到有什么应用场景,如果知道该如何使用的小伙伴可以在评论区告诉我哦~

函数指针

不仅仅是变量拥有属于自己的地址,就连函数也有地址哦!

我们先看一看下面的代码演示:

#include 
void test()
{
     
printf("hehe\n");
}
int main()
{
     
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}

在这里插入图片描述
运行上面的代码,我们会发现打印出来的两个值是一样的。

这也就说明

函数也有地址,而且函数名就代表着函数本身的地址!

函数指针的创建

我们该怎么样创建函数的指针变量呢?

void test()
{
     
printf("hehe\n");
}


//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

上面的代码中,pfun1是指针变量,因为它先跟*结合,说明该变量是一个指针变量,前面的void说明了它指向的函数的返回类型为空,后面的(),表示这个它指向的函数的形参也为空

再看下面的栗子

C | 指针的相关知识(二)_第2张图片

函数指针的使用

我们再来看一个栗子:

#include

//简单定义了一个加法函数
int Add(int x, int y) {
     

	return x + y;
}

int main() {
     

	//创建了两个指针变量
	int (*pfun1)(int, int);
	int (*pfun2)(int x, int y);

	//给两个指针变量赋值
	pfun1 = &Add;
	pfun2 = Add;

	//通过指针去调用函数,并打印返回值
	printf("%d\n", (*pfun1)(3, 4));
	printf("%d\n", pfun2(6, 7));


	return 0;
}

通过上面的栗子我们可以知道

  1. 创建函数指针变量时,后面形参列表的形参变量符号可以省略
  2. 给指针变量赋值的时候,函数名和&函数名的实际效果都一样
  3. 函数指针调用函数的时候可以解引用,也可以直接通过指针变量名来调用函数

回调函数

通过函数指针,我们就可以用来调用函数了。

回调函数就是一个通过函数指针调用的函数。

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

其中,库函数qsort的形参列表,就是使用了回调函数的方法哦!

有兴趣的小伙伴也可以自己去研究一下这个库函数,有时间的话,我也会分享一篇关于qsort函数的使用的博客哦~

数组指针

同样的,数组也有自己的地址,用来存放数组的地址的指针,就是数组指针啦~

数组指针的创建

C | 指针的相关知识(二)_第3张图片

数组指针的使用


int main(){
     

	//定义了一个整型数组
	int arr[10] = {
      0 };

	//定义了一个整数数组的指针
	int(*p)[10];

	//给整型数组的指针赋值
	p = &arr;


	return 0;
}

指针数组

栗子如下:

int *arr[10];
//数组的每个元素是int*

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

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];

大家觉得哪个才是正确的指针数组的创建呢?
答案是:parr1
parr1 先和 [] 结合,说明parr1是数组,数组的内容是什么呢?
是 int (*)() 类型的函数指针。

结语

今天的内容到这里就要结束啦!

由于今天的内容难度比较大,而且本人的水平有限,难免会有出错的地方,如果有错的地方,还请小伙伴们再评论区提出哦~

让我们一起学习,一起进步吧!

创作不易呀,希望小伙伴们可以动动小手,给我一个关注、一个赞还有评论哦~

C | 指针的相关知识(二)_第4张图片

你可能感兴趣的