c語言初學習問題集錦

1.    爲什麼整型可以賦給char型?

  •   因爲字符說到底還是用一個無符號整型來表示的,具體每個字符用什麼值來表示請見ascii碼錶。。
    former=0就相當於把ascii碼錶裏用0表示的字符賦值給了former,你既可以把這個字符以char型打印出來,也可以把它當無符號整數0來操作。在c語言裏,甚至可以對char型加加減減。
  •   字符型本就是以ASCII存的,就是整型
  • 系統自動進行了隱式轉換,但int——>char可能造成數據丟失
3.    內置類型爲什麼具有臨時量?;長性?在哪存的?什麼導致的?
a:臨時變量產生於:1.值傳遞   2.強制類型轉換
4.    Eaxebxecxedxedi esi edpesp?(以下結果爲百度複製粘貼尷尬
a:

eax, ebx, ecx, edx, esi, edi, ebp, esp等都是X86 彙編語言中CPU上的通用寄存器的名稱,是32位的寄存器。如果用C語言來解釋,可以把這些寄存器當作變量看待。

比方說:add eax,-2 ;   //可以認爲是給變量eax加上-2這樣的一個值。

這些32位寄存器有多種用途,但每一個都有“專長”,有各自的特別之處。

EAX 是"累加器"(accumulator), 它是很多加法乘法指令的缺省寄存器。

EBX 是"基地址"(base)寄存器, 在內存尋址時存放基地址。

ECX 是計數器(counter), 是重複(REP)前綴指令和LOOP指令的內定計數器。

EDX 則總是被用來放整數除法產生的餘數。

ESI/EDI分別叫做"源/目標索引寄存器"(source/destination index),因爲在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目標串.

EBP是"基址指針"(BASE POINTER), 它最經常被用作高級語言函數調用的"框架指針"(frame pointer). 在破解的時候,經常可以看見一個標準的函數起始代碼:
  
  push ebp ;保存當前ebp
  mov ebp,esp ;EBP設爲當前堆棧指針
  sub esp, xxx ;預留xxx字節給函數臨時變量.
  ...
  
  這樣一來,EBP 構成了該函數的一個框架, 在EBP上方分別是原來的EBP, 返回地址和參數. EBP下方則是臨時變量. 函數返回時作 mov esp,ebp/pop ebp/ret 即可.

ESP 專門用作堆棧指針,被形象地稱爲棧頂指針,堆棧的頂部是地址小的區域,壓入堆棧的數據越多,ESP也就越來越小。在32位平臺上,ESP每次減少4字節。

 

386部分寄存器:

5.    Mov push pop call ret jmp?
a:

mov

傳送指令MOV

1.立即數送寄存器或主存 MOV reg/mem , im

mov al , 4 ; al←4,字節傳送

mov cx , 0ffh ; cx←00ffh,字傳送

mov si , 200h ; si←0200h,字傳送

mov byte ptr [si] , 0ah ; byte ptr 說明是字節操作

mov word ptr [si+2 ], 0bh ; word ptr 說明是字操作

# 注意立即數是字節量還是字量

# 明確指令是字節操作還是字操作


2.寄存器送(段)寄存器或主存 MOV reg/mem/seg , reg;
mov ax , bx ; ax←bx,字傳送

mov ah , al ; ah←al,字節傳送

mov ds , ax ; ds←ax,字傳送

mov [bx] , al ; [bx]←al,字節傳送

3.主存送(段)寄存器 MOV reg/seg , mem;
mov al , [bx]

mov dx , [bp] ; dx←ss:[bp]

mov es , [si] ; es←ds:[si]

# 不存在存儲器向存儲器的傳送指令

4.段寄存器送寄存器或主存 MOV reg/mem , seg;

mov si , ds

mov ax , es ; ax←es

mov ds , ax ; ds←ax←es

# 對段寄存器的操作有一些限制MOV指令傳送功能

 

push/pop

堆棧是存儲器中專用的緩衝區,用於暫存寄存器數據或地址指針,push/pop就用於堆棧的操作,這兩個指令一般用在:
1、子程序調用,爲了保護現場,把所要用的寄存器中的內容先暫時保存起來,在子程序調用結束之前,按照先進後出的原則,把數據恢復。
2、有時候,需要臨時用一下某些寄存器,也可用一下,憑個人喜好;
這兩個指令必須成對使用(特殊用途除外),你只要壓入了那些東西,並且知道他們的順序就行了,所操作的是字符還是數據,就不用知道了。
堆棧操作指令
堆棧是一個先進後出的主存區域,位於堆棧段中,使用SS段寄存器記錄器段地址。棧只有一個出口,即當前棧頂。棧頂是地址較小的一端(低端),它用堆棧指針寄存器SP指定。堆棧的兩種基本操作,對應兩條基本指令:
(1)、進棧指令push
push reg/mem/seg;sp<-sp-2,ss<-reg/mem/seg
進棧指令先使堆棧指令sp減2,然後把一個字操作數存入堆棧頂部。堆棧操作的對象只能是字操作數,進棧時底字節存放於低地址,高字節存放於高地址,sp相應向低地址移動兩個字節單元。
push AX
PUSH [2000H]
PUSH CS
(2)、出棧指令pop
pop reg/seg/mem;reg/seg/mem<-ss:[sp],sp<-sp+2
出棧指令把棧頂的一個字傳送至指定的目的操作數,然後堆棧指針sp加2。目的操作數應爲字操作數,字從棧頂彈出時,低地址字節送低字節,高地址字節送高字節。
pop AX
POP [2000H]
POP SS堆棧可以用來臨時存放數據,以便隨時恢復它們。也常用於子程序見傳遞參數。
注意幾點:
(1)、因爲堆棧指針sp總是指向已經存入數據的棧頂(不是空單元),所以PUSH指令是將(SP)減2,後將內容壓棧(即先修改SP是指指向空單元,後壓入數據),而POP是先從棧頂彈出一個字,後將堆棧指針SP加2.
(2)、PUSH CS是合法的,但是POP CS是不合法的。
(3)、因爲SP總是指向棧頂,而用PUSH和POP指令存取數時都是在棧頂進行的,所以堆棧是先進後出或叫後進先出的。棧底在高地址,堆棧是從高地址向低地址延伸的,所有棧底就是最初的棧頂。
(4)、用PUSH指令和POP指令時只能按字訪問堆棧,不能按字節訪問堆棧。
(5)、PUSH和POP指令都不影響標誌

 

jmp

轉移指令的原理(jmp)

1.可以修改IP,或同時修改CS和IP的指令統稱轉移指令。概括地講,轉移指令就是可以控制CPU執行內存中某處代碼的指令。
2.8086CPU的轉移行爲有以下幾種:
  (1)只修改IP時,稱爲段內轉移。例如:jmp ax
  (2)同時修改CS和IP時,成爲段間轉移。例如:jmp 1000:0
3.由於轉移指令對IP值修改範圍的不同,段內轉移又分爲:短轉移和近轉移。
  (1)短轉移IP的修改範圍是-128~127
  (2)近轉移IP的修改範圍是-32768~32767
4.8086CPU的轉移指令分爲以下幾類:
  (1)無條件轉移指令(如:jmp)
  (2)條件轉移指令
  (3)循環指令(如:Loop)
  (4)過程
  (5)中斷
這些轉移指令的前提條件可能不同,但轉移的基本原理是相同的。
5.操作符offset是一個僞指令,它的功能是取得標號的偏移地址。
案例:將s處的一條指令複製到s0處
assume cs:codesg
  codesg segment
      s: mov ax,bx         ;mov ax,bx佔兩個字節
         mov si,offset s   ;得到標號s所在的偏移地址賦值給si
         mov di,offset s0  ;得到標號s0所在的偏移地址賦值給di
         mov ax,cs:[si]
         mov cs:[di],ax    
     s0: nop               ;nop佔一個字節
         nop
 codesg ends
end
6.jmp爲無條件轉移,可以只修改IP,也可以同時修改CS和IP的值
  jmp指令要給出兩個信息:
  (1)轉移的目的地址。
  (2)轉移的距離(段間轉移,段內轉移的短轉移和近轉移)
7.jmp short 標號(轉到標號處執行)
  這種指令實現的是段內短轉移。jmp指令中的"標號"是代碼段中的標號,指明瞭指令要轉移的目的地,轉移指令結束,CS:IP指向標號處的指令。
8.CPU在執行jmp指令時並不需要指明轉移的目標地址。
9.在“jmp short 標號“指令所對應的機器碼中,並不包含轉移的目的地址,而包含的是轉移的位移。
10."jmp short 標號" 的功能是:(IP)=(IP)+8位位移
   (1)8位位移=標號處的地址-jmp指令後的第一個字節的地址。
   (2)short 指明此處的位移是8位
   (3)8位位移的的範圍爲-128~127,用補碼錶示。
   (4)8位位移由編譯程序編譯時算出。
11.jmp near ptr 標號 表示段內近轉移。
12."jmp near ptr 標號"的功能是:(IP)=(IP)+16位位移
   (1)16位位移=標號處的地址-jmp指令後的第一個字節的地址。
   (2)near ptr 指明此處的位移是16位,進行段內近轉移。
   (3)16位位移範圍是-32768~32767,用補碼錶示。
   (4)16位位移由編譯程序在編譯時算出。
13."jmp far ptr 標號"實現的是段間轉移,又稱遠轉移。功能如下:
    (CS)=標號所在段的段地址;(IP)=標號所在段的偏移地址。
     far ptr 指明瞭指令用標號的段地址和偏移地址修改CS和IP。
 
14.轉移地址在內存中的jmp指令有以下幾種:
   (1)jmp word ptr 內存單元地址(段內轉移)
      功能:從內存單元地址開始處存放着一個字,是轉移的目的的偏移地址。
      內存單元地址可以用尋址方式的任意格式給出。
   比如,下列指令:
   mov ax,0013h
   mov ds:[0],ax
   jmp word ptr ds:[0]
   (2)jmp dword ptr 內存單元地址(段間轉移)
      功能:從內存單元地址開始處放着兩個字,高地址處放着轉移的目的段地址,低地址處放着轉移的目的地址的偏移地址。
      (CS)=(內存單元地址+2)
      (IP)=(內存單元地址)
      內存單元地址可以用尋址方式的任意格式給出。
    比如,下列指令:
    mov ax,0123h
    mov ds:[0],ax
    mov word ptr ds:[2],0
    jmp dword ptr ds:[0]
    執行後,(CS)=0,(IP)=0123H,CS:IP指向0000:0123
15.jcxz指令爲有條件轉移,所有有條件轉移指令都是短轉移。
   指令格式:jcxz 標號(如果(cx)=0,轉移到標號出執行)
   當cx<>0時,什麼也不做(程序下下執行)
16.loop指令是循環指令,所有的循環指令都是短轉移。
   指令格式:loop 標號((cx)=(cs)-1,如果(cx)<>0,轉移到標號出執行)
   當cx=0時,什麼也不做(程序下下執行)
17.根據位移進行轉移是爲了方便程序段在內存中的浮動裝配
18.根據位移進行轉移的指令,它們的轉移範圍受到轉移位移的限制,如果在源程序中出現了轉移範圍超界的問題,在編譯的時候編譯器將報錯。

 

call/ret

1.call和ret指令都是轉移指令,它們都修改IP的值,或同時修改CS和IP的值。它們經常共同用語實現子程序的設計。
2.ret指令用棧中的數據,修改IP的內容,從而實現近轉移。
3.retf指令用棧中的數據,修改CS和IP的內容,從而實現遠轉移。
4.CPU執行ret指令時,相當於進行:
  pop IP
  執行retf指令時,相當於進行:
  pop IP
  pop CS
5.CPU執行call指令時,進行兩步操作:
  (1)將當前的IP或CS和IP壓入棧中;
  (2)轉移
6.call指令不能實現短轉移,call指令實現轉移的方法和jmp指令的原理相同。
7.call 標號(將當前的IP壓棧後,轉到標號處執行指令)
  CPU執行此種格式的call指令時,進行如下的操作:
  (1)(sp)=(sp)-2
     ((ss)*16+(sp))=(IP)
  (2)(IP)=(IP)+16位位移
  call執行"call標號"時,相當於進行:
  push IP
  jmp near ptr 標號
8."call far ptr 標號"實現的是段間轉移.
   CPU執行此種格式的call指令時,進行如下的操作:
   (1)(sp)=(sp)-2
      ((ss)*16+(sp))=(CS)
      (sp)=(sp)-2
      ((ss)*16+(sp))=(IP)
   (2)(cs)=標號所在段的段地址
      (IP)=標號在段中的偏移地址
   CPU執行"call far ptr 標號"時,相當於進行:
   push CS
   push IP
   jmp far ptr 標號
9.指令格式:call 16位 reg
   功能:
   (sp)=(sp)-2
   ((ss)*16+(sp))=(IP)
   (IP)=(16位 reg)
   CPU執行"call 16位 reg"時,相當於:
   push IP
   jmp 16 位 reg
10.轉移地址在內存中的call指令有兩種格式.
   (1)call word ptr 內存單元地址
      CPU執行"cakk word ptr 內存單元地址"時,相當於:
      push IP
      jmp word ptr 內存單元地址
   (2)call dword ptr 內存單元地址
      CPU執行"calldword ptr 內存單元地址"時,相當於:
      push CS
      push IP
      jmp dword ptr 內存單元地址
11.利用call和ret可以實現子程序的機制,框架如下:
    標號:
         指令
         ret
12.mul是乘法指令,使用mul做乘法的時候需注意一下兩點.
   (1)兩個相乘的數:兩個相乘的數,要麼都是8位,要麼都是16位.如果是8位,默認放在AL中,另一個放在8位的reg或內存字節單元中;如果是16位,一個默認放在AX中,另一個放在16位reg過內存字單元中.
   (2)結果:如果是8位乘法,默認放在AX中;如果是16位乘法,結果高位放在DX中,低位放在AX中.
   格式如下:
   mul reg
   mul 內存單元
   例如:
   (1)計算100*10
    mov al,100
    mov bl,10
    mul bl
    結果:(AX)=1000(03E8H)
   (2)計算100*10000
    mov ax,100
    mov bx,10000
    mul bx
    結果:(AX)=4240H,(DX)=000FH (F4240H=1000000)
13.call和ret指令共同支持了彙編語言編程中的模塊化設計.
14.用寄存器來存儲參數和結果是最常用的方法.
15.對於批量數據的傳遞,我們將它放在內存中,然後將它們所在內存控件的首地址放在寄存器中,傳遞給需要的子程序.(聯想:C的指針和數組關係)
16.當出現寄存器衝突時(如多次使用CX),解決方法是在子程序的開始將子程序中所有用到的寄存器中的值都保存起來,在子程序返回前再恢復(棧的使用)

6.    Mallockmallocpmalloc?
  1. kmalloc和vmalloc是分配的是內核的內存,malloc分配的是用戶的內存
  2. kmalloc保證分配的內存在物理上是連續的,vmalloc保證的是在虛擬地址空間上的連續,malloc不保證任何東西(猜測的,不一定正確)
  3. kmalloc能分配的大小有限,vmalloc和malloc能分配的大小相對較大
  4. 內存只有在要被DMA訪問的時候才需要物理上連續
  5. vmalloc比kmalloc要慢
  6. kmalloc()是內核中最常見的內存分配方式,它最終調用夥伴系統的__get_free_pages()函數分配,根據傳遞給這個函數的flags參數,決定這個函數的分配適合什麼場合,如果標誌是GFP_KERNEL則僅僅可以用於進程上下文中,如果標誌GFP_ATOMIC則可以用於中斷上下文或者持有鎖的代碼段中。

    kmalloc返回的線形地址是直接映射的,而且用連續物理頁滿足分配請求,且內置了最大請求數(2**5=32頁)。

  7. vmalloc優先使用高端物理內存,但性能上會打些折扣。

    vmalloc分配的物理頁不會被交換出去
    vmalloc返回的虛地址大於(PAGE_OFFSET + SIZEOF(phys memory) + GAP),爲VMALLOC_START----VMALLOC_END之間的線形地址

    vmalloc使用的是vmlist鏈表,與管理用戶進程的vm_area_struct要區別,而後者會swapped;

  8. kmap()是主要用在高端存儲器頁框的內核映射中,一般是這麼使用的:
    使用alloc_pages()在高端存儲器區得到struct page結構,然後調用kmap(struct *page)在內核地址空間PAGE_OFFSET+896M之後的地址空間中(PKMAP_BASE到FIXADDR_STAR)建立永久映射(如果page結構對應的是低端物理內存的頁,該函數僅僅返回該頁對應的虛擬地址
    )
    kmap()也可能引起睡眠,所以不能用在中斷和持有鎖的代碼中

    不過kmap 只能對一個物理頁進行分配,所以儘量少用。

    對於高端物理內存(896M之後),並沒有和內核地址空間建立一一對應的關係(即虛擬地址=物理地址+PAGE_OFFSET這樣的關係),所以不能使用get_free_pages()這樣的頁分配器進行內存的分配,而必須使用alloc_pages()這樣的夥伴系統算法的接口得到struct *page結構,然後將其映射到內核地址空間,注意這個時候映射後的地址並非和物理地址相差PAGE_OFFSET.

    kmalloc和get_free_page申請的內存位於物理內存映射區域,而且在物理上也是連續的,它們與真實的物理地址只有一個固定的偏移,因此存在較簡單的轉換關係。而vmalloc申請的內存則位於vmalloc_start~vmalloc_end之間,與物理地址沒有簡單的轉換關係,雖然在邏輯上它們也是連續的,但是在物理上它們不要求連續。

7.    X86支持最大的內存爲多大?
a:x86是對基於intel處理器的系統的標準縮寫。X與處理器沒有任何關係,不分32位還是64位,都屬於X86。支持內存的大小則取決於32位還是64位,與X86無關。

  X86是一個對所有*86系統的簡單的通配符定義,是一個intel通用計算機系列的編號,也標識一套通用的計算機指令集合。由於早期intel的CPU編號都是如8086,80286來編號,由於這整個系列的CPU都是指令兼容的,所以都用X86來標識所使用的指令集合。如今的英特爾的處理器,都是支持X86指令系統的,所以都屬於X86家族。

  8086是16位處理器;直到1985年32位的80386的開發,這個架構都維持是16位。接着一系列的處理器表示了32位架構的細微改進,推出了數種的擴充,直到2003年AMD對於這個架構發展了64位的擴充,並命名爲AMD64。後來Intel也推出了與之兼容的處理器,並命名爲Intel 64。兩者一般被統稱爲x86-64或x64,開創了x86的64位時代。
  
  32位的X86CPU,支持最大4GB內存,但通過其它技術其實可以擴展這個容量,像微軟的服務器系統win2003就可以使用超過4G的內存;而目前64位的x86的CPU,可以理論上上動手動腳16EB內存,微軟的64位系統可以支持到192GB。

8.    字符和字節不是一個概念?
a:

字符與字節它們完全不是一個位面的概念,所以兩者之間沒有“區別”這一說法。在不同編碼裏,字符和字節的對應關係是不同的。一般來說,半角英文狀態下一個字母或數字(稱之爲字符)佔用一個字節,一個漢字用兩個字節表示。在不同的編碼方式下一個字符佔的字節數是不同的,所以兩者是不能劃等號的。

  1. 1個字節(Byte)等於8個bit位,每個bit位是0/1兩種狀態,也就是說一個字節可以表示256個狀態,計算機裏用字節來作爲最基本的存儲單位。

  2. 字符,在計算機和電信技術中,一個字符是一個單位的字形、類字形單位或符號的基本信息。

  3. 字符是指計算機中使用的字母、數字、字和符號,包括:1、2、3、A、B、C、~!·#¥%……—*()——+等等。

    ①ASCII碼中,一個英文字母(不分大小寫)佔一個字節的空間,一箇中文漢字佔兩個字節的空間。一個二進制數字序列,在計算機中作爲一個數字單元,一般爲8位二進制數,換算爲十進制。最小值0,最大值255。

    ②UTF-8編碼中,一個英文字符等於一個字節,一箇中文(含繁體)等於三個字節。

    ③Unicode編碼中,一個英文等於兩個字節,一箇中文(含繁體)等於兩個字節。

    文本符號:英文標點佔一個字節,中文標點佔兩個字節。舉例:英文句號“.”佔1個字節的大小,中文句號“。”佔2個字節的大小。

    ④UTF-16編碼中,一個英文字母字符或一個漢字字符存儲都需要2個字節(Unicode擴展區的一些漢字存儲需要4個字節)。

    ⑤UTF-32編碼中,世界上任何字符的存儲都需要4個字節。

9.    地址爲什麼是32位?
a:  物理地址即CPU的地址線,你可以簡單的理解爲32根線,每根線有高電平與低電平來區分1和0作爲地址編碼.例如訪問內存時32根線分別爲1和0時組合起來的數字換算成十進制就是2的32次方,即4G.當然,實際尋址是可以採用映射、虛擬等方式訪問超過這個限度的地址的,例如Windows Server 2003 企業版的32位版本最高可支持到32GB內存.

10.  計算機裏爲什麼以補碼的形式存儲?

a:

引子

你知道計算機中以什麼形式存儲整數嗎?是符號位加值位嗎?值位是按照正常的二進制方式存儲嗎?

如果後兩個問題你都回答是,那就意味着當用3位二進制進行存儲、且符號位0表示正1表示負時,1會存儲成001,-1會存儲成101。可惜事實不是這樣,計算機中是用補碼的形式而不是剛剛那種看上去很自然的形式存儲整數,補碼雖然也是用符號位加值位來表示,但表示的規則不太一樣:1會存成001,-1會存成111

如果三個問題你都回答對了,你知道計算機中整數以補碼的形式存儲,但你知道爲什麼要用這種形式嗎?以及「正數的補碼等於原碼;負數的補碼等於反碼加1,而反碼等於原碼符號位不變,其餘各位取反」這樣的補碼到底意味着什麼?(假設你不知道,請接着往下看吧 XD)

先看使用補碼的目的,然後忘掉上面那個補碼定義,跟我從這個目的開始,一步步探索補碼的本質。
目的:爲了簡化計算機基本運算電路,使加減法都只需要通過加法電路實現,也就是讓減去一個正數或加上一個負數這樣的運算可以用加上一個正數來代替。於是改變負數存儲的形式,存儲成一種可以直接當成正數來相加的形式,這種形式就是補碼。(正數不用變,所以接下來的討論中一般略去正數)

補碼是怎麼把減法變成加法的?

用時鐘理解減法變加法

這是一個身邊的例子,當你校對時鐘的時候,假設發現鍾是6點,但實際上現在才2點,也就是它走快了4個小時,你可以有兩種方法進行校正,一種是逆時針撥回4個小時到2點,另一種是順時針撥6個小時到12點然後再撥2小時,也就是順時針撥8個小時到2點。所以對於時鐘的錶盤來說,設-N表示逆時針撥N個小時,N表示順時針撥動N個小時,那麼-4 = +8,同樣還會有 -1 = +11-5 = +7,甚至也可以 -4 = +8 = +20 = +32 = -16

這裏邊隱藏了什麼規律?其實在數學中,-4、+8、+20、+32、-16可以歸爲符合某個條件的同一類數字 —— 對於模12同餘

中文維基上對於模和同餘的定義是:兩個整數a、b,若它們除以正整數m所得的餘數相等,則稱a、b對於模m同餘。

而在一個可溢出計數系統中,把計數系統容量作爲模,那麼所有對此模同餘的數在此計數系統中都會有同樣的表示,而且運算等價。
比如上面例子中的時鐘表盤就是一個可溢出計數系統,模爲12,所以-4、+8、+20、+32、-16這些對模12同餘的數在時鐘表盤上的表示是一樣的,而且對時針做這些操作的結果也是一樣的,都會撥到同樣的位置。

一個n位二進制構成的計數系統,因爲會捨棄溢出的高位,所以也是一個可溢出的計數系統,它的模爲2n2n(從0數到2n12n−1,再多就溢出)
由此可以推理,在一個3位二進制構成的模爲8的計數系統中,-2,-10,6,14可以用同樣的二進制數來表示,同時減10和加14會得到一樣的結果。

引出 補碼

所以,只要 補碼 是一個負數的正同餘數,那麼就能實現加這個正同餘補碼跟加另一個負數是一樣結果的效果。對一個負數來說,有無數個正同餘數滿足條件,爲了減少不必要的運算,可以規定補碼就取其中最小的正數。

可能因爲通過原碼求 補碼 是一個補模運算,所以它被稱爲 補碼

注意,這裏的 補碼 都被我用特殊標識,因爲這還不是計算機裏真正的存儲的補碼形式,它應該叫補數,不過相信我,已經很接近了

完善 補碼

這種 補碼 表示還有點問題

通過轉換成 補碼 ,減一個數確實變成加一個數了,看似很不錯,但卻有一個明顯的問題,那就是數本身的符號丟失了。
比如3位二進制,正常表示0~7,使用補碼法它能代替-8~-1的運算,但它不能真正表示-8~-1,因爲你不知道它到底是正數還是負數。
我們把負數轉換成了一種在運算中更讓計算機喜歡的形式,但它卻丟失了自己本身作爲數的信息。

怎麼解決這個問題,可能有人很快就拍腦袋:那就加一位來表示正負得了。但這樣的話運算時怎麼辦,從第二位開始算麼?那進位去位的時候是不是也需要特別注意一下不要影響到符號位?你會發現這個問題並不是那麼簡單。

怎麼完善 補碼

不知道大牛是怎麼想到的,問題解決得非常完美:

  • 在保持補碼特性的前提下 (也就是減一個數還是照樣變成加一個數)
  • 增加正負的表示 (能真正表示-8~-1了,只用看符號位是0還是1)
  • 還能讓運算時不用另外區分符號位,直接把符號位當成值位進行運算,而結果的正負號自然會符合這個正負表示法(也就是符號位的進位和值位的進位都會自然地合理)

而且解決方式真的皮,簡單到出人意料,就是前面你拍腦袋想到的辦法:加一位來表示正負
具體做法是:在左側高位增加一個符號位,這個符號位連同前面我們推演出的 僞補碼 一同構成真正完善的補碼
實現的效果:通過讀取符號位能得知數的正負,同時符號位在加法運算中跟值位一樣參與運算、進位、退位。

最後

總結一下

  • 使用補碼的目的:簡化計算機基本運算電路,使加減法都只需要用加法電路實現,用加法替代減法。
  • 補碼爲什麼能達到這個目的:n位二進制可以構成一個可溢出計數系統,在這樣的系統中,把計數系統容量作爲模,所有對此模同餘的數在此計數系統中都會有同樣的表示,而且運算等價。而補碼就是負數的最小正同餘數,所以加一個負數和減一個正數都可以用加一個補碼來表示。
  • 怎麼計算補碼:正數的補碼是它本身;對負數求最小正同餘數(模爲值位的容量)放入值位,符號位置爲1,得到負數的補碼。

到這裏「整數爲什麼以補碼的形式存儲」這個問題基本就解答清楚了,你會發現裏邊都沒有反碼的影子,對,就是這樣,用反碼以及教材裏那套計算補碼的方法來理解補碼都是緣木求魚,那它們是用來幹什麼的?值位取反加一這種算法是怎麼冒出來的?(求補碼的簡便算法) 以及大牛對補碼的完善爲什麼可行?(補碼正確性的證明) 感興趣的同學可以點擊超鏈接繼續看補充內容。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章