指针的函数传参的详细讲解(一)

简介: 本文介绍了计算机内存管理与C语言中指针的基本概念及应用。从内存单元的划分到指针的定义,解释了指针作为地址的作用及其数据类型特性。文章通过代码示例详细说明了一级指针、二级指针的使用方法,以及`void*`指针的特点和限制。此外,还探讨了指针运算规则、函数指针传参解引用的原理,并结合链表实例分析了一级与二级指针在实际场景中的区别。最后附带两道指针练习题,帮助读者巩固理解。


地址就好比一栋楼上的每层楼的房间编号,有了房间编号就容易去找到对应的房间,不需要挨个对应查找;

如果把上⾯的例⼦对照到计算机中,⼜是怎么样呢?

我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的 数据也会放回内存中,那我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何 ⾼效的管理呢?

其实也是把内存划分为⼀个个的内存单元,每个内存单元的⼤⼩取1个字节;

其中每个内存单元就好比一个学生宿舍,一个字节空间里有8个比特位,比如一个宿舍就是一个字节单位,而8个学生每一个就是一个比特位,

每个字节单元都有对应的序号,也就比如每个宿舍都有对应的宿舍号,在计算机中我们 把内存单元的编号也称为地址。C语⾔中给地址起 了新的名字叫:指针。

这句话要说他对,他也对,要说错,也有点问题;

所以每个指针他自己就有自己的地址,而其该指针在内存中存的便是他指向的内容地址;

所以指针就是地址;但其类型不是地址,是数据类型;

指针的简单使用
int main()
{
int a = 1;
int* p = &a;
return 0;
}

在内存中其存的内容便是

解引用
解引用简单来说,就是指针通过地址找到该指针指向的内存空间,进行访问与修改;

看以下代码,便是解引用的使用

如何拆解指针类型
我们看到p的类型是 int* ,我们该如何理解指针的类型呢?

这⾥p左边写的是 int 是在说明p是指针变量,⽽前⾯的 int 是在说明pa指向的是整型(int) 类型的对象。

同理还有char类型的指针,double的.....

指针的大小
前⾯的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后 是1或者0,那我们把32根地址线产⽣的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4 个字节才能存储。 如果指针变量是⽤来存放地址的,那么指针变的⼤⼩就得是4个字节的空间才可以。同理64位机器,假设有64根地址线,⼀个地址就是64个⼆进制位组成的⼆进制序列,存储起来就需要 8个字节的空间,指针变量的⼤⼩就是8个字节。

二级指针
C语言中的二级指针其实就是指向指针的指针,

指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。同理可以构建三级或者N级指针,但是一般情况下用不到多级指针,只有二级指针在一些情况中需要使用,以下是二级指针的初始化:

include

int main(void)
{
int var;
int ptr1;
int *
ptr2; //初始化一个整型的二级指针

var = 50;
ptr1 = &var;  /*获取var的地址*/
ptr2 = &ptr1; /*获取ptr1的地址*/

printf("var 的值是:var = %d\n", var);
printf("var 的值是:*ptr1 = %d\n", *ptr1);
printf("var 的值是:**ptr2 = %d\n", **ptr2);

return 0;

}

简单来说二级指针就是指向一级指针的指针,可以存放一级指针的地址的指针。

void指针的注意事项
当我们需要一个指针,但又一开始不明确一开始准备将其名为什么类型的时候,就可以尝试使用void
类型的指针,他是可以接收任何类型的地址,同样大小跟普通的指针大小无异,

void* 的指针以为不为任何类型,所以不能进行任何的操作,比如加整数,减整数......

指针的运算规则

指针加整数向后移动多少个字节单位,就是以其该指针指向的类型所决定

比如以下代码

发现pa指针加加后,指针地址指向位置向后移动了4个字节单位大小,

而pch却移动了1个字节大小,所以 指针加整数向后移动多少个字节单位,就是以其该指针指向的类型所决定,指向的类型多少个直接,+1就向后移动多少个字节单位大小;

同样还存有指针-整数也是同样道理,

由于指针加指针的值是一个相对于原数组地址相差较大的数值,该数值很有可能超越了我们所定义的数组的右边界,这样获得的地址值将是一个“盲值”,虽然它确实存在,但我们不能对这个地址做任何处理,因为我们无法得知这个位置原先存储的是什么变量,所以我们认为这是个非法的。

只有当两个指针都指向同一个数组中的元素时,才允许从一个指针减去连一个指针。

int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int* pa = arr;
int* pb = &arr[9];
printf("%d\n", pb - pa);

运行结果是9,而不是36,所以该结论是正确的

对于指针,还有用const修饰,野指针,assert断言,字符指针,字符指针常量,这里不在多赘述

感兴趣的可以去了解,在网上搜一下就出来了。

函数的指针传参与解引用
这里将会详细讲解 函数的指针传参与解引用问题

我们学c语言遇见过最多的库函数便是scanf与printf,那么你真的了解他吗?

对于scanf我们都知道,对应的数后面要填地址,那么我们又知道(除了特殊条件下)数组名表示首元素地址,那我们是不是可以用函数名来单独接收第一个元素呢?

那么看一下代码

我们理解是正确的,那么再看以下代码

int main()
{
int a = 10;
int p = &a;
int
ps = p;
*ps = 50;
return 0;
}

请问运行完后的代码会有什么效果???

A: a会变为50 B: p存的地址会变

C: a不会变化还为10 D:没有任何效果

选什么呢?

其实会将a变为50;

问题又回来了,如果将ps变为二级指针呢?别的不改

int main()
{
int a = 10;
int p = &a;
int** ps = p;
ps = 50;
return 0;
}

原因就是

在给定的代码中,存在类型不匹配的错误。问题出在将变量 a 的地址赋值给指向指针的指针 ps 时。实际上,应该使用 & 符号来获取指针 p 的地址。

在原始代码中,将整数指针 p 赋值给指向指针的指针 ps 是不正确的,因为 ps 应该指向一个指针,而不是一个整数。

通过更正代码中的错误,我们可以确保指向指针的指针 ps 正确指向指针 p,从而通过双重指针 ps 来修改变量 a 的值。

归根揭底,不能跳级式的使用指针。

了解了这些我们知道需要加一个&

int main()
{
int a = 10;
int p = &a;
int** ps = &p;
ps = 50;
return 0;
}

效果会产生什么?

更上面同样道理,但是ps是一个指向指针的指针,那么对其解引用就会修改p的内容,改变其指向的内容

16位的50就是32

理解了这些,我们在讲以下链表哪里的指针内容

typedef int SLDataType;

typedef struct SLList
{
SLDataType x;
struct SLList* next;
}SL;

int main()
{
SL SLList=NULL;
SLPushFront(&SLList, 5);
return 0;
}
我们要想进行修改指针指向的内容,我们就需要传地址,那么我们是要修改指针的内容,就需要进行用二级指针接收, SL*
接收

然后进行解引用就可以访问到SLList 然后就可以修改SLList 的内存

这一点很好理解,那么我们看以下力扣上链表的函数

wow, 为什么要用一级指针接收,还要返回一级指针????

一开始我也思考了很久,后来慢慢就理解了,其基本原理就是 上面举的第二个例子,因为他这个函数传的不是地址而是

SLPushFront(SLList, 5);
传的是其值,但其他本身就又是一个指针,其本身就存着别人的地址,归根揭底还是传的是地址,所以用一级指针接收,进行解引用,同样可以改变指针指向的内存,

简单来说就是SLList这个指针本身就存着头节点的地址,传值相当于传的是头节点的地址,相对于二级指针的区别就是不能修改SLList指针指向的位置,不能更换头节点,只能更换头节点的指向

内容便是以上,如果哪里有问题,可以评论,随时回复

最后附上两道指针题:

image.png

大家看一下,这是什么???

目录
相关文章
|
8月前
|
存储 人工智能 算法
数据结构实验之C 语言的函数数组指针结构体知识
本实验旨在复习C语言中的函数、数组、指针、结构体与共用体等核心概念,并通过具体编程任务加深理解。任务包括输出100以内所有素数、逆序排列一维数组、查找二维数组中的鞍点、利用指针输出二维数组元素,以及使用结构体和共用体处理教师与学生信息。每个任务不仅强化了基本语法的应用,还涉及到了算法逻辑的设计与优化。实验结果显示,学生能够有效掌握并运用这些知识完成指定任务。
180 4
|
机器学习/深度学习 搜索推荐 算法
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
【再识C进阶2(下)】详细介绍指针的进阶——利用冒泡排序算法模拟实现qsort函数,以及一下习题和指针笔试题
144 0
|
9月前
链表指针的传参,传值和传地址
本文讨论了链表操作中指针传参的问题,特别是指针的传值与传地址的区别,并提供了修正代码,以确保链表插入操作能正确地修改指针指向的地址。
69 1
链表指针的传参,传值和传地址
|
9月前
|
存储 C语言 C++
如何通过指针作为函数参数来实现函数的返回多个值
在C语言中,可以通过将指针作为函数参数来实现函数返回多个值。调用函数时,传递变量的地址,函数内部通过修改指针所指向的内存来改变原变量的值,从而实现多值返回。
|
9月前
|
存储 搜索推荐 C语言
如何理解指针作为函数参数的输入和输出特性
指针作为函数参数时,可以实现输入和输出的双重功能。通过指针传递变量的地址,函数可以修改外部变量的值,实现输出;同时,指针本身也可以作为输入,传递初始值或状态。这种方式提高了函数的灵活性和效率。
|
9月前
利用指针函数
【10月更文挑战第2天】利用指针函数。
61 1
|
9月前
|
算法 搜索推荐 C语言
【C语言篇】深入理解指针4(模拟实现qsort函数)
【C语言篇】深入理解指针4(模拟实现qsort函数)
68 2
|
11月前
|
程序员 C语言
指针在函数参数和返回值中的使用
指针在函数参数和返回值中的使用
|
10月前
|
Linux
在Linux内核中根据函数指针输出函数名称
在Linux内核中根据函数指针输出函数名称
|
11月前
|
存储 搜索推荐 C语言
C语言中的指针函数:深入探索与应用
C语言中的指针函数:深入探索与应用
216 1