Go语言基础知识(一)

Go 语言学习的入门部分,如果有C语言的基础类比学习会非常迅速。总结来说 Go语言 和 C语言很相似,语法更为简单,所以写起来会相对迅速很多。以下是基础语法的笔记。

>教程来自 http://www.yiibai.com/go/go_start.html
感谢博主的精心编写。

导入包

import "fmt"
import "math"

或者下面:

import (
    "fmt"
    "math"
)

导出名字

go 中,首字母大写名称是被导出的。全小写的名字是不会被导出。

fmt.Println(math.Pi)      //这里PI 改为 pi 就是不可以的。

函数

 func add(x int ,y int) int {
    return x + y
}

或者

func add(x,y int) int {
    return x + y 
}

函数中命名返回值

func split (sum int )(x,y int){
    x = sum * 4 /9 ;
    y = sum - x ;
    return ;
}

变量

变量定义看起来颇为随意,很方便。

var c, python , java = true , false , "no!" 
var i , j int = 1 , 2 

func main(){
    fmt.Println(i,j,c,python,java)
}

变量在函数内还可以去掉var等关键字,直接退化为如下操作:

func main(){
    var i , j int = 1 , 2 
    k := 3
    c , python , java = true , false , "no!"
    fmt.Println(i,j,k,c,python,java)
}

基本数据类型

bool
string
int  int8  int16  int32  int64
uint uint8 uint16 uint32 uint64 uintptr
byte //uint8 别名
rune //int32 的别名
     //代表一个unicode码
float32 float64
complex64 complex128

int,uint,uintptr在32位系统上是32位,而在64位系统上是64位。一般直接使用int就可以了。一般仅有特定理由才用定长整型或者无符号整型。

var (
    ToBe    bool      = false
    MaxInt uint64     = 1<<64 - 1
    z      complex128 = cmplx.Sqrt(-5 + 12i) 
)

func main(){
    const f = "%T(%v)\n"
    
    fmt.Printf(f,z,z)
    fmt.Printf(f,MaxInt,MaxInt)
    fmt.Printf(f,ToBe,ToBe)
}

零值

变量在定义时候没有明确的初始化时会赋值为零值.

数值类型为 **0**
布尔类型为 **false**
字符串为 ""  **空字符串**

package main
import "fmt"

func main(){
    var i int
    var f float64
    var b bool
    var s string
    fmt.Printf("%v %v %v %q\n",i,f,b,s)

}

类型转换

表达式T(v)将值v转换为类型T。

一些关于数值的转换

var i int = 42
var f float64 = float64(i)
var u uint = uint(f)

//或者更为简单的形式
i := 42
f := float64(i)
u := uint(f)

类型推导

在定义一个变量却并不显式指定其类型的时候(使用 := 语法或者 var =表达式语法),变量的类型由(等号)右侧的值推导得出.

当右值定义了类型时候,新变量的类型与其相同:

var i int
j := i  // j也是一个int

当时当右边包含了未指明类型的数字常量时候,新的变量就可能是int、float64或complex128 。 这取决于常量的精度:

i := 42            //int
f := 3.142         //float64
g := 0.867 + 0.5i  //complex128

常量

常量的定义与变量类似,只不过使用 const 关键字。

常量可以是字符、字符串、布尔或数字类型的值。

常量不能使用 := 语法定义。

package main

import "fmt"

const Pi = 3.14

func main(){
    const World = "世界"
    fmt.Println("Hello",World)
    fmt.Println("Happy",Pi,"Day")
    
    const Truth = true
    fmt.Println("Go rules?",Truth)
}

数值常量

数值常量是高精度的值。

一个未指定类型的常量由上下文来决定其类型。

也尝试一下输出 needInt(Big) 吧。(int 可以存放最大64位的整数,根据平台的不同有时会更少。)

const(
    Big   = 1  << 100
    small = Big >> 99
)

func needInt(x int) int {return x * 10 + 1}
func needFloat(x float64) float64 {
    return x * 0.1
}

控制流

for

Go 只有一种循环结构 — for循环。这点和C比较相似。具体如下:

func main(){
    sum : = 0 
    for i := 0 ; i < 10 ; i++ {
       sum += i
    }
    fmt.Println(sum)
}

其中循环初始化语句和后置语句都是可选的:

func main(){
    sum := 1 
    for ; sum < 1000 ; {
        sum += sum 
    }
    fmt.Println(sum)
}

这里和C语言很相似,但是不需要括号,如果有括号反而会报错。

for 也可以变成 C语言的中while

func main(){
    sum := 1
    for sum < 1000 {
        sum += sum
    }
    fmt.Println(sum)
}

或者变为死循环

func main(){
    for {    //无退出条件,变成死循环
    
    }
}

if

就像 for循环一样,Go 的 if语句也不要求用 ()括号括起来,但是 {} 大括号表示作用域的内容还是必须要有的。

func sqrt(x float64) string {
    if x<0 {
        return sqrt(-x) + "i"
    }
    return fmt.Sprint(math.Sqrt(x))
}

if的便捷语句

for语句一样,if语句可以在条件之前执行一个简单的语句。由这个语句定义的变量的作用域仅在 if 范围之内。

func pow(x,n,lim float64) float64 {
    if v := math.Pow(x,n); v < lim {
        return v
    }
    return lim 
}

if和else

if 的便捷语句定义的变量同样可以在任何对应的else块中使用。(提示:两个 pow 都在main 中调用 fmt.Println 前执行完了)

func pow(x,n,lim float64) float64 {
    if v:math.Pow(x,n); v<lim{
        return v
    } else {
        fmt.Printf("%g >= %g\n",v,lim)
    }
    return lim
}

swith语句

switch除非以 fallthrough 语句结束,否则分支会自动终止。

switch os: rumtime.GOOS ; os{
    case "darwin":
        fmt.Println("OS X")
    case "linux":
        fmt.Println("Linux")
    default:
        //freebsd , openbsd,
        //plan9,windows...
        fmt.Printf("%s.",os)
}

switch 的执行顺序

switch 的条件从上到下执行,当匹配成功的时候停止。

switch i {
    case 0 :
    case f():
}
//当i==0时候不会调用 **f()**

today := time.Now().Weekday()
switch time.Saturday {
    case today + 0 :
        fmt.Println("Today.")
    case today + 1:
        fmt.Println("Tomorrow")
    case today + 2:
        fmt.Println("In two days")
    default:
        fmt.Println("Too far away")

}

没有条件的swtich

没有条件的switch 同 switch true一样。这一构造使得可以用更清晰的形式来编写长的if-then-else 链。

t := time.Now()
switch {
    case t.Hour() < 12 :
        fmt.Println("Good morning!")
    case t.Hour() < 17 :
        fmt.Println("Good afternoon!")
    default:
        fmt.Println("Good evening.")
}

defer 语句

defer 语句会延迟函数的执行,直到上层函数返回。延迟调用的参数会立即生成,但是在上层函数返回前函数都不会调用。

func main(){
    defer fmt.Println("world")
    
    fmt.Println("hello")
}

defer 栈
  延迟函数调用被压入一个栈中。当函数后进先出的顺序调用被延迟的函数调用。

fmt.Println("counting")

for i:= 0 ; i<10 ; i++{
    defer fmt.Println(i)
}
fmt.Println("done")

指针

go 具有指针。指针保存了变量的内存地址。

类型 *T 是执行类型为 T 的指针,其零值是 nil

var p *int 

&符号会生成一个指向其作用对象的指针。

i := 42
p = &i

* 符号表示指针指向的底层的值。

fmt.Println(*p)   //通过指针p读取i
*p = 21           //通过指针p设置i

这也就是通常所说的“间接引用”或“非直接引用”。

与C不同,Go 没有指针运算。

i,j := 100 , 666

p := &i
fmt.Println(*p)    //通过p读取I的值
*p = 777
fmt.Println(i)

p = &j
*p = *p / 37 
fmt.Println(j)

结构体字段

结构体字段使用点号来访问(这点和C很像)

type Vertex struct {
    X int
    Y int
}

func main(){
    v:= Vertex(1,2)
    v.X = 4
    fmt.Println(v.X)
}

结构指针

结构体字段可以通过结构体指针来访问。

通过指针间接的访问是透明的

type Vertex struct{
    X int
    Y int
}

func main(){
    v := Vertex{1,2}
    p := &v
    p.X = 1e9
    fmt.Println(v)
}

结构体符文

结构体符文表示通过结构体字段的值作为列表来新分配一个结构体。

使用 Name: 语法可以仅列部分字段。(字段名的顺序无关。)

特殊的前缀 & 返回一个指向结构体的指针。

type Vertex struct {
    X,Y int
}

var (
    v1 = Vertex{1,2}  //类型为 Vertex
    v2 = Vertex{X:1}  //Y:0 被省略
    v3 = Vertex{   }  //X:0 和 Y:0
    p = &Vertex{1,2}  //类型为 *Vertex
)

数组 (和C语言的数组概念类似)

类型 [n]T 是一个有 n 个类型为 T 的值的数组。

表达式

var a [10]int 

定义变量 a 是一个有十个整数的数组。

数组的长度是其类型的一部分,因此数组不能改变大小。这看起来是一个制约,但是Go提供一个更加便利的方式来使用数组。

切片(slice)

一个 slice 会指向一个序列的值,并且包含了长度信息。

[]T 是一个元素类型为 T 的切片(slice)。

len(s) 返回 slice s 的长度。

func main(){
    s := []int{2,3,4,5,11,13}
    fmt.Println("s ==",s)
    for i:= 0 ; i < len(s) ; i++{
        fmt.Printf("s[%d] == %d\n",i,s[i])
    }
}

slice的切片
  切片slice 可以包含任意的类型,包括另一个 slice

func main(){
    //create a tic-tac-toe board
    game := [][]string{
        []string{"_","_","_"},
        []string{"_","_","_"},
        []string{"_","_","_"},
    }
    game[0][0] = "X"
    game[2][2] = "O"
    game[2][0] = "X"
    game[1][0] = "O"
    game[0][2] = "X"
    printBoard(game)
}

func printBoard(s [][]string){
    for i:=0 ; i < len(s); i++{
        fmt.Printf("%s\n",strings.Join(s[i]," "))  //TODO 不懂
    }
}

对slice切片

slice可以重新切片,创建新的 slice 值指向相同的数组。

表达式:

s[lo:hi]

表示从 lohi-1 的slice元素,含前端,不包含后端。因此:

s[lo:lo]

是空的,而

s[lo:lo+1]

具备一个元素。

func main(){
    s := []int {2,3,5,7,11,13}
    fmt.Println("s == ",s)
    fmt.Println("s[1:4] ==",s[1:4])
    
    //省略下标从0 开始
    fmt.Println("s[:3] == ",s[:3])
    
    //省略上标到 len(s) 结束
    fmt.Println("s[4:] == ",s[4:])
}

构造 slice
  slice 由函数 make 创建。这会分配一个全是零值的数组并且返回一个 slice 指向这个数组:

a := make([]int,5) // len(a) = 5

为了指定容量,可传递第三个参数到make :

b := make([]int,0,5) //len(b) = 0 , cap(b) = 5
b = b[:cap(b)]       //len(b) = 5 , cap(b) = 5
b = b[1:]            //len(b) = 4 , cap(b) = 4

以下为示例代码:

func main(){
    a := make([]int,5)
    printSlice("a",a)
    
    b := make([]int,0,5)
    printSlice("b",b)
    
    c := b[:2]
    printSlice("c",c)
    
    d := c[2:5]
    printSlice("d",d)
}

func printSlice(s string, x []int){
    fmt.Printf("%s len=%d cap = %d %v\n",s,len(x),cap(x),x)
}

nil slice

slice 的零值是 nil

一个 nilslice 的长度和容量是 0

 var z []int
 fmt.Println(z,len(z),cap(z))
 if z == nil{
     fmt.Println("nil!")
 }

向slice添加元素

slice 的末尾添加元素是一种常见操作,因此 Go 提供了一个内建函数 append 。内建函数的文档对 append 有详细介绍。

func append(s []T, vs ...T) []T

append 的第一个参数是 s 是一个元素类型为 Tslice ,其余类型为 T 的值将会附加到该 slice 的末尾。

append 的结果是一个包含原 **slice** 所有元素加上新添加的元素 **slice**。

如果 s 的底层数组太小,而不能容纳所有值的时候,会分配一个更大的数组。返回 slice 会指向这个新分配的数组。

func main(){
    var a []int
    printSlice("a",a)

    a = append(a,0)
    printSlice("a",a)

    a = append(a,1)
    printSlice("a",a)

    a = append(a,2,3,4)
    printSlice("a",a)
}

func printSlice(s string, x []int){
    fmt.Printf("%s len=%d cap=%d %v\n", s , len(x) , cap(x) , x)
}

范围(range)

for 循环的 range 格式可以对 slice 或者 map 进行迭代循环。

当使用 for 循环遍历一个 slice 时候,每次迭代 range 将返回两个值。第一个是当前的下标(序号),第二个是该下标所对应元素的一个拷贝。

var pow = []int{1,2,4,8,16,32,64,128}

func main(){
    for i,v:= range pow{
        fmt.Printf("2 ** %d = %d \n",i,v)
    }
}

通过赋值给 _ 来忽略序号和值。

如果只需要索引值,去掉 “,value” 的部分就可以了。

func main(){
    pow := make([] int,10)
    for i := range pow{
        pow[i] = 1 << uint(i)
    }
    
    for _,value := range pow{
        fmt.Printf("%d\n",value)
    }
}
    原文作者:爪爪熊
    原文地址: https://www.jianshu.com/p/26101beb79db
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞