[GO語言基礎] 三.變量聲明、數據類型、標識符及編程練習12題

作爲網絡安全初學者,會遇到採用Go語言開發的惡意樣本。因此從今天開始從零講解Golang編程語言,一方面是督促自己不斷前行且學習新知識;另一方面是分享與讀者,希望大家一起進步。前文介紹了什麼是GO語言及爲什麼我要學習Golang;這篇文章將介紹Go的編譯運行、語法規範、註釋轉義及API標準庫知識。 這系列文章入門部分將參考“尚硅谷”韓順平老師的視頻和書籍《GO高級編程》,詳見參考文獻,並結合作者多年的編程經驗進行學習和豐富,且看且珍惜吧!後續會結合網絡安全進行GO語言實戰深入,加油~

這些年我學過各種編程語言,從最早的C語言到C++,再到C#、PHP、JAVA,再到IOS開發、Python,到最新的GO語言,學得是真的雜。有時候覺得編程語言恰恰是最簡單的,而通過一門編程語言能夠解決實際問題或深入底層纔是其價值所在,並且當我們學好一門編程語言後,其他編程語言都非常類似,殊途同歸,學起來也很迅速。

在這裏插入圖片描述

源碼下載地址:

前文參考:


一.變量

1.什麼是變量

爲什麼需要變量呢?
一個程序就是一個世界,不論使用哪種高級程序語言編寫代碼,變量都是其程序的基本組成單位。如下圖所示的sum和sub都是變量。

在這裏插入圖片描述

變量的定義:
變量相當於內存中一個數據存儲空間的表示,可以將變量看作是一個房間的門牌號,通過門牌號能找到房間;通過變量名可以訪問到變量的值。變量使用的常見三個步驟:

  • 聲明變量或定義變量
  • 變量賦值
  • 變量使用

在這裏插入圖片描述

變量入門示例:
變量表示內存中的一個存儲區域,該區域有自己的變量名和數據類型。下面是一個簡單案例:

package main
import "fmt"

func main() {
   
    
	//定義變量
	var n int
	//賦值變量
	n = 10
	//使用變量
	fmt.Println("n =", n)
}

2.變量的聲明

Go語言變量使用的三種方式:

  • (1) 指定變量類型,聲明後若不復制,使用默認值,如int的默認值是0;
    var i int
    fmt.Println(“i =”, i)

  • (2) 根據值自行判定變量類型(類型推導);
    var num = 3.14
    fmt.Println(“num =”, num)

  • (3) 省略var,=是賦值, :=是聲明變量並賦值。注意 :=左側變量不應該是已經聲明過的,否則會編譯錯誤
    name := “eastmount”
    fmt.Println(“name =”, name)
    等價於
    var name string
    name = “eastmount”




package main
import "fmt"

func main() {
   
     
	//方法一:指定變量類型 int默認值爲0
	var i int
	fmt.Println("i =", i)

	//方法二:根據值自行判定變量類型
	var num = 3.14
	fmt.Println("num =", num)

	//方法三:省略var  :=聲明變量並賦值
	name := "eastmount"
	fmt.Println("name =", name)
}

輸出結果如下圖所示:

在這裏插入圖片描述

多變量聲明:
在編程中,通常會遇到一次性聲明多個變量的情況。Golang同樣提供了對應的功能,如下所示:

  • var n1, n2, n3 int
  • var n1, name, n3 = 100, “csdn”, 3.14
  • n1, name, n3 := 100, “csdn”, 3.14

代碼如下,注意不要重複定義變量,否則會報錯。

package main
import "fmt"

func main() {
   
     
	//方法一:指定變量類型 int默認值爲0
	var n1, n2, n3 int
	fmt.Println("n1 =", n1, "n2 =", n2, "n3 =", n3)

	//方法二:根據值自行判定變量類型
	var m1, name, m3 = 100, "csdn", 3.14
	fmt.Println("m1 =", m1, "name =", name, "m3 =", m3)

	//方法三:省略var  :=聲明變量並賦值
	k1, k2, k3 := 100, "yxz", 3.14
	fmt.Println("k1 =", k1, "k2 =", k2, "k3 =", k3)
}

輸出結果如下圖所示:

在這裏插入圖片描述

那麼,如何一次性聲明多個全局變量呢?

  • 在Go中函數外部定義的變量就是全局變量
  • 全局變量如果只定義不使用,不會報錯
  • 一次性聲明
package main
import "fmt"

//定義全局變量
var n = 100
var m = 200
var k = "eastmount"

//等價於一次性聲明
var (
	o = 300
	p = 400
	q = "yxz"
)

func main() {
   
     
	//輸出全局變量
	fmt.Println("n =", n, "m =", m, "k =", k)
}

3.變量的注意事項

注意事項:

  • 變量在某個區域的數據值可以在同一類型範圍內不斷變化,但不能改變數據類型(強類型轉換)

在這裏插入圖片描述

  • 變量在同一作用域(在一個函數或代碼塊)內不能重名

在這裏插入圖片描述

  • 變量=變量名+值+數據類型,這一點大家需要注意,變量的三要素

  • Golang的變量如果沒有賦初值, 編譯器會使用默認值,比如int爲0,string爲空串等

變量知識總結:

  • 聲明變量
    基本語法:var 變量名 數據類型
    比如“var n int”表示聲明一個變量,變量名爲n;“var num float32”表示聲明一個單精度小數類型的變量

  • 初始化變量
    在聲明變量時賦初值,比如“var n int = 12”,如果聲明時就直接賦值,可省略數據類型,比如“var m = 30”
  • 變量賦值
    先聲明變量“var num int”,此時默認值爲0,再給變量賦值“num=78”

加號用法:

  • 當左右兩邊都是數值型是,做加法運算
  • 當左右兩邊都是字符串,做字符串拼接
package main
import "fmt"

func main() {
   
      
	//加法運算
	var i, j = 2, 3
	var res = i + j
	fmt.Println("res =", res)

	//字符串拼接
	var str1 = "hello"
	var str2 = "world"
	var str3 = str1 + str2
	fmt.Println("str3 =", str3)
}

在這裏插入圖片描述


二.數據類型

每一種數據都定義了明確的數據類型,並在內存中分配不同大小的內存空間。常用的數據類型分爲基本數據類型和派生/複雜數據類型。

  • 基本數據類型
    數值型(整型、浮點型)、字符型(單個字符)、布爾型、字符串(Go中歸屬爲基本數據類型)、複數類型(complex64、complex128)、rune
  • 派生/複雜數據類型
    指針、數組、結構體、管道、函數、切片、接口、map

在這裏插入圖片描述

在這裏插入圖片描述

1.整型

整數類型就是用於存放整數值,比如0、-2、23等。

(1) 整形的各個類型
注意,一個字節爲8位(bit),計算機採用二進制(0或1),即2^8表數範圍。

  • 有符號的int8表示爲-128 ~ 127。下圖bit7爲符號位,最大值爲bit0至bit6均爲1,故爲 2 7 − 1 2^7-1 271,即127;由於存在+0和-0,因此將 -0位賦值給負數,因此最小值爲-128。
  • 無符號uint8表示爲0 ~ 255。無符號最大值爲bit0至bit7均爲1,故爲 2 8 − 1 2^8-1 281,即255。

在這裏插入圖片描述

類型 有無符號 佔用存儲空間 表數範圍
int8 1字節 − 128 ∼ 127 -128 \sim 127 128127
int16 2字節 − 2 15 ∼ 2 15 − 1 - 2^{15} \sim 2^{15}-1 2152151
int32 4字節 − 2 31 ∼ 2 31 − 1 - 2^{31} \sim 2^{31}-1 2312311
int64 8字節 − 2 63 ∼ 2 63 − 1 - 2^{63} \sim 2^{63}-1 2632631

案例如下:

package main
import "fmt"

func main() {
   
       
	var i int = 1
	fmt.Println("i =", i)

	var j int8 = 127
	fmt.Println("j =", j)
}

輸出結果如下圖所示:

在這裏插入圖片描述

(2) 無符號整形

類型 有無符號 佔用存儲空間 表數範圍
uint8 1字節 0 ∼ 255 0 \sim 255 0255
uint16 2字節 0 ∼ 2 16 − 1 0 \sim 2^{16}-1 02161
uint32 4字節 0 ∼ 2 32 − 1 0 \sim 2^{32}-1 02321
uint64 8字節 0 ∼ 2 64 − 1 0 \sim 2^{64}-1 02641

案例如下,如果uint8賦值爲256則會提示邊界溢出“.\type03_01.go:14:6: constant 256 overflows uint8”。

在這裏插入圖片描述

(3) 其他int類型

類型 有無符號 佔用存儲空間 表數範圍
int 32位系統4個字節
64位系統8個字節
− 2 31 ∼ 2 31 − 1 − 2 63 ∼ 2 63 − 1 - 2^{31} \sim 2^{31}-1 \\ - 2^{63} \sim 2^{63}-1 23123112632631
uint 32位系統4個字節
64位系統8個字節
0 ∼ 2 32 − 1 0 ∼ 2 64 − 1 0 \sim 2^{32}-1 \\ 0 \sim 2^{64}-1 0232102641
rune 與int32等價,表示一個unicode碼,常用處理中文 − 2 31 ∼ 2 31 − 1 - 2^{31} \sim 2^{31}-1 2312311
byte 與uint8等價,存儲字符選用 0 ∼ 255 0 \sim 255 0255

案例如下:

package main
import "fmt"

func main() {
   
       
	var i int = -20
	fmt.Println("i =", i)

	var j uint = 127
	fmt.Println("j =", j)

	var k rune = 1024
	fmt.Println("k =", k)

	var m byte = 255
	fmt.Println("m =", m)
}

輸出結果如下:

在這裏插入圖片描述

(4) 整形的注意事項

  • Golang各證書類型分爲有符號和無符號,int、uint的大小和系統有關
  • Golang的整型默認聲明爲int型
  • 查看某個變量的字節大小(unsafe.Sizeof)和數據類型(fmt.Printf->%T)
  • Golang程序中整型變量使用時,遵守保小不保大的原則,即:在保證程序正確運行下,儘量使用佔用空間小的數據類型,比如年齡
  • bit是計算機中最小存儲單位,byte是計算機中基本存儲單元(1 byte=8 bit)
package main
import "fmt"
import "unsafe"

func main() {
   
       
	//查看變量的數據類型
	//fmt.Printf() 用於格式化輸出
	var num int = 1024
	fmt.Println("num =", num)
	fmt.Printf("num 的數據類型是 %T \n", num)

	//查看變量佔用的空間大小
	var n int64 = 10
	var name = "eastmount"
	fmt.Printf("n 的數據類型是 %T; 佔用字節數是 %d \n", n, unsafe.Sizeof(n))
	fmt.Printf("name 的數據類型是 %T; 佔用字節數是 %d \n", name, unsafe.Sizeof(name))

	//Golang程序中整型變量使用盡量遵守保小原則
	//在保證程序正確運行下,儘量使用空間小的數據類型
	var age byte = 28
	fmt.Printf("n 的數據類型是 %T; 佔用字節數是 %d \n", age, unsafe.Sizeof(age))
}

輸出結果如下圖所示:

在這裏插入圖片描述


2.浮點型

浮點型用於存放小數,比如3.14、-1.9等,兩種類型如下(沒有double類型)。

  • 單精度float32:4字節
  • 雙精度float64:8字節

浮點數都是有符號的,浮點數在機器中存放形式爲:

  • 浮點數=符號位+指數位+尾數位

在這裏插入圖片描述

浮點數的尾數部分可能丟失,造成精度損失。

  • float64的精度要比float32準確,如果我們要保存一個高精度數據,則應該選用float64;軟件開發中也推薦使用float64
package main
import "fmt"

func main() {
   
        
	//浮點數定義
	var price float32 = 89.12
	fmt.Println("price =", price)

	var num1 float32 = -0.00081
	var num2 float64 = -78942.00912
	fmt.Println("num1 =", num1, "num2 =", num2)
	
	//精度損失
	var num3 float32 = -123.0000203
	var num4 float64 = -123.0000203
	fmt.Println("\n精度損失")
	fmt.Println("num3 =", num3, "num4 =", num4)
}

輸出結果如下圖所示,可以看到float32精度損失。

在這裏插入圖片描述

浮點數的注意事項

  • Golang浮點類型包括float32和float64兩種,不存在double類型
  • Golang浮點類型有固定的範圍和字段長度,不受具體操作系統的影響
  • Golang的浮點型默認聲明爲float64類型
  • 浮點型常量有兩種表示
    – 十進制數形式,如:3.14、.1234,必須有小數點
    – 科學技術法形式,如:5.1234e2=5.12*10的2次方,5.12E-2=5.12/10的2次方

  • 通常情況推薦使用float64,它比float32更精確
package main
import "fmt"
import "unsafe"

func main() {
   
        
	//浮點數默認聲明爲 float64類型
	var num = 89.12
	fmt.Printf("num的數據類型是 %T; 佔用字節數是 %d \n", num, unsafe.Sizeof(num))

	//十進制數形式
	num1 := 3.14
	num2 := .123
	fmt.Println("num1 =", num1, "num2 =", num2)

	//科學計數法形式
	num3 := 1.234e2      //1.234 * 10的2次方
	num4 := 1.234E2      //1.234 * 10的2次方
	num5 := 1.234E-2     //1.234 / 10的2次方
	fmt.Println("num3 =", num3, "num4 =", num4, "num5 =", num5)
}

輸出結果如下圖所示:

在這裏插入圖片描述


3.字符類型

Golang中沒有專門的字符類型,如果要存儲單個字符(字母),一般使用 byte 來保存(ASCII碼錶)。注意,是單個字母,而漢字是3個字節。

字符串是一串固定長度的字符連接起來的字符序列。Go的字符串是由單個字節連接起來的,也就是說傳統的字符串是由字符組成的,而Go的字符串不同,它是由字節組成的

舉個簡單示例:

  • 如果保存的字符在ASCII表中,比如 [0-9, a-z, A-Z, …] 直接可以保存到byte
  • 如果保存的字符對應碼值大於255,這是可以考慮使用int類型保存
  • 如果需要按照字符的方式輸出,這是需要格式化輸出,即fmt.Printf("%c", num)
package main
import "fmt"

func main() {
   
         
	//定義字符類型
	var c1 byte = 'a'
	var c2 byte = '6'

	//當我們直接輸出byte值 即輸出對應字符的ASCII碼值
	fmt.Println("c1 =", c1)
	fmt.Println("c2 =", c2)

	//如果需要輸出對應字符 需要使用格式化輸出
	fmt.Printf("c1=%c c2=%c\n", c1, c2)

	//var c3 byte = '楊'
	//overflow溢出
	var c3 int = '楊'
	fmt.Printf("c3=%c c3對應碼值=%d\n", c3, c3)
}

輸出結果如下圖所示,比如“楊”對應Unicode編碼10進製爲“26472”。由於ASCII碼只能存儲127個字符,英文字母和字符可以,但是中文字符或其他國家字符很多,故衍生出UTF-8其他編碼方式。

在這裏插入圖片描述

字符型的注意事項

  • 字符常量是用單引號(’)括起來的單個字符,例如var c1 byte=‘a’,var c2 int=‘中’
  • Go中允許使用轉義字符 \’ 來講其後的字符轉變爲特殊字符型常量,比如var c3 char = ‘\n’,表示換行符
  • Go語言的字符使用UTF-8編碼,英文字母1個字節,漢字3個字節。如果想查詢字對應的utf8編碼,使用網址:
    http://www.mytju.com/classcode/tools/encode_utf8.asp

在這裏插入圖片描述

  • 在Go中,字符的本質是一個整數,直接輸出時,是該符對應的UTF-8編碼的碼字
  • 可以直接給某個變量賦一個數字,然後按格式化輸出%c,會輸出該數字對應的unicode字符
  • 字符類型可以進行運算,相當於一個整數,因爲它都對應有Unicode編碼
package main
import "fmt"

func main() {
   
         
	//直接賦值數字 格式化輸出字符
	var c1 int = 22269   // 22269->'國'  120->'x'
	fmt.Printf("c1=%c\n", c1)

	//字符類型可以運算 相當於一個整數 運算時按碼值運行
	var c2  = 10 + 'a'
	fmt.Printf("c3=%c c3對應碼值=%d\n", c2, c2)
}

輸出結果如下圖所示:

在這裏插入圖片描述

字符類型本質探討

  • 字符型存儲到計算機中,需要將字符對應的碼值(整數)找出來
    – 存儲:字符 -> 對應碼值 -> 二進制 -> 存儲
    – 讀取:二進制 -> 碼值 -> 字符 -> 讀取

  • 字符和碼值的對應關係是通過字符編碼表決定的,事先規定好的
  • Go語言的編碼都統一成utf-8,從而解決各種亂碼問題

4.布爾型

布爾(bool)類型數據只允許取值true和false,用於表示真和假。它僅佔1個字節,常用於邏輯運算,適用於程序流程控制(後續詳細介紹)。

  • if條件控制語句
  • for循環控制語句

下面舉個簡單的案例:

package main
import "fmt"
import "unsafe"

func main() {
   
          
	//定義數據類型
	var num = false
	fmt.Println("num =", num)

	//注意bool類型佔用1個字節 只能取true或false
	fmt.Println("佔用空間 =", unsafe.Sizeof(num))
}

輸出結果如下:

在這裏插入圖片描述

注意:Go語言中bool類型不可以使用0或非0的整數替代false或true,這和C語言不同。


5.字符串類型

字符串是一串固定長度的字符連接起來的字符序列。Go字符串是由單個字節連接起來的。Go語言的字符串的字節使用UTF-8編碼標識Unicode文本。

package main
import "fmt"

func main() {
   
           
	//定義字符串類型
	var name string = "我的名字叫Eastmount!"
	fmt.Println(name)
}

輸出結果如下圖所示:

在這裏插入圖片描述

字符串的注意事項

  • Go語言字符串的字符使用UTF-8編碼標識Unicode文本,這樣Golang統一使用UTF-8編碼,中文亂碼問題不在困擾我們。編碼問題一直是C語言、Java、Python2常見問題
  • 字符串一旦被複制,字符串就不能修改,即Go中字符串是不可變的(原子性)

在這裏插入圖片描述

字符串兩種表示形式

  • 雙引號:會識別轉移字符
  • 反引號:以字符串的原生形式輸出,包括換行和特殊字符,可以實現防止攻擊、輸出源代碼等效果
package main
import "fmt"

func main() {
   
           
	//雙引號字符串
	str1 := "Eastmount\nCSDN"
	fmt.Println(str1)

	//反引號字符串 ``
	str2 := `
	package main
	import "fmt"

	func main() {
		//定義字符串類型
		var name string = "我的名字叫Eastmount!"
		fmt.Println(name)
	}
	`
	fmt.Println(str2)
}

輸出結果如下圖所示:

在這裏插入圖片描述

字符串拼接

  • var str = “hello” + “world”
  • str += “yangxiuzhang”

當一行字符串太長時,需要使用到多行字符串,可以進行如下處理:

在這裏插入圖片描述


6.基本數據類型的默認值

在Golang中,數據類型都有一個默認值,當程序員沒有賦初值時,它就會保留默認值(或零值)。常見默認值如下圖所示:

在這裏插入圖片描述

舉例如下:

在這裏插入圖片描述


7.基本數據類型轉換

Golang和Java、C不同,Go在不同類型的變量之間賦值時需要顯示轉換。換句話說,Golang中數據類型不能自動轉換。

強制轉換基本語法

  • 表達式 T(v) 將值 v 轉換爲類型 T
  • T:數據類型,比如int32、int64、float64等
  • v:需要轉換的變量
package main
import "fmt"

func main() {
   
             
	//數據類型轉換
	var a int32 = 100

	//整型->浮點型
	var b float32 = float32(a)
	var c int8 = int8(a)

	//低精度->高精度
	var d int64 = int64(a)
	fmt.Printf("a=%v b=%v c=%v d=%v\n", a, b, c, d)
	fmt.Printf("a=%T b=%T c=%T d=%T\n", a, b, c, d)

	//浮點型->整型
	var e float32 = 3.14
	var f int32 = int32(e)
	fmt.Printf("e=%v f=%v\n", e, f)
	fmt.Printf("e=%T f=%T\n", e, f)
}

輸出結果如下圖所示:

在這裏插入圖片描述

注意事項

  • Go中數據類型的轉換可以從表示範圍小到表示範圍大,也可以從範圍大到範圍小
  • 被轉換的是變量存儲的數據(即值),變量本身的數據類型並沒有變化
  • 在轉換中,比如將 int64 轉換成 int8(-128到127),編譯時不會報錯,只是轉換的結果按溢出處理,和期待的結果不一樣。因此在轉換時,需要考慮範圍

在這裏插入圖片描述

  • 數據類型不一致的運算錯誤,建議轉換成相同的數據類型。

在這裏插入圖片描述

由於n3是int8類型,賦值語句含128所以編譯不通過;而n4也是int8類型,但編譯能通過,結果溢出處理。

在這裏插入圖片描述


8.基本數據類型和string轉換

在程序開發中,經常將基本數據類型轉換成string或將string轉換成基本數據類型。

(1) 基本數據類型轉換成string

  • 方法1:fmt.Sprintf("%參數", 表達式)
    Sprintf根據format參數生成格式化的字符串並返回該字符串,參數需要和表達式的數據類型匹配。

在這裏插入圖片描述

代碼如下:

package main
import "fmt"

func main() {
   
              
	//變量定義
	var num1 int = 99
	var num2 float64 = 3.14
	var b bool = true
	var char byte = 'h'
	var str string
	
	//fmt.Sprintf轉換
	str = fmt.Sprintf("%d", num1)
	fmt.Printf("str type %T str=%q\n", str, str)
	
	str = fmt.Sprintf("%f", num2)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%t", b)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = fmt.Sprintf("%c", char)
	fmt.Printf("str type %T str=%q\n", str, str)
}

輸出結果如下圖所示:

在這裏插入圖片描述

  • 方法2:使用strconv包的函數
    – func FormatBool(b bool) string
    – func FormatFloat(f float64, fmt byte, prec, bitSize int) string
    – func FormatInt(i int64, base int) string
    – func FormatUint(i uint64, base int) string
    – func Itoa(int(num))




package main
import "fmt"
import "strconv"

func main() {
   
              
	//變量定義
	var num1 int = 99
	var num2 float64 = 3.14
	var b bool = true
	var num3 int64 = 4567
	var str string
	
	//strconv.FormatInt 10進制
	str = strconv.FormatInt(int64(num1), 10)
	fmt.Printf("str type %T str=%q\n", str, str)

	//strconv.FormatFloat(num2, 'f', 10, 64)
	//說明:'f'格式 10表示小數位保留10位 64表示小數float64
	str = strconv.FormatFloat(num2, 'f', 10, 64)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = strconv.FormatBool(b)
	fmt.Printf("str type %T str=%q\n", str, str)

	str = strconv.Itoa(int(num3))
	fmt.Printf("str type %T str=%q\n", str, str)
}

輸出結果如下圖所示:

在這裏插入圖片描述

(2) string類型轉基本數據類型
使用strconv包的函數:

  • func ParseBool(str string) (value bool, err error)
  • func ParseFloat(s string, bitSize int) (f float64, err error)
  • func ParseInt(s string, base int, bitSize int) (i int64, err error)
  • func ParseUint(s string, b int, bitSize int) (n uint64, err error)

需要說明,因爲返回的是int64或float64,如希望得到int32、float32,則需要調用 int32(num)處理。

在這裏插入圖片描述

注意:在將String類型轉換成基本數據類型時,要確保String類型能夠轉成有效的數據。比如把“123”轉成一個整數,但不能把“hello”轉成一個整數;如果這樣Go直接將其轉成0,其他類型也是同樣的道理,float轉成0、bool轉成false。

在這裏插入圖片描述


三.指針

1.基本介紹

由於後續很多內容(如引用)都會涉及到指針,C語言中它也是一個難點,因此這裏我們先介紹指針的基礎知識,更好地幫助我們學習Golang。

對於基本數據類型來說,變量存的就是值,也叫值類型。獲取變量的地址使用“&”,比如:

  • var num int
  • 獲取num的地址是&num
package main
import "fmt"

func main() {
   
               
	//基本數據類型在內存佈局
	var i int = 10
	//i的地址 &i
	fmt.Println("i的地址=", &i)
	fmt.Println("i的值=", i)
}

輸出結果:

i的地址= 0xc0000100a0
i的值= 10

在這裏插入圖片描述


2.指針類型

指針變量存的是一個地址,這個地址指向的空間存的纔是值,比如:

  • var ptr *int = &num

舉例說明指針在內存的佈局。

在這裏插入圖片描述

package main
import "fmt"

func main() {
   
                
	//基本數據類型在內存佈局
	var i int = 10
	//i的地址 &i
	fmt.Println("i的地址=", &i)
	fmt.Println("i的值=", i)

	/*
	 var ptr *int = &i
	   1.ptr是一個指針變量
	   2.ptr的類型是*int
	   3.ptr本身的值是&i
	*/
	var ptr *int = &i
	fmt.Printf("ptr=%v\n", ptr)
}

輸出結果如下圖所示:

在這裏插入圖片描述


3.獲取指針類型所指向的值

使用 * 實現,比如 var ptr int,使用ptr獲取ptr指向的值。

package main
import "fmt"

func main() {
   
                 
	//基本數據類型在內存佈局
	var i int = 10
	//i的地址 &i
	fmt.Println("i的地址=", &i)
	fmt.Println("i的值=", i)

	/*
	 var ptr *int = &i
	   1.ptr是一個指針變量
	   2.ptr的類型是*int
	   3.ptr本身的值是&i
	*/
	var ptr *int = &i
	fmt.Printf("ptr=%v\n", ptr)

	//獲取指針類型指向的值
	fmt.Printf("ptr的地址=%v\n", &ptr)
	fmt.Printf("ptr指向的值=%v\n", *ptr)
}

輸出結果如下所示:

  • i的地址= 0xc0000100a0
  • i的值= 10
  • ptr=0xc0000100a0
  • ptr的地址=0xc000006030
  • ptr指向的值=10

舉例說明:

在這裏插入圖片描述


4.指針修改值

編寫一個程序,獲取一個int變量num的地址並顯示終端;再將num的地址賦值給指針ptr,通過ptr去修改num的值。

package main
import "fmt"

func main() {
   
                  
	//基本數據類型
	var num int = 10
	fmt.Printf("num的地址=%v 原始值=%v\n", &num, num)

	//指針
	var ptr *int
	ptr = &num
	fmt.Printf("ptr的地址=%v 指向的值爲=%v 自身=%v\n", &ptr, *ptr, ptr)

	//修改值
	*ptr = 666
	fmt.Printf("num修改後的值=%v\n", num)
}

輸出結果如下圖所示:

在這裏插入圖片描述

下面這三個練習也推薦大家嘗試。

在這裏插入圖片描述


5.值類型和引用類型

(1) 值類型

  • 值類型:有對應的指針類型,形式爲“ *數據類型 ”,比如int對應的指針就是“*int”,float32對應的指針類型就是“*float32”,以此類推
  • 值類型的基本數據類型包括:int、float、bool、string、數組和結構體struct
  • 值類型:變量直接存儲值,內存通常在棧中分配

在這裏插入圖片描述

(2) 引用類型

  • 變量存儲的一個地址,這個地址對應的空間纔是真正存儲數據(值),內存通常在堆上分配,當沒有任何變量引用這個地址時,該地址對應的數據空間就成爲一個垃圾空間,由GC來回收。
    - 引用類型的基本數據類型包括:指針、slice切片、map、管道、interface等

在這裏插入圖片描述

內存的棧區和堆區示意圖如下:

在這裏插入圖片描述


四.標識符和關鍵字

1.標識符

Golang對各種變量、方法和函數等命名時使用的字符序列稱爲標識符。凡是自己可以起名字的地方也都叫標識符。其命名規則如下:

  • 由26個英文字母大小寫、0-9、下劃線(_)組成
  • 數字不可以開頭,比如正確的“var num int”、錯誤的“var 3num int”
  • Golang中嚴格區分大小寫,Name和name是兩個不同的變量
  • 標識符不能包含空格
  • 下劃線(_)本身在Go中是一個特殊的標識符,稱爲空標識符。可以代表任何其它的標識符,但是它對應的值會被忽略,所以僅能作爲佔位符使用,不能作爲標識符使用
  • 不能以系統保留關鍵字作爲標識符(一共有25個),比如break、if等

在這裏插入圖片描述

標識符命名注意事項:

  • 包名:保持package的名字和目錄儘量保持一致,建議採取有意義的包名,不要和標準庫衝突(大家可以去Go開源代碼學習下包名)。

在這裏插入圖片描述

  • 變量名、函數名、常量名建議採用駝峯法命名
    比如:var stuName string = “csdn”,形如xxxYyyyZzzz…

  • 如果變量名、函數名、常量名首字母大寫,則可以被其他的包訪問;如果首字母小寫,則只能在本包中使用( 首字母大寫是公開的,首字母小寫是私有的 ),在Golang中沒有public、private等關鍵字,這也是Go與其他語言的區別

舉例說明,首先在utils.go中定義一個變量;然後在main.go中使用該變量。

在這裏插入圖片描述

在這裏插入圖片描述

untils.go

package model

//定義一個變量
var HeroName string = "武松"

main.go

package main
import (
	"fmt"
	//導入utils.go文件的變量或函數 引入該model包
	"go_code/chapter03/datatype/model"
)

func main() {
   
                    
	//該區域的數據可以在同一類型範圍內變化
	var n int = 10
	n = 30
	n = 50
	fmt.Println("n=", n)

	//使用utils.go的HeroName變量 包名.標誌符
	fmt.Println(model.HeroName)
}

輸出結果如下圖所示:

在這裏插入圖片描述

如果變量名定義時是小寫“heroNam”則會報錯

在這裏插入圖片描述


2.關鍵字

在Go中,爲簡化代碼編譯過程中對代碼的解析,系統僅保留25個關鍵字,如下表所示:

在這裏插入圖片描述

除了保留關鍵字外,Go還提供了36個預定的標誌符,包括基礎數據類型和系統內嵌函數。

在這裏插入圖片描述


五.GO編程練習

1.題目

(1) 分別定義常見的數據類型(整型、浮點型、字符型、布爾型、字符串型)變量,輸出對應結果並查看變量的空間大小、數據類型。

變量名稱        數據類型        佔用空間        對應的值
num1     int     8       -12
num2     uint8   1       127
num3     int64   8       12345
num4     float32         4       3.14
num5     float64         8       314.15
num6     int     8       26472
num7     bool    1       false
num8     string          16      Eastmount

(2) 判斷數字9的奇偶性輸出它是奇數或偶數。

num = 9
這是一個奇數

(3) 有人用溫度計測量出華氏法表示的溫度(如69°F),先要求把它轉換爲以攝氏法表示的溫度(如20°C),輸入值爲69。

在這裏插入圖片描述

華氏度 f= 69
攝氏度 c= 20.555555555555557
攝氏度取整 c= 20

(4) 通過兩種方法(調用函數和循環)實現計算字符串“Eastmount”長度。

The length of "Eastmount" is 9.
E a s t m o u n t
The length of "Eastmount" is 9.

(5) 循環依次輸出“East 秀璋”字符串的所有字符。

Unicode遍歷字符串
Unicode: E  69
Unicode: a  97
Unicode: s  115
Unicode: t  116
Unicode:    32
Unicode:31168
Unicode:29835

utf-8遍歷字符串
Unicode: E  69
Unicode: a  97
Unicode: s  115
Unicode: t  116
Unicode:    32
Unicode: ç  231
Unicode: §  167
Unicode:128
Unicode: ç  231
Unicode:146
Unicode:139

(6) 實現字符串循環拼接,將變量str拼接成“a”到“z”並輸出。

abcdefghijklmnopqrstuvwxyz

(7) 從鍵盤上輸入整數、浮點數和字符,然後賦值給變量並輸出結果。

Eastmount
28
60.2
我的名字是: Eastmount
我的年齡是: 28
我的體重是: 60.2

(8) 任意輸入一個字母,實現大小寫自動轉換輸出。

請輸入任意字母:
A
對應的ASCII碼值: 65
A => a

請輸入任意字母:
h
對應的ASCII碼值: 104
h => H

(9) 實現多種數據類型轉換(int和float轉換、float和string轉換)。

a=100 int32, b=100 float32
c=3.14 float32, d=3 int32
e=3.14 float64, f=3.140000 string
e=123.456 string, f=123.456 float64

(10) 指針基本概念,定義變量i,然後指針ptr指向該值,輸出對應值及地址。

i的地址= 0xc0000100a0
i的值= 10
ptr=0xc0000100a0
ptr的地址=0xc000006030
ptr指向的值=10

(11) 編寫一個程序,獲取一個int變量num的地址並顯示終端;再將num的地址賦值給指針ptr,通過ptr去修改num的值。

666
num的地址= 0xc0000100a0
num的值= 54
ptr的地址=0xc000006030
ptr指向的值=54

修改值後: num=512 0xc0000100a0
修改值後: ptr=512 0xc000006030

(12) 輸入a和b兩個整數,調用指針按從大到小的順序輸出a和b。

20 88
a=88, b=20
max=88, min=20    //p1和p2

2.解答

注意:程序實現的方法由千萬種,作者更多是提供一個場景讓你獨立思考,獨立解決問題。希望你喜歡這些題目,不要覺得枯燥,很多題目後續都會結合實際項目及經驗進行介紹。

(1) 分別定義常見的數據類型(整型、浮點型、字符型、布爾型、字符串型)變量,輸出對應結果並查看變量的空間大小、數據類型。

  • 查看某個變量的字節大小(unsafe.Sizeof)和數據類型(fmt.Printf->%T)
package main
import "fmt"
import "unsafe"

func main() {
   
                       
	var num1 int = -12
	fmt.Println("變量名稱\t數據類型\t佔用空間\t對應的值")
	fmt.Printf("num1 \t %T \t %d \t %v \n", num1, unsafe.Sizeof(num1), num1)

	var num2 byte = 127
	fmt.Printf("num2 \t %T \t %d \t %v \n", num2, unsafe.Sizeof(num2), num2)

	var num3 int64 = 12345
	fmt.Printf("num3 \t %T \t %d \t %v \n", num3, unsafe.Sizeof(num3), num3)

	var num4 float32 = 3.14
	fmt.Printf("num4 \t %T \t %d \t %v \n", num4, unsafe.Sizeof(num4), num4)

	var num5 float64 = 3.1415e2
	fmt.Printf("num5 \t %T \t %d \t %v \n", num5, unsafe.Sizeof(num5), num5)

	var num6 int = '楊'
	fmt.Printf("num6 \t %T \t %d \t %v \n", num6, unsafe.Sizeof(num6), num6)

	var num7 bool = false
	fmt.Printf("num7 \t %T \t %d \t %v \n", num7, unsafe.Sizeof(num7), num7)

	var num8 string = "Eastmount"
	fmt.Printf("num8 \t %T \t %d \t %v \n", num8, unsafe.Sizeof(num8), num8)
}

輸出結果如下圖所示:

在這裏插入圖片描述


(2) 判斷數字9的奇偶性輸出它是奇數或偶數。

package main
import "fmt"

func main() {
   
                        
	var num int = 9
	fmt.Println("num =", num)

	//判斷奇偶性
	if num % 2 == 0 {
   
                        
		fmt.Println("這是一個偶數")
	} else {
   
                        
		fmt.Println("這是一個奇數")
	}
}

輸出結果如下圖所示:

在這裏插入圖片描述


(3) 有人用溫度計測量出華氏法表示的溫度(如69°F),先要求把它轉換爲以攝氏法表示的溫度(如20°C),輸入值爲69。

package main
import "fmt"

func main() {
   
                         
	var f float64
	var c float64

	//溫度轉換
	f = 69.0
	c = (5.0 / 9) * (f - 32)
	fmt.Println("華氏度 f=", f)
	fmt.Println("攝氏度 c=", c)
	fmt.Println("攝氏度整數 c=", int64(c))
}

輸出結果如下圖所示:

在這裏插入圖片描述


(4) 通過兩種方法(調用函數和循環)實現計算字符串“Eastmount”長度。

package main
import "fmt"
 
func main() {
   
                          
    var str string
	str = "Eastmount"

	//計算字符串長度
	fmt.Printf("The length of \"%s\" is %d. \n", str, len(str))
	
	//循環計算字符串長度
	var num int = 0
	for _, s := range str {
   
                          
		fmt.Printf("%c ", s)
		num += 1
	}
	fmt.Printf("\nThe length of \"%s\" is %d. \n", str, num)
}

輸出結果如下圖所示:

在這裏插入圖片描述

注意,當字符串中包含多字節字符時,要用到標準庫utf8中的RuneCountInString函數來獲取字符串的長度。代碼如下:

package main
import (
    "fmt"
    "unicode/utf8"
)
 
func main() {
   
                          
	//多字節字符
	test := "Hello, 世界"
	fmt.Println("bytes =", len(test))                      //bytes = 13
    fmt.Println("runes =", utf8.RuneCountInString(test))   //runes = 9
}

(5) 循環依次輸出“East 秀璋”字符串的所有字符。

package main
import "fmt"
 
func main() {
   
                           
	str := "East 秀璋"

	//方法1:Unicode遍歷字符串
	fmt.Printf("Unicode遍歷字符串\n")
	for _, s := range str {
   
                           
		fmt.Printf("Unicode: %c  %d\n", s, s)
	}
	

	//方法2:utf-8遍歷字符串
	fmt.Printf("utf-8遍歷字符串\n")
	for i := 0; i < len(str); i++ {
   
                           
		ch := str[i]
		fmt.Printf("Unicode: %c  %d\n", ch, ch)
	}
}

輸出結果如下圖所示:

在這裏插入圖片描述


(6) 實現字符串循環拼接,將變量str拼接成“a”到“z”並輸出。

package main
import "fmt"
 
func main() {
   
                            
	var str string
	var tt string
	var ch byte = 'a'

	for i := 0; i < 26; i++ {
   
                            
		tt = fmt.Sprintf("%c", ch)
		str += tt
		ch += 1
	}
	fmt.Println(str)
}

上述代碼注意類型轉換,輸出結果如下圖所示:

在這裏插入圖片描述


(7) 從鍵盤上輸入整數、浮點數和字符,然後賦值給變量並輸出結果。

  • 第一種: fmt.Scan(地址列表)
    參數傳入地址列表。輸入變量之間可以採用空格或者換行
  • 第二種: fmt.Scanln(地址列表)
    與Scan不同在於自帶換行,因此輸入變量間不能採用換行
  • 第三種:fmt.Scanf(“格式化字符串”, 地址列表)格式化輸入
    限制固定的輸入格式
package main
import "fmt"

func main() {
   
                             
	var (
		name string
		age int
		weight float32
	)

	//從終端獲取用戶的輸入內容
	fmt.Scan(&name, &age, &weight)
	fmt.Println("我的名字是:", name)
	fmt.Println("我的年齡是:", age)
	fmt.Println("我的體重是:", weight)
}

輸出結果如下圖所示:

在這裏插入圖片描述


(8) 任意輸入一個字母,實現大小寫自動轉換輸出。

  • A的ASCII碼值爲65,a的ASCII碼值爲97
package main
import "fmt"

func main() {
   
                              
	var ch byte
	var re byte

	//從終端獲取用戶的輸入內容
	fmt.Println("請輸入任意字母:")
	fmt.Scanf("%c", &ch)
	fmt.Println("對應的ASCII碼值:", ch)

	//大小寫轉換
	if ch >= 'A' && ch <= 'Z' {
   
                              
		re = ch + 32
	} else if ch >= 'a' && ch <= 'z' {
   
                              
		re = ch - 32
	}
	fmt.Printf("%c => %c", ch, re)
}

輸出結果如下圖所示:

在這裏插入圖片描述


(9) 實現多種數據類型轉換(int和float轉換、float和string轉換)。

package main
import "fmt"
import "strconv"

func main() {
   
                               
	//整型->浮點型
	var a int32 = 100
	var b float32 = float32(a)
	fmt.Printf("a=%v %T, b=%v %T\n", a, a, b, b)

	//浮點型->整型
	var c float32 = 3.14
	var d int32 = int32(c)
	fmt.Printf("c=%v %T, d=%v %T\n", c, c, d, d)

	//其他類型->string
	var e float64 = 3.14
	var f string
	f = fmt.Sprintf("%f", e)
	fmt.Printf("e=%v %T, f=%v %T\n", e, e, f, f)

	//string->其他類型
	var g string = "123.456"
	var h float64
	h, _ = strconv.ParseFloat(g, 64)
	fmt.Printf("e=%v %T, f=%v %T\n", g, g, h, h)
}

輸出結果如下圖所示:

在這裏插入圖片描述


(10) 指針基本概念,定義變量i,然後指針ptr指向該值,輸出對應值及地址。

package main
import "fmt"

func main() {
   
                                
	//基本數據類型在內存佈局
	var i int = 10
	//i的地址 &i
	fmt.Println("i的地址=", &i)
	fmt.Println("i的值=", i)

	/*
	 var ptr *int = &i
	   1.ptr是一個指針變量
	   2.ptr的類型是*int
	   3.ptr本身的值是&i
	*/
	var ptr *int = &i
	fmt.Printf("ptr=%v\n", ptr)

	//獲取指針類型指向的值
	fmt.Printf("ptr的地址=%v\n", &ptr)
	fmt.Printf("ptr指向的值=%v\n", *ptr)
}

輸出結果如下圖所示:

在這裏插入圖片描述


(11) 編寫一個程序,獲取一個int變量num的地址並顯示終端;再將num的地址賦值給指針ptr,通過ptr去修改num的值。

package main
import "fmt"

func main() {
   
                                 
	//獲取一個int變量num的地址並顯示終端
	var num int
	fmt.Scanf("%c", &num)
	fmt.Println("num的地址=", &num)
	fmt.Println("num的值=", num)

	//定義ptr指針變量
	var ptr *int = &num
	fmt.Printf("ptr的地址=%v\n", &ptr)
	fmt.Printf("ptr指向的值=%v\n", *ptr)

	//通過ptr去修改num的值
	*ptr = 512
	fmt.Printf("修改值後: num=%d %v\n", num, &num)
	fmt.Printf("修改值後: ptr=%d %v\n", *ptr, &ptr)
}

輸出結果如下圖所示:

在這裏插入圖片描述


(12) 輸入a和b兩個整數,調用指針按從大到小的順序輸出a和b。

  • 解題:用指針方法來處理,不交換整型變量的值,而是交換兩個指針變量的值
package main
import "fmt"

func main() {
   
                                  
	//變量定義
	var a int
	var b int
	
	//輸入兩個整數
	fmt.Println("請輸入兩個整數:")
	fmt.Scanf("%d %d", &a, &b)

	//p1指向a p2指向b
	var p1 *int = &a
	var p2 *int = &b
	var p *int

	//如果a<b則交換p1與p2的值 a存儲大值
	if a < b {
   
                                  
		p = p1
		p1 = p2
		p2 = p
	}

	//從大到小輸出a、b結果
	fmt.Printf("a=%v, b=%v\n", a, b)

	//輸出p1和p2所指向變量的值
	fmt.Printf("max=%v, min=%v\n", *p1, *p2)
}

輸出結果如下圖所示,但遺憾的是a和b值並沒有交換,而p1確實指向較大值,p2指向較小值。

在這裏插入圖片描述

這與C語言也存在一些差異,C語言代碼如下,請大家下來思考具體原因。

在這裏插入圖片描述


六.總結

寫到這裏,這篇基礎性Golang文章介紹完畢,希望您喜歡!

  • 一.變量
    1.什麼是變量
    2.變量的聲明
    3.變量的注意事項


  • 二.數據類型
    1.整型
    2.浮點型
    3.字符類型
    4.布爾型
    5.字符串類型
    6.基本數據類型的默認值
    7.基本數據類型轉換
    8.基本數據類型和string轉換







  • 三.指針
    1.基本介紹
    2.指針類型
    3.獲取指針類型所指向的值
    4.指針修改值
    5.值類型和引用類型




  • 四.標識符和關鍵字
    1.標識符
    2.關鍵字

  • 五.GO編程練習
    1.題目
    2.解答

Go基本概念和數據類型瞭解後,後面的文章將詳細介紹Go語言的運算、條件語句和循環語句知識,並結合案例進行普及。希望這篇基礎性文章對您有幫助,寫得不好的地方還請海涵。同時非常感謝參考文獻中的大佬們的文章分享,尤其是韓順平老師,深知自己很菜,得努力前行。也希望自己能深入下去,未來四年好好研究Go編程語言,做更多實際工程,寫更好的文章,共勉!

源代碼下載地址:

在這裏插入圖片描述

2020年8月18新開的“娜璋AI安全之家”,主要圍繞Python大數據分析、網絡空間安全、人工智能、Web滲透及攻防技術進行講解,同時分享論文的算法實現。娜璋之家會更加系統,並重構作者的所有文章,從零講解Python和安全,寫了近十年文章,真心想把自己所學所感所做分享出來,還請各位多多指教,真誠邀請您的關注!謝謝。

在這裏插入圖片描述

(By:娜璋AI之家 Eastmount 2021-01-31 星期天 夜於貴陽 https://blog.csdn.net/Eastmount)


參考文獻:


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