2.1 信息存储
-
8 位的块代表一个字节,作为最小的可寻址内存单位,而不是直接访问内存中单独的位。内存的每个字节都由唯一的一个数字来标识,成为它的地址,所有可能地址的集合被称为
虚拟地址空间(virtual address space)
理解:内存中是一位一位的块,且是2
进制的,每八位代表一个字节块,而计算机中使用的就是这个块, 而每个块的起始由一个数字表示。
在现代计算机中,以32
位操作系统来举例,cpu只能寻址32位的话,那么表示能寻到的地址范围是
0^0 ~ 0^32-1
那么最终的结果就是0^32 / 1024 / 1024 = 4GB.
-
C
语言中,一个指针的值都是某个存储块的第一个字节的 虚拟 虚拟 虚拟地址,C
编译器把每个指针和类型
信息联系起来,这样就可以根据指针值得类型,生成不通得机器级代码,来访问存储在指针所指向位置处得值。但生成得实际的机器码并 不包含 不包含 不包含关于数据类型的信息。
- question: 指针本身是什么,存储在哪儿?
- 指针的存储:指针本身作为一个变量,它的值(即它指向的地址)存储在内存中的某个位置。这个位置的地址是由操作系统的内存管理单元
(MMU)
和编译器在程序运行时动态分配的。 - 地址的分配:当程序创建一个指针变量时,操作系统和编译器会决定这个指针变量的存储位置。这个过程涉及到内存分配算法,如堆分配、栈分配或全局/静态存储区分配。
- 指针的存储:指针本身作为一个变量,它的值(即它指向的地址)存储在内存中的某个位置。这个位置的地址是由操作系统的内存管理单元
-
“32位”程序或“64位”程序取决于该程序是如何编译的,而不是其运行的机器类型。比如,使用
linux> gcc -m32 prog.c
编译的程序,可以同时在32
位和64
位机器上运行,反之,-m64
就只能在64
位机器上运行。 -
随着标准的变化,在C语言中,建议使用指定大小的类型,比如:
int32_t
和int64_t
来避免不同架构或机器上的位的差别。 -
在现代计算机的历史中,为什么说,一个声明为int类型的程序对象能被用来存储一个指针,这在大多数32位的机器上能够正常工作,但是在一台64位的机器上却会导致问题?
int
类型的对象在32
位和64
位大小一般都为4
字节, 但sizeof(pointer*)
在64
位机器上为8
字节,用内存占据4
字节的类型对保存8
字节的指针信息,是不够的,会有内存溢出问题。
大小端
排列表示一个对象的字节有两个通用的规则。考虑一个 w
位的整数,其位表示为:
[X_w-1, X_w-2, ..., X_1, X_0]
, 其中,X_w-1
是最高有效位,而X_0
是最低有效位。假设w
是8
的倍数,这些位就能被分组为字节,其中最高有效字节包含位[X_w-1, X_w-2, ..., X_w-8]
, 而最低有效字节包含[X_7, X_6, ..., X_0]
。
某些机器选择在内存中按照从最低有效字节到最高有效字节的顺序存储对象,这种是----小端存储
某些机器选择在内存中按照从最高有效字节到最低有效字节的顺序存储对象,这种是----大端存储
一个测大小端的 c
代码示例, 只有核心部分:
char *c = NULL;
int value = 0x12345678;
int i = 0;
for (i; i < 4; i++) {
c = (char*)&value + i;
printf("0x%.2x\n", *c);
}
补码/反码/原码
目前的理解,所谓的补码,源码,反码,就是对第一个也就是最高位的处理方式的不同。
源码和反码我很少遇到过。
补码常用于做有符号数和无符号数的转换
其规则: 数值可能会改变,但是位模式不变
- 补码,w 代表位数
B 2 T w = − X w − 1 ⋅ 2 w − 1 + ∑ i = 0 w − 2 x i 2 i B2T_w = -X_{w-1}·2^{w-1} + \sum_{i=0}^{w-2}x_i2^i B2Tw=−Xw−1⋅2w−1+i=0∑w−2xi2i
int a = -1;
gdb: p/t a
gdb: 11111111111111111111111111111111
-
反码
+0: [00…0]
-0: [11…1]
B 2 O w = − X w − 1 ⋅ ( 2 w − 1 − 1 ) + ∑ i = 0 w − 2 x i 2 i B2O_w = -X_{w-1}·(2^{w-1}-1) + \sum_{i=0}^{w-2}x_i2^i B2Ow=−Xw−1⋅(2w−1−1)+i=0∑w−2xi2i -
原码
+0: [00…0]
-0: [10…0]
B 2 O w = ( − 1 ) x w − 1 ∑ i = 0 w − 2 x i 2 i B2O_w = (-1)^{x_{w-1}}\sum_{i=0}^{w-2}x_i2^i B2Ow=(−1)xw−1i=0∑w−2xi2i
乘以常数
大多数机器上,乘法指令相当慢,需要 10
个或者更多的时钟周期,但其他的整数运算,如加法、减法、位级运算和移位只需要 1
个始终周期。
编译器会使用一些优化手段,试着用移位和加法运算的组合来代替乘以常数因子的乘法。
x ∗ 14 − − > 2 3 + 2 2 + 2 1 o r ( x < < 4 ) − ( x < < 1 ) / / ( 14 = = 2 4 − 2 1 ) x * 14 --> 2^3 + 2^2 + 2^1 \ or \ (x << 4) - (x << 1) //(14 == 2^4 - 2^1) x∗14−−>23+22+21 or (x<<4)−(x<<1)//(14==24−21)
- 无论是无符号还是有符号(补码)运算,乘以
2
的幂都有可能会导致溢出. (也就是左移操作)
浮点数,二进制小数
十进制:
d
=
∑
i
=
−
n
m
1
0
i
×
d
i
十进制: d = \sum_{i=-n}^m10^i×d_i
十进制:d=i=−n∑m10i×di
因此
12.3
4
10
=
1
∗
1
0
1
+
2
∗
1
0
0
+
3
∗
1
0
−
1
+
4
∗
1
0
−
2
=
12
∗
34
100
12.34_{10} = 1*10^1 + 2*10^0 + 3*10^{-1} + 4*10^{-2} = 12*\frac{34}{100}
12.3410=1∗101+2∗100+3∗10−1+4∗10−2=12∗10034
对于有限长度的编码,那么十进制表示法不能准备表达像
1
3
\frac{1}{3}
31和
5
7
\frac{5}{7}
75这样的数。类似,小数的二进制表示法只能表示能够被写成
x
∗
2
y
x*2^y
x∗2y的数。其他的值只能够被近似地表示