有符号数和无符号数(CSAPP第二章)

写在最前面,先亮出c primer plus中的对有符号数的描述,我觉得也很透彻

如何表示有符号整数取决于硬件而不是c语言
也许表示有符号数最简单的方式是用1位存储符号,剩下七位存储数字本身(哈哈,也就是我们的原码做的事啦~),这种用符号量法表示的数字范围为-127~+127

这种方法的缺点是:+0 和 -0很容易混淆,而且用两个位组合来表示一个值很浪费(这里我理解的是,由于硬件电路的不同,符号位需要用一个字节来存储,后面的数据的7位也需要一个字节来存储,所以用两字节加起来16位空间来存储一个数据,太浪费了)

然后就是用我们的二进制补码的方式来存储有符号数(这里没有提反码的事,有的书上说反码是为了计算补码的,无论如何我们称他为历史前进方向上的牺牲品吧。。。)

二进制补码使用1字节来存储数据,其用一字节的后7位表示0~127,高位用来表示符号,若高位为1则表示负数,为0则表示正数。

该方法是这样区分有符号数和无符号数的:
如一个二进制向量:10000000,如其为无符号数,则为1*2^7=128
若其为有符号数,则其第一位为1,则表示负值,用一个九位数10000000 - 10000000 = 1000000 = 128,则其表示的数为-128(10000000这个数在向量法中是表示为-0的。当然我觉得这里一块用CSAPP的方法更好理解)

1.副作用和序列点

副作用是对数据对象或文件的修改
如 state = 50;它的副作用是使state的值变为50,其主要目的是对表达式50求值
这里介绍一下,c语言中表达式是由运算符和运算对象组成,最简单的表达式是不带运算符的一个常量和变量,所以50也是表达式了。
序列点是程序执行的点,在该点上所有的副作用都在进入下一步之前发生
语句中的分号就标记了一个序列点~

2.一个很好的打印出数字在内存中二进制位的例子
#include<stdio.h>

typedef unsigned char *byte_pointer;

//unsigned char * 一个字节指针引用一个字节序列


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)
{
	printf("show_int:\n");
	show_bytes((byte_pointer)&x,sizeof(int));
}

void show_float(float x)
{
	printf("show_float:\n");
	show_bytes((byte_pointer)&x,sizeof(float));
}

void show_pointer(void *x)
{
	printf("show_pointer:\n");
	show_bytes((byte_pointer)&x,sizeof(void*));
}


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 argc, char const *argv[])
{
	int number;
	scanf("%d",&number);

	test_show_bytes(number);

	return 0;
}

3.以后需要查ascii码值的时候直接man ascii就好了

4.布尔环(加法逆元)

布尔环中每一个元素的加法逆元是其自己本事,不过这里的加法运算是"^"

//对于任何值a,都有下面等式成立
a^a = 0
(a^b)^a = b
5.用向量来表示有限集合

表示方法如下:
位向量 a = [01101001]表示的集合为 A = {0,3,5,9}
也就是第几位置1了,第几位就在集合内

6.c语言中的位运算和逻辑运算

位运算包括: | & ~ ^ (按位进行布尔运算)
逻辑运算包括:|| && ! (所有非0参数都表示true,如果对第一个参数求值就能确定表达式的结果,就不会对第二个参数求值了)

7.与移位运算有关的操作符优先级问题

1<<2+3<<4
表达式先运算的是2+3,然后再计算1<<5<<4

8.整数表示 —— 为什么int等类型取值范围不对称

对于这个问题,我们需要了解一下无符号数和有符号数在内存中是如何存储的,我们都以位向量[1011]来做例子(需要明确在底层其二进制串都是相同的)

无符号数的存储:

[1011]编译方式为:直接加上每个位的权重:1*2^3 + 0 * 2 ^2 + 1 * 2 ^1 + 1 * 2 ^0 = 11,即其表示的数为11

有符号数的存储:

现在我们的计算机已经大多数用的数反码来存储有符号数:
则[1011]编译方式为:第一个权重为1 * (-2 ^ 3) + 0 * 2 ^2 + 1 * 2 ^1 + 1 * 2 ^0 = -5 即其表示的数为-5

这里既然介绍了补码,就不得不介绍原码和反码了。我们先看一下它们的表示形式:

就我自己认为,原码,反码和补码是一个历史进步的过程。最开始人们想要存储有符号数时,就单独拿出来一位来当作符号位,也就是我们的原码。
这样是挺好理解的,但是对于硬件电路的设计来说,每次还要为符号位单独设一个处理方式的电路,就显得很麻烦,于是就有了反码,这里将数字都是统一存储了,只是其第一位所赋予的权重不一样,然后历史进程可能觉得这样的电路也是不好设计吧,毕竟还要设计一个减1,所以就有了现在的补码~

那我们就来解决为什么其上下限不对称的问题:原因就是中间有一个0,占了上限的一个位置啦~

9.有符号数与无符号数之间的转换


10.强制类型转换:其底层存储的二进制位还是没有改变,变的是解释这些位的方式
11.扩展与截断

扩展有两种:符号扩展和零扩展
右移有两种:逻辑右移(直接填0)和算数右移(填的是最高位)

无符号数是0扩展,有符号数是符号扩展
在这里插入图片描述
在这里插入图片描述

12.从一个数据大小变成另一个数据大小

把short类型的数据转换成unsigned类型的时候:先改变大小,在完成有符号到无符号的转变
也就是说 (unsigned) x 等价于 (unsigned) (int) x
而不是 (unsigned) (unsigned short) x

13.整数的加减法运算

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_37414405/article/details/85171753