2.1
重点
- 计算机的表示法是用有限数量的位来对数字编码,所以当结果太大以致不能表示时,某些运算就会溢出。
- 通过了解数字的实际表示,我们能够了解可以表示的值的范围和不同算术计算的属性。
- 计算机中使用8位的字节来作为最小的可寻址的内存单位,而不是访问内存中单独的位。
- 机器级程序将内存视为一个非常大的字节数组,称为虚拟内存。
- 内存中的每个字节都由一个唯一的数字来标识,称为他的地址。
- 所有地址的集合就称为虚拟地址空间。
16进制表示法
- 十六进制使用0-9以及字符A-F来表示16个可能的值。
+-------+-------+-------+--------+--------+------+------------+-------------------------+
十六进制| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F |
+-------+---+-------+--------+--------+------+------------+-------------------------+---+
十进制 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
+-------+------+-------+--------+--------+------+------------+--------------------------+
二进制 |0000|0001|0010|0011|0100|0101|0110|0111|1000|1001|1010|1011|1100|1101|1110|1111|
+-----+-------+-------+--------+--------+------+------------+---------------------------+
- 以0X或者0x开头的数字通常被认为是十六进制的值,字符A-F可以大写也可以小写
- 二进制转化为十六进制:如果位总数不是4的倍数,那就从右向左切分,少于4位用0补足。
0x39A7F8 ----> 0011 1001 1010 0111 1111 1000
1100100101111011 ----> 1100 1001 0111 1011 -----> 0xC97B
0xD5E4C -----> 1101 0101 1110 0100 1100
1001101110011110110101 ----> 0010 0110 1110 0111 1011 0101 ---> 0x26E7B5
字数据大小
对一个字长为N位的机器而言,虚拟地址的范围为0-2^N-1
- 32位程序与64位程序的区别在与程序是如何编译的。
- 使用确定类型大小的整数类型是程序员控制数据表示的最佳途径
- 程序员应该力图使他们的程序在不同的机器上和编译器上可移植,可移植性的一个方面就是程序对于不同数据类型的确切大小不敏感
寻址和字节顺序
- 小端字节序与大端字节序
- 字节顺序的重要性:
- 网络数据传输过程中
- 阅读表示整数数据的字节序列时
- 当编写规避正常的类型系统的程序时
- sizeof(T)返回存储一个类型为T的对象所需要的字节数
- 使用sizeof而不是一个固定的值是向编写在不同类型上可移植的代码迈进了一步。
#include<stdio.h>
typedef unsigned char* show_bytes;
void show_int(show_bytes start, size_t len)
{
size_t i;
for(i = 0; i < len; i++)
{
printf("%x ", start[i]);
}
printf("\n");
}
char* itobs(int n, char* ps)
{
int i;
const int size = 8 * sizeof(int);
for (i = size - 1; i >= 0; i--, n >>= 1)
{
ps[i] = (01 & n) + '0';
}
ps[size] = '\0';
return ps;
}
void show_bstr(const char* str)
{
size_t i = 0;
while (str[i])
{
putchar(str[i]);
if (++i % 4 == 0 && str[i])
putchar(' ');
}
printf("\n");
}
int main(void)
{
int x = 0x8;
char strBits[8 * sizeof(int) + 1] = {0};
show_bytes start = (show_bytes)&x;
itobs(x, strBits);
printf("sizeof(show_byte) = %d, &start= %x\n", sizeof(start), *start);
show_int(start, 1);
show_int(start, 2);
show_int(start, 3);
show_int(start, sizeof(x));
show_bstr(strBits);
//show_int(start, 5);
*start = 0x90;
printf("x = %x\n", x);
}
结果:
78
78 56
78 56 34
78 56 34 12
0001 0010 0011 0100 0101 0110 0111 1000
x = 12345690
- typedef 声明提供一种给数据类型命名的方式
- 强制类型转换可以将一种数据类型转换为另一种,他们不会改变真实的指针,只是告诉编译器以新的数据类型来看待被指向的数据
表示字符串
- C语言中字符串被编码为一个以null(其值为0)字符结尾的字符数组。
- 文本数据比二进制数据具有更强的平台独立性。
布尔代数简介
a = 01101001
b = 01010101
~a = 10010110
~b = 10101010
a & b = 01000001
a | b = 01111101
a ^ b = 00111100
C语言中的位级运算
- 确定一个危机表达式的结果最好的方法,就是将十六进制的参数扩展成二进制表示并执行二进制运算,然后再转换为十六进制。
a b
a a^b
b a^b
b a
- 位级运算的一个创建用法就是实现掩码运算,掩码是一个位模式,表示从一个字中选出位的集合。
- ~0将生成一个全1的掩码。
0x87654321 & 0xff = 0x00000021
0x87654321 | 0xff = 0x876543ff
0x87654321 ^ ~0xff = 0x87654321 ^ 0xffffff00 = 0x789ABC21
C语言中的逻辑运算
- 按位运算只有在特殊情况,也就是参数被限制为0或者1时,才和对应的逻辑运算有相同的行为。
- 如果对第一个参数求值就能确定结果,那么逻辑运算符就不会对第二个参数求值。
C语言中的移位运算
x = 01100011 10010101
x << 4 = 00110000 01010000
x >> 4(逻辑右移) 00000110 00001001
x >> 4(算术右移) 00000110 11111001
- 几乎所有的编译器都对有符号数使用算术右移。对于无符号数,右移必须是逻辑的。
在许多机器上,当移动一个N位的值时,移位指令只考虑位移量的低log_2N
- 因此实际位移量是通过计算K mod N来得到的。不过对于C语言程序时没有保证的,所以应该按照位移量小于待移位值的位数。