指针(C语言)

1.内存和地址:

内存与指针有着不可分割的联系,当CPU需要数据时会从内存中调用数据从而实现程序运行,但内存中的数据是怎样分布的,CPU又是如何准确的调用到想用的数据?我们一步一步的来说;首先:内存是由一个个内存单元构成,每个内存单元的存储大小为一个字节,而一个字节又等于8个bit位,为什么一个整型的大小是4个字符,因为一个整型在内存中是由2进制所表达的,而2进制有着32个bit位,因此一个整型大小相当于32个bit位,也就是四个字节。bit是最小的内存单位,再往上是byte,一个byte等于8个bit大小,再往上是KB,一KB等于1024个byte大小,一MB等于1024个KB大小等等;在说完了内存之后再说说地址,CPU与内存之间是有数据线连接而成,一般分为32个或者64个线,而每根线所能输出的值只有0和1,因此在32根或者64根连起来后形成了一个2进制的数(因此在编译器上的X86和X64就是代表32位环境和64位环境),正好是表达所需要的内存的地址,然后就可以精准的调用数据。而地址又如何在需要时就能准确的给出呢,这就用到了我们的主角“指针”了,指针是用来存放变量内存地址的变量,有了指针就可以对地址进行访问,调用数据。因此指针在程序的运行中起到重要的作用。

2.指针的表达方法:

指针如何用表达式表示出来,例如:int *p = &a;这句话的意思是将变量a的地址存储在变量p中,“&”意思为取地址(顾名思义将地址取出),“*”的意思是代表该变量为指针,int*的意思是p是一个存放整型地址的变量。所以在变量名前出现的(比如int)代表指针的类型,再举例:char*p = &a,其中char为变量类型为字符;char*p="abcdef",意思为p为数组名,其中存放的是每个字符的地址;而char(*p)[]="abcdef",意思是p为存放数组的地址的指针变量,该数组为字符数组,元素为abcdef/0,所以可以得出*与变量类型的结合优先级大于变量的优先级。再来说*的意思,*p表达的是将p中存放的地址拿出来,当单独为p时表达的是地址,*p表达的是地址中所存储的变量,因此&与*更像是一对操作符,一个负责找到地址,一个负责对地址进行解析拿出存储的数据。指针的类型也代表了指针存放地址的大小,比如当字符型指针存放一个整型的时候,字符型指针只能存放一字节的大小,而整型却有着4个字节的大小,因此字符型指针只能存放32个二进制中的8个,剩余的无法存放,当你想要访问剩余的24个二进制时,你可以通过地址+1来访问,但是在你未定义储存这些数据的指针时,就会出现野指针,野指针是非常的危险的指针,通常未对指针定义就使用时会造成非法访问,如何避免野指针?首先可以用NULL,意思为空地址,让未被定义的指针不再成为野指针,而是空指针这样就不会发生非法访问的风险,其次再我们进行指针访问时,要小心指针越界,多加注意自己的程序是否会进行非法访问的危险操作。或者是assert断言,当程序与条件不符合时,会进行报错并给出报错信息。

3.指针的传址调用和传值调用:

二者都需要涉及到调用函数,假设ADD为一个将要调用的函数,那么在主函数内的形式为例如ADD(c,d)(c,d为变量,将要传给ADD函数)而在主函数外部会进行一个声明:Int ADD(int a,int b)(int 为函数的输出类型,a,b为创建的临时变量并且c,d将变量的值赋给了a,b)这样为传值调用,虽然函数内部进行了计算,但是本质上c和d的值并未发生改变。再来说传址调用,意思时传过去的变量不再是一个具体的值,而是他的地址,而函数的声明部分为:int ADD(int*p,int *c)(创建了临时指针存放地址,然后对地址中的变量进行计算,本质上是对c,d的地址进行计算因此c和d的值会随着发生改变。)

4.指针数组与数组指针

指针数组与数组指针的区别在于前者是存放指针的数组(本质上是数组,前文以提及),后者是类型为数组的指针,二维数组与一维数组就是指针数组,char*p又可以写为char p[],在一维数组中当传递了字符串时比如“abcde”那么就相当于接收了字符串每个元素的地址,而数组名p就相当于该数组首元素的地址,而p的地址往后加一就是往后移动char类型的大小(一个字节)来到第二个字符“b",二维数组也是如此(数组间每个元素都是连续排放在数组中),比如char  arr[3][5],代表着3行5列,实际上就是15个单元内存连续排放arr不再时首元素的地址,而是首个数组的地址(看作3个数组,每个数组5个元素)当arr解引用时*arr代表的才是首元素的地址。二维数组与一维数组的声明也有区别:一维数组:int arr[]  二维数组:int (*arr)[]或者int arr[][];再数组的调用时我们会发现【】内的数不一定为元素个数也可以运行起来,是因为数组的调用实际上只是传递了数组首元素的地址,二维数组的话是传递了第一行的地址。所以【】内的数实际上没有意义,编译器会在编译的过程中自动识别已经初始化的地址进行计算,所以编译器已经知道你的元素个数,但是二维数组的列数无法省略,因为这是相当于数组中每个元素里的元素的个数,在数组的计算或者遍历都需要用到列数,所以不能省略和取随机值。否怎会出错。

5.回调函数与qsort函数:

回调函数的意思是在函数中调用另一个函数,比如在主函数中调用函数:ADD(a,b,APP)(a,b都是变量)APP是我又在该函数内调用的另一个函数,称为回调函数。而qsort函数是对回调函数充分利用的函数,qsort函数的作用主要用来排序数组,无论是任何的类型都可以进行排序。其声明的形式是  void qsort(void *base,size_t num,size_t size,int (*cmp)(const void*,const void*));void表示该函数的返回类型,base表示数组首元素的地址,num为数组的元素的个数,size表示单个元素的大小,cmp为比较两个元素大小的函数,再返回值告诉qsort进行排序。需要注意的是const表示的是该地址中的变量无法修改,因此在交换的时候无法成功,所以我们需要对变量进行转类型,就转为元素类型即可,比如*(int *)p1),这样就可以对内容进行交换。从而实现排序。另外qsort函数需要头文件#include<stdilb.h>.

6.sizeof与strlen:

我们对这两个都不陌生,一个是用来计算整型数组的大小,一个是用来计算字符数组的大小。但也有区别:sizeof函数是库函数不需要头文件,给他值就可以计算该值占了多大的内存。而strlen不一样,他需要头文件,#include<string,h>,并且他的计算逻辑是根据给的地址往后进行计算,直到出现“/0”为止,若没有/0则会输出随机值。所以要辨清要求在运用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值