在讲解指针之前,先了解一下,数据怎么在内存中存储的,什么是变量和数据类型
数据在内存中存储
计算机要处理的信息是多种多样的,如数字、文字、符号、图形、音频、视频等,但是对于计算机来说,它们在内存中都是一样的,都是以二进制的形式来表示。
这里做一个关于内存条的简单讲解,内存条是一个非常精密的部件,包含了上亿个电子元器件,它们很小,达到了纳米级别。这些元器件,实际上就是电路;电路的电压会变化,要么是 0V,要么是 5V,只有这两种电压。5V 是通电,用1来表示,0V 是断电,用0来表示。所以,一个元器件有2种状态,0 或者 1。
我们通过电路来控制这些元器件的通断电,会得到很多0、1的组合。例如,8个元器件有 28=256 种不同的组合,16个元器件有 216=65536 种不同的组合。虽然一个元器件只能表示2个数值,但是多个结合起来就可以表示很多数值了。
1个元器件称为1比特(Bit)或1位,8个元器件称为1字节(Byte)
以此类推:
- 8×1024个元器件就是1024Byte,简写为1KB;
- 8×1024×1024个元器件就是1024KB,简写为1MB;
- 8×1024×1024×1024个元器件就是1024MB,简写为1GB。
一般情况下我们不一个一个的使用元器件,而是将8个元器件看做一个单位,即使表示很小的数,例如 1,也需要8个,也就是 00000001。在这里大家就会有疑问,有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用 0 和 1 表示足以,那么我们该怎么做呢,c语言里面提供了一个叫位域(位段)的概念,只能用在结构体里面,在结构体定义时,我们可以指定某个成员变量所占用的二进制位数(Bit),基本上很少用到,就不讲解了,大家只需要理解8位为一个字节就行,一个字节为一个单位(计算机为什么要以8位为一个字节,大家有兴趣的可以下去查询)。
从上面讲解得知,所有的数据都以二进制的形式进行存储,多个数据在内存中是连续存储的,彼此之间没有明显的界限。
变量和数据类型
变量
既然知道数据在内存怎么存储了,那么我们接下来就要操作这块内存了,操作之前就需要知道这块内存的地址,比如
int a;
a=10;
这个语句的意思是:在内存中找一块区域,命名为 a,用它来存放整数10。
因为 a 的值可以改变,所以我们给它起了一个形象的名字,叫做变量(Variable)。int a;创造了一个变量 a,我们把这个过程叫做变量定义。a=10;把10交给了变量 a,我们把这个过程叫做给变量赋值;又因为是第一次赋值,也称变量的初始化,或者赋初值。
数据类型
变量是给这块内存起的名字,有了变量就可以找到并使用这份数据,实际上a会转换成一个地址,这块内存的首地址,多个数据在内存中是连续存储的,彼此之间没有明显的界限,如果不明确指明数据的长度,计算机就不知道何时存取结束,即使数据取出来后,是数字还是字符呢?诸如数字、文字、符号、图形、音频、视频等数据都是以二进制形式存储在内存中的,它们并没有本质上的区别。所以存储数据的时候不只需要地址,还有数据的长度和处理方式。
上面的int a;就表明,这份数据是整数,不能理解为像素、声音等。int 有一个专业的称呼,叫做数据类型(Data Type)。在C语言中,有多种数据类型,例如:
说 明 | 字符型 | 短整型 | 整型 | 长整型 | 单精度浮点型 | 双精度浮点型 | 无类型 |
---|---|---|---|---|---|---|---|
数据类型 | char | short | int | long | float | double | void |
长 度 | 1 | 2 | 4 | 4 | 4 | 8 | 没有指定长度 |
数据类型除了指明数据的解释方式,还指明了数据的长度
数据是放在内存中的,在内存中存取数据要明确三件事情:数据存储在哪里、数据的长度以及数据的处理方式。大家要记住这句话,以后程序中所有对内存的操作,都是围绕这三个方面
指针
计算机中所有的数据都必须放在内存中,为了正确地访问这些数据,必须为每个字节都编上号码,就像门牌号、身份证号一样,每个字节的编号是唯一的,根据编号可以准确地找到某个字节。我们将内存中字节的编号称为地址(Address)或指针(Pointer)
大家要记住地址是字节(Byte)的编号,而不是位(Bit)的编号。
之前讲过CPU 访问内存时需要的是地址,而不是变量名和函数名!变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,它们都会被替换成地址。编译和链接过程的一项重要任务就是找到这些名称所对应的地址。
如果一块内存中存的是int类型的数据,我们就把这块内存叫做int型变量,那么如果这块内存存的是地址呢,我们就称它为指针变量,指针变量里面的值就是某一块内存的首地址(内存是连续的),而这一块内存里面可以存储数组、字符串、函数,也可以是另外的一个普通变量或指针变量。
定义指针变量与定义普通变量非常类似,不过要在变量名前面加星号*,格式为:
datatype *name;
或者
int *p;
含义是p是指向int类型的指针变量(实际上这句话可不好理解,我自己的理解就是内存储存数据需要三个条件,地址、长度和处理方式,我们定义一个指针变量的时候,需要传入一个指针(也叫地址),光一个指针可不行,我们还需要长度和处理方式,所以需要加上int类型,就组成了int *p),实际上只要记住一句话,内存的操作需要三个条件,地址、长度和处理方式,再复杂的指针好理解。
int a = 100;
int *p_a = &a;
p_a 需要的一个地址,a 前面必须要加取地址符&,否则是不对的,*是一个特殊符号,表明一个变量是指针变量,指针变量存储了数据的地址,通过指针变量能够获得该地址上的数据,格式为:
*p_a ;
这里的 * 称为指针运算符,用来取得某个地址上的数据,看到这里大家就会有疑问,* p_a 和a取值是不是一样的,实际上最后得到的值肯定是一样的,但是对于cpu来说,* p_a比a多了一步操作,因为* p_a本身也标明是一块内存,程序被编译和链接后,a、p_a 被替换成相应的地址,cpu会通过p_a本身的地址,然后再取得变量 p _a本身的值,这个值是变量 a 的地址,然后再通过这个值取得变量 a 的数据,前后共有两次运算;而使用 a 的话,可以通过地址 0X1000 直接取得它的数据,只需要一步运算。