[Go语言]基础介绍

Posted jiangwei0512

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Go语言]基础介绍相关的知识,希望对你有一定的参考价值。

Go语言编程环境

Windows下直接下载安装包,这里在https://studygolang.com/dl下载到的go1.17.windows-amd64.msi。安装完成之后可以通过go version查看:

PS F:\\Gitee\\go\\Code\\Basic> go version
go version go1.17 windows/amd64

还可以查看go的基本使用方式:

PS F:\\Gitee\\go\\Code\\Basic> go
Go is a tool for managing Go source code.

Usage:

        go <command> [arguments]

The commands are:

        bug         start a bug report
        build       compile packages and dependencies
        clean       remove object files and cached files
        doc         show documentation for package or symbol
        env         print Go environment information
        fix         update packages to use new APIs
        fmt         gofmt (reformat) package sources
        generate    generate Go files by processing source
        get         add dependencies to current module and install them
        install     compile and install packages and dependencies
        list        list packages or modules
        mod         module maintenance
        run         compile and run Go program
        test        test packages
        tool        run specified go tool
        version     print Go version
        vet         report likely mistakes in packages

Use "go help <command>" for more information about a command.

Additional help topics:

        buildconstraint build constraints
        buildmode       build modes
        c               calling between Go and C
        cache           build and test caching
        environment     environment variables
        filetype        file types
        go.mod          the go.mod file
        gopath          GOPATH environment variable
        gopath-get      legacy GOPATH go get
        goproxy         module proxy protocol
        importpath      import path syntax
        modules         modules, module versions, and more
        module-get      module-aware go get
        module-auth     module authentication using go.sum
        packages        package lists and patterns
        private         configuration for downloading non-public code
        testflag        testing flags
        testfunc        testing functions
        vcs             controlling version control with GOVCS

Use "go help <topic>" for more information about that topic.

下面是一个Go语言代码示例:

package main

import "fmt"

func main() {
	fmt.Printf("Hello World\\n")
}

编译和运行:

PS F:\\Gitee\\go\\Code\\Basic> go build .\\helloworld.go
PS F:\\Gitee\\go\\Code\\Basic> dir


    目录: F:\\Gitee\\go\\Code\\Basic


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----         2021/8/18     16:06        1937408 helloworld.exe
-a----         2021/8/18     16:02             78 helloworld.go


PS F:\\Gitee\\go\\Code\\Basic> .\\helloworld.exe
Hello World

到这里,Go语言的基本编程环境已经基本可以使用。

VS Code和插件安装

使用VS Code来编辑和调试Go语言代码,其它都没有问题,但在安装插件之后会一直报错(VS Code需要打开go文件之后才会提示安装额外工具,此时点击安装之后会提示失败),原因是它依赖于一些额外的工具,而这些工具无法下载,需要使用代理。这里通过go env参数设置代码:

go env -w GOPROXY=https://goproxy.io,direct

之后重启VS Code,再次提示时选择全部安装就能够成功:

Tools environment: GOPATH=C:\\Users\\Administrator\\go
Installing 10 tools at C:\\Users\\Administrator\\go\\bin in module mode.
  gopkgs
  go-outline
  gotests
  gomodifytags
  impl
  goplay
  dlv
  dlv-dap
  staticcheck
  gopls

Installing github.com/uudashr/gopkgs/v2/cmd/gopkgs (C:\\Users\\Administrator\\go\\bin\\gopkgs.exe) SUCCEEDED
Installing github.com/ramya-rao-a/go-outline (C:\\Users\\Administrator\\go\\bin\\go-outline.exe) SUCCEEDED
Installing github.com/cweill/gotests/gotests (C:\\Users\\Administrator\\go\\bin\\gotests.exe) SUCCEEDED
Installing github.com/fatih/gomodifytags (C:\\Users\\Administrator\\go\\bin\\gomodifytags.exe) SUCCEEDED
Installing github.com/josharian/impl (C:\\Users\\Administrator\\go\\bin\\impl.exe) SUCCEEDED
Installing github.com/haya14busa/goplay/cmd/goplay (C:\\Users\\Administrator\\go\\bin\\goplay.exe) SUCCEEDED
Installing github.com/go-delve/delve/cmd/dlv (C:\\Users\\Administrator\\go\\bin\\dlv.exe) SUCCEEDED
Installing github.com/go-delve/delve/cmd/dlv@f95340ae1bf9 (C:\\Users\\Administrator\\go\\bin\\dlv-dap.exe) SUCCEEDED
Installing honnef.co/go/tools/cmd/staticcheck (C:\\Users\\Administrator\\go\\bin\\staticcheck.exe) SUCCEEDED
Installing golang.org/x/tools/gopls (C:\\Users\\Administrator\\go\\bin\\gopls.exe) SUCCEEDED

All tools successfully installed. You are ready to Go :).

下载到的文件位于C:\\Users\\Administrator\\go。之后在代码目录下增加launch.json文件:

{
  // 使用 IntelliSense 了解相关属性。
  // 悬停以查看现有属性的描述。
  // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch",
      "type": "go",
      "request": "launch",
      "mode": "auto",
      "program": "${file}",
      "env": {
        "GOPATH": "C:/Users/Administrator/go",
        "GOROOT": "D:/Program Files/Go"
      },
      "args": [],
    }
  ]
}

其中的GOPATHGOROOT需要根据实际情况配置,可以通过go env查看,之后就可以通过按F5来编译和使用了,下面是执行helloworld.go代码的结果:

Starting: C:\\Users\\Administrator\\go\\bin\\dlv-dap.exe dap --listen=127.0.0.1:54909
DAP server listening at: 127.0.0.1:54909
Hello World
Process 12460 has exited with status 0
Detaching
dlv dap (15060) exited with code: 0

fmt

学习代码过程中需要了解各种语法的演示结果,这个时候就首先需要掌握如何显示结果,这就需要知道打印方式。

Go语言主要使用fmt包来处理打印。

首先需要打入包:

import "fmt"

其次介绍几个常用的打印函数:

// Print formats using the default formats for its operands and writes to standard output.
// Spaces are added between operands when neither is a string.
// It returns the number of bytes written and any write error encountered.
func Print(a ...interface{}) (n int, err error)

// Println formats using the default formats for its operands and writes to standard output.
// Spaces are always added between operands and a newline is appended.
// It returns the number of bytes written and any write error encountered.
func Println(a ...interface{}) (n int, err error)

// Printf formats according to a format specifier and writes to standard output.
// It returns the number of bytes written and any write error encountered.
func Printf(format string, a ...interface{}) (n int, err error)

格式化输出Printf()会用到占位符,说明如下:

%v		值的默认格式
%+v		添加字段名(如结构体)
%#v		相应值的Go语法表示
%T		相应值的类型的Go语法表示
%%		字面上的百分号,并非值的占位符
%t		truefalse
%b		二进制表示
%c		相应Unicode码点所表示的字符
%d		十进制表示 
%o		八进制表示 
%q		单引号围绕的字符字面值,由Go语法安全地转义 
%x		十六进制表示,字母形式为小写a-f 
%X		十六进制表示,字母形式为大写A-F 
%U		Unicode格式:U+1234,等同于"U+%04X"
%b		无小数部分的,指数为二的幂的科学计数法,与 strconv.FormatFloat中的 'b' 转换格式一致。例如 -123456p-78 
%e		科学计数法,例如-1234.456e+78 
%E		科学计数法,例如-1234.456E+78 
%f		有小数点而无指数,例如 123.456 
%g		根据情况选择%e或%f以产生更紧凑的(无末尾的0)输出 
%G		根据情况选择%E或%f以产生更紧凑的(无末尾的0)输出
%s		字符串或切片的无解译字节 
%q		双引号围绕的字符串,由Go语法安全地转义 
%x		十六进制,小写字母,每字节两个字符 
%X		十六进制,大写字母,每字节两个字符
%p		十六进制表示,前缀0x

从上面的说明可以看出来使用%v最简单直接,它会对应到其它的类型,比如bool就对应到%tint就对应到%d。

Go语法基础

关键字

关键字说明
package定义包名
import导入包名
const常量声明
var变量声明
func函数定义
defer延迟执行
go并发语法糖
return函数返回
struct定义结构类型
interface定义接口类型
map声明或创建map类型
chan声明或创建通道类型
if elseif else语句关键字
for、range、break、continuefor循环使用的关键字,rangefor语句中迭代数组、切片、通道和集合
switch、select、type、case、default、fallthrouthswitchselect语句使用的关键字
gotogoto跳转语句关键字

变量

完整声明:

var varName dataType [ = value ]

跟其它语言将类型放在前面的用法有差异,这里类型放在了变量名之后,下面是一些例子:

var a int = 1
var a int = 2 * 3
var b int = a

短类型:

varName := value

:=声明只能出现在函数内(包括方法内)。

Go编译器会自动进行数据类型判断。

内置数据类型

可以在builtin\\builtin.go找到定义:

// bool is the set of boolean values, true and false.
type bool bool

// true and false are the two untyped boolean values.
const (
	true  = 0 == 0 // Untyped bool.
	false = 0 != 0 // Untyped bool.
)

// uint8 is the set of all unsigned 8-bit integers.
// Range: 0 through 255.
type uint8 uint8

// uint16 is the set of all unsigned 16-bit integers.
// Range: 0 through 65535.
type uint16 uint16

// uint32 is the set of all unsigned 32-bit integers.
// Range: 0 through 4294967295.
type uint32 uint32

// uint64 is the set of all unsigned 64-bit integers.
// Range: 0 through 18446744073709551615.
type uint64 uint64

// int8 is the set of all signed 8-bit integers.
// Range: -128 through 127.
type int8 int8

// int16 is the set of all signed 16-bit integers.
// Range: -32768 through 32767.
type int16 int16

// int32 is the set of all signed 32-bit integers.
// Range: -2147483648 through 2147483647.
type int32 int32

// int64 is the set of all signed 64-bit integers.
// Range: -9223372036854775808 through 9223372036854775807.
type int64 int64

// float32 is the set of all IEEE-754 32-bit floating-point numbers.
type float32 float32

// float64 is the set of all IEEE-754 64-bit floating-point numbers.
type float64 float64

// complex64 is the set of all complex numbers with float32 real and
// imaginary parts.
type complex64 complex64

// complex128 is the set of all complex numbers with float64 real and
// imaginary parts.
type complex128 complex128

// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

// int is a signed integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, int32.
type int int

// uint is an unsigned integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, uint32.
type uint uint

// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

// iota is a predeclared identifier representing the untyped integer ordinal
// number of the current const specification in a (usually parenthesized)
// const declaration. It is zero-indexed.
const iota = 0 // Untyped int.

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type // Type must be a pointer, channel, func, interface, map, or slice type

// Type is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type int

// Type1 is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type1 int

// IntegerType is here for the purposes of documentation only. It is a stand-in
// for any integer type: int, uint, int8 etc.
type IntegerType int

// FloatType is here for the purposes of documentation only. It is a stand-in
// for either float type: float32 or float64.
type FloatType float32

// ComplexType is here for the purposes of documentation only. It is a
// stand-in for either complex type: complex64 or complex128.
type ComplexType complex64

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface {
	Error() string
}

基本类型说明如下:

  • 整型
    • byte
    • int
    • int8
    • int16
    • int32
    • int64
    • uint
    • uint8
    • uint16
    • uint32
    • uint64
    • uintptr

byte其实就是uint8

  • 浮点型
    • float32
    • float64

浮点数字字面值被自动类型推断为float64,即var f := 10.0中,f的类型是float64

由于计算机精度的问题,浮点类型之间不应该用==!=来进行比较。

  • 复数
    • complex64
    • complex128

complex64由两个float32构成,complex128由两个float64构成。

复数的示例:

package main

import "fmt"

func main() {
	var c complex64 =  2.3 + 4.5i
	fmt.Printf("c = %v\\n", c)	// c = (2.3+4.5i)
	fmt.Printf("real: %v\\n", real(c))	// 2.3
	fmt.Printf("imag: %v\\n", imag(c))	// 4.5
}
  • 字符和字符串
    • string(字符串)
    • rune(字符)

rune其实就是int32

字符串不包含NULL字符,这跟c语言里面是不一样的。

字符串的底层实现是一个二元的数据结构,一个是指针指向字符串数组的起点,另一个是长度。

  • 错误类型
    • error
  • 布尔
    • booltruefalse,默认是false
package main

import "fmt"

func main() {
	var a bool
	fmt.Printf("a = %t\\n", a)	// a = false
}

常量值

说明
ture false表示bool类型的两个值,真和假。
iota常量计数器,用在常量表达式中。
nil指针/引用类型的变量的默认值是nil。

iota可以看作自增的枚举类型,下面是使用示例:

package main

import "fmt"

func main() {
	const x = iota	// 0
	const y = iota	// 还是0
  fmt.Printf("x = %d, y = %d\\n", x, y)
	const (
		a = iota	// 0
		b = iota	// 1
		c = 10    // 10, iota为2
		d = iota  // 所以这里是3
	)
	fmt.Printf("a = %d, b = %d, c = %d, d = %d\\n", a, b, c, d)
	const (
		x0 = iota	// 0
		x1				// 1
		x2				// 2
	)
	fmt.Printf("x0 = %d, x1 = %d, x2 = %d\\n", x0, x1, x2)
}

空白

就是下划线(_),可作为占位符,比如下面的例子:

package main

import "fmt"

func two_return_value() (int, int) {
	return 1, 2
}

func main() {
	// a := two_return_value() // 错误
	_, a := two_return_value()
	fmt.Printf("a = %d\\n", a)
}

注释部分不能使用,需要改成它下面那行的样子。

复合数据类型

指针

* pointerType

指针类型使用*后接其指向的类型名。

指针示例:

package main

import "fmt"

func main() {
	var a int = 1;
	var p *int = &a
	fmt.Printf("p = %v, *p = %v\\n", p, *p)	// p = 0xc0000140a8, *p = 1
}

指针也有解引用,即这里的*p

Go语言中没有->,访问结构体成员还是.

Go语言不支持指针操作,比如上例中没有p++这种操作。Go语言支持垃圾回收,而支持操作会给垃圾回收带来不便,所以在语言层面上禁止了。

Go编译器支持“栈逃逸”机制,可以将局部变量的空间分配到堆上,下面是一个示例:

package main

import "fmt"

func sum (a int, b int) *int {
	sum := a + b
	return &sum	// 不糊报错,sum会被分配到堆上
}

func main() {
	a := sum(1, 2)
	fmt.Printf("sum = %v\\n", *a)
}

sum()函数中将局部变量的指针返回了,在c语言中是会有问题的,因为sum的值在函数栈上,推出函数之后其值是不可知的,但是在Go语言中可以通过“栈逃逸”机制,使得sum的值被分配到堆中保留下来。

数组

[n] elementType

数组类型使用[n]后跟数组元素类型来表示,n表示数组元素的个数,n可以使...,表示其长度有初始化的成员个数来决定。

数组示例:

package main

import "fmt"

func main() {
	var arr1 [2]int;	// 有2个int类型元素的数组,元素值默认是0
	arr2 := [...]int{1, 2}	// [...]后面跟字面值量初始化数组,长度也是固定下来的,这里是2
	arr3 := [3]int{1, 2}	// 长度是3,但是定义了2个,则剩下的是0
	arr4 := [...]int{1: 1, 2: 2}	// 通过索引来初始化,索引从0开始,所以这里长度是3

	fmt.Printf("arr1: %v\\n", arr1)
	fmt.Printf("arr2: %v\\n", arr2)
	fmt.Printf("arr3: %v\\n", arr3)
	fmt.Printf("arr4: %v\\n", arr4)
}

数组是值类型,数组赋值或者作为参数传递给函数,都是值拷贝。下面是一个说明示例:

func double(arr [3]int, size int) {
	for i := 0; i < size; i++ {
		arr[i] = arr[i] * 2
	}
	fmt.Printf("arr in doube(): %v\\n", arr)
}

func main() {
	arr4 := [...]int{1: 1, 2: 2}	// 通过索引来初始化,索引从0开始,所以这里长度是3

	fmt.Printf("arr before double(): %v\\n", arr4)	// arr before double(): [0 1 2]
	double(arr4, len(arr4))							// arr in doube(): [0 2 4]
	fmt.Printf("arr after double(): %v\\n", arr4)	// arr after double(): [0 1 2]
}

可以看到函数外的值并没有被改变。

数组长度是数组类型的组成部分,[1]int[2]int是两个类型。上面的double()函数中只能传递[3]int类型的数据,否则会报错:

cannot use arr1 (type [2]int) as type [3]int in argument to double

遍历数组的两种方式:

for i,v := range arr4 {
    fmt.Printf("%d: %d\\n", i, v)
}

len := len(arr4)
for i := 0; i < len; i++ {
    fmt.Printf("%d: %d\\n", i, arr4[i])
}

切片

[] elementType

切片类型使用[]后跟切片元素类型来表示。

切片是一种变长数组。

切片是一种引用类型。

切片示例:

package main

import "fmt"

func main() {
	arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
	slice1 := arr[:]	// 完整切片
	slice2 := arr[2:]	// 从第三个元素(索引2)开始切
	slice3 := arr[2:5]	// 切第三个到第5个,注意索引5指定的那个值不包含
	slice4 := make([]int, 10)	//长度为10,容量为10,元素默认值0
	slice5 := make([]int, 10, 15)	//长度为10,容量为15,元素默认值0


	fmt.Printf("slice1 : %v\\n", slice1)	// slice1 : [0 1 2 3 4 5 6 7 8 9]
	fmt.Printf("slice2 : %v\\n", slice2)	// slice2 : [2 3 4 5 6 7 8 9]
	fmt.Printf("slice3 : %v\\n", slice3)	// slice3 : [2 3 4]
	fmt.Printf("slice4 : %v\\n", slice4)	// slice4 : [0 0 0 0 0 0 0 0 0 0]
	fmt.Printf("slice5 : %v, len: %v, cap: %v\\n", slice5, len(slice5), cap(slice5))	// slice5 : [0 0 0 0 0 0 0 0 0 0], len: 10, cap: 15
	// append(slice5, 1)	// 报错:append(slice5, 1) evaluated but not used
	slice5 = append(slice5, 1)	// append()返回增长之后的切片
	fmt.Printf("slice5 : %v\\n", slice5)	// slice5 : [0 0 0 0 0 0 0 0 0 0 1]
	copy(slice5, slice1)	// 

以上是关于[Go语言]基础介绍的主要内容,如果未能解决你的问题,请参考以下文章

go语言基础之init函数的介绍

Go语言基础之包

go基础 01

[Go] 通过 17 个简短代码片段,切底弄懂 channel 基础

Go语言基础之包

Go语言基础之单元测试