C语言 指针的进阶(一):字符指针、数组指针、指针数组、函数指针

指针的进阶(一)目录:


字符指针

在开始讲解这一章节之前,我们需要了解指针前面声明的类型的意义

类型 * 指针名

对于指针来说,我们在给指针进行声明时,我们声明的类型并不是指针的类型,而是指针所指向的地址的类型,也就是指针看待这段地址的方式,它该如何读取数据,它如果加一该移动多少位,只有了解了这个才能方便理解下面的内容

我们首先来说的就是字符指针char*

一般我们是这样运用字符指针的

	char a = 'a';
	char* p = &a;

但是我们还能以这种方式运用

	char*str = "hello world";

这时候,经常就会有初学者以为我们的指针指向的是hello world这个字符串,但其实这里只是把字符串的首元素的地址保存到了这个指针当中。
在这里插入图片描述
同时我们还需要理解这样的指针与字符串的区别。

例如:

	char str1[] = "hello";
	char *str2 = "world";

这里的str1是在栈上开辟一块内存空间来存放hello这段字符,而这里的str2是用指针指向常量区的‘’world‘’这段字符,所以我们无法给str2进行赋值与修改,因为常量是不可改变的,我们也无法将这str1与str2进行比较,因为他们是本质不同的两样东西。
借此,在引申一个小内容

	#include<stdio.h>
int main()
{
	char str1[] = "helloworld.";
	char str2[] = "helloworld.";
	char* str3 = "helloworld.";
	char* str4 = "helloworld.";
	if(str1 == str2)
		printf("1\n");
	else
		printf("0\n"); 
	if(str3 == str4)
		printf("1\n");
	else
		printf("0\n"); 
	return 0;
}

在这里插入图片描述
为什么在这里,str1与str2不同呢?
因为我们给字符串赋值时,是在栈上面开辟一个内存空间,来给这个字符串存放一个数值,虽然他们存放的内容是一样的,但是它们存放在不同的内存块中,所以进行比较的时候是不同的
那为什么str3和str4是相同的呢?
因为它们是字符指针,它们指向的都是常量区中的hello world这段字符,它们指向的内容一样,所以它们相同。
这也就是为什么我们在进行字符串的比较的时候运用的都是strcmp这个函数的原因。


数组指针和指针数组

在我刚刚接触指针的时候,我经常会搞混两个东西,一个是数组指针,一个是指针数组,因为它们太过相似,但如果你了解了运算符的优先级以及我之前说的指针如何看待类型,你就会很快的区分它们
在这里插入图片描述
上图中的两个指针是不是特别相似?那我们如何区分呢?

首先,我们先看几个运算符的优先级对于p1,[] 的优先级是很明显高于 *的,所以它的本质就应该是一个数组,而剩下的int *就应该是它所存放的数据类型,所以它是一个存放指针元素的数组,即指针数组。
而对于p2,它的()的优先级是很明显高于 [] 的,所以我们应该先去考虑括号中的内容,括号中是一个指针 *,所以它的本质就应该是一个指针,而剩下的就是它所指向的地址的类型,所以它是一个指向数组的指针,即数组指针。

如果我们要了解一个数组指针是如何作用的,我们首先要了解这样一个知识点。
在这里插入图片描述
对于一个数组,它的数组名和&数组名有什么区别。
我们都知道数组名是这个数组的首元素的地址,但是&数组名却不是很了解。

int main()
{
	int arr[10] = {0};
	printf("%p",arr);
	printf("%p",&arr);
	return 0;
}

在这里插入图片描述
我们查看它们的地址,却完全的一样,那是不是证明它们相同呢?

int main()
{
	int arr[10] = {0};
	printf("%p",arr+1);
	printf("%p",&arr+1);
	return 0;
}

其实不是,我们再给它们分别+1
在这里插入图片描述
这里区别就显现出来了,arr+1加的只是一个元素的大小,而&arr+1加了一整个数组的大小。
所以我们就能了解,&arr指向的是这一整个数组的地址。
而我们的数组指针,是一个指向数组地址的指针,所以数组指针应该这样指向一个数组。

	int (*p)[10] = &arr;

函数指针

在了解函数指针前我们要先了解一个小的误区

在这里插入图片描述
这两个哪一个才是函数指针呢?
还是之前判断数组指针的那个方法,我们看优先级,先看括号,它是一个指针,剩下的就是它所指向的类型,一个返回值为void的函数。
而第二个其实就是一个函数的声明,声明一个返回值为void*的函数

我们该如何调用这个函数指针呢?
在这里插入图片描述
无论是直接调用还是进行解引用后调用,我们都可以直接使用这个函数

#include<stdio.h>

void print()
{
	printf("1\n");
}

int main()
{
	void (*p)();
	p = print;
	p();
	(*p)();
	return 0;
} 

在这里插入图片描述
在使用函数指针的时候,因为函数的特殊性,在很多情况下代码的可读性会十分差,如下面这段代码。

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

在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/20191109224245462.pg
这是它的参数类型

这是它的函数名
外面剩下的则是它的返回类型。
所以它是一个参数为int 和函数指针的一个函数,返回值为函数指针。
这样的代码让人很难理解。
那该如何简化呢?我们可以用typedef进行简化

	typedef void(*p)();

简化完后
在这里插入图片描述
这样我们的代码可读性就大大的提升了。

第一部分就讲到这里吧

发布了60 篇原创文章 · 获赞 78 · 访问量 6322

猜你喜欢

转载自blog.csdn.net/qq_35423154/article/details/102991271