go語言基礎知識
go語言的基本事項
- go run hello.go 直接運行,輸出結果(原理也是編譯后執行)
- go build hello.go 生成可執行程序,運行可執行程序,輸出結果
- 注意 go語言中花括號不能單獨占一行,否則會報錯
package main
import "fmt"
func main(){ //go語言中此處的花括號不能單獨占一行,否則會報錯
fmt.Println("hello world")
}
go語言一條語句占一行,如果一行需要執行多個語句 使用 分號 隔開
go語言的輸出語句有3種方式
- import "fmt" 后適用fmt.Println(x) -- 輸出
- println(x) -- 輸出
- fmt.Printf("%d",x) -- 格式化輸出
關鍵字
下面列舉了 Go 代碼中會使用到的 25 個關鍵字或保留字:
break | default | func | interface | select |
---|---|---|---|---|
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
除了以上介紹的這些關鍵字,Go 語言還有 36 個預定義標識符:
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
---|---|---|---|---|---|---|---|---|
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
字符串的拼接和變量的定義方式
定義變量的三種方式
- 正常使用var定義變量
- 使用var定義變量,但是不定義類型,通過賦初值的方式,go編譯器自動識別
- 使用:=的方式來進行 新變量的定義,僅限于新變量 -- 適用于定義在函數內部
//字符串 可以使用+ 進行拼接
fmt.Println("this is my func")
fmt.Println("hello ,wolrd" + "xiaozhuzhu")
//定義變量
var name string="qingbing"
var age,tail int=24,170
fmt.Println(name, age , tail)
fmt.Println(name)
fmt.Println(age)
fmt.Println(tail)
//定義變量的三種方式
//1
var a int = 1
fmt.Println(a)
//2 使用var定義變量,但是不定義類型,通過賦初值的方式,go編譯器自動識別
var b = "hello"
fmt.Println(b)
//3 使用:=的方式來進行 新變量的定義,僅限于新變量
//:= 左側如果沒有聲明新的變量,就產生編譯錯誤
c := 20
fmt.Println(c)
//c:=30 //報錯,因為c已經不是新變量的
c=30 //正確,是一個正常的賦值操作
fmt.Println(c)
c,d:=40,90 //這樣是合法的
fmt.Println(c,d)
因式分解的方式,僅僅適用于定義全局變量
//因式分解的方式,僅僅適用于定義全局變量
var
(
g_a int = 1
g_b,g_c int=1,2
)
空白符
空白標識符 _ 也被用于拋棄值,如值 5 在:_, b = 5, 7 中被拋棄。
_ 實際上是一個只寫變量,你不能得到它的值。這樣做是因為 Go 語言中你必須使用所有被聲明的變量,但有時你并不需要使用從一個函數得到的所有返回值。
//空白符
_,e := 2,3
fmt.Println(e)
const常量
- 定義const常量
//定義const常量
const width,height = 10,5
var area int=width*height
fmt.Println("面積為", area) //50
- const常量用作枚舉
const(
unknow = 0
man = 1
woman = 2
)
println(unknow,man,woman) //0 1 2
- 常量可以用len(), cap(), unsafe.Sizeof()函數計算表達式的值。常量表達式中,函數必須是內置函數,否則編譯不過:
const(
a = "hello"
b = len(a)
c = unsafe.Sizeof(a)
)
println(a,b,c) //hello 5 16
iota的用法
iota,特殊常量,可以認為是一個可以被編譯器修改的常量。
iota 在 const關鍵字出現時將被重置為 0(const 內部的第一行之前),const 中每新增一行常量聲明將使 iota 計數一次(iota 可理解為 const 語句塊中的行索引)。
iota 可以被用作枚舉值:
//itoa的用法
const(
g_a = iota
g_b
g_c
g_d
)
const(
g_e = iota
g_f = "hello"
g_g
g_h = iota
g_i
)
const(
g_j = 1<<iota
g_k
g_l
g_m
)
println(g_a,g_b,g_c,g_d)
println(g_e,g_f,g_g,g_h,g_i)
println(g_j,g_k,g_l,g_m)
//0 1 2 3
//0 hello hello 3 4
//1 2 4 8
運算符
go語言的運算符和C語言的運算符基本一致
Go 沒有三目運算符,不能適用?:
算術運算符
關系運算符
邏輯運算符
位運算符
賦值運算符
其他運算符
語言條件語句
- if xxx
if xxx {
...
}
- if xxx {...} else{...}
if xxx{
...
}else{
...
}
- if xxx{ ... if xxx { ...}}
if xxx{
if xxx {
...
}
...
}
- switch
package main
import "fmt"
func main(){
grade:= 90
if grade >= 90{
println("優秀")
}else if grade >=70 && grade <90{
println("良好")
}else{
println("差")
}
var x interface{} //計算類型
switch i := x.(type){
case nil:
fmt.Printf(" x 的類型 :%T\n",i)
case int:
fmt.Printf("x 是 int 型")
default:
println("未知")
}
}
-
select
類似于C語言中的select,用于多路IO復用
for循環的方式
- 三種方式
- 類似C語言中的for
- 類似C語言中的while
- 死循環
package main
import "fmt"
func main(){
//類似C語言中的for
var sum int
for i:=1;i<=10;i++{
sum +=i
}
fmt.Println(sum)
//類似于while
for sum >30{
sum -= 10
fmt.Println(sum)
}
//死循環
for {
...
}
- For-each range 循環
//for-each range 循環的方式
name := []string{"qqq","yyy"}
for i,str:= range name{
fmt.Printf("%d -- %s\n",i,str)
}
//0 -- qqq
//1 -- yyy
------------------------------------------------------------------------
str := []string{"北京", "天津", "山東"}
//可以默認丟掉第二個返回值
for i := range str {
fmt.Printf("%d -- %s\n", i, str[i])
}
函數
go語言的函數,可以有多個返回值,其余和C語言沒有什么區別
作用域
與C語言一致
- 局部變量
- 全局變量
- 函數形參
數組&切片
思想和C語言一致,數組是固定長度的
切片是動態擴容的,類似于C++的vector
寫法如下:
name := []string{"qingbing","yongchao"}
nums :=[]int{1,2,3,4,5,6}
指針
var ptr1 *int
二級指針
var a int
var ptr *int
var pptr **int
ptr = &a
pptr = &ptr
指針數組
var ptr [5]*int
結構體
go語言中的結構體變量,和結構體指針,訪問結構體成員的時候,都是使用 點(.)來進行訪問,如下:
//定義一個結構體
type info struct{
name string
age int
height int
}
//使用
var stu info
stu.name = "qingbing"
stu.age = 24
stu.height = 170
fmt.Println(stu.name,stu.age,stu.height)
var stu2 *info = &stu
stu2.name = "yongchao"
stu2.age = 24
stu2.height = 160
fmt.Println(stu2.name,stu2.age,stu2.height)
切片slice
Go 語言切片是對數組的抽象。
Go 數組的長度不可改變,在特定場景中這樣的集合就不太適用,Go中提供了一種靈活,功能強悍的內置類型切片("動態數組"),與數組相比切片的長度是不固定的,可以追加元素,在追加時可能使切片的容量增大。
- 使用var定義
- 定義空slice
- 使用:=定義
- 使用make來定義 make([]type,len,cap)
- apend 和 copy的使用
package main
/*
author:qingbing
file:slice
function:study slice for golang
*/
import "fmt"
func main(){
//定義切片的方式
//1、使用var定義
var s1 = []int{1,2,3};
printInfo(s1);
//2、定義空slice
var s2 []int
printInfo(s2);
//3、使用:=定義
ss := []int{3,4,5,6}
printInfo(ss);
//4、使用make來定義 make([]type,len,cap)
s3 := make([]int,2,3)
printInfo(s3);
//復制操作
s3[0] = 3
printInfo(s3);
//覆蓋整個slice
s1 = s3
printInfo(s1);
//apend 和 copy的使用
s3 = append(s3,6,7,8,9)
printInfo(s3);
//擴容
s4 := make([]int,len(s3),cap(s3) * 3)
copy(s4,s3)
printInfo(s4);
//s[2:]
println(s4[1:])
println(s4[:4])
println(s4[1:3])
fmt.Printf("s4[1:] = %v \n",s4[1:])
fmt.Printf("s4[:4] = %v \n",s4[:4])
fmt.Printf("s4[1:3] = %v \n",s4[1:3])
}
func printInfo(s[]int){
fmt.Printf("len = %d, cap = %d, slic = %v\n",len(s),cap(s),s);
}
范圍Range
Go 語言中 range 關鍵字用于 for 循環中迭代數組(array)、切片(slice)、通道(channel)或集合(map)的元素。
在數組和切片中它返回元素的索引和索引對應的值,在集合中返回 key-value 對。
- range 對于 數組、切片
- 對于字符串
- range對于map集合
- 占位符_
package main
/*
author:qingbing
file:range
function:study range for golang
*/
import "fmt"
func main(){
//1、range 對于 數組、切片
s := []string{"apple","pen"}
for i,value := range s{
fmt.Println(i,value)
}
//2、對于字符串
for i,value := range "hello"{
fmt.Println(i,value)
}
//3、range對于map集合
m := map[string]string{"name":"xiaopang","age":"25"}
for i,value := range m{
fmt.Println(i,value)
}
//4、占位符_
sum := 0
nums := []int{1,2,3,4,5}
for _,value := range nums{
sum += value
}
fmt.Println(sum)
}
MAP集合
Map 是一種無序的鍵值對的集合。Map 最重要的一點是通過 key 來快速檢索數據,key 類似于索引,指向數據的值。
Map 是一種集合,所以我們可以像迭代數組和切片那樣迭代它。不過,Map 是無序的,我們無法決定它的返回順序,這是因為 Map 是使用 hash 表來實現的。
//類似于key-value的形式
map[string]string
m := map[string]string{"name":"xiaozhu","age":"15"}
mm := make(map[string]string)
countryCapitalMap [ "France" ] = "巴黎"
countryCapitalMap [ "Italy" ] = "羅馬"
countryCapitalMap [ "Japan" ] = "東京"
countryCapitalMap [ "India " ] = "新德里"
delete() 函數
delete() 函數用于刪除集合的元素, 參數為 map 和其對應的 key
delete(countryCapitalMap,"France")
遞歸函數
Go 語言支持遞歸。但我們在使用遞歸時,開發者需要設置退出條件,否則遞歸將陷入無限循環中。
遞歸函數對于解決數學上的問題是非常有用的,就像計算階乘,生成斐波那契數列等。
遞歸算階乘
package main
import "fmt"
func fabulaqi(n uint 64) (result uint64){
if n>0 {
return fabulaqi(n-1)*n
}
return 1
}
func main(){
fmt.Println("result : ",fabulaqi(15))
}
菲波拉契數列
func fabolaqi(n uint64)(result utin64){
if n<2{
return n
}else{
return fabolaqi(n-2)+fabolaqi(n-1)
}
}
接口
Go 語言提供了另外一種數據類型即接口,它把所有的具有共性的方法定義在一起,任何其他類型只要實現了這些方法就是實現了這個接口
package main
import "fmt"
//接口
type phone interface {
call()
show()
}
type xiaomi struct {
name string
ads string
}
type huawei struct {
name string
ads string
}
//接口實現
func (x xiaomi) call() {
fmt.Println("phoneName :", x.name)
}
func (x xiaomi) show() {
fmt.Println("advertisement :", x.ads)
}
func (h huawei) call() {
fmt.Println("phoneName :", h.name)
}
func (h huawei) show() {
fmt.Println("advertisement :", h.ads)
}
func main() {
x := xiaomi{"mi note2", "for fire"}
x.call()
x.show()
h := huawei{"hw p40", "your better phone"}
h.call()
h.show()
}
錯誤
Go 語言通過內置的錯誤接口提供了非常簡單的錯誤處理機制。error類型是一個接口類型,這是它的定義:
package main
import "fmt"
//定義數據結構
type DivideError struct {
devidee int
devider int
}
//錯誤處理實現Error()接口
func (de *DivideError) Error() string {
strdata := `
error,divide is zero
dividee is %d
divider is zero
`
return fmt.Sprintf(strdata, de.devidee)
}
//實現功能接口
func Divide(dividee int, divider int) (result int, errMsg string) {
if divider == 0 {
data := DivideError{dividee, divider}
errMsg = data.Error()
return
} else {
return dividee / divider, ""
}
}
func main() {
a := 10
b := 0
result, err := Divide(a, b)
if err != "" {
fmt.Println(err)
return
}
fmt.Printf("%d / %d == %d \n", a, b, result)
}
go語言的并發
Go 語言支持并發,我們只需要通過 go 關鍵字來開啟 goroutine 即可。goroutine 是輕量級線程,goroutine 的調度是由 Golang 運行時進行管理的。goroutine 語法格式:
- go的并發也是線程不安全的,需要加鎖才安全
package main
import (
"fmt"
"time"
)
func say(s string) {
var i int
for i = 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
var num int = 0
//goroutine 是線程不安全的
func countNum() {
var i int
for i = 0; i < 10; i++ {
time.Sleep(5 * time.Millisecond)
num++
}
}
func main() {
//go say("hello")
//say("world")
go countNum()
countNum()
fmt.Println(num)
}
通道(channel)
通道(channel)是用來傳遞數據的一個數據結構。通道可用于兩個 goroutine 之間通過傳遞一個指定類型的值來同步運行和通訊。操作符 <- 用于指定通道的方向,發送或接收。如果未指定方向,則為雙向通道。
- 注意:默認情況下,通道是不帶緩沖區的。發送端發送數據,同時必須有接收端相應的接收數據。以下實例通過兩個 goroutine 來計算數字之和,在 goroutine 完成計算后,它會計算兩個結果的和:
通道可以設置緩沖區,通過 make 的第二個參數指定緩沖區大小
Go 通過 range 關鍵字來實現遍歷讀取到的數據,類似于與數組或切片
package main
import "fmt"
//不帶緩沖的 通道
func getSum(s []int, c chan int) {
sum := 0
for _, value := range s {
sum += value
}
c <- sum
}
func getSum2(c chan int, n int) {
x, y := 0, 1
var i int
for i = 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c) //關閉通道
}
func main() {
//不帶緩沖的 通道
// s := []int{3, 5, -2, 3, 4, 7, 1, 1, 1}
// c := make(chan int)
// go getSum(s[:3], c)
// go getSum(s[3:6], c)
// go getSum(s[6:], c)
// x, y, z := <-c, <-c, <-c
// fmt.Println(x, y, z, x+y+z)
//帶緩沖的通道
c := make(chan int, 10)
go getSum2(c, cap(c))
for value := range c {
fmt.Println(value)
}
}
自己調用別的包/自己的包
自己調用別人的包或者自己的包,如上目錄結構
- 自己寫的包名,要和目錄名一樣
- 使用go mod 模塊 ,執行 go mod init mystudy
mylib.go
package mylib
func Add(a, b int) int {
return a + b
}
main.go
package main
import (
"fmt"
"mystudy/mylib"
)
func main() {
fmt.Println(mylib.Add(2, 3))
}
以上為本期全部內容,如有疑問可以在評論區或后臺提出你的疑問,我們一起交流,一起成長。
好家伙要是文章對你還有點作用的話,請幫忙點個關注,分享到你的朋友圈,分享技術,分享快樂
技術是開放的,我們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。
作者:小魔童哪吒