您的位置:首页 > 移动开发

[CSAPP]信息的表示和处理

2017-12-10 12:09 302 查看
第二章信息的表示和处理主要研究三种最重要的数字表示:

1)无符号(unsigned)编码基于传统的二进制表示法,表示大于或者等于零的数

2)补码(two’s-complement)编码是表示有符号数的最常见的方式,有符号整数就是可以为正或负的数字

3)浮点数(floating-point)编码是表示实数的科学计数法的以2位基数的版本。

浮点运算是不可结合的,例如在多数机器上表达式(3.14+le20)-le20求得的值会是0.0,而3.14+(le20-le20)求得的值是3.14.

整数运算和浮点运算会有不同的数学属性是因为他们处理数字表示有限性的方式不同:整数的表示虽然只能编码一个相对叫小的数值范围,但是这种表示是精确地;

浮点数虽然可以编码一个较大的数值范围,但是这种表示只是近似的。

大多数计算机使用8位的块,或者字节(byte),作为最小的可寻址的内存单位,而不是访问内存中单独的位。机器级程序将内存视为一个非常大的字节数组,称为虚拟内存。内存的每个字节都由一个唯一的数字来标识,称为它的地址,所有可能地址的集合就称为虚拟地址空间。虚拟地址空间只是一个展现给机器级程序的概念性映像。

寻址和字节顺序

最低有效字节在最前面的方式称为小端法。

最高有效字节在最前面的方式称为大端法。

假设变量x的值是0x01234567,类型为int,位于地址0x100处:

大端法:

0x1000x1010x1020x103
01234567
小端法:

0x1000x1010x1020x103
67452301
下面是一个关于大端 小端方式非常经典的例子:

#include <stdio.h>
typedef unsigned char * byte_pointer;
void show_bytes(byte_pointer start,size_t len)
{
size_t i;
for(i = 0 ;i < len;i++)
{
printf(" %.2x",start[i]);
}
printf("\n");
}

void show_int(int x)
{
show_bytes((byte_pointer)&x, sizeof(x));
}

void show_float(int x)
{
show_bytes((byte_pointer)&x, sizeof(x));
}

void show_pointer(void *x)
{
show_bytes((byte_pointer)&x, sizeof(x));
}
void test_show_bytes(int val)
{
int ival = val;
float fval = (float)ival;
int *pval = &ival;
show_int(ival);
show_float(fval);
show_pointer(pval);

}

int main()
{
int x = 0x00003039;//12345的十六进制形式
test_show_bytes(x);

return 0;
}


程序输出:

39 30 00 00

39 30 00 00

c8 f5 bf ef fe 7f 00 00

上面代码中使用typedef将数据类型byte_pointer定义为一个指向类型为unsigned char的对象的指针,这样一个字节指针引用一个字节序列,其中每个字节都被认为是一个非负整数。

函数show_int() show_float() show_pointer()中调用show_bytes时将指针强制类型转换为unsigned char *。这种强制类型转换告诉编译器,程序应该把这个指针看成指向一个字字节序列,而不是指向一个原始数据类型的对象。

逻辑运算

在逻辑运算中比较常用的是如下形式

a&&(5/a):将不会造成被零除

p&&*p++:不会导致间接引用空指针

C语言中移位操作

移位预算从左至右结合。

左移:向左移动k位,丢弃最高的k位,并在右端补k个0。

右移:逻辑右移和算术右移

1)逻辑右移

逻辑右移在左端高位补k个0.

2)算术右移

算术右移是在左端补k个最高有效位的值,对有符号整数数据的运算非常有用。

#include<stdio.h>
#include <string.h>
typedef unsigned char * byte_pointer;
void show_bytes(byte_pointer start,size_t len)
{
size_t i;
for(i = 0 ;i < len;i++)
{
printf(" %.2x",start[i]);
}
printf("\n");
}

void show_int(int x)
{
show_bytes((byte_pointer)&x, sizeof(x));
}

void show_float(int x)
{
show_bytes((byte_pointer)&x, sizeof(x));
}

void show_pointer(void *x)
{
show_bytes((byte_pointer)&x, sizeof(x));
}
void test_show_bytes(int val)
{
int ival = val;
float fval = (float)ival;
int *pval = &ival;
show_int(ival);
show_float(fval);
show_pointer(pval);

}

float sum_element(float a[],unsigned length)
{
int i;
float result = 0;
for(i = 0;i <= length-1;i++)
{
printf("length-1 = %u\n",length-1);
result += a[i];
}
return result;
}
int main()
{
short x = 12345;
short mx = -x;
show_bytes((byte_pointer)&x,sizeof(short));
show_bytes((byte_pointer)&mx,sizeof(short));

unsigned int u = 4294967295;
int tu = (int )u;
printf("u = %u,tu = %d\n",u,tu);

/*有符号无符号之间的转换*/
int m = -1;
unsigned n = 2147483648;
printf("m= %u = %d\n",m,m);
printf("n= %u = %d\n",n,n);

/*扩展一个数字的表示,注意函数show_bytes的第二个参数*/
short sx = -12345;
unsigned short usx = sx;
int ix = sx;
unsigned ux = usx;
show_bytes((byte_pointer)&sx,sizeof(short));

show_bytes((byte_pointer)&usx,sizeof(short));

show_bytes((byte_pointer)&ix,sizeof(int));

show_bytes((byte_pointer)&ux,sizeof(unsigned));

/*从一个数据大小到另一个数据大小的转换,以及无符号和有符号数字之间的转换的相对顺序能够
影响一个程序的行为,考虑如下代码。在上面的代码中转换使用了short,而下面的代码并没有使用short,因此上面的转换等价于(unsigned)(unsigned short)sx,转换后得到53191,而下面的转换等价于:(unsigned)(int)sx,求值得到4294954951.
*/
short small = -12345;
unsigned large = small;

printf("large = %u:\t",large);

show_bytes((byte_pointer)&large,sizeof(unsigned));

return 0;
}


float sum_element(float a[],unsigned length)
{
int i;
float result = 0;
for(i = 0;i <= length-1;i++)
{
printf("length-1 = %u\n",length-1);
result += a[i];
}
return result;
}


当length=0时代码存在bug,-1转换为无符号,即UMAX即4294967295。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  csapp 补码 原码 反码