日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
操作無符號整數(shù)的注意事項(xiàng)

在很多強(qiáng)類型編程語言中都會(huì)有一種特殊的類型——無符號整數(shù)類型,該數(shù)據(jù)類型在使用過程中往往稍不留意就會(huì)引發(fā)出乎意料的bug。

網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、成都微信小程序、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了海陵免費(fèi)建站歡迎大家使用!

至于,有什么注意事項(xiàng)以及需要了解的知識點(diǎn),一起來看看吧。

1. go源碼中的數(shù)據(jù)類型

 
 
 
 
  1. // go源碼位置: src/math/const.go 
  2. // 
  3. // Integer limit values. 
  4. const ( 
  5.     MaxInt8   = 1<<7 - 1 
  6.     MinInt8   = -1 << 7 
  7.    
  8.     MaxInt16  = 1<<15 - 1 
  9.     MinInt16  = -1 << 15 
  10.    
  11.     MaxInt32  = 1<<31 - 1 
  12.     MinInt32  = -1 << 31 
  13.    
  14.     MaxInt64  = 1<<63 - 1 
  15.     MinInt64  = -1 << 63 
  16.    
  17.     MaxUint8  = 1<<8 - 1 
  18.     MaxUint16 = 1<<16 - 1 
  19.     MaxUint32 = 1<<32 - 1 
  20.     MaxUint64 = 1<<64 - 1 
  21.  
  22.  
  23. // go源碼位置: src/builtin/builtin.go 
  24. // 
  25. // uint8 is the set of all unsigned 8-bit integers. 
  26. // Range: 0 through 255. 
  27. type uint8 uint8 
  28.  
  29. // uint16 is the set of all unsigned 16-bit integers. 
  30. // Range: 0 through 65535. 
  31. type uint16 uint16 
  32.  
  33. // uint32 is the set of all unsigned 32-bit integers. 
  34. // Range: 0 through 4294967295. 
  35. type uint32 uint32 
  36.  
  37. // uint64 is the set of all unsigned 64-bit integers. 
  38. // Range: 0 through 18446744073709551615. 
  39. type uint64 uint64 
  40.  
  41. // int8 is the set of all signed 8-bit integers. 
  42. // Range: -128 through 127. 
  43. type int8 int8 
  44.  
  45. // int16 is the set of all signed 16-bit integers. 
  46. // Range: -32768 through 32767. 
  47. type int16 int16 
  48.  
  49. // int32 is the set of all signed 32-bit integers. 
  50. // Range: -2147483648 through 2147483647. 
  51. type int32 int32 
  52.  
  53. // int64 is the set of all signed 64-bit integers. 
  54. // Range: -9223372036854775808 through 9223372036854775807. 
  55. type int64 int64 
  56.  
  57. // float32 is the set of all IEEE-754 32-bit floating-point numbers. 
  58. type float32 float32 
  59.  
  60. // float64 is the set of all IEEE-754 64-bit floating-point numbers. 
  61. type float64 float64 

 從源碼中可以看出:

  1. 無符號類型只有正數(shù)值域(最小值為0),沒有負(fù)數(shù)值域
  2. 有符號類型有正、負(fù)數(shù)值域
  3. 無符號類型正數(shù)值域數(shù)值個(gè)數(shù)是有符號類型正數(shù)值域數(shù)值個(gè)數(shù)的2倍

以 uint8 與 int8 為例,無符號類型 uint8,正數(shù)值域 0 ~ 255 共 256個(gè)數(shù)值。有符號類型 int8,正數(shù)值域 0 ~ 127 共 128個(gè)數(shù)值。

2. 無符號類型與有符號類型的值域

可能你會(huì)問:相同長度的無符號、有符號類型,值域?yàn)槭裁词沁@樣的分布?

看過計(jì)算機(jī)微機(jī)原理的同學(xué)大概都會(huì)略知一二,明白其緣由:

  1. 計(jì)算機(jī)只認(rèn)識0、1,每個(gè)0、1被稱為1個(gè)bit (bit是計(jì)算機(jī)中最小的單位)
  2. 計(jì)算機(jī)系統(tǒng)中所有的數(shù)值以及數(shù)據(jù)都使用0、1串組合存儲(chǔ)
  3. 不同的0、1組合,由于使用不同編碼、解碼方式才被賦予了不同的含義,進(jìn)而擁有了各種不同數(shù)據(jù)類型

就拿長度都是8 bit的無符號類型uint8與有符號類型int8來說:

uint8無符號類型的8個(gè)bit位都用來表示數(shù)值

int8有符號類型的8個(gè)bit中,只有后7個(gè)bit位用來表示數(shù)值。剩余的一個(gè)bit用來表示符號位,為0表示正數(shù)值,為1表示負(fù)數(shù)值。

在二進(jìn)制中,1個(gè)bit長度之差造成的表達(dá)值域就是2倍

3. 無符號類型與有符號類型的加減法

先看一段代碼:

 
 
 
 
  1. func demo() { 
  2.     var a uint8 = 1 
  3.     var b uint8 = 2 
  4.     v1 := a - b 
  5.     fmt.Println("uint8 1-2=", v1) 
  6.  
  7.     var c int8 = 1 
  8.     var d int8 = 2 
  9.     v2 := c - d 
  10.     fmt.Println("int8 1-2=", v2) 
  11.  
  12.     fmt.Println("---------------") 
  13.      
  14.     var e uint8 = math.MaxUint8 
  15.     var f uint8 = 1 
  16.     v3 := e + f 
  17.     fmt.Printf("uint8 255+1=%d  %T\n", v3, v3) 
  18.  
  19.     var g int8 = math.MaxInt8 
  20.     var h int8 = 1 
  21.     v4 := g + h 
  22.     fmt.Printf("int8 127+1=%d %T\n", v4, v4) 

聰明的你,猜下執(zhí)行結(jié)果會(huì)是什么?

 
 
 
 
  1. uint8 - v1 1-2= 255 
  2. int8 - v2 1-2= -1 
  3. --------------- 
  4. uint8 - v3 255+1=0  uint8 
  5. int8 - v4 127+1=-128 int8 

結(jié)果分析:

  1. v3、v4在進(jìn)行相加操作時(shí),由于運(yùn)算結(jié)果超出了對應(yīng)的數(shù)值位長度而發(fā)生溢出,導(dǎo)致溢出的數(shù)據(jù)位無效
  2. v2結(jié)果正確
  3. v1不僅是本文重點(diǎn)之一,也會(huì)在很多場合中稍有不慎就導(dǎo)致嚴(yán)重bug

在網(wǎng)上看到的一個(gè)關(guān)于無符號整形減法產(chǎn)生的bug,如下圖所示:

4. 關(guān)于無符號整形加減法的一些結(jié)論

先說一些關(guān)于無符號整形加減法的結(jié)論:

1.無符號整形進(jìn)行加法操作時(shí)會(huì)像其他類型一樣,在運(yùn)算結(jié)果超出數(shù)值位時(shí)發(fā)生溢出

2.無符號整形進(jìn)行減法操作時(shí),運(yùn)算結(jié)果有兩種情況

2.1 減數(shù)>=被減數(shù),則最終結(jié)果大于等于0

2.2 減數(shù)<被減數(shù),最終結(jié)果也大于等于0

關(guān)于無符號整形進(jìn)行減法比較特殊,減數(shù)小于被減數(shù)時(shí)結(jié)果也大于等于0,是不是很意外。

總結(jié)一句話概括:無符號類型數(shù)值無論加減操作,其結(jié)果從不會(huì)小于0。

5. 無符號類型數(shù)值相減問題

 
 
 
 
  1. [root@localhost workspace]# cat -n t.go 
  2.      1 package main 
  3.      2 
  4.      3 import"fmt" 
  5.      4 
  6.      5 func main (){ 
  7.      6 var a uint8 = 1 
  8.      7 var b uint8 = 2 
  9.      8      v1 := a - b 
  10.      9 
  11.     10      fmt.Println("uint8 - v1 1-2=", v1) 
  12.     11  } 

 第8行代碼執(zhí)行了兩個(gè)uint8無符號類型減法操作,得到結(jié)果v1。

 
 
 
 
  1. [root@localhost workspace]# go build -gcflags="-N -l -S" t.go 
  2. # command-line-arguments 
  3. "".main STEXT size=222 args=0x0 locals=0x80 funcid=0x0 
  4.     0x000000000 (/root/workspace/t.go:5)    TEXT    "".main(SB), ABIInternal, $128-0 
  5.     ...... 
  6.     0x002b00043 (/root/workspace/t.go:8)    MOVBLZX "".a+54(SP), AX 
  7.     0x003000048 (/root/workspace/t.go:8)    ADDL    $-2, AX // !!! 加 -2 
  8.     0x003300051 (/root/workspace/t.go:8)    MOVB    AL, "".v1+52(SP) 
  9.     0x003700055 (/root/workspace/t.go:10)   MOVB    AL, ""..autotmp_3+55(SP) 

首先,要說明一點(diǎn):在計(jì)算機(jī)中沒有減法,只有加法操作(出乎你的意料)。

通過匯編代碼可以看出 a-b 被轉(zhuǎn)換成了a + (-b),即 1-2 = 1+(-2)。

按理說1-2應(yīng)該等于-1才對,這其中又發(fā)生了什么呢?

6. 負(fù)數(shù)的表達(dá)形式——補(bǔ)碼

前面說過,計(jì)算機(jī)只認(rèn)識二進(jìn)制的0、1,2屬于10進(jìn)制的數(shù)值,10進(jìn)制則可以看做是一種計(jì)算機(jī)上的編解碼規(guī)則。那么,其對應(yīng)的二進(jìn)制又是什么呢?

 
 
 
 
  1. func demo2() { 
  2.     var a, b, c uint8 
  3.     a = 1 
  4.     b = 2 
  5.     c = a + (-b) 
  6.  
  7.     fmt.Printf( 
  8.         "a的二進(jìn)制為:%08b \n"+ 
  9.         "b的二進(jìn)制為:%08b \n"+ 
  10.         "-b的二進(jìn)制為:%08b \n"+ 
  11.         "c的二進(jìn)制為:%08b \n"+ 
  12.         "c的十進(jìn)制為:%d", a, b, -b, c, c, 
  13.     ) 

 執(zhí)行結(jié)果為:

 
 
 
 
  1. a的二進(jìn)制為:00000001 
  2. b的二進(jìn)制為:00000010 
  3. -b的二進(jìn)制為:11111110 
  4. c的二進(jìn)制為:11111111 
  5. c的十進(jìn)制為:255 

在計(jì)算機(jī)系統(tǒng)里面,數(shù)值有三種編碼:原碼、反碼、補(bǔ)碼。

  1. 反碼、補(bǔ)碼一般用于負(fù)數(shù),反碼=負(fù)數(shù)對應(yīng)正數(shù)的原碼取反,補(bǔ)碼=反碼+1
  2. 正數(shù)的原碼、反碼、補(bǔ)碼一樣
  3. 負(fù)數(shù)分兩種情況:3.1 對于有符號類型:負(fù)數(shù)的數(shù)值位使用補(bǔ)碼表示,同時(shí)設(shè)置符號位為13.2 對于無符號類型:負(fù)數(shù)的數(shù)值位使用補(bǔ)碼表示,由于無符號位,故無需設(shè)置符號位

由上文若干規(guī)則可知:

  1. uint8 類型的 -b 對應(yīng)的二進(jìn)制為 11111110,uint8 類型 a 對應(yīng)二進(jìn)制為00000001
  2. c=a+(-b),則對應(yīng)bit位相加為11111111
  3. 同類型相加結(jié)果還為同一類型,所以c仍然為uint8
  4. 由于無符號類型的值域不存在負(fù)數(shù)域,所以11111111轉(zhuǎn)換為十進(jìn)制為255

7. 一些疑惑

你是否跟我一樣,存在一些疑惑?

無符號類型可以賦值為負(fù)數(shù)嗎?

你可能會(huì)問:無符號類型既然永遠(yuǎn)不為負(fù)數(shù),那么可以賦值為負(fù)數(shù)嗎?

 
 
 
 
  1. func demo3() { 
  2.     var a uint8 
  3.     a = -2 
  4.  
  5.     fmt.Println(a) 

執(zhí)行結(jié)果:

 
 
 
 
  1. # command-line-arguments 
  2. ./main.go:59:4: constant -2 overflows uint8 

通過報(bào)錯(cuò)信息可知,是無法給無符號類型賦值負(fù)數(shù)的。

無符號類型不可以賦值負(fù)數(shù),為什么可以進(jìn)行取負(fù)操作?

既然無符號類型不可以賦值為負(fù)數(shù),為什么無符號類型可以取負(fù)操作?

 
 
 
 
  1. func demo3() { 
  2.     var a uint8 
  3.     a = 2 
  4.  
  5.     fmt.Println(-a) 

可能你又會(huì)問:-a需要跟a類型一致才對,-a不能表示為無符號類型,為什么沒報(bào)錯(cuò)呢?

 
 
 
 
  1. [root@localhost workspace]# cat -n t.go 
  2.      1 package main 
  3.      2 
  4.      3 import"fmt" 
  5.      4 
  6.      5 func main (){ 
  7.      6 var a1 uint8 
  8.      7      a1 = 2 
  9.      8 
  10.      9      fmt.Printf("%b\n", -a1) 
  11.     10  } 
  12.  
  13.  
  14. [root@localhost workspace]# go build -gcflags="-N -l -S" t.go 
  15. # command-line-arguments 
  16. "".main STEXT size=197 args=0x0 locals=0x80 funcid=0x0 
  17.     0x000000000 (/root/workspace/t.go:5)    TEXT    "".main(SB), ABIInternal, $128-0 
  18.     ...... 
  19.     0x002b00043 (/root/workspace/t.go:9)    MOVB    $-2, ""..autotmp_1+71(SP) 
  20.     0x003000048 (/root/workspace/t.go:9)    XORPS   X0, X0 
  21.     0x003300051 (/root/workspace/t.go:9)    MOVUPS  X0, ""..autotmp_2+80(SP) 
  22.     0x003800056 (/root/workspace/t.go:9)    LEAQ    ""..autotmp_2+80(SP), AX 
  23.     0x003d00061 (/root/workspace/t.go:9)    MOVQ    AX, ""..autotmp_4+72(SP) 
  24.     0x004200066 (/root/workspace/t.go:9)    TESTB   AL, (AX) 
  25.     0x004400068 (/root/workspace/t.go:9)    MOVBLZX ""..autotmp_1+71(SP), CX 
  26.     0x004900073 (/root/workspace/t.go:9)    LEAQ    type.uint8(SB), DX //!!! type.uint8對-2進(jìn)行類型轉(zhuǎn)換 
  27.     0x005000080 (/root/workspace/t.go:9)    MOVQ    DX, ""..autotmp_2+80(SP) 
  28.     0x005500085 (/root/workspace/t.go:9)    LEAQ    runtime.staticuint64s(SB), DX 
  29.     0x005c00092 (/root/workspace/t.go:9)    LEAQ    (DX)(CX*8), CX 
  30.     0x006000096 (/root/workspace/t.go:9)    MOVQ    CX, ""..autotmp_2+88(SP) 
  31.     0x006500101 (/root/workspace/t.go:9)    TESTB   AL, (AX) 
  32.     0x006700103 (/root/workspace/t.go:9)    JMP 105 
  33.     0x006900105 (/root/workspace/t.go:9)    MOVQ    AX, ""..autotmp_3+96(SP) 
  34.     0x006e00110 (/root/workspace/t.go:9)    MOVQ    $1, ""..autotmp_3+104(SP) 
  35.     0x007700119 (/root/workspace/t.go:9)    MOVQ    $1, ""..autotmp_3+112(SP) 
  36.     0x008000128 (/root/workspace/t.go:9)    LEAQ    go.string."%b\n"(SB), CX 
  37.     0x008700135 (/root/workspace/t.go:9)    MOVQ    CX, (SP) 
  38.     0x008b00139 (/root/workspace/t.go:9)    MOVQ    $3, 8(SP) 
  39.     0x009400148 (/root/workspace/t.go:9)    MOVQ    AX, 16(SP) 
  40.     0x009900153 (/root/workspace/t.go:9)    MOVQ    $1, 24(SP) 
  41.     0x00a200162 (/root/workspace/t.go:9)    MOVQ    $1, 32(SP) 
  42.     0x00ab00171 (/root/workspace/t.go:9)    PCDATA  $1, $0 
  43.     0x00ab00171 (/root/workspace/t.go:9)    CALL    fmt.Printf(SB) 
  44.     ...... 

通過匯編可以看到,通過type.uint8(SB), DX對運(yùn)算結(jié)果進(jìn)行了類型轉(zhuǎn)換。

因此,我們可以得出結(jié)論:-a是a與負(fù)號(-)的一種運(yùn)算,運(yùn)算結(jié)果的最終類型會(huì)被轉(zhuǎn)換為與a一致。

總結(jié)

本文通過若干示例,展示了無符號類型與有符號類型的差別和注意事項(xiàng)。

那么,什么時(shí)候用無符號類型,什么時(shí)候用有符號類型呢?

  1. 運(yùn)算結(jié)果期待包含負(fù)數(shù),則不能用無符號類型,此時(shí)最好使用有符號類型
  2. 運(yùn)算結(jié)果不需要包含負(fù)數(shù),并且希望類型的正數(shù)值域足夠大,此時(shí)最好使用無符號類型
  3. 能不用無符號類型就少用無符號類型,減少bug產(chǎn)生!!!

其他特殊場景,如:在go語言runtime中GPM的邏輯處理器P結(jié)構(gòu)上,P存儲(chǔ)goroutine的本地隊(duì)列頭尾位置使用了無符號類型。

 
 
 
 
  1. type p struct { 
  2.     ...... 
  3.     // Queue of runnable goroutines. Accessed without lock. 
  4.     runqhead uint32// 本地運(yùn)行隊(duì)列 頭位置 
  5.     runqtail uint32// 本地運(yùn)行隊(duì)列 尾位置 
  6.     runq     [256]guintptr // 每個(gè)P可以有256個(gè)G 
  7.   ...... 
  8.  
  9. // runqput tries to put g on the local runnable queue. 
  10. // If next if false, runqput adds g to the tail of the runnable queue. 
  11. // If next is true, runqput puts g in the _p_.runnext slot. 
  12. // If the run queue is full, runnext puts g on the global queue. 
  13. // Executed only by the owner P. 
  14. // runqput把G放到p里。如果next為true,就放到下一個(gè)。否則就追加到隊(duì)尾。如果隊(duì)列滿了,就放到全局隊(duì)列。 
  15. func runqput(_p_ *p, gp *g, next bool) { 
  16.     ...... 
  17.     h := atomic.Load(&_p_.runqhead) // load-acquire, synchronize with consumers 
  18.     t := _p_.runqtail 
  19.    
  20.     // 放本地隊(duì)列 
  21.     ift-h < uint32(len(_p_.runq)) { 
  22.         _p_.runq[t%uint32(len(_p_.runq))].set(gp) 
  23.         atomic.Store(&_p_.runqtail, t+1) // store-release, makes the item available for consumption 
  24.         return 
  25.     } 
  26.     ...... 

由于t、h的數(shù)值是一直在進(jìn)行+1操作,會(huì)超過uint32的最大表示范圍。

思考當(dāng) t、h溢出之后會(huì)怎么樣?會(huì)有問題嗎?


網(wǎng)站題目:操作無符號整數(shù)的注意事項(xiàng)
本文來源:http://www.dlmjj.cn/article/dpiodse.html