深入理解指针(1)

05-29 1354阅读

在之前我们学习了许多c语言的基础知识,让我们初步了解了c语言,接下来将来到c语言中一个重点的知识章节--指针,学习完指针后将会让我们对c语言有更深入的理解,接下来就开始指针的讲解

深入理解指针(1)

1.内存与地址

1.指针

在了解内存与地址前,先来看一个生活中的案例

我们都知道当到一个陌生的楼房里时,,如果这个楼房很大通过门牌号去查找将会是一个快速找到的方法,如果得到房间号,就可以快速的找房间
深入理解指针(1)

在生活中有了门牌号就可以快速找到房间,大大提升了效率

如果把上面的例子对照到计算机中,又是怎么样呢?

我们知道在计算机中都有内存,可能是4G/8G/16G等等,但无论是多大的内存都是一份相当大的空间,而数据又是放在内存当中的,我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中
深入理解指针(1)

那在内存这么大的空间里,数据是依据什么来找到的呢?那那些内存空间如何高效的管理呢?

在生活当中把楼房划分成一个个房间,其实在内存当中也是相同的道理,为了更高效的对内存进行使用和管理,其实也是把内存划分为⼀个个的内存单元,每个内存单元的大小取1个字节,我们可以理解为内存像楼房一样被划分成了许多房间,每个房间就是一个内存单元

有的读者会提出1个字节是什么?

这时我们就要知道计算机中的单位

bit - ⽐特位

Byte - 字节         1Byte=8Bit

KB                       1kB=1024Byte

MB                      1MB=1024KB

GB                       1GB=1024MB        

TB                        1TB=1024GB

PB                        1PB=1024TB

 

其中1比特在内存中可以存储1个二进制位中的1或者0

在计算机中的每个内存单元,相当于⼀个学生宿舍,⼀个字节空间里面能放8个比特位,就好比同学们住的八人间,每个人是⼀个比特位。

深入理解指针(1)

为了CPU能更高效调用内存,就如给楼房的房间编号一样,内存中每个内存单元也有不同的编号,这也就是内存单元的地址,在c语言中给这些地址起了新的名字:指针

在此可以理解为:内存单元编号=地址=指针

2.如何理解地址的产生

通过以上的讲解我们知道CPU是通过地址来获取内存中的数据,再将处理后的数据存放在内存当中,那么该过程中内存单元的地址是怎么传送的呢?

首先,必须理解,计算机内是有很多的硬件单元,而硬件单元是要互相协同工作的。但是硬件与硬件之间是互相独立的,要使它们之间建立通信这就需要用"线"连起来

CPU和内存之间也是有大量的数据交互的,所以,两者必须也用线连起来。在这两者之间有地址总线,控制总线,数据总线。在此我们需要了解的是地址总线

深入理解指针(1)

 CPU访问内存中的某个字节空间,必须知道这个字节空间在内存的什么位置,而因为内存中字节

很多,所以需要给内存进行编址。且计算机中的编址,并不是把每个字节的地址记录下来,而是通过硬件设计完成的。

我们可以简单理解,32位机器有32根地址总线,每根线只有两态,表示0,1【电脉冲有无】,那么⼀根线,就能表示2种含义,2根线就能表示4种含义,依次类推。32根地址线,就能表示2^32种含义,每⼀种含义都代表⼀个地址。地址信息被下达给内存,在内存上,就可以找到该地址对应的数据,将数据在通过数据总线传入CPU内寄存器。

2.指针变量与地址

1.取地址操作符(&)

int main()
{
	int a = 20;
	return 0;
}

变量创建的本质其实其实是在内存当中申请内存空间 例如以上代码就申请了4个字节的内存空间来存放20

深入理解指针(1)

以上就是20所存放的内存空间地址,但为什么显示的是14的地址呢?

其实这的14是用16进制表示,转换成10进制就是20

在以上还可以发现20所存放每个字节都有地址,再一次验证了在上文提到的每个内存单元都有地址

通过调试中发现内存中都没有关于a变量的信息,那么a是否对于编译器来说是没有作用的呢?

其实确实是这样的,对于编译器来说通过地址就可以找到内存空间,其实这个a是给我们程序员来看的,让我们知道使用a就是使用对应内存的4个字节

那我们如何才能拿到a的地址呢?

这时就要用到&--(取地址操作符),若在以上代码中加入&a,它的作用就是拿到变量的地址
注:这里的&是一个单目操作符,不要与按位与&混淆,按位与是一个双目操作符

深入理解指针(1)

我们知道变量是有4个地址,那么&之后取出的是哪个呢? 

#include
int main()
{
	int a = 20;
	printf("%p", &a);
	return 0;
}

这时就要用到%p这个占位符,%p的作用在printf函数中是打印出地址

运行程序就可以看到打印出的是a首个字节的地址,所以就可以知道&取出的是第一个字节的地址

2.指针变量和解引用操作符(*) 

1.指针变量 

深入理解指针(1)

 那如果想把变量的地址再存储到另一个变量当中应该如何操作呢?

如果这时将&a存放到pa变量中,那么这个变量的类型就是int*

pa是用来存放指针(地址)的,所以pa是指针变量

int*pa=&a 在这里pa左边写的是 int* , * 是在说明pa是指针变量,而前面的 int 是在说明pa指向的是整型(int)类型的对象

2.解引用操作符

#include
int main()
{
	int a = 20;
	int* pa=&a;
	return 0;
}

在以上代码中pa里存放着a的地址,那如果要通过pa来找到a应该怎么表示呢?
这时就要用到*(解引用操作符),*pa就是通过pa这个指针变量内存放的值找到a

例如要把a的值改成200,就可以通过*pa来实现

#include
int main()
{
	int a = 20;
	int* pa=&a;
    *pa=200;
    printf("%d",a);
	return 0;
}

 这时有的读者会发问为什么要通过pa来将a的值修改,不是可以直接让a=200来实现吗?

在电视剧狂飙中当高齐强身份很高时有一些事就不再适合亲自出手,就比如说他对老墨说我想吃鱼,老墨就会帮他处理棘手的事    有时我们只是拿到了地址信息,这时就只能通过解引用指针变量的方式来改变变量的值

3.指针变量的大小

首先我们要知道指针变量是用来存放地址的,一个地址需要多大空间,那么指针变量就有多大

前面的内容我们了解到,32位机器假设有32根地址总线,每根地址线出来的电信号转换成数字信号后是1或者0,那我们把32根地址线产生的2进制序列当做⼀个地址,那么⼀个地址就是32个bit位,需要4个字节才能存储。

如果指针变量是用来存放地址的,那么指针变量的大小就得是4个字节的空间才可以。

同理64位机器,假设有64根地址线,⼀个地址就是64个二进制位组成的⼆进制序列,存储起来需要8个字节的空间,指针变量的大小就是8个字节

深入理解指针(1)

深入理解指针(1)

结论:

• 32位平台下地址是32个bit位,指针变量大小是4个字节

• 64位平台下地址是64个bit位,指针变量大小是8个字节 
• 指针变量的大小与指针变量的类型无关,只要指针类型的变量,在相同的平台下,大小都是相同的。

深入理解指针(1)
深入理解指针(1)

3. 指针变量类型的意义 

在以上讲解中我们知道了指针变量的大小与指针的类型无关,那么指针变量的类型有什么意义呢?

其实指针变量大小还是有意义的,接下来我们将学习其意义

1. 指针的解引用

深入理解指针(1)
在以上当中将a的值初始化为0x11223344,因为0x之后表示16进制的数,而一个16进制数又可以转化为4个二进制数,两个16进制数就可以转化为8个二进制数,所占空间就为1字节

这样就可以让存储这个数内存当中的每个内存单元都存放数

深入理解指针(1)

当讲&a放入int*类型的指针变量时,*pa会发现a所指向都每个内存单元都被被改成了0,*pa=0一次访问了4个字节

深入理解指针(1) 在以上可以发现当讲指针变量pa的类型改为char*时,就*pa=0就只能将a当中的一个字节改为0,而且他的未改变,*pa=0一次访问了1个字节

 结论:指针的类型决定了,对指针解引用的时候有多大的权限(⼀次能操作几个字节)

2. 指针+-整数

深入理解指针(1)

由以上代码可见&n+与pa+1都使得地址增加了4,而pc+1只让地址增加了1 

深入理解指针(1) 我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。

这就是指针变量的类型差异带来的变化。指针+1,其实跳过1个指针指向的元素。

深入理解指针(1)正如以上代码可以发现指针可以+1,那也可以-1。

深入理解指针(1) 

结论:指针的类型决定了指针向前或者向后走一步有多大(距离) 

3 void* 指针

深入理解指针(1)

在以上代码中可以看到当用char*类型的指针变量pc存储整形变量n的地址时,编译器会爆出以上警告

深入理解指针(1)深入理解指针(1)而当用void*来接收&n时候就不会出现警告

在指针类型中有⼀种特殊的类型是 void * 类型的,可以理解为无具体类型的指针(或者叫泛型指

针),这种类型的指针可以用来接受任意类型地址。 

深入理解指针(1)

注:void* 类型的指针也是有局限性的,不能直接进行指针的+-整数和解引用的运算。

那么 void* 类型的指针到底有什么用呢?
⼀般 void* 类型的指针是使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可以

实现泛型编程的效果。使得⼀个函数来处理多种类型的数据,在之后深入会讲解。 

4. 关键字const 

1.const修饰变量

深入理解指针(1)

当对被const修饰后的变量重新赋值时,程序如以上一样就会报错

上述代码中n是不能被修改的,其实n本质是变量,所以被const修饰后就成为了常变量

只不过被const修饰后,在语法上加了限制,只要我们在代码中对n就行修改,就不符合语法规则,就报错,致使没法直接修改n。

2.const修饰指针变量

⼀般来讲const修饰指针变量,可以放在*的左边,也可以放在*的右边,但意义是不⼀样的

int main()
{
	int a = 20;
	const int*pa=&a;
	*pa = 0;
	return 0;
}

深入理解指针(1)

 

int main()
{
	int a = 20;
	 int const*pa=&a;
	*pa = 0;
	return 0;
}

深入理解指针(1)

深入理解指针(1) 通过以上3个不同的代码可以发现
• const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。

但是指针变量本身的内容可变。

• const如果放在*的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指

向的内容,可以通过指针改变。

5. 指针运算

1.指针+- 整数

深入理解指针(1)

我们知道数组在内存当中是练习存放的,之前找数组元素都是通过数组下标来查找的,学习了指针后,这时就可以通过地址都方法找到数组元素 

#define  _CRT_SECURE_NO_WARNINGS 1
#include
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
   int* p = &arr[0];
   int sz = sizeof(arr) / sizeof(arr[0]);
  for(int i=0;i
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]