核心观点:类型约定了对数据的解释方式
信息和数据
来自 wikipedia 的定义
wikipedia 上的定义真的是太晦涩了
在我的理解中,数据就是不带有任何属性的数字。例如一个数字1,就是一个单纯的数据。
仅通过一个数字 1,你是无法得到任何信息的,因为你不知道这个1到底代表什么意思。只有结合具体的语境,你才能从中获得有用的信息。
例如1本书、1个人、1头猪等等
上面的书、人、猪实际上就是对数据的解释方式。他们分别将 1 解释为书的数量、人的数量、猪的数量。
因此为了获取信息,除了知道数据本身外,你还得需要知道对数据的解释方式。
所以,信息 = 数据 + 数据的解释方式
想一想谍战片子里面的电报机,电报机所发送的实际上是一些长短不一的摩尔斯电码,这些摩尔斯电码就是数据,外部人员不知道这些电码代表的是啥,但是专业的谍报人员对其翻译后,就可以从其中获取到有用的信息。这里的翻译实际上就是在对数据进行解释。翻译工作可能是依靠一个密码本来进行的,密码本上约定了数据的解释方式。
例子
l i v e
这四个字母,如果用从左至右的方式来解释,将是 live,如果用从右至左的方式来解释,将是 evil。
1 0 1 1
这四个数字,如果每两个一组进行解释,将会得到
10 11
如果每四个一组进行解释,将会得到
1011
同样的数据,使用不同的解释方式,就能得到不一样的信息。
类型约定了对数据的解释方式
c 语言中有如下一些基础数据类型,摘自https://www.runoob.com/cprogramming/c-data-types.html
整数类型
浮点类型
再回顾一下我的核心观点:类型约定了对数据的解释方式
在这里我就取 char 类型和 short 类型来解释。char 类型的大小是 1 字节,short 类型的大小是 2 字节。一字节 byte 等于 8 bit,一个 bit 就是一个二进制位,值为 0 或 1。实际上 char 约定了以 8 个 bit 为一组来解释,而 short 则约束了以 16 个 bit 为一组来解释。
现在有一个 16 个 bit 大小的空间,空间里的内容为:
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
如果用 char (每 8 个 bit 为一组)来解释,则可以解释为两个 char,char0 = 00000000 = 0,char1 = 00000001 = 1。
如果用 short (每 16 个 bit 为一组)来解释,则可以解释为 1 个 short,short0 = 0000000000000001 = 1。
变量(variable) = 地址(address) + 类型(type)
一个变量由两个要素组成,分别是变量的地址,以及变量的类型。
address 确定了存放变量的字面值的空间位置
type 约定了如何解释这段空间
在 c 语言中,初始化一个变量的方式是:
char a = 1;
例如在这里,
- 变量 a 的 address 是 &a
- 类型是 char,它决定了将会以 8 个 bit 为单位解释 address 中的内容
- a 的字面值是 1,字面值是通过 address 和 char 通过得到的
在 address 处存放了数据本体,利用 type 来解释数据,最终得到字面值这个信息。
对变量的访问是通过访问该变量的地址来完成的
在 c 语言中,对于变量的访问实际上都是通过访问该变量的地址来完成的。但是只有地址还不够,如何解释这篇地址的内容,是以 8 个 bit 为单位亦或者是以 16 个 bit 为单位,这就需要用到类型了。
变量在初始化时,会被分配一个地址。这个地址就是变量的家,变量的地址是不会发生改变的,它一辈子只会有一个家,不会搬家。
变量的初始化
char c = 1;
在这行代码里,实际上会有如下两步操作:
为变量 c 分配一个空间,这个空间就是 c 的家,假设 c 住在地球一号村,二号楼0号房间吧,就用 0x120 来表示 c 的地址吧。
将 0x120 ~ 0x128 这 8 个 bit 的空间里的内容初始化为 1(其实 c 里面地址的单位是 1 个 byte,而非 bit),变成下面这个样子:
为何是 8 个 bit 而不是其他数字的 bit?这是 c 的类型所决定的,因为类型是 char,所以是 8 个 bit。
对 c 的修改
c = 2
实际上还是分为两步:
第一步还是先得到 c 的地址,之前说过变量的地址是不会发送改变的,因此这里变量 c 的地址也就是 0x120
将 0x120 ~ 0x128 这 8 个 bit 的空间里的内容初始化为 2,变成了下面这样:
对 c 的访问
char i = c;
- 得到 c 的地址 0x120
- 对 0x120 ~ 0x128 这 8 个 bit 大小的空间进行解释。类型约定了对数据的解释方式,而这里的类型是 char,所以用 8 个 bit 的方式来解释。
- 对 i 的操作也是类似的...
指针
指针变量本质和普通的变量没有太大区别,依旧由值和类型组成,同样也会被分配一个地址。
但是它相比普通变量多了一个 *
(dereference) 操作。dereference 是指针变量所特有的操作,普通的变量是无法进行 dereference 操作的,否则会发生编译错误。
定义一个指针变量
char* p = 0x120;
p 的值是 0x120 ,p 的类型是 char* (指向 char 类型的指针)。
char* = char + *
* 表明这个变量是一个指针,因此有 dereference 能力
char 约定了在 dereference 时,它将会以 8 个 bit为 单位来解释
deference
通过 dereference ,可以对地址中的内容进行操作。
*p = 1
这句代码将会把 0x120 ~ 0x128 这 8 个 bit 的空间里的内容置为 1。
对指针变量的修改
p = 2
与上面所说的变量的修改过程是一样。
一些具体的例子
基本原则
- 变量 = address + type
- 变量会被分配一个地址,且这个地址是固定的
- 对变量的访问是通过访问变量的地址来完成的,变量的类型约定了对地址中内容的解释方式
例子1:通过指针来修改变量的值
char c = 1; // 为 c 分配地址,假设 c 的地址是 0x120,c 的值是 1,0x120 ~ 0x128 这 8 个 bit 的内容是 1
char* pc = &c; // pc 是一个指针,它的值是 c 的地址,则 pc 的值是 0x120
*pc = 2; // dereference 操作,将 0x120 ~ 0x128 这 8 个 bit 的内容设置为 2
printf("%d",c); // 取 0x120 ~ 0x128 这 8 个 bit 的内容进行解释,得到 2,因此输出 2
例子2:指向指针的指针
char* pc = null; // 为 pc 分配地址,假设 pc 的地址是 0x1200,pc 的值是 0,0x1200 ~ 0x1220 这 32 个 bit 的内容是 0(在 32 为环境下,指针的大小是 32 bit)
char** ppc = &pc; // ppc 的值为 0x1200
char c = 1; // 为 c 分配地址,假设 c 的地址是 0x120,c 的值是 1,0x120 ~ 0x128 这 8 个 bit 的内容是 1
*ppc = &c; // 将 0x1200 ~ 0x1220 这 32 个 bit(char*) 的内容设置为 0x120,此时 pc 的值为 0x120 了
*pc = 2; // 取 0x120 ~ 0x128 这 8 个 bit(char) 的内容设置为 2, 此时 c 的值变成 2 了