C语言指针基本讲解

C语言指针基本讲解

按照惯例,这里应该先讲一下指针的概念。但是我不想这么做,我想先讲一下指针的作用,因为上来就怼概念容易让人懵逼。

由于这里只是最简单的讲解一下指针,所以什么节约内存啊这些进阶的作用我就不讲了,因为我自己也没太搞懂。这里我就只讲一个最基础的用处:**可以在函数内部改变传进来的实参的值。**如果不用指针,我们是无法实现这个功能的。

举个例子,我写一个Swap函数,传进来a和b两个实参,我希望能在函数内部交换a和b的值,并作用于外部的全局变量a和b。

如果不用指针的话,大家应该都会这么写:

#include <stdio.h>

void Swap(int x,int y)
{
	int temp;
	temp=x;
	x=y;
	y=temp;
}

int main () {
	int a=1,b=2;
	printf("交换之前,a:%d,b:%d\n",a,b);
	Swap(a,b);
	printf("交换之后,a:%d,b:%d",a,b);
}

然而结果令人遗憾,并没有交换值。
在这里插入图片描述
为什么呢,因为在C语言中,实参和形参之间是单向的值传递,也就是说我并非把a和b这两个变量的“本体”传过来了,我只是传过来了他们两个变量的值而已,本体是没有变化的。

我再举个生动的例子,假如你有一个word文件,然后你的同事小明要借用下你的word文件,很可能会在里面做些改动,你会怎么做?当然是复制粘贴一份给他咯,你莫非还让小明直接到你的电脑上操作啊?那万一小明工作做完了,然后把word改得乱七八糟,你说小明啊既然你事儿都做完了,那就帮我把word还原成你操作前的样子吧,小明说哎哟我肚子好疼我得先回家了,你咋办,锅还不是得你背……

那么回到之前的话题,你的word相当于实参,你拷给小明的word相当于形参,小明的工作相当于函数,他的工作做完了,他把拷过来的word也就删掉了,你电脑上的word是不会有变化的。

那怎样才能在函数里改变实参的值?现在就要说到指针了。

我之前在学习微机原理的时候,有一次课堂测验就是把一段c语言代码转化成汇编代码,其实那段c语言代码很简单的,也就是声明几个变量再赋值而已。但是用汇编写出来的话,你先得在Data Memory(数据存储器)里面,根据变量的数据类型在某个内存地址上留出一定空间,同时把变量的值保存到Program Memory(程序存储器)的某个内存地址上面,最后从第一个内存地址上把变量取到一个寄存器里,再从第二个内存地址上把值取到一个寄存器里,这两个寄存器的值拷到一起存在某个内存地址上,才能完成声明变量再赋值这个功能。

我举个形象的例子,就像客人来住酒店一样,前台会先问是情侣还是单身狗啊,情侣的话那就大床房,单身狗那随便开个标间,这就是根据变量的数据类型来留出一定空间,然后会让你掏出身份证,记录下身份信息,所有都统计完了以后会给你个房卡,房间号就是所谓的内存地址了,你这个变量的值就存在里面。

说了这么多终于要说到指针了,指针指的是什么呢,其实就是上面反复提及的内存地址。也可以说指针是一个常量,一个指针就是一个地址。那么存放指针的变量,我们称之为指针变量。

如何定义一个指针变量呢?
其一般形式为类型说明符 *变量名;,如下所示:

int *p1; //*p1是指向整型变量的指针变量
char *p2; //*p2是指向字符变量的指针变量

你可能会感到疑惑,这个*是什么东西,怎么没见过,其实它叫做指针运算符,还有个&叫做地址运算符。具体怎么用,我们来实际看看:

int a;//随便定义一个整型变量
int *p;//定义一个指向整型变量的指针变量,指向哪种数据类型是必须提前声明的,而且不能变
p=&a;//&a表示的是a这个变量的内存地址,也就是前文所说这位客人的房间号。可以把&联想成“取出它的内存地址”这样一种操作

这样就完成了定义指针,再给指针赋值的基础操作。你可能会疑惑,定义的时候不是星号p吗,为什么赋值要写成p=&a呢?因为p才是指针变量,里面保存着它所指向的变量的内存地址。而星号是指针运算符,星号p的意思是去找到p这个指针变量里面的地址所对应的变量,举个例子的话,就像是在酒店里你知道了房间号,要去找到这个房间里住的人的名字这种操作。

结合上面的代码来看,*p和a是一个意思。*p=1和a=1达到的效果是一样的。不过前提是你指定了p所指向的变量,也就是p=&a这一步。

还有一点要注意的,因为p是个指针变量,所以给它赋值也必须得用地址,也就是&+变量名。

有了这些基础知识,我们就可以写一个能够改变全局变量的函数了

void Swap(int *p1,int *p2)
{
	int temp;
	temp=*p1;
	*p1=*p2;
	*p2=temp;
}
int main()
{
	int a=1,b=2;
	int *p1,*p2;
	p1=&a;
	p2=&b;
	printf("交换之前,a:%d,b:%d\n",a,b);
	Swap(p1,p2);
	printf("交换之后,a:%d,b:%d\n",a,b);
}

效果立竿见影,在函数执行完了以后,a和b的值交换了。
在这里插入图片描述

在Swap函数中,我们传进来了两个指针变量,然后交换了它们所指向的变量的值。

void Swap(int *p1,int *p2)
{
	int temp;
	temp=*p1;
	*p1=*p2; /p1原本指向的是变量a,这里p1指向了p2所指向的变量。也就是b。所以a所在的内存地址保存的已经是b的值了
	*p2=temp;/同上,b所在的内存地址也保存成了a的值
}

这种交换法,就不是之前的值传递那么简单,你直接跑过去操作内存了,把人家内存地址上保存的值都改了,那可是计算机存储数据的核心所在。相当于你只想把word拷一份给小明,可是小明拿着U盘冲过来剪切走了你的文档。

写到这里,希望大家可以理解指针的基础内容操作了。但是我还没讲完。

我在UNSW读书的第一学期,学到了(被逼着养成了)一个良好的编程习惯,那就是写完代码之后把自己当成搞测试的,各种奇葩的输入各种边界值使劲操你自己的代码,直到没有任何问题为止,你才能提交。

所以现在我们来尝试魔改一下我刚才写的代码。如果把Swap函数改成这样呢?

void Swap(int *p1,int *p2)
{
	int *temp; /原来版本:int temp;
	*temp=*p1;
	*p1=*p2;
	*p2=*temp;
}

这样是不行的,因为temp这个指针变量,你都没有说清楚人家要指向哪个变量(temp=&?),你就开始给它所指向的变量赋值了(*temp=xxx),这种操作把计算机搞得一脸懵逼,但它又不敢违抗你的命令,所以它会机选一个内存地址来给temp赋值,万一那个被选中的地址里放着重要数据,那你不就凉了吗。

第二种情况:

void Swap(int *p1,int *p2)
{
	int *temp; 
	temp=p1; /原来版本:p1和p2前面加上指针运算符
	p1=p2;
	p2=temp;

这样子输出的结果就和预期不符了。
在这里插入图片描述
原因在于这种方式没有手动操作内存,又走上了值传递的老路。传进来两个指针变量,想要交换他们的值,和最开始我们传进来两个整型变量,妄想直接一波改变他们两个的值并没有本质区别。

以上就是我对指针基础内容的讲解,希望可以帮到大家。

猜你喜欢

转载自blog.csdn.net/weixin_44067569/article/details/85319169