go學(xué)習(xí)
學(xué)的時(shí)候感覺go對(duì)于指針和引用的性質(zhì)和C++很像未蝌,后來發(fā)現(xiàn)它就是基于C改的举农。
筆記來源:
https://www.runoob.com/go/go-tutorial.html
基礎(chǔ)實(shí)例
Go 語言的基礎(chǔ)組成有以下幾個(gè)部分:
- 包聲明
- 引入包
- 函數(shù)
- 變量
- 語句 & 表達(dá)式
- 注釋
package main //第一行代碼 package main 定義了包名纹坐。你必須在源文件中非注釋的第一行指明這個(gè)文件屬于哪個(gè)包鹊奖,如:package main挥唠。package main表示一個(gè)可獨(dú)立執(zhí)行的程序液走,每個(gè) Go 應(yīng)用程序都包含一個(gè)名為 main 的包。
import "fmt" //下一行 import "fmt" 告訴 Go 編譯器這個(gè)程序需要使用 fmt 包(的函數(shù)钱慢,或其他元素)逮京,fmt 包實(shí)現(xiàn)了格式化 IO(輸入/輸出)的函數(shù)。
//像C++中 #include <cstdio>
func main() {
/* 這是我的第一個(gè)簡單的程序 */
fmt.Println("Hello, World!")
}
【利用變量命名的大小寫來實(shí)現(xiàn)訪問權(quán)限】
當(dāng)標(biāo)識(shí)符(包括常量束莫、變量懒棉、類型、函數(shù)名麦箍、結(jié)構(gòu)字段等等)以一個(gè)大寫字母開頭漓藕,如:Group1,那么使用這種形式的標(biāo)識(shí)符的對(duì)象就可以被外部包的代碼所使用(客戶端程序需要先導(dǎo)入這個(gè)包)挟裂,這被稱為導(dǎo)出(像面向?qū)ο笳Z言中的 public)享钞;標(biāo)識(shí)符如果以小寫字母開頭,則對(duì)包外是不可見的诀蓉,但是他們?cè)谡麄€(gè)包的內(nèi)部是可見并且可用的(像面向?qū)ο笳Z言中的 protected )栗竖。
需要注意的是 { 不能單獨(dú)放在一行,所以以下代碼在運(yùn)行時(shí)會(huì)產(chǎn)生錯(cuò)誤:
package main
import "fmt"
func main()
{ // 錯(cuò)誤渠啤,{ 不能在單獨(dú)的行上
fmt.Println("Hello, World!")
}
Go 語言的字符串可以通過 + 實(shí)現(xiàn)(Python 和 Java也可以)
變量
聲明變量的一般形式是使用 var 關(guān)鍵字:
var identifier type
可以一次聲明多個(gè)變量:
var identifier1, identifier2 type
package main
import "fmt"
func main() {
var a string = "Runoob"
fmt.Println(a)
var b, c int = 1, 2
fmt.Println(b, c)
}
變量聲明
第一種狐肢,指定變量類型,如果沒有初始化沥曹,則變量默認(rèn)為零值份名。
package main
import "fmt"
func main() {
// 聲明一個(gè)變量并初始化
var a = "RUNOOB"
fmt.Println(a)
// 沒有初始化就為零值
var b int
fmt.Println(b)
// bool 零值為 false
var c bool
fmt.Println(c)
}
數(shù)值類型(包括complex64/128)為 0
布爾類型為 false
字符串為 ""(空字符串)
-
以下幾種類型為 nil:
var a *int var a []int var a map[string] int var a chan int var a func(string) int var a error // error 是接口
第二種,根據(jù)值自行判定變量類型妓美。
package main
import "fmt"
func main() {
var d = true
fmt.Println(d)
}
第三種僵腺,省略 var, 注意 *:=* 左側(cè)如果沒有聲明新的變量,就產(chǎn)生編譯錯(cuò)誤壶栋,格式:**
v_name := value
例如:
var intVal int
intVal :=1 // 這時(shí)候會(huì)產(chǎn)生編譯錯(cuò)誤
intVal,intVal1 := 1,2 // 此時(shí)不會(huì)產(chǎn)生編譯錯(cuò)誤辰如,因?yàn)橛新暶餍碌淖兞浚驗(yàn)?:= 是一個(gè)聲明語句
因此可以將 var f string = "Runoob" 簡寫為 f := "Runoob":
package main
import "fmt"
func main() {
f := "Runoob" // var f string = "Runoob"
fmt.Println(f)
}
多變量聲明
//類型相同多個(gè)變量, 非全局變量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要顯示聲明類型贵试,自動(dòng)推斷
vname1, vname2, vname3 := v1, v2, v3 // 出現(xiàn)在 := 左側(cè)的變量不應(yīng)該是已經(jīng)被聲明過的琉兜,否則會(huì)導(dǎo)致編譯錯(cuò)誤
// 這種因式分解關(guān)鍵字的寫法一般用于聲明全局變量
var (
vname1 v_type1
vname2 v_type2
)
package main
var x, y int
var ( // 這種因式分解關(guān)鍵字的寫法一般用于聲明全局變量
a int
b bool
)
var c, d int = 1, 2
var e, f = 123, "hello"
//這種不帶聲明格式的只能在函數(shù)體中出現(xiàn)
//g, h := 123, "hello"
func main(){
g, h := 123, "hello"
println(x, y, a, b, c, d, e, f, g, h)
}
值類型和引用類型
所有像 int、float毙玻、bool 和 string 這些基本類型都屬于值類型豌蟋,使用這些類型的變量直接指向存在內(nèi)存中的值。
當(dāng)使用等號(hào) =
將一個(gè)變量的值賦值給另一個(gè)變量時(shí)桑滩,如:j = i
夺饲,實(shí)際上是在內(nèi)存中將 i 的值進(jìn)行了拷貝。
一個(gè)引用類型的變量 r1 存儲(chǔ)的是 r1 的值所在的內(nèi)存地址(數(shù)字),或內(nèi)存地址中第一個(gè)字所在的位置往声。
這個(gè)內(nèi)存地址稱之為指針擂找,這個(gè)指針實(shí)際上也被存在另外的某一個(gè)值中。
同一個(gè)引用類型的指針指向的多個(gè)字可以是在連續(xù)的內(nèi)存地址中(內(nèi)存布局是連續(xù)的)浩销,這也是計(jì)算效率最高的一種存儲(chǔ)形式贯涎;也可以將這些字分散存放在內(nèi)存中,每個(gè)字都指示了下一個(gè)字所在的內(nèi)存地址慢洋。
當(dāng)使用賦值語句 r2 = r1 時(shí)塘雳,只有引用(地址)被復(fù)制。
如果 r1 的值被改變了普筹,那么這個(gè)值的所有引用都會(huì)指向被修改后的內(nèi)容败明,在這個(gè)例子中,r2 也會(huì)受到影響太防。
注意事項(xiàng)
如果在相同的代碼塊中妻顶,我們不可以再次對(duì)于相同名稱的變量使用初始化聲明,例如:a := 20 就是不被允許的蜒车,編譯器會(huì)提示錯(cuò)誤 no new variables on left side of :=讳嘱,但是 a = 20 是可以的,因?yàn)檫@是給相同的變量賦予一個(gè)新的值酿愧。
如果你在定義變量 a 之前使用它沥潭,則會(huì)得到編譯錯(cuò)誤 undefined: a。
如果你聲明了一個(gè)局部變量卻沒有在相同的代碼塊中使用它嬉挡,同樣會(huì)得到編譯錯(cuò)誤钝鸽,例如下面這個(gè)例子當(dāng)中的變量 a:【**和其它語言不一樣,局部變量聲明沒有用會(huì)報(bào)錯(cuò)庞钢,如果需要值接收不需要的函數(shù)返回值拔恰,用 '_' **】
但是全局變量是允許聲明但不使用的。 同一類型的多個(gè)變量可以聲明在同一行焊夸,如:
var a, b, c int
多變量可以在同一行進(jìn)行賦值,如:
var a, b int
var c string
a, b, c = 5, 7, "abc"
上面這行假設(shè)了變量 a蓝角,b 和 c 都已經(jīng)被聲明阱穗,否則的話應(yīng)該這樣使用:
a, b, c := 5, 7, "abc"
右邊的這些值以相同的順序賦值給左邊的變量,所以 a 的值是 5使鹅, b 的值是 7揪阶,c 的值是 "abc"。
這被稱為 并行 或 同時(shí) 賦值患朱。
如果你想要交換兩個(gè)變量的值鲁僚,則可以簡單地使用 a, b = b, a,兩個(gè)變量的類型必須是相同。
空白標(biāo)識(shí)符 _ 也被用于拋棄值冰沙,如值 5 在:_, b = 5, 7 中被拋棄侨艾。
_ 實(shí)際上是一個(gè)只寫變量,你不能得到它的值拓挥。這樣做是因?yàn)?Go 語言中你必須使用所有被聲明的變量唠梨,但有時(shí)你并不需要使用從一個(gè)函數(shù)得到的所有返回值。
并行賦值也被用于當(dāng)一個(gè)函數(shù)返回多個(gè)返回值時(shí)侥啤,比如這里的 val 和錯(cuò)誤 err 是通過調(diào)用 Func1 函數(shù)同時(shí)得到:val, err = Func1(var1)当叭。
常量
常量是一個(gè)簡單值的標(biāo)識(shí)符,在程序運(yùn)行時(shí)盖灸,不會(huì)被修改的量蚁鳖。
常量中的數(shù)據(jù)類型只可以是布爾型、數(shù)字型(整數(shù)型赁炎、浮點(diǎn)型和復(fù)數(shù))和字符串型醉箕。
常量的定義格式:
const identifier [type] = value
你可以省略類型說明符 [type],因?yàn)榫幾g器可以根據(jù)變量的值來推斷其類型甘邀。
- 顯式類型定義:
const b string = "abc"
- 隱式類型定義:
const b = "abc"
多個(gè)相同類型的聲明可以簡寫為:
const c_name1, c_name2 = value1, value2
常量還可以用作枚舉:
const (
Unknown = 0
Female = 1
Male = 2
)
數(shù)字 0琅攘、1 和 2 分別代表未知性別、女性和男性松邪。
常量可以用len(), cap(), unsafe.Sizeof()函數(shù)計(jì)算表達(dá)式的值坞琴。常量表達(dá)式中,函數(shù)必須是內(nèi)置函數(shù)逗抑,否則編譯不過:
package main
import "unsafe"
const (
a = "abc"
b = len(a)
c = unsafe.Sizeof(a)
)
func main(){
println(a, b, c)
//abc 3 16
}
16的來源:
使用unsafe.Sizeof()打印字符串剧辐,可以發(fā)現(xiàn)它的長度是一個(gè)定值16,通過查看源碼邮府,我們知道string的結(jié)構(gòu)如下:
type stringStruct struct {
str unsafe.Pointer
len int
}
字符串的實(shí)際存儲(chǔ)是一個(gè)byte數(shù)組荧关,length長度就是字符數(shù)組的長度。這里的byte數(shù)組存儲(chǔ)的是字符串的utf-8的編碼
特殊常量iota
iota褂傀,特殊常量忍啤,可以認(rèn)為是一個(gè)可以被編譯器修改的常量。
iota 在 const關(guān)鍵字出現(xiàn)時(shí)將被重置為 0(const 內(nèi)部的第一行之前)仙辟,const 中每新增一行常量聲明將使 iota 計(jì)數(shù)一次(iota 可理解為 const 語句塊中的行索引)同波。
iota 可以被用作枚舉值:
const (
a = iota
b = iota
c = iota
)
第一個(gè) iota 等于 0,每當(dāng) iota 在新的一行被使用時(shí)叠国,它的值都會(huì)自動(dòng)加 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" //獨(dú)立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢復(fù)計(jì)數(shù)
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
//0 1 2 ha ha 100 100 7 8
}
package main
import "fmt"
const (
i=1<<iota
j=3<<iota
k
l
)
func main() {
fmt.Println("i=",i)
fmt.Println("j=",j)
fmt.Println("k=",k)
fmt.Println("l=",l)
}
以上實(shí)例運(yùn)行結(jié)果為:
i= 1
j= 6
k= 12
l= 24
iota 表示從 0 開始自動(dòng)加 1粟焊,所以 i=1<<0, j=3<<1(<< 表示左移的意思)冤狡,即:i=1, j=6孙蒙,這沒問題,關(guān)鍵在 k 和 l悲雳,從輸出結(jié)果看 k=3<<2挎峦,l=3<<3。
簡單表述:
i=1:左移 0 位,不變?nèi)詾?1;
j=3:左移 1 位,變?yōu)槎M(jìn)制 110, 即 6;
k=3:左移 2 位,變?yōu)槎M(jìn)制 1100, 即 12;
l=3:左移 3 位,變?yōu)槎M(jìn)制 11000,即 24怜奖。
注:<<n==*(2^n)浑测。
運(yùn)算符
位運(yùn)算符
運(yùn)算符 | 描述 | 實(shí)例 |
---|---|---|
& | 按位與運(yùn)算符"&"是雙目運(yùn)算符。 其功能是參與運(yùn)算的兩數(shù)各對(duì)應(yīng)的二進(jìn)位相與歪玲。 | (A & B) 結(jié)果為 12, 二進(jìn)制為 0000 1100 |
| | 按位或運(yùn)算符"|"是雙目運(yùn)算符迁央。 其功能是參與運(yùn)算的兩數(shù)各對(duì)應(yīng)的二進(jìn)位相或 | (A | B) 結(jié)果為 61, 二進(jìn)制為 0011 1101 |
^ | 按位異或運(yùn)算符"^"是雙目運(yùn)算符。 其功能是參與運(yùn)算的兩數(shù)各對(duì)應(yīng)的二進(jìn)位相異或滥崩,當(dāng)兩對(duì)應(yīng)的二進(jìn)位相異時(shí)岖圈,結(jié)果為1。 | (A ^ B) 結(jié)果為 49, 二進(jìn)制為 0011 0001 |
<< | 左移運(yùn)算符"<<"是雙目運(yùn)算符钙皮。左移n位就是乘以2的n次方蜂科。 其功能把"<<"左邊的運(yùn)算數(shù)的各二進(jìn)位全部左移若干位,由"<<"右邊的數(shù)指定移動(dòng)的位數(shù)短条,高位丟棄导匣,低位補(bǔ)0。 | A << 2 結(jié)果為 240 茸时,二進(jìn)制為 1111 0000 |
>> | 右移運(yùn)算符">>"是雙目運(yùn)算符贡定。右移n位就是除以2的n次方。 其功能是把">>"左邊的運(yùn)算數(shù)的各二進(jìn)位全部右移若干位可都,">>"右邊的數(shù)指定移動(dòng)的位數(shù)缓待。 | A >> 2 結(jié)果為 15 ,二進(jìn)制為 0000 1111 |
package main
import "fmt"
func main() {
var a uint = 60 /* 60 = 0011 1100 */
var b uint = 13 /* 13 = 0000 1101 */
var c uint = 0
c = a & b /* 12 = 0000 1100 */
fmt.Printf("第一行 - c 的值為 %d\n", c )
c = a | b /* 61 = 0011 1101 */
fmt.Printf("第二行 - c 的值為 %d\n", c )
c = a ^ b /* 49 = 0011 0001 */
fmt.Printf("第三行 - c 的值為 %d\n", c )
c = a << 2 /* 240 = 1111 0000 */
fmt.Printf("第四行 - c 的值為 %d\n", c )
c = a >> 2 /* 15 = 0000 1111 */
fmt.Printf("第五行 - c 的值為 %d\n", c )
}
指針和引用
下表列出了Go語言的其他運(yùn)算符渠牲。
運(yùn)算符 | 描述 | 實(shí)例 |
---|---|---|
& | 返回變量存儲(chǔ)地址 | &a; 將給出變量的實(shí)際地址旋炒。 |
* | 指針變量。 | *a; 是一個(gè)指針變量 |
package main
import "fmt"
func main() {
var a int = 4
var b int32
var c float32
var ptr *int
/* 運(yùn)算符實(shí)例 */
fmt.Printf("第 1 行 - a 變量類型為 = %T\n", a );
fmt.Printf("第 2 行 - b 變量類型為 = %T\n", b );
fmt.Printf("第 3 行 - c 變量類型為 = %T\n", c );
/* & 和 * 運(yùn)算符實(shí)例 */
ptr = &a /* 'ptr' 包含了 'a' 變量的地址 */ //ptr是個(gè)指針签杈,內(nèi)容是a的地址
fmt.Printf("a 的值為 %d\n", a);
fmt.Printf("*ptr 為 %d\n", *ptr);//*取指針里的內(nèi)容
fmt.Println(ptr)
}
第 1 行 - a 變量類型為 = int
第 2 行 - b 變量類型為 = int32
第 3 行 - c 變量類型為 = float32
a 的值為 4
*ptr 為 4
0xc000010090
循環(huán)語句
1.無限循環(huán)瘫镇,不用while
如果循環(huán)中條件語句永遠(yuǎn)不為 false 則會(huì)進(jìn)行無限循環(huán),我們可以通過 for 循環(huán)語句中只設(shè)置一個(gè)條件表達(dá)式來執(zhí)行無限循環(huán):
package main
import "fmt"
func main() {
for true {
fmt.Printf("這是無限循環(huán)答姥。\n");
}
}
- 少有的goto語法
Go 語言的 goto 語句可以無條件地轉(zhuǎn)移到過程中指定的行铣除。
goto 語句通常與條件語句配合使用√哂浚可用來實(shí)現(xiàn)條件轉(zhuǎn)移通孽, 構(gòu)成循環(huán)序宦,跳出循環(huán)體等功能睁壁。
但是背苦,在結(jié)構(gòu)化程序設(shè)計(jì)中一般不主張使用 goto 語句, 以免造成程序流程的混亂潘明,使理解和調(diào)試程序都產(chǎn)生困難行剂。
實(shí)例
在變量 a 等于 15 的時(shí)候跳過本次循環(huán)并回到循環(huán)的開始語句 LOOP 處:
package main
import "fmt"
func main() {
/* 定義局部變量 */
var a int = 10
/* 循環(huán) */
LOOP: for a < 20 {
if a == 15 {
/* 跳過迭代 */
a = a + 1
goto LOOP
}
fmt.Printf("a的值為 : %d\n", a)
a++
}
}
函數(shù)
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("Google", "Runoob")
fmt.Println(a, b)
}
- 函數(shù)參數(shù)
函數(shù)如果使用參數(shù),該變量可稱為函數(shù)的形參钳降。
形參就像定義在函數(shù)體內(nèi)的局部變量厚宰。
調(diào)用函數(shù),可以通過兩種方式來傳遞參數(shù):
傳遞類型 | 描述 |
---|---|
值傳遞 | 值傳遞是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)復(fù)制一份傳遞到函數(shù)中遂填,這樣在函數(shù)中如果對(duì)參數(shù)進(jìn)行修改铲觉,將不會(huì)影響到實(shí)際參數(shù)。 |
引用傳遞 | 引用傳遞是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)的地址傳遞到函數(shù)中吓坚,那么在函數(shù)中對(duì)參數(shù)所進(jìn)行的修改撵幽,將影響到實(shí)際參數(shù)。 |
默認(rèn)情況下礁击,Go 語言使用的是值傳遞盐杂,即在調(diào)用過程中不會(huì)影響到實(shí)際參數(shù)
如果想進(jìn)行引用傳遞:
引用傳遞是指在調(diào)用函數(shù)時(shí)將實(shí)際參數(shù)的地址傳遞到函數(shù)中,那么在函數(shù)中對(duì)參數(shù)所進(jìn)行的修改哆窿,將影響到實(shí)際參數(shù)链烈。
引用傳遞指針參數(shù)傳遞到函數(shù)內(nèi),以下是交換函數(shù) swap() 使用了引用傳遞:
package main
import "fmt"
func main() {
/* 定義局部變量 */
var a int = 100
var b int= 200
fmt.Printf("交換前挚躯,a 的值 : %d\n", a )
fmt.Printf("交換前强衡,b 的值 : %d\n", b )
/* 調(diào)用 swap() 函數(shù)
* &a 指向 a 指針,a 變量的地址
* &b 指向 b 指針秧均,b 變量的地址
*/
swap(&a, &b)#傳地址
fmt.Printf("交換后食侮,a 的值 : %d\n", a )
fmt.Printf("交換后,b 的值 : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址上的值 */
*x = *y /* 將 y 值賦給 x */
*y = temp /* 將 temp 值賦給 y */
}
函數(shù)方法
Go 語言中同時(shí)有函數(shù)和方法目胡。一個(gè)方法就是一個(gè)包含了接受者的函數(shù)锯七,接受者可以是命名類型或者結(jié)構(gòu)體類型的一個(gè)值或者是一個(gè)指針。所有給定類型的方法屬于該類型的方法集誉己。語法格式如下:
func (variable_name variable_data_type) function_name() [return_type]{
/* 函數(shù)體*/
}
下面定義一個(gè)結(jié)構(gòu)體類型和該類型的一個(gè)方法:
(它沒有面向?qū)ο竺际匀绻忻嫦驅(qū)ο蟮牟僮鳎梢赃@樣實(shí)現(xiàn)巨双。)
package main
import (
"fmt"
)
/* 定義結(jié)構(gòu)體 */
type Circle struct {
radius float64
}
func main() {
var c1 Circle
c1.radius = 10.00
fmt.Println("圓的面積 = ", c1.getArea())
}
//該 method 屬于 Circle 類型對(duì)象中的方法
func (c Circle) getArea() float64 {
//c.radius 即為 Circle 類型對(duì)象中的屬性
return 3.14 * c.radius * c.radius
}
形參會(huì)作為函數(shù)的局部參數(shù)使用噪猾。
指針
變量是一種使用方便的占位符,用于引用計(jì)算機(jī)內(nèi)存地址筑累。
Go 語言的取地址符是 &袱蜡,放到一個(gè)變量前使用就會(huì)返回相應(yīng)變量的內(nèi)存地址。
package main
import "fmt"
func main() {
/* 定義局部變量 */
var a int = 100
var b int= 200
fmt.Printf("交換前 a 的值 : %d\n", a )
fmt.Printf("交換前 b 的值 : %d\n", b )
/* 調(diào)用函數(shù)用于交換值
* &a 指向 a 變量的地址
* &b 指向 b 變量的地址
*/
swap(&a, &b);
fmt.Printf("交換后 a 的值 : %d\n", a )
fmt.Printf("交換后 b 的值 : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址的值 */
*x = *y /* 將 y 賦值給 x */
*y = temp /* 將 temp 賦值給 y */
}