信息的表示
2.1.1 十六进制表示法
1) 对应表
十进制 | 十六机制 | 二进制 |
---|---|---|
1 | 1 | 1 |
2 | 2 | 10 |
3 | 3 | 11 |
4 | 4 | 100 |
5 | 5 | 101 |
6 | 6 | 110 |
7 | 7 | 111 |
8 | 8 | 1000 |
9 | 9 | 1001 |
10 | A | 1010 |
11 | B | 1011 |
12 | C | 1100 |
13 | D | 1101 |
14 | E | 1110 |
15 | F | 1111 |
2)i + 4j形式
n的m次方的n进制就是1后面m个0 例如:2的5次方的二进制就是1后面5个0: 100000, 16的2次方的16进制就是1后面2个0: 100
2048=2^11
11=4*2+ 3(2的3次方后面2个0)
所以 16进制形式为 800
2.1.1 字
每台计算机都有一个字长(word size),指明整数和指针数据的标称大小。
2.1.3数据大小
C声明 | 32位机器 | 64位机器 |
---|---|---|
char | 1 | 1 |
short int | 2 | 2 |
int | 4 | 4 |
long int | 4 | 8 |
long long int | 8 | 8 |
char* | 4 | 8 |
float | 4 | 4 |
double | 8 | 8 |
2.1.4寻址和字节顺序
#include <stdio.h>
typedef unsigned char * byte_pointer; // 定义数据类型 char*指针
/**
*以16进制的形式输出字节序列(计算机中的数据都可以看做字节序列)
*@start 字节序列的首个字节的地址
*@len 字节序列的长度
*/
void show_bytes(byte_pointer start, int len){
int i;
for(i = 0; i < len; i++){
printf("%.2x ", start[i]); // 以16进制的形式(至少2个16进制位,2个16进制为刚好可以表示一个字节)输出当前字节
}
printf("\n");
}
/**
*输出int类型数字的字节序列(大端序和小端序刚好相反)
*/
void show_int(int val){
show_bytes((byte_pointer)&val, sizeof(int));
}
void show_float(float val){
show_bytes((byte_pointer)&val, sizeof(float));
}
void show_pointer(void* val){
show_bytes((byte_pointer)&val, sizeof(void *));
}
int main(int argc, char* argv[]){
show_int(12345); // 39 30 00 00 (linux64输出的是小端序), 实际16进制是 00 00 30 39
int val = 0x87654321;
byte_pointer valp = (byte_pointer)&val;
show_bytes(valp, 1); // 21(linxu64,说明是小端序)
show_bytes(valp, 2); // 21 43
show_bytes(valp, 3); // 21 43 65
float b = 12345.0;
show_bytes((byte_pointer)&b, 4); // 00 e4 40 46 ,实际是 46 40 e4 00
return 0;
}
#include <stdio.h>
/**
*
*字节序列的二进制表示(小端序操作系统)
*@start 第一个字节的地址
*@len 字节序列的长度
*/
void show_small_endpoint_bin(char * start, int len){
unsigned char c;
unsigned int n; // 无符号,右移运算只能是逻辑右移,左端补0;有符号数,右移运算会采用算术右移,左端补1
char * p = start;
for(int i = len-1; i >= 0; i--){ // 如 int 占4个字节,因为是小端序,所以倒过来输出
c = p[i];
n = 0x80; // 1000 0000 = 0x80;
for(int j = 0; j < 8; j++){ // 1个字节8个位
if (c & n){
printf("1");
}else {
printf("0");
}
if(j == 3){
printf("-");
}
n >>= 1;
}
printf(" ");
}
printf("\n");
}
int main(int argc, char* argv[]){
unsigned int a = 12345;
float b = 12345.0;
show_small_endpoint_bin((char*)&a, sizeof(unsigned int)); // 编码后计算机内部存放: 0x3930 0000 0000-0000 0000-0000 0011-0000 0011-1001
show_small_endpoint_bin((char*)&b, sizeof(float)); // 编码后计算机内部存放:0x4640e400 0100-0110 0100-0000 1110-0100 0000-0000
return 0;
}
2.1.5 表示字符串
C语言中字符串被编码位一个以null(0)字符结尾的字符数组。
#include <stdio.h>
#include <string.h>
typedef unsigned char * byte_pointer; // 定义数据类型 char*指针
/**
*以16进制的形式输出字节序列(计算机中的数据都可以看做字节序列)
*@start 字节序列的首个字节的地址
*@len 字节序列的长度
*/
void show_bytes(byte_pointer start, int len){
int i;
for(i = 0; i < len; i++){
printf("%.2x ", start[i]); // 以16进制的形式(至少2个16进制位,2个16进制为刚好可以表示一个字节)输出当前字节
}
printf("\n");
}
int main(int argc, char* argv[]){
const char * s = "abcdef";
show_bytes((byte_pointer)s, strlen(s)); //61 62 63 64 65 66
return 0;
}
2.1.6 表示代码
考虑下面的C函数:
int sum(int x, int y){
reurn x + y;
}
当我们在示例机器上编译时,生成的机器码不同。
- Linxu32: 55 89 e5 8b 45 0c 03 45 08 c9 c3
- Windows: 55 89 e5 8b 45 0c 03 45 08 5d c3
- Sun: 81 c3 e0 08 90 02 00 89
- Linux64: 55 48 89 e5 89 7d fc 89 75 f8 03 45 fc c9 c3
2.1.7 布尔代数简介
与 或 非 异或
2.1.8 C语言中的位级运算
转成二进制按位做与 或 非 异或 运算。
位级运算的一个常见例子就是掩码运算。 如掩码 0xFF 表示低8位全是1, 一个整数x=0x89abcdef 与 0xFF做与运算可得到x的低8位,其他位则全是0;
2.1.9 C语言中的逻辑运算
C语言还提供了一组逻辑运算符 ||、&&、!分别对应命题逻辑中的OR、AND、NOT运算。 逻辑运算很容易和位运算相混淆,但它们的功能是完全不同的。逻辑运算认为所有非0的参数 都表示TRUE,而参数0表示FALSE。它们返回1或者0,分别表示TRUE或者FALSE。
2.1.10 C语言中的位移运算
C语言还提供了一组位移运算,以便向左或者向右移动位模式。
- 左移(<<)k位:丢弃最高k位,并在右端补k个0;
- 逻辑右移(>>)k位: 左端补k个0;
- 算术右移(>>)k位: 左端补k个最高有效位的值。
操作 | 值1 | 值2 |
---|---|---|
参数x | 0110 0011 | 1001 0101 |
x<<4 | 00xx 0000 | 0101 0000 |
x>>4(逻辑右移) | 0000 0110 | 0000 1001 |
x>>4(算术右移) | 0000 0110 | 1111 1001 |
C语言标准并没有明确定义应该使用哪种右移。对于无符号数据,右移必须是逻辑的。 而对于有符号数据算术的或逻辑的都可以。然而实际上,几乎所有的编译器都对有符号数据使用算术右移, 且许多程序员也都假设机器会使用这种右移动。
另一方面,java对进行右移有明确的定义,>> 做算术右移, >>>做逻辑右移。