c语言指针学习

本文详细解析了内存单元、地址编号、指针的使用、不同类型指针的特性、解引用操作、const对指针的影响、指针大小、加减运算、野指针的产生与规避、函数传值与传址、数组指针与二级指针的区别、以及二维数组和函数指针的概念。
摘要由CSDN通过智能技术生成

内存

计算机把内存分为一个个单元,每一个单元大小为一个字节,每个单元都有单独的地址编号,就像酒店里的地址一样,可以方便我们通过 ,地址快速访问内存中的元素。

                            一块内存的地址是连续的不间断的,地址由低到高。

指针

C语言在创建变量就是在内存中申请空间,不同类型的变量就申请不同大小的内存空间。

(比如int 4个字节 short 2个字节 long 4个字节float 4个字节等等)

如何得到地址

为了得到变量的地址,就会使用&取地址操作符,& 就是取出变量的较小字节的地址。

如图 int a=1,a占四个字节  &a取出的地址就是0X00000000

指针的类型

int*pa=&a;取出a的地址放到变量pa中,pa的类型是int*。‘ * ’说明pa是指针变量,int说明pa地址所指向的内存存放的是int类型的数据。 比较特殊的是void类型的指针,也叫泛型指针,他可以接受任何类型的地址,但是不能直接进行解引用操作和加减运算。

例子

char*pc=&ch; int(*parr)[4]=&arr;  void(*fun)(int,int)=function;上述指针的类型分别是char*

int(*)[4], void(*)(fun,fun)。

指针的解引用

指针类型的解引用能决定访问多少个字节。可通过强制类型转换来改变变量指针的类型,解应用访问内存字节的大小。

int a=0; int*p=&a;   char *pa=(char*)&a; 解应用a可以访问4个字节,解应用pa可以访问1个字节 

const对指针的影响

const如果在*的左边那么, 该指针不能解引用改变指向变量的值,但是可以改变指针本身所存放的地址。

const如果在*的右边那么,那么不可以改变指针所存放的地址,但是可以解引用改变所指向内存存放的值。

指针的大小

指针变量的大小在不同的平台是不一样的,32位机器假设有32根地址总线,8个比特位为一个字节,那么指针大小是4个字节,同理在64位平台是8个字节。

指针的加减运算

不同类型的指针加减运算跳过的字节大小是不一样的,int*类型的加1跳过四个字节,char*是1个,int(*)[4]类型的整形数组 ,数组取地址加1跳过16个字节。

两个指针都指向同一个数组里面(同一块连续的空间),两个指针相减是的绝对值,是 求出两个指针之间的元素的个数。

野指针

野指针的原因

1 没有初始化 2指针的越界访问 比如数组用指针越界访问 3 使用过的指针 不再使用时候没有释放

如何规避野指针

1 指针初始化,如果不知道指针指向哪里的时候附上null

2避免指针越界访问

3对于不使用的指针 要及时释放地址,使用free();

函数传值和传址

在使用函数的时候, 函数的形参是 在内存临时开辟新的空间,在函数调用结束的时候空间就会销毁,由于形参和实参是两块不相同的空间,改变函数的形参,并不会影响实参数。

为了能改遍实参,所以我们可以通过传地址的方式,使用指针形参来接收,在函数内部解引用来改变实参的值。

数组指针  (一维数组 二维数组)

int arr[10]={1,2,3,4,5,6,7,8,9,0} 

数组名就是首元素的地址,我们通过打印数组名 arr的地址 和数组第一个元素的地址 &arr[0]发现是同一个地址。

通常数组名就是代表首元素地址,但是有两个例外比如 sizeof(arr) arr就是整个数组,这时候是计算出整个数组的大小。另外一个就是取地址数组名 &arr 这里是取出这个数组的地址,这里和arr数组名打印出来的地址是一样的,但是对这两个地址进行加1减1的操作时,一个是跳过40个字节,就是一个数组,另外一个是跳过4个字节,就是一个元素,原因是这两个指针类型是不同的。

                                        int*pa=arr;                     int(*pa2)[10]=&arr;

同时把这个两个地址用两个指针变量来接收,就可以发现一个指针的类型是 int*,另外一个类型是int(*)[10],所以进行加1减1的操作时,跳过的元素个素也不一样。 

所以我们对于数组内元素的访问,可以通过地址来解应用来进行。访问第几个元素就可以通过给首元素地址加几解引用来进行。注意数组的元素下标是从0开始的。

                                                   *(arr+i)=arr[ i ] ;

                                                   这两种方式是等价的

一位维数组传参的本质就是就是传地址,数组名是数组⾸元素的地址;那么在数组传参

的时候,传递的是数组名,也就是说本质上数组传参本质上传递的是数组⾸元素的地址。

所以函数形参上应该使⽤指针变量来接收⾸元素的地址。那么在函数内部我们写sizeof(arr)

 计算的是⼀个地址的⼤⼩(单位字节)⽽不是数组的⼤⼩(单位字节)。正是因为函

数的参数部分是本质是指针,所以在函数内部是没办法求的数组元素个数的。所以一维

数组传参 ,形参数本质上可以写成数组的形式和地址的形式。

二级指针​​​​​​​

二级指针本质上也是指针,因为一级指针也是个变量,存放一级指针变量的地址的变量就是二级指针。

                                    int a=10;   int*pa=&a; int** paa=&pa;

右边的*代表paa是个指针变量,int*是paa变量存放的地址,指向的内存中存放的数据是int*类型

指针数组

数组就是相同类型元素的集合,指针数组就是一个数组,他和整形数组 ,字符数组,浮点型数组都是数组,只是数组内部存放元素的类型是不同的。指针数组存放元素都是指针类型的。

  int*pa[3]; pa因为优先级的关系首先与[]相结合 说明pa是数组,存放元素的类型是int*。

 int(*pa)[3];数组指针。因为有括号与*相结合所以是个指针。

                           指针数组和数组指针平时写的时候要注意区分

所以我们可以通过指针数组来模拟出一个二位数组,

                                  也可以直接写成 arrs[i][j],两种方式是等价的

arrs[i]是访问arrs数组的元素,arrs[i]找到的数组元素指向了整型⼀维数组,arrs[i][j]就是整型⼀维数组中的元素。

要注意二位数组在内存上的空间是连续的,所以指针数组模拟出的二维数组是不一样的。

二维数组传参

我的理解  二维数组就是一堆一维数组的集合,里面的每个元素都是一维数组,而且一维数组的大小是相同的,一维数组所存放元素的类型也是相同的,在内存上的空间是连续的。

int parr[3][5]={{1,2,3,4,5,},{2,3,4,5,6,},{3,4,5,6,7}};

&parr是取出整个二维数组的地址,和一维数组是一样的,地址进行加1的操作是跳过整个二维数组。例如parr数组是跳过60个字节。

地址名是首元素地址,parr就是这个二维数的对个元素的地址,就是第一个一位数组的地址,所以用指针变量接收要写成  int(*parr1)[5]=parr;对于parr加i就是跳过一个元素,也就是跳过一个一维数组,在解引用得到一维数组的数组名。一维数组名就是一维数组首元素的地址,通过加j就是访问一维数组中的元素。

二位数组的传参和一位数组一样 ,就是就是传数组名,也就是二维数组的首原素地址,行参就是写成一位数组指针的形式 或者二维数组的形式。

例子:void*function(int(*)p[5]);void*function(int p[3][5]);

行可以省去 列不能省去

函数指针

函数也是有地址的,可以通过函数指针来存放函数的地址。和数组一样 函数名也是代表函数的地址。因为取地址函数名和函数名地址打印出来都是一样的。

函数指针的 写法是。 返回类型 (*指针变量名)(形参的类型)

例子,int(*func)(int ,int);这个函数指针的类型是 int(*)(int ,int),指向的是返回值是int类型,形参是两个int类型的函数。

通过函数指针写函数指针数组,也可以通过函数指针实现回调函数。

例子,通过回调函数来实现简单的计算器,因为这几个函数的类型都相同

通过数组下标得到函数名,函数名就是函数的地址,地址解引用得到函数。但是函数名可以不用解引用直接使用。

*(pfarr+1)(a,b)==pfarr[1](a,b)==add(a,b)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值