GO语言基础(结构+语法+类型+变量)
阅读原文时间:2022年03月17日阅读:1

GO语言基础(结构+语法+类型+变量)

Go 语言的基础组成有以下几个部分:

  • 包声明
  • 引入包
  • 函数
  • 变量
  • 语句 & 表达式
  • 注释

接下来让我们来看下简单的代码,该代码输出了"Hello World!":

实例

package main

import "fmt"

func main() {
/* 这是我的第一个简单的程序 */
fmt.Println("Hello, World!")
}

让我们来看下以上程序的各个部分:

使用 fmt.Print("hello, world\n") 可以得到相同的结果。

Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。

执行 Go 程序

让我们来看下如何编写 Go 代码并执行它。步骤如下:

$ go run hello.go Hello, World!

$ go build hello.go $ ls hello    hello.go $ ./hello Hello, World!

注意

需要注意的是 { 不能单独放在一行,所以以下代码在运行时会产生错误:

实例

package main

import "fmt"

func main()
{ // 错误,{ 不能在单独的行上
fmt.Println("Hello, World!")
}

上一章节我们已经了解了 Go 语言的基本组成结构,本章节我们将学习 Go 语言的基础语法。


Go 标记

Go 程序可以由多个标记组成,可以是关键字,标识符,常量,字符串,符号。如以下 GO 语句由 6 个标记组成:

fmt.Println("Hello, World!")

6 个标记是(每行一个):

1. fmt 2. . 3. Println 4. ( 5. "Hello, World!" 6. )


行分隔符

在 Go 程序中,一行代表一个语句结束。每个语句不需要像 C 家族中的其它语言一样以分号 ; 结尾,因为这些工作都将由 Go 编译器自动完成。

如果你打算将多个语句写在同一行,它们则必须使用 ; 人为区分,但在实际开发中我们并不鼓励这种做法。

以下为两个语句:

fmt.Println("Hello, World!")
fmt.Println("yp:runoob.com")


注释

注释不会被编译,每一个包应该有相关注释。

单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾。如:

// 单行注释 /* Author by yp 我是多行注释 */


标识符

标识符用来命名变量、类型等程序实体。一个标识符实际上就是一个或是多个字母(A~Z和a~z)数字(0~9)、下划线_组成的序列,但是第一个字符必须是字母或下划线而不能是数字。

以下是有效的标识符:

mahesh kumar abc move_name a_123 myname50 _temp j a23b9 retVal

以下是无效的标识符:

  • 1ab(以数字开头)
  • case(Go 语言的关键字)
  • a+b(运算符是不允许的)

字符串连接

Go 语言的字符串可以通过 + 实现:

实例

package main
import "fmt"
func main() {
fmt.Println("Google" + "Runoob")
}

以上实例输出结果为:

GoogleRunoob


关键字

下面列举了 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

print

println

real

recover

string

true

uint

uint8

uintptr

程序一般由关键字、常量、变量、运算符、类型和函数组成。

程序中可能会使用到这些分隔符:括号 (),中括号 [] 和大括号 {}。

程序中可能会使用到这些标点符号:.、,、;、: 和 …。


Go 语言的空格

Go 语言中变量的声明必须使用空格隔开,如:

var age int;

语句中适当使用空格能让程序更易阅读。

无空格:

fruit=apples+oranges;

在变量与运算符间加入空格,程序看起来更加美观,如:

fruit = apples + oranges;


格式化字符串

Go 语言中使用 fmt.Sprintf 格式化字符串并赋值给新串:

实例

package main

import (
"fmt"
)

func main() {
// %d 表示整型数字,%s 表示字符串
var stockcode=123
var enddate="2020-12-31"
var url="Code=%d&endDate=%s"
var target_url=fmt.Sprintf(url,stockcode,enddate)
fmt.Println(target_url)
}

输出结果为:

Code=123&endDate=2020-12-31

在 Go 编程语言中,数据类型用于声明函数和变量。

数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。

Go 语言按类别有以下几种数据类型:

序号

类型和描述

1

布尔型

布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。

2

数字类型

整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。

3

字符串类型:

字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。

4

派生类型:

包括:

(a) 指针类型(Pointer)

(b) 数组类型

(c) 结构化类型(struct)

(d) Channel 类型

(e) 函数类型

(f) 切片类型

(g) 接口类型(interface)

(h) Map 类型


数字类型

Go 也有基于架构的类型,例如:int、uint 和 uintptr。

序号

类型和描述

1

uint8

无符号 8 位整型 (0 到 255)

2

uint16

无符号 16 位整型 (0 到 65535)

3

uint32

无符号 32 位整型 (0 到 4294967295)

4

uint64

无符号 64 位整型 (0 到 18446744073709551615)

5

int8

有符号 8 位整型 (-128 到 127)

6

int16

有符号 16 位整型 (-32768 到 32767)

7

int32

有符号 32 位整型 (-2147483648 到 2147483647)

8

int64

有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

浮点型

序号

类型和描述

1

float32

IEEE-754 32位浮点型数

2

float64

IEEE-754 64位浮点型数

3

complex64

32 位实数和虚数

4

complex128

64 位实数和虚数


其他数字类型

以下列出了其他更多的数字类型:

序号

类型和描述

1

byte

类似 uint8

2

rune

类似 int32

3

uint

32 或 64 位

4

int

与 uint 一样大小

5

uintptr

无符号整型,用于存放一个指针

变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。

变量可以通过变量名访问。

Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。

声明变量的一般形式是使用 var 关键字:

var identifier type

可以一次声明多个变量:

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)  

}

以上实例输出结果为:

Runoob 1 2

变量声明

第一种,指定变量类型,如果没有初始化,则变量默认为零值

var v_name v_type v_name = value

零值就是变量没有做初始化时系统默认设置的值。

实例

package main
import "fmt"
func main() {

// 声明一个变量并初始化  
var a = "RUNOOB"  
fmt.Println(a)

// 没有初始化就为零值  
var b int  
fmt.Println(b)

// bool 零值为 false  
var c bool  
fmt.Println(c)  

}

以上实例执行结果为:

RUNOOB 0 false

  • 数值类型(包括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 是接口

实例

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)
}

输出结果是:

0 0 false ""

第二种,根据值自行判定变量类型

var v_name = value

实例

package main
import "fmt"
func main() {
var d = true
fmt.Println(d)
}

输出结果是:

true

第三种,如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误,格式:

v_name := value

例如:

var intVal int intVal :=1 // 这时候会产生编译错误,因为 intVal 已经声明,不需要重新声明

直接使用下面的语句即可:

intVal := 1 // 此时不会产生编译错误,因为有声明新的变量,因为 := 是一个声明语句

intVal := 1 相等于:

var intVal int intVal =1

可以将 var f string = "Runoob" 简写为 f := "Runoob":

实例

package main
import "fmt"
func main() {
f := "Runoob" // var f string = "Runoob"

fmt.Println(f)  

}

输出结果是:

Runoob

多变量声明

//类型相同多个变量, 非全局变量 var vname1, vname2, vname3 type vname1, vname2, vname3 = v1, v2, v3 var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断 vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误 // 这种因式分解关键字的写法一般用于声明全局变量 var ( vname1 v_type1 vname2 v_type2 )

实例

package main

var x, y int
var ( // 这种因式分解关键字的写法一般用于声明全局变量
a int
b bool
)

var c, d int = 1, 2
var e, f = 123, "hello"

//这种不带声明格式的只能在函数体中出现

//g, h := 123, "hello"

func main(){
g, h := 123, "hello"
println(x, y, a, b, c, d, e, f, g, h)
}

以上实例执行结果为:

0 0 0 false 1 2 123 hello 123 hello


值类型和引用类型

所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:

当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:

你可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。

值类型变量的值存储在堆中。

内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局,并且位置分配也可能不同。

更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。

一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。

这个内存地址称之为指针,这个指针实际上也被存在另外的某一个值中。

同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。

当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。

如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。


简短形式,使用 := 赋值操作符

我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。

a 和 b 的类型(int 和 bool)将由编译器自动推断。

这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。

注意事项

如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,例如:a := 20 就是不被允许的,编译器会提示错误 no new variables on left side of :=,但是 a = 20 是可以的,因为这是给相同的变量赋予一个新的值。

如果你在定义变量 a 之前使用它,则会得到编译错误 undefined: a。

如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,例如下面这个例子当中的变量 a:

实例

package main

import "fmt"

func main() {
var a string = "abc"
fmt.Println("hello, world")
}

尝试编译这段代码将得到错误 a declared but not used。

此外,单纯地给 a 赋值也是不够的,这个值必须被使用,所以使用

fmt.Println("hello, world", a)

会移除错误。

但是全局变量是允许声明但不使用的。 同一类型的多个变量可以声明在同一行,如:

var a, b, c int

多变量可以在同一行进行赋值,如:

var a, b int var c string a, b, c = 5, 7, "abc"

上面这行假设了变量 a,b 和 c 都已经被声明,否则的话应该这样使用:

a, b, c := 5, 7, "abc"

右边的这些值以相同的顺序赋值给左边的变量,所以 a 的值是 5, b 的值是 7,c 的值是 "abc"。

这被称为 并行 或 同时 赋值。

如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a,两个变量的类型必须是相同。

空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。

_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。

并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1 函数同时得到:val, err = Func1(var1)。