【C语言】进阶指针night

213 阅读8分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第18天,点击查看活动详情

🚩write in front🚩

🔎大家好,我是泽奀,希望你看完后。能对你有所帮助,不足请指正,共同学习交流🔎
🏅2021年度博客之星物联网与嵌入式开发TOP5~2021博客之星Top100~阿里云专家^星级博主~掘金⇿InfoQ创作者~周榜34»总榜1712🏅
🆔本文由 謓泽 原创 CSDN首发🙉如需转载还请通知⚠
📝个人主页-掘金 (juejin.cn)💬
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​
📣系列专栏-[C系列] 从0到1 - 泽奀的专栏 - 掘金 (juejin.cn)🎓
✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本📩

 💥指针的安全 

指针的安全问题很重要也就是在程序中如何使用,如果你能避免指针的危险性那么你指针就肯定学的还是不错的。指针固然是很好用的,但好用的同时又避免不了指针自身实际上是很危险的一个东西,所以我们在使用指针的操作一定要考虑指针的安全性避免程序挂掉或者造成崩溃。

我认为对指针的安全常见问题存在这③个问题

①:野指针

②:空指针

③:字节(地址)的改变

以上这三种都是我认为我们会常见的一些问题所在,在博客当中我也都写到过。所以当我们在使用指针的时候应该思考下我所定义的这个指针是不是一个安全性高的指针。

再举出一些例子供大家参考。示例代码如下 👇

#include <stdio.h>

int main(void)
{
    char str='c';
	int *ptr=(int *)&str;
	*ptr=10086;
	printf("%d\n",ptr);
    return 0;
}

解析代码如下👇:

①:指针 ptr 是一个 int * 类型的指针,它指向的类型是 int 。它指向的地址就是 str 的首地址,在32位系统为4字节,64位为8字节。

②:改变了 str 所占的一个字节,还把和 str 相临的高地址方向的三个字节也改变了。这三个字节是干什么的?只有编译程序知道,而写程序的人是不太可能知道的。也许这三个字节里存储了非常重要的数据,也许这三个字节里正好是程序的一条代码,而由于你对指针的马虎应用,这三个字节的值被改变了!这会造成崩溃性的错误。 (关键点在于强制改变char的类型导致取出的字节数发生改变,而这个是我们并不清楚的)

那么相信你对这个能完全掌握的话,对指针的理解会更上一层楼。 那么再举出一个例子供大家去理解:示例代码如下 👇

#include <stdio.h>

int main(void)
{
	char a;
	int *ptr = &a;
	ptr++;
	*ptr = 10086;
	printf("%d\n", *ptr);
	return 0;
}

解析代码如下👇:

这段代码实际上是可以运行成功的,但是它的漏洞实际上很多。就比如很明显的一点:

从char 到 int 实际上类型并不兼合。**

ptr 对指针 ptr 进行自加1 运算后,ptr 指向了和整形变量a 相邻的高地址方向的一块存储区。这块存储区里是什么?这可出现了巨大的bug,如果一个项目这样的话,很有可能就会丢失一块重要的数据。

而*ptr = 10086,这里就更加离谱了,所以这个指针就近指向了哪里?


🖊指针练习 

既然都看到这里了,还不上手做下关于指针的练习题吗(╹ڡ╹ )

相信当你做完的时候会对指针理解会更加的深刻哟,当然一下几道是相较于来说比较基础。

🔥练习 ①  

题目→输入一个整形数组为10个元素,使指针累加起来数组所有元素之和。

本道题目的解题步骤实际上很容易,接下来说说解题思路↓

根据要求先输入十个元素,使用循环遍历数组当中的所有的元素,最后再把所有的元素进行相加。那么实际上本道题目就完成了。简单明了(¬‿¬)

示例代码如下所示↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#define num 10
int main(void)
{
	int sum = 0;//总和
	int i = 0;
	int* arr[num] = { 0 };
	int* p = arr;
	printf("请输入十个元素->:");
	for (i = 0; i < num; i++)
	{
		scanf("%d", &arr[i]);
	}
	while (*p < arr)
	{
		sum += *p;//每一次元素的相加
		*p++;//指向下一个元素
	}
	//打印sum
	printf("sum = %d\n", sum);
	return 0;
}

运行结果🖊

请输入十个元素->:10 20 30 40 50 60 70 80 90 100

sum = 550

🔥练习 ② 

题目→创建一个函数实现用指针实现两个值的交换,不能创建临时变量来进行交换替换。

本道题目有两点我们需要注意下↓

  1. 指针实现两个值的交换。
  2. 不能创建临时变脸来进行交换。

这里来说下,为什么要用指针变量进行交换。通过指针传递方式,形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行的操作。如果你不通过指针的方式进行交换的话,一旦出了函数的形参当中就立马会销毁其中的值,就达不到交换的结果。

其次,不能创建临时变量来进行交换。其实这个很好办用按位异或就可以了,那么我们要知道按位异或是什么才行接下来来介绍下什么是按位异或(^∀^●)ノシ

那么我们要知道按位异或的运算规则才行。0^0=0,0^1=1,1^0=1,1^1=0;

示例代码如下所示↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

void swap(int *x, int *y)
{
	//按位异或	   在这里我们可以使用代入法:假设 是 x = 1,y = 2
	*x ^= *y;//x = x^y // 0001 ^ 0010 = 0011 = 3 此时x = 3
	*y ^= *x;//y = x^y // 0011 ^ 0010 = 0001 = 1 此时y = 1 
	*x ^= *y;//x = x^y // 0011 ^ 0001 = 0010 = 2 此时x = 2		结果 x = 2,y = 1 
}
int main(void)
{
	int a = 0, b = 0;
	printf("请输入两个数字:");
	scanf("%d %d", &a, &b);
	printf("a,b交换前:a = %d,b = %d\n", a, b);
	swap(&a, &b);
	printf("a,b交换后:a = %d,b = %d\n", a, b);

	return 0;
}

运行结果🖊

请输入两个数字:10 20

a,b交换前:a = 10,b = 20

a,b交换后:a = 20,b = 10

🔥练习 ③ 

题目→创建自定义函数,从而实现strcat()的功能,用指针进行实现。

strcat() 的功能就是连接字符串,假设在arr1当中是"Hello ",而arr2当中是'Cyyds',此时所连接起来的字符就因该是"Hello Cyyds",如果不了解strcat()的函数声明可以去看看,这样对做题的帮助是很大的。

示例代码如下↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<assert.h>
char *My_strcat(char *dest, const char *src)
{
	assert(dest && src != NULL);//断言
	char *ret = dest;
	while (*dest != '\0')//'\0'的ASCLL码值就是0
	{
		dest++;
	}
    //dest指向的是'\0'
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main(void)
{
	char arr1[20] = "hello C";
 	char arr2[20] = "yuyan";
	printf("%s\n", My_strcat(arr1, arr2));
	return 0;
}

运行结果🖊

hello yuyan

🔥练习 ④

那么接下来我们再来看一组代码,示例代码如下↓

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
void Revise(int *p1)
{
	*p1 = 1314;
}
int main(void)
{
	int *p1 = NULL;
	int p = 520;
	p1 = &p;

	printf("*p1 = %d\n , p1 = %d\n , &p1 = %d\n , &(*p1) = %d\n , p = %d\n , &p = %d\n", *p1, p1, &p1, &(*p1), p, &p);
	Revise(p1);
	printf("*p1 = %d\n , p1 = %d\n , &p1 = %d\n , &(*p1) = %d\n , p = %d\n , &p = %d\n", *p1, p1, &p1, &(*p1), p, &p);
	return 0;
}

试着把上面的代码给分析出来,其实不难就是这"指针比较臭"而已(ง •_•)ง

​编辑

这个&p1的地址的本身的地址,而我们取&(*p1)的地址是取解引用p1的值然后再取出&(*p1)的地址。也就相当于是取地址p赋值给指针变量p1。如果你觉得我这句话对你来说不怎么理解的话,那么多看几遍还是可以理解的。


📢最后 の talk

指针可以说在C语言真的算比较难了也是因为指针有些人就被劝退了,但是指针学好了。但是学好C语言指针好处是大大滴多的(这个在前面的初阶指针已经讲的非常清楚了)。刚开始学指针的时候我特别懵,尽管现在我也觉得蛮懵的。但是起码比刚开始好多了不至于连指针数组和数组指针都分不清不会用了(●'◡'●),指针这个东西一定要花时间多看多打代码多调试。

💬如果你是刚学C语言的初学者或者是指针,推荐看看前面博主写的一篇初阶指针的内容🌹

【C语言】万字速通初阶指针“zero → One“

🔎链接🔎⇥【C语言】万字速通初阶指针 zero → One - 掘金 (juejin.cn)

相信对你有所帮助,再来看这个可能就会好点了。不过在学指针的时候最好是多打代码,以及做指针的练习,这样对你所理解指针会有更深层的理解,搞不懂的地方其实C语言的调试我觉得如果你会调试的话能帮你解决一半以上的问题,C语言编译器当中的调试工具其实可以说就是你在自己上手"打代码最好的老师了",当然我也就这么说,还要靠自己的理解🥰

🎉如果觉得对你有帮助的话,麻烦点个赞支持一下🎉

📚总而言之就是一句话»C语言指针非常重要!!!坚持下去奥里给🔥🔥🔥

帮助到你的话别忘了支持下博主🌹