深入理解指针(1)
文章目录
- 深入理解指针(1)
- 一. 内存和地址
- (一)内存
- (二)如何理解编址
- 二. 指针变量和地址
- (一)取地址操作符`&`
- (二)指针变量和解引用操作符`*`
- 1. 指针变量
- 2. 如何拆解指针类型
- 3. 解引用操作符
- (三)指针变量的大小
- 三.指针变量类型的意义
- (一)指针的解引用
- (二)指针`+-`整数
- (三)`void*` 指针
- 四.指针运算
- (一)指针`+-` 整数
- (二)指针 `-` 指针
- (三)指针的关系运算
一. 内存和地址
(一)内存
生活中,每个房间有了房间号,就能提高效率,快速的找到房间。对照到计算机中,计算机上CPU(中央处理器)在处理数据时,需要的数据是在内存中读取的,处理后的数据也会放回内存中。
要高效地管理这些内存空间,也是把内存划分为一个个的内存单元,每个内存单元的大小取1个字节,一个字节空间里面能放8个比特位。每个内存单元也都有一个编号(相当于宿舍房间的门牌号),有了这个内存单元的编号,CPU就可以快速找到一个内存空间。在计算机把内存单元的编号称为地址。C语言中给地址起了新的名字叫:指针。
所以可以理解为:内存单元的编号 == 地址 == 指针
计算机中常见的单位:一比特位可以存储一位2进制:1或者0
- 1Byte = 8bit
- 1KB = 1024Byte
- 1MB = 1024KB
- 1GB = 1024MB
- 1TB = 1024GB
- 1PB = 1024TB
(二)如何理解编址
计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。
计算机内有很多的硬件单元,但是硬件与硬件之间是互相独立的。要用"线"连起来,使硬件单元相互之间能进行数据传递来通信从而协同工作。而CPU和内存之间也有大量的数据交互的,所以,两者必须也用线连起来。不过,此处只介绍一组线——地址总线。
32位机器有32根地址总线,每根线只有两态,表示0,1【电脉冲有无】,那么1根线,就能表示2种含义,32根地址线,就能表示232种含义,每一种含义都代表一个地址。地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,再将数据通过数据总线传入CPU内寄存器。
二. 指针变量和地址
(一)取地址操作符&
在C语言中创建变量其实就是向内存申请空间,例如:
上述的代码就是创建了整型变量a,内存中申请4个字节,用于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是:
- 0x65BD0FF934
- 0x65BD0FF935
- 0x65BD0FF936
- 0x65BD0FF937
&a取出的是a所占4个字节中地址较小的字节的地址。
整型变量占用4个字节,只要知道了第一个字节地址,就可以顺藤摸瓜访问到4个字节的数据。
(二)指针变量和解引用操作符*
1. 指针变量
通过取地址操作符(&)拿到的地址是一个数值,例如:0x65BD0FF934,这个数值有时候也需要存储起来,方便后期再使用,把这样的地址值存放在指针变量中。
指针变量也是一种变量,这种变量是用来存放地址的,存放在指针变量中的值都会理解为地址。
例如:
inta=10;int*pa=&a;//取出a的地址并存储到指针变量pa中2. 如何拆解指针类型
上例里的指针pa的类型是int*。
*是在说明pa是指针变量*前⾯的int表示pa指向的对象是整型类型。
例如:ch,。
charch='w';//ch是char类型的变量char*pc=&ch;//所以ch的地址是char*类型3. 解引用操作符
将地址保存起来,怎么使用?
C语言中只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针)指向的对象,需要用到的操作符叫解引用操作符(*)。
inta=100;int*pa=&a;*pa=0;printf("%d\n",a);//0上面代码中,*pa的意思就是通过pa中存放的地址,找到指向的空间,其实就是a变量;*pa = 0,这个操作符就是把a变量的值改成了0。
(三)指针变量的大小
32位机器,有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,把32根地址线产生的2进制序列当做一个地址,那么一个地址就是32个bit位,需要4个字节才能存储,指针变量的大小就得是4个字节的空间才可以。
同理64位机器,有64根地址线,一个地址就是64个二进制位组成的二进制序列,存储起来就需要8个字节的空间,指针变量的大小就是8个字节。
#include<stdio.h>//指针变量的⼤⼩取决于地址的⼤⼩//32位平台下地址是32个bit位(即4个字节)//64位平台下地址是64个bit位(即8个字节)intmain(){printf("%zd\n",sizeof(char*));printf("%zd\n",sizeof(short*));printf("%zd\n",sizeof(int*));printf("%zd\n",sizeof(double*));return0;}结论:
• 32位平台下地址是32个bit位,指针变量大小是4个字节
• 64位平台下地址是64个bit位,指针变量大小是8个字节
• 注意:指针变量的大小和类型无关,只取决于地址线,在相同的平台下,大小都是相同的。
三.指针变量类型的意义
(一)指针的解引用
代码1:
代码2:
调试可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第一个字节改为0。
结论:指针的类型决定了,对指针解引用的时候有多大的权限(一次能操作几个字节)。
例如:char*的指针解引用只能访问一个字节,而int*的指针的解引用能访问四个字节。
(二)指针+-整数
char*类型的指针变量+1,跳过1个字节,int*类型的指针变量+1,跳过了4个字节。
这就是指针变量的类型差异带来的变化。指针+1,其实是跳过1个指针指向的元素。
结论:指针的类型决定了指针向前或者向后走一步有多大(距离)。
(三)void*指针
在指针类型中有一种特殊的类型是void *类型,可以理解为无具体类型的指针,这种类型的指针可以用来接受任意类型地址。但是,void*类型的指针不能直接进行指针的+-整数和解引用的运算。
用途:void*类型的指针在函数参数的部分使用,用来接收不同类型数据的地址,这样的设计使得一个函数可以处理多种类型的数据。
四.指针运算
指针的基本运算有三种,分别是:
- 指针
+-整数 - 指针
-指针 - 指针的关系运算
(一)指针+-整数
数组在内存中是连续存放的,只要知道第一个元素的地址,就能找到后面的所有元素。
方法1:
intarr[10]={1,2,3,4,5,6,7,8,9,10};int*pa=&arr[0];//首元素地址inti=0;for(i=0;i<10;i++){printf("%d ",*(pa+i));//指针不动,i变来改变地址,从而打印数组里的值}方法2:
for(i=0;i<10;i++){printf("%d ",*pa);//指针动,从而打印数组里的值pa++;//缺点:指针改变了,如果后面还要用,要让p回到原来位置,再初始化一下}(二)指针-指针
指针-指针:得两指针之间的元素个数。(前提:两指针指向同一块空间)
例:strlen函数:计算字符串\0之前的字符个数,用指针-指针实现strlen函数。
#include<stdio.h>size_tmy_strlen(char*p){char*start=p;while(*p!='\0')//!='\0'可以去掉,因为\0的ASCll码是0{p++;}returnp-start;//\0的地址-首地址即字符的个数}intmain(){chararr[]={"abcdef"};size_ta=my_strlen(arr);//自定义函数,数组名等价数组首元素的地址printf("%zu\n",a);//6return0;}(三)指针的关系运算
intarr[]={1,2,3,4,5,6,7,8,9,10};int*p=&arr[0];intsz=sizeof(arr)/sizeof(arr[0]);//元素个数while(p<&arr[sz])//只是用一下地址,没有非法访问,用地址来循环.{printf("%d ",*p);p++;}