1. 程式人生 > >Go學習(2):基本語法

Go學習(2):基本語法

基本語法

一、二進位制、八進位制、十進位制、十六進位制【擴充套件內容】

1.1 進位制

二進位制:逢二進一,數值只有0和1。

八進位制:逢八進一,數值有0,1,2,3,4,5,6,7

十進位制:逢十進一,數值有0,1,2,3,4,5,6,7,8,9

十六進位制:逢十六進一,數值有0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F

1.2 進位制轉換

二進位制、八進位制、十六進位制轉為十進位制

十進位制轉為二進位制、八進位制、十六進位制

1.3 原碼,反碼,補碼

1.3.1 機器數和真值

機器數:一個數在計算機中的二進位制表示形式。叫做這個數的機器數。機器數是帶符號的,最高位0表示正數,1表示負數。
	示例:
		比如10進制中的+3,計算機長度為8位。轉為二進位制是0000 0011。
		比如-3,轉為二進位制是1000 0011。

真值:就是我們日常說的十進位制的正數,負數和0,比如-1,-2,-100,0,1,2,1000,100000等等。
	因為第一位是符號位,所以機器數的形式值就不等於真正的數值。
	比如1000 0011,
		作為負數可以是-3,作為正數可以說131.
	為了區分,將帶符號位的計算數對應的真正的數值稱為機器數的真值。

1.3.2 原碼,反碼,補碼

原碼:就是符號位加上真值的絕對值,即第一位表示符號位,其餘位表示值。
+1 = [0000 0001]原
-1 = [1000 0001]原
原碼是人腦最容易理解和計算的表示方式.

反碼:正數的反碼是其本身,負數的反碼是在其原碼的基礎上,符號位不變,其餘各位按位取反。
+1 = [0000 0001]原 = [0000 0001]反
-1 = [1000 0001]原 = [1111 1110]反
一個反碼錶示的是負數, 人腦無法直觀的看出來它的數值. 通常要將其轉換成原碼再計算。

補碼:正數的補碼是其本身,負數的補碼是在原碼的基礎上,符號位不變,其餘各位取反後+1。
+1 = [0000 0001]原 = [0000 0001]反 = [0000 0001]補
-1 = [1000 0001]原 = [1111 1110]反 = [1111 1111]補
對於負數, 補碼錶示方式也是人腦無法直觀看出其數值的. 通常也需要轉換成原碼在計算其數值.

於是人們開始探索 將符號位參與運算, 並且只保留加法的方法. 首先來看原碼。計算十進位制的表示式: 1-1=0

1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2

如果用原碼錶示, 讓符號位也參與計算, 顯然對於減法來說, 結果是不正確的.這也就是為何計算機內部不使用原碼錶示一個數.

為了解決原碼做減法的問題, 出現了反碼。計算十進位制的表示式:

1-1=0

1 - 1 = 1 + (-1)
= [0000 0001]原 + [1000 0001]原
= [0000 0001]反 + [1111 1110]反
= [1111 1111]反 = [1000 0000]原
= -0

發現用反碼計算減法, 結果的真值部分是正確的. 而唯一的問題其實就出現在”0”這個特殊的數值上. 雖然人們理解上+0和-0是一樣的, 但是0帶符號是沒有任何意義的. 而且會有[0000 0000]原和[1000 0000]原兩個編碼表示0.

於是補碼的出現, 解決了0的符號以及兩個編碼的問題:

1-1 = 1 + (-1)
= [0000 0001]原 + [1000 0001]原
= [0000 0001]補 + [1111 1111]補
= [0000 0000]補=[0000 0000]原

這樣0用[0000 0000]表示, 而以前出現問題的-0則不存在了.而且可以用[1000 0000]表示-128:

二、變數與常量

2.1 什麼是變數

變數是為儲存特定型別的值而提供給記憶體位置的名稱。在go中宣告變數有多種語法。

2.2 宣告變數

var名稱型別是宣告單個變數的語法。

以字母或下劃線開頭,由一個或多個字母、數字、下劃線組成

宣告一個變數

第一種,指定變數型別,聲明後若不賦值,使用預設值

var name type
name = value

第二種,根據值自行判定變數型別(型別推斷Type inference)

如果一個變數有一個初始值,Go將自動能夠使用初始值來推斷該變數的型別。因此,如果變數具有初始值,則可以省略變數宣告中的型別。

var name = value

第三種,省略var, 注意 :=左側的變數不應該是已經宣告過的(多個變數同時宣告時,至少保證一個是新變數),否則會導致編譯錯誤(簡短宣告)

name := value

// 例如
var a int = 10
var b = 10
c : = 10

這種方式它只能被用在函式體內,而不可以用於全域性變數的宣告與賦值

示例程式碼:

package main
var a = "Hello"
var b string = "World"
var c bool

func main(){
    println(a, b, c)
}

執行結果:

Hello World false

多變數宣告

第一種,以逗號分隔,宣告與賦值分開,若不賦值,存在預設值

var name1, name2, name3 type
name1, name2, name3 = v1, v2, v3

第二種,直接賦值,下面的變數型別可以是不同的型別

var name1, name2, name3 = v1, v2, v3

第三種,集合型別

var (
    name1 type1
    name2 type2
)

注意事項

如果在相同的程式碼塊中,我們不可以再次對於相同名稱的變數使用初始化宣告,例如:a := 20 就是不被允許的,編譯器會提示錯誤 no new variables on left side of :=,但是 a = 20 是可以的,因為這是給相同的變數賦予一個新的值。

如果你在定義變數 a 之前使用它,則會得到編譯錯誤 undefined: a。

如果你聲明瞭一個區域性變數卻沒有在相同的程式碼塊中使用它,同樣會得到編譯錯誤,例如下面這個例子當中的變數 a:

func main() {
   var a string = "abc"
   fmt.Println("hello, world")
}

嘗試編譯這段程式碼將得到錯誤 a declared and not used

此外,單純地給 a 賦值也是不夠的,這個值必須被使用,所以使用

在同一個作用域中,已存在同名的變數,則之後的宣告初始化,則退化為賦值操作。但這個前提是,最少要有一個新的變數被定義,且在同一作用域,例如,下面的y就是新定義的變數

package main

import (
	"fmt"
)

func main() {
	x := 140
	fmt.Println(&x)
	x, y := 200, "abc"
	fmt.Println(&x, x)
	fmt.Print(y)
}

執行結果:

0xc04200a2b0
0xc04200a2b0 200
abc

空白識別符號 _ 也被用於拋棄值,如值 5 在:_, b = 5, 7 中被拋棄

_ 實際上是一個只寫變數,你不能得到它的值。這樣做是因為 Go 語言中你必須使用所有被宣告的變數,但有時你並不需要使用從一個函式得到的所有返回值

並行賦值也被用於當一個函式返回多個返回值時,比如這裡的 val 和錯誤 err 是通過呼叫 Func1 函式同時得到:val, err = Func1(var1)

1.變數必須先宣告,才能夠使用,而且每個變數只能被宣告一次。
2.因為go是強型別語言,賦值型別要對應
3.name := value,這種宣告變數的方式,不能在函式外部使用
4.預設值:也叫零值。

2.3 常量宣告

常量是一個簡單值的識別符號,在程式執行時,不會被修改的量。

常量中的資料型別只可以是布林型、數字型(整數型、浮點型和複數)和字串型

不曾使用的常量,在編譯的時候,是不會報錯的

顯示指定型別的時候,必須確保常量左右值型別一致,需要時可做顯示型別轉換。這與變數就不一樣了,變數是可以是不同的型別值

const identifier [type] = value
顯式型別定義: const b string = "abc"
隱式型別定義: const b = "abc"
package main

import "fmt"

func main() {
   const LENGTH int = 10
   const WIDTH int = 5   
   var area int
   const a, b, c = 1, false, "str" //多重賦值

   area = LENGTH * WIDTH
   fmt.Printf("面積為 : %d", area)
   println()
   println(a, b, c)   
}

執行結果:

面積為 : 50
1 false str

常量可以作為列舉,常量組

const (
    Unknown = 0
    Female = 1
    Male = 2
)

常量組中如不指定型別和初始化值,則與上一行非空常量右值相同

package main

import (
	"fmt"
)

func main() {
	const (
		x uint16 = 16
		y
		s = "abc"
		z
	)
	fmt.Printf("%T,%v\n", y, y)
	fmt.Printf("%T,%v\n", z, z)
}

執行結果:

uint16,16
string,abc

2.4 iota

iota,特殊常量,可以認為是一個可以被編譯器修改的常量

在每一個const關鍵字出現時,被重置為0,然後再下一個const出現之前,每出現一次iota,其所代表的數字會自動增加1

iota 可以被用作列舉值:

const (
    a = iota
    b = iota
    c = iota
)

第一個 iota 等於 0,每當 iota 在新的一行被使用時,它的值都會自動加 1;所以 a=0, b=1, c=2 可以簡寫為如下形式:

const (
    a = iota
    b
    c
)

iota 用法

package main

import "fmt"

func main() {
    const (
            a = iota   //0
            b          //1
            c          //2
            d = "ha"   //獨立值,iota += 1
            e          //"ha"   iota += 1
            f = 100    //iota +=1
            g          //100  iota +=1
            h = iota   //7,恢復計數
            i          //8
    )
    fmt.Println(a,b,c,d,e,f,g,h,i)
}

執行結果:

0 1 2 ha ha 100 100 7 8

如果中斷iota自增,則必須顯式恢復。且後續自增值按行序遞增

自增預設是int型別,可以自行進行顯示指定型別

數字常量不會分配儲存空間,無須像變數那樣通過記憶體定址來取值,因此無法獲取地址

三、基本資料型別

以下是go中可用的基本資料型別

2.1 布林型bool

布林型的值只可以是常量 true 或者 false。一個簡單的例子:var b bool = true

2.2 數值型

1、整型

  • int8
    有符號 8 位整型 (-128 到 127)
  • int16
    有符號 16 位整型 (-32768 到 32767)
  • int32
    有符號 32 位整型 (-2147483648 到 2147483647)
  • int64
    有符號 64 位整型 (-9223372036854775808 到 9223372036854775807)
  • uint8
    無符號 8 位整型 (0 到 255)
  • uint16
    無符號 16 位整型 (0 到 65535)
  • uint32
    無符號 32 位整型 (0 到 4294967295)
  • uint64
    無符號 64 位整型 (0 到 18446744073709551615)

int和uint:根據底層平臺,表示32或64位整數。除非需要使用特定大小的整數,否則通常應該使用int來表示整數。
大小:32位系統32位,64位系統64位。
範圍:-2147483648到2147483647的32位系統和-9223372036854775808到9223372036854775807的64位系統。

2、浮點型

  • float32

    IEEE-754 32位浮點型數

  • float64

    IEEE-754 64位浮點型數

  • complex64

    32 位實數和虛數

  • complex128

    64 位實數和虛數

3、其他

  • byte

    類似 uint8

  • rune

    類似 int32

  • uint

    32 或 64 位

  • int

    與 uint 一樣大小

  • uintptr

    無符號整型,用於存放一個指標

2.3 字串型

字串就是一串固定長度的字元連線起來的字元序列。Go的字串是由單個位元組連線起來的。Go語言的字串的位元組使用UTF-8編碼標識Unicode文字

	var str string
	str = "Hello World"

2.4 派生型別(複合型別)

(a) 指標型別(Pointer)
(b) 陣列型別
(c ) 結構化型別(struct)
(d) Channel 型別
(e) 函式型別
(f) 切片型別
(g) 介面型別(interface)
(h) Map 型別

四、運算子

3.1 算術運算子

+ - * / %(求餘) ++ --

3.2 關係運算符

== != > < >= <=

3.3 邏輯運算子

運算子 描述
&& 所謂邏輯與運算子。如果兩個運算元都非零,則條件變為真
|| 所謂的邏輯或操作。如果任何兩個運算元是非零,則條件變為真
! 所謂邏輯非運算子。使用反轉運算元的邏輯狀態。如果條件為真,那麼邏輯非操後結果為假

3.4 位運算子

A B A&B A|B A^B
0 0 0 0 0
0 1 0 1 1
1 1 1 1 0
1 0 0 1 1

這裡最難理解的就是^了,只要認為AB兩者都相同的時候,為0,其他都為1

假設A為60,B為13

運算 描述 示例
& 二進位制與操作副本位的結果,如果它存在於兩個運算元 (A & B) = 12, 也就是 0000 1100
| 二進位制或操作副本,如果它存在一個運算元 (A | B) = 61, 也就是 0011 1101
^ 二進位制異或操作副本,如果它被設定在一個運算元但不能同時是位元 (A ^ B) = 49, 也就是 0011 0001
<< 二進位制左移位運算子。左邊的運算元的值向左移動由右運算元指定的位數 A << 2 will give 240 也就是 1111 0000
>> 二進位制向右移位運算子。左邊的運算元的值由右運算元指定的位數向右移動 A >> 2 = 15 也就是 0000 1111

3.5 賦值運算子

運算子 描述 示例
= 簡單的賦值操作符,分配值從右邊的運算元左側的運算元 C = A + B 將分配A + B的值到C
+= 相加並賦值運算子,它增加了右運算元左運算元和分配結果左運算元 C += A 相當於 C = C + A
-= 減和賦值運算子,它減去右運算元從左側的運算元和分配結果左運算元 C -= A 相當於 C = C - A
*= 乘法和賦值運算子,它乘以右邊的運算元與左運算元和分配結果左運算元 C *= A is equivalent to C = C * A
/= 除法賦值運算子,它把左運算元與右運算元和分配結果左運算元 C /= A 相當於 C = C / A
%= 模量和賦值運算子,它需要使用兩個運算元的模量和分配結果左運算元 C %= A 相當於 C = C % A
<<= 左移位並賦值運算子 C <<= 2 相同於 C = C << 2
>>= 向右移位並賦值運算子 C >>= 2 相同於 C = C >> 2
&= 按位與賦值運算子 C &= 2 相同於 C = C & 2
^= 按位異或並賦值運算子 C ^= 2 相同於 C = C ^ 2
|= 按位或並賦值運算子 C |= 2 相同於 C = C | 2

3.6優先順序

運算子優先順序
有些運算子擁有較高的優先順序,二元運算子的運算方向均是從左至右。下表列出了所有運算子以及它們的優先順序,由上至下代表優先順序由高到低:

優先順序 運算子
7 ^ !
6 * / % << >> & &^
5 + - | ^
4 == != < <= >= >
3 <-
2` &&
1 ||

當然,你可以通過使用括號來臨時提升某個表示式的整體運算優先順序。