Golang 基础知识

本文整合了 Golang 的基础知识体系,包括基础语法规范、包管理(packageimport) 以及特有的指针概念,适合 Go 语言初学者参考。

1. 简介

简介

GO 语言特点

  1. 运行效率高,开发高效,部署简单 (可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了)
  2. 语言层面支持并发,易于利用多核实现并发 (最大特点)
  3. 内置 runtime(作用:性能监控,GC 等),支持垃圾回收
  4. 丰富标准库,强大网络库
  5. 内置强大的工具( gofmt ),跨平台编译,内嵌 C 支持

GO 语言的应用

  1. 服务器编程,以前你如果使用 C 或者 C++ 做的那些事情,用 Go 来做很合适,例如处理日志、数据打包、虚拟机处理、文件系统等。
  2. 分布式系统,数据库代理器等
  3. 网络编程,这一块目前应用最广,包括Web应用、API应用、下载应用、
  4. 内存数据库,前一段时间 google 开发的 groupcache,couchbase 的部分组建
  5. 云平台,目前国外很多云平台在采用 Go 开发,CloudFoundy 的部分组建,前 VMare 的技术总监自己出来搞的 apcera 云平台。

GO 语言命令行工具

  1. go build 用于编译源码文件,代码包,依赖包;
  2. go run 可以编译并运行Go源码文件
  3. go get 主要用来动态获取远程代码包

编译 windows x86_64 GOOS=windows GOARCH=amd64 go build hello.go

GO 安装

使用 linux

1
2
3
4
5
6
7
8
9
10
11
12
13
14
cd /usr/src
wget https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz
tar -xvf go*.linux-amd64.tar.gz -C /usr/local/

cat > /etc/profile.d/go_env.sh < EOF
export GOROOT="/usr/local/go"
export GOPATH=$HOME/go
export GOBIN=$GOROOT/bin
export PATH=$PATH:$GOBIN
EOF

source /etc/profile
go version
go env

一些配置

1
2
3
4
go env -w GOPROXY=https://goproxy.cn,https://mirrors.aliyun.com/goproxy/,direct
go env -w GO111MODULE=on

go env -w GOPRIVATE="*.example.com"

2. 基础语法

一般结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 当前程序的包名
package main

// 导入其他包
import . "fmt"

// 常量定义
const PI = 3.14

// 全局变量的声明和赋值
var name = "gopher"

// 一般类型声明
type newType int

// 结构的声明
type gopher struct{}

// 接口的声明
type golang interface{}

// 由main函数作为程序入口点启动
func main() {
Println("Hello World!")
}

变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package main
import "fmt"

var x1, y1 int
var ( // 这种因式分解关键字的写法一般用于声明全局变量
a1 int
b1 bool
)

var c1, d1 int = 1, 2
var e1, f1 = 123, "hello"

var h1 int // 全局变量 允许声明但不使用
// e1 := "weilai" // 只能被用在函数体内,而不可以用于全局变量的声明与赋值


func main() {
/*
多行注释
*/
// 一行注释
var a string = "weilai" // 声明一个变量并初始化
var b, c int = 1, 2 // 一次声明多个变量:
var d int // 声明变量,不初始化,一般情况下,值类型默认为 0, 布尔类型默认值为 false ,string 默认值为空字符串 ''
var z bool // bool 0值 false
fmt.Println(z)
fmt.Println(a) // 打印 a 换行
fmt.Print(b, c) // 不会换行
fmt.Println(d)
fmt.Println("hello") // 会换行
fmt.Println("world" )
fmt.Print("hello")
fmt.Print("world")

// var e int 这行存在 下行就会报错就会报错 no new variables on left side of :=
e := "weilai" // 等同于 var e string = "weilai" // 使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明
f, g := 20, 30
f, g = g, f // 如果你想要交换两个变量的值,,两个变量的类型必须是相同。

// h := 40 // 声明但未使用 局部变量 会报错
fmt.Println("e=", e , ",f=" , f , ",g=" , g)
fmt.Printf("e=%s,f=%d,g=%d" , e , f , g) // Printf 是格式化输出
var vname1, vname2, vname3 = "v1", "v2", "v3"
fmt.Printf(vname1, vname2, vname3)
println(x1, y1, a1, b1, c1, d1, e1, f1)

_,numb,strs := numbers() //只获取函数返回值的后两个
fmt.Println(numb,strs)
}
//一个可以返回多个值的函数
func numbers()(int,int,string){
a , b , c := 1 , 2 , "str"
return a,b,c
}

/*

false
weilai
1 20
hello
world
helloworlde= weilai ,f= 30 ,g= 20
e=weilai,f=30,g=20v1%!(EXTRA string=v2, string=v3)0 0 0 false 1 2 123 hello
2 str

*/

3. 规范

变量

声明,初始化,赋值

  1. 同一行声明多个变量并赋值: var a,b,c int =1,2,3 或者 d,e := 4,5
  2. 全局变量声明必须使用var关键字,局部变量可以省略(a,b,c int =1,2,3)
  3. 变量 _ 表示丢弃

    变量类型转换

  4. 类型转换必须是显示的,不存在隐式转换
  5. 只发生在兼容类型之间
  6. <变量名称> [:]= <目标类型>(<需要转换的变量>)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    package main
    import (
    "fmt"
    "reflect"
    )

    func main(){

    var a float32 = 3.1
    c := int(a)

    fmt.Println(c)
    fmt.Println(reflect.TypeOf(c))

    }

变量可见性规则

  1. 大写字母开头的变量可导出,其他包可以读取,是共有变量
  2. 小写字母开头的变量不可导出,是私有变量

常量

常量定义形式

常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型

  1. 显示 const identifier [type] = value
  2. 隐式 const identifier = value (通常叫做 无类型常量)
  3. 常量可以使用 内置表达式定义 eg: len(),unsafe.Size0f()

特殊常量 iota

  1. iota 在 const 关键字出现时被重置为 0
  2. const 中每新增一行常量声明 iota 就计数一次
  3. iota 只能在常量中使用

iota 常见使用法:

  1. 跳值使用法
  2. 插队使用法
  3. 表达式隐式使用法
  4. 单行使用法

4. package与import

package

  1. package 是最基本的分发单位 和 工程管理中依赖关系的体现
  2. 每个 GO 语言源代码文件开头 都拥有一个 package 声明,表示源代码所属的代码包
  3. 要生成 GO 语言可执行程序,必须要有 mainpackage 包,且必须在该包下 有 main() 函数
  4. 同一个路径下(文件夹) 只能存在 一个 package, 一个 package 可以拆分为多个源文件组成

import 导入包的几种方式:点,别名与下划线

在写 Go 代码的时候经常用到 import 这个命令用来导入包文件,看到的方式参考如下:

1
2
3
import(
"fmt"
)

然后在代码里面可以通过如下的方式调用
fmt.Println("hello world")

上面这个fmt是Go语言的标准库,他其实是去 GOROOT 下去加载该模块,当然 Goimport 还支持如下两种方式来加载自己写的模块:

相对路径 import "./model" // 当前文件同一目录的 model 目录,但是不建议这种方式 import

绝对路径 import "shorturl/model" // 加载GOPATH/src/shorturl/model模块

三种导入包的使用方法。

  1. 点操作import( . "fmt" )
    可以省略前缀的包名,fmt.Println("hello world") 可以省略的写成 Println("hello world")

  2. 别名操作import( f "fmt" ) 调用包函数时前缀变成了重命名的前缀,即 f.Println("hello world")

  3. _ 操作import ( "database/sql" _ "github.com/ziutek/mymysql/godrv" )

_ 操作其实只是引入该包。即使用 _ 操作引用包是无法通过包名来调用包中的导出函数,而是只是为了简单的调用其 init 函数()


5. 指针

变量是一种使用方便的占位符,用于引用计算机内存地址。

Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
var a int = 10

fmt.Printf("变量的地址: %x\n", &a )
}

指针声明 : var var_name *var-type eg: var fp *float32

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package main

import "fmt"

func main() {
var a int= 20 /* 声明实际变量 */
var ip *int /* 声明指针变量 */

ip = &a /* 指针变量的存储地址 */

fmt.Printf("a 变量的地址是: %x\n", &a )

/* 指针变量的存储地址 */
fmt.Printf("ip 变量的存储地址: %x\n", ip )

/* 使用指针访问值 */
fmt.Printf("*ip 变量的值: %d\n", *ip )
}

空指针 :当一个指针被定义后没有分配到任何变量时,它的值为 nil

1
2
3
4
5
6
7
8
9
package main

import "fmt"

func main() {
var ptr *int
var ptr1 *
fmt.Printf("ptr 的值为 : %x\n", ptr ) /* 0 */
}