1. 程式人生 > >有符號數和無符號數(CSAPP第二章)

有符號數和無符號數(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.整數的加減法運算

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述