Go基础之程序结构数据类型
Posted AC_Jobim
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go基础之程序结构数据类型相关的知识,希望对你有一定的参考价值。
Go基础(一)之程序结构、数据类型
一、简介
1.1 Go语言的介绍
- Go语言是编程语言设计的又一次尝试,是对类C语言的重大改进,它不但能让你访问底层操作系统,还提供了强大的网络编程和并发编程支持。Go语言的用途众多,可以进行网络编程、系统编程、并发编程、分布式编程。
- Go语言的推出,旨在不损失应用程序性能的情况下降低代码的复杂性,具有“部署简单、并发性好、语言设计良好、执行性能好”等优势,目前国内诸多 IT 公司均已采用Go语言开发项目。
- Go语言有时候被描述为“C 类似语言”,或者是“21 世纪的C语言”。Go 从C语言继承了相似的表达式语法、控制流结构、基础数据类型、调用参数传值、指针等很多思想,还有C语言一直所看中的编译后机器码的运行效率以及和现有操作系统的无缝适配。
- 因为Go语言没有类和继承的概念,所以它和 Java 或 C++ 看起来并不相同。但是它通过接口(interface)的概念来实现多态性。Go语言有一个清晰易懂的轻量级类型系统,在类型之间也没有层级之说。因此可以说Go语言是一门混合型的语言。
- 此外,很多重要的开源项目都是使用Go语言开发的,其中包括 Docker、Go-Ethereum、Thrraform 和 Kubernetes。
更多请参考go的官方文档:https://studygolang.com/pkgdoc
1.2 环境配置
Go安装包下载:
解压之后的一些文件:
各文件的含义:
目录名 | 说明 |
---|---|
api | 每个版本的 api 变更差异 |
bin | go 源码包编译出的编译器(go)、文档工具(godoc)、格式化工具(gofmt) |
doc | 英文版的 Go 文档 |
lib | 引用的一些库文件 |
misc | 杂项用途的文件,例如 android 平台的编译、git 的提交钩子等 |
pkg | Windows 平台编译好的中间文件 |
src | 标准库的源码 |
test | 测试用例 |
环境变量配置
需要配置的环境变量:
-
新建系统变量
GOROOT
,这个是配合go编译器安装的目录 -
新建系统变量
GOPATH
,自己写的代码要放到这个变量中配置的目录中,go编译器才会找到并编译 -
修改path变量,把你的go安装的bin目录添加到里面去
环境变量设置好后,可以通过go env
命令来进行测试。
1.3 快速入门
开发一个hello.go程序,可以输出"hello,world”
hello.go代码:
对上图的说明:
-
go文件的后缀是.go
-
package main
:表示该hello.go文件所在的包是main,在 go 中,每个文件都必须归属于一个包。 -
import "fmt"
:表示引入一个包,包名fmt,引入该包后,就可以使用fmt包的函数,比如: fmt.Println() -
func main()
- func是一个关键字,表示一个函数。
- main是函数名,是一个主函数,即我们程序的入口。
-
fmt.Println(“hello”):表示调用fmt包的函数Println输出“hello,world”
代码的执行:
-
通过**
go build
**命令对该go文件进行编译,生成.exe文件 -
运行hello.exe文件即可
-
可以通过**
go run
**命令可以直接运行hello.go程序 -
编译时可以指定生成的可执行文件名
1.4 Go开发的注意事项
-
Go源文件以"go”为扩展名。
-
Go应用程序的执行入口是main()函数,main函数保存在名为main的包里。如果 main 函数不在main包里,构建工具就不会生成可执行的文件
-
Go语言严格区分大小写。
-
Go方法由一条条语句构成,每个语句后不需要分号(编译器会主动把特定符号后的换行符转换为分号)。比如:函数的左括号
必须和
func
函数声明在同一行上,且位于末尾,不能独占一行,而在表达式x + y
中,可在+
后换行,不能在+
前换行(译注:以+结尾的话不会被插入分号分隔符,但是以x结尾的话则会被分号分隔符,从而导致编译错误) -
go语言定义的变量或者import的包如果没有使用到,代码不能编译通过
二、程序结构
2.1 标识符
- Golang 对各种变量、方法、函数等命名时使用的字符序列称为标识符
表示符命名规则:
- 以字母或下画线开始,由多个字母、数字和下画线组合而成。
- 区分大小写。
- 使用驼峰( camel case)拼写格式。
- 局部变量优先使用短名。
- 不要使用保留关键字。
- 不建议使用与预定义常量、类型、内置函数相同的名字。
- 专有名词通常会全部大写,例如escapehtml。
注意:符号名字首字母大小写决定了其作用域。首字母大写的为导出成员,可被包外引用,而小写则仅能在包内使用。
空标识符:
和Python类似,Go也有个名为“_
”的特殊成员 ( blank identifier )。通常作为忽略占位符使用,可作表达式左值,无法读取内容。
import "strconv"
func main(
x, _ := strconv.Atoi("12") //忽略Atoi的err返回值
println(x)
空标识符可用来临时规避编译器对未使用变量和导入包的错误检查。
系统保留关键字:
-
关键字即是被Go语言赋予了特殊含义的单词,也可以称为保留字。Go语言中的关键字一共有 25 个
系统的预定义标识符:
-
在Go语言中还存在着一些特殊的标识符,叫做预定义标识符
2.2 变量
变量的定义:
-
var
声明语句可以创建一个特定类型的变量,然后给变量附加一个名字,并且设置变量的初始值。变量声明的一般语法如下:var 变量名字 类型 = 表达式
-
其中**
类型
**或 **= 表达式
**两个部分可以省略其中的一个。- 如果省略的是类型信息,那么将根据初始化表达式来推导变量的类型信息。
- 如果初始化表达式被省略,那么将用零值初始化该变量。
var x int // 自动初始化为0 var y = false // 自动推断为bool类型
-
数值类型变量对应的零值是0
-
布尔类型变量对应的零值是false
-
字符串类型对应的零值是空字符串
-
接口或引用类型(包括slice、指针、map、chan和函数)变量对应的零值是nil。
-
数组或结构体等聚合类型对应的零值是每个元素或字段都是对应该类型的零值
-
可一次定义多个变量,也可以初始化定义不同类型
var i, j, k int // int, int, int var b, f, s = true, 2.3, "four" // bool, float64, string
-
可以以组方式整理多行变量定义
var ( x, y int a, s = 100, "abc" )
简短模式:
-
使用更加简短的变量定义和初始化语法:
func main() x := 100 a, s := 1, "abc"
简短模式的使用限制:
- 定义变量,同时显示初始化
- 不能提供数据类型,根据初始化进行类型推断
- 只能用在函数内部
简短模式使用注意:
-
:=
是一个变量声明语句,而=
是一个变量赋值操作i, j = j, i // 交换 i 和 j 的值
-
简短模式并不总是重新定义变量,也可能是部分退化的赋值操作。退化的前提条件:最少有一个新变量被定义,且必须是同一作用域
func main() x := 100 x, y := 200, "abc" // 注意:x退化为赋值操作,仅有y是变量定义 println(x) //200 println(y) //abc
变量的使用细节:
-
未使用错误:编译器将未使用局部变量当作错误。不要觉得麻烦,这有助于培养良好的编码习惯
var x int func main() y := 10 //报错:y declared and not used
-
查看变量的字节大小和数据类型
//查看某个变量的字节大小和数据类型 var n2 int64 = 10 fmt.Printf("n2的类型 %T n2占用的字节数是 %d ",n2,unsafe.Sizeof(n2)) //n2的类型 int64 n2占用的字节数是 8
2.3 常量
-
常量值必须是
编译期可确定
的字符、字符串、数字或布尔值。可指定常量类型,或由译器通过初始化值推断。 -
常量使用const修饰
-
可在函数代码块中定义常量,不曾使用的常量不会引发编译错误
const x,y int =123, 0x22 const s = "hello, world! " const c = '我' //以组的方式定义多行变量 const( i, f= 1, 0.123 //int, float64(默认) b = false ) //可在函数代码块中定义常量,不曾使用的常量不会引发编译错误 func main() const x = 123 println(x) const y = 1.23 //未使用,不会引发编译错误
-
在常量组中如果不指定类型和初始化值,则与上一行非空常量初始化表达式值相同
func main() const ( x uint16 = 123 y // 与上一行x类型、右值相同 s = "abc" z // 与s类型、右值相同 ) fmt.Printf("%T, %v\\n", y, y) //uint16, 123 fmt.Printf("%T, %v\\n", s, s) //string, abc
iota 常量生成器:
-
在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一。
type Weekday int const ( Sunday Weekday = iota Monday Tuesday Wednesday Thursday Friday Saturday )
-
可在多常量定义中使用多个iota,它们各自单独计数,只须确保组中每行常量的列数量相同即可。
const( _, _ = iota, iota * 10 //0, 0 * 10 a, b //1, 1 * 10 c, d //2, 2 * 10 )
2.4 运算符
算数运算符:
注意:
-
Go语言明确不支持三元运算符
-
Golang 的自增自减只能当做一个独立语言使用
-
Golang 的++和 --只能写在变量的后面,不能写在变量的前面,即:只有 a++ a–没有++a --a
关系运算符:
逻辑运算符:
下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 | (A && B) 为 False |
|| | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 | (A || B) 为 True |
! | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 | !(A && B) 为 True |
赋值运算符:
下表列出了所有Go语言的赋值运算符。
运算符 | 描述 | 实例 |
---|---|---|
= | 简单的赋值运算符,将一个表达式的值赋给一个左值 | C = A + B 将 A + B 表达式结果赋值给 C |
+= | 相加后再赋值 | C += A 等于 C = C + A |
-= | 相减后再赋值 | C -= A 等于 C = C - A |
*= | 相乘后再赋值 | C *= A 等于 C = C * A |
/= | 相除后再赋值 | C /= A 等于 C = C / A |
%= | 求余后再赋值 | C %= A 等于 C = C % A |
<<= | 左移后赋值 | C <<= 2 等于 C = C << 2 |
>>= | 右移后赋值 | C >>= 2 等于 C = C >> 2 |
&= | 按位与后赋值 | C &= 2 等于 C = C & 2 |
^= | 按位异或后赋值 | C ^= 2 等于 C = C ^ 2 |
|= | 按位或后赋值 | C |= 2 等于 C = C | 2 |
位运算符:
位运算符对整数在内存中的二进制位进行操作。假定 A 为60,B 为13
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与运算符”&”是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 | (A & B) 结果为 12, 二进制为 0000 1100 |
| | 按位或运算符”|”是双目运算符。 其功能是参与运算的两数各对应的二进位相或 | (A | B) 结果为 61, 二进制为 0011 1101 |
^ | 按位异或运算符”^”是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 | (A ^ B) 结果为 49, 二进制为 0011 0001 |
<< | 左移运算符”<<”是双目运算符。左移n位就是乘以2的n次方。 其功能把”<<”左边的运算数的各二进位全部左移若干位,由”<<”右边的数指定移动的位数,高位丢弃,低位补0。 | A << 2 结果为 240 ,二进制为 1111 0000 |
>> | 右移运算符”>>”是双目运算符。右移n位就是除以2的n次方。 其功能是把”>>”左边的运算数的各二进位全部右移若干位,”>>”右边的数指定移动的位数。 | A >> 2 结果为 15 ,二进制为 0000 1111 |
其他运算符:
运算符 | 描述 | 实例 |
---|---|---|
& | 返回变量存储地址 | &a; 将给出变量的实际地址。 |
* | 指针变量。 | *a; 是一个指针变量 |
func main()
a := 100
fmt.Println("a 的地址=", &a)
var ptr *int = &a
fmt.Println("ptr 指向的值是=", *ptr) //ptr 指向的值是= 100
运算符优先级:
2.5 流程控制
if-else 流程控制:
func main()
var age int
fmt.Println("请输入年龄:")
fmt.Scanln(&age)
if age > 18
fmt.Println("你年龄大于18~....")
else
fmt.Println("你的年龄不大这次放过你了")
switch分支结构:
基本语法:
switch 表达式
case 表达式1,表达式2,…… :
// 语句块1
case 表达式3,表达式4,…… :
// 语句块2
// 多个case,结构同上
default :
// 语句块3
注意:
- 匹配项后面也不需要再加 break
- 多个表达式使用逗号间隔
- switch 后也可以不带表达式,类似if --else分支来使用
- 如果在case 语句块后增加fallthrough ,则会继续执行下一个case,也叫 switch穿透
func main()
var n1 int32 = 5
var n2 int32 = 20
switch n1
case n2, 10, 5 : // case 后面可以有多个表达式
fmt.Println("ok1~")
case 90 :
fmt.Println("ok2~")
default :
fmt.Println("ok2~")
//switch 后也可以不带表达式,类似 if --else分支来使用。【案例演示】
var score int = 90
switch
case score > 90 :
fmt.Println("成绩优秀..")
case score >=70 && score <= 90 :
fmt.Println("成绩优良...")
case score >= 60 && score < 70 :
fmt.Println("成绩及格...")
default :
fmt.Println("不及格")
//switch 的穿透 fallthrought
var num int = 10
switch num
case 10:
fmt.Println("ok1")
fallthrough //默认只能穿透一层
case 20:
fmt.Println("ok2")
fallthrough
case 30:
fmt.Println("ok3")
default:
fmt.Println("没有匹配到..")
-
Type Switch: switch 语句还可以被用于type-switch 来判断某个interface变量中实际指向的变量类型
func main() var x interface var y = 10.0 x = y switch i := x.(type) case nil: fmt.Printf("× 的类型~:%T",i) case int: fmt.Printf("x 是 int 型") case float64: fmt.Printf("x 是 f1oat64 型") case func(int) float64: fmt.Printf("x 是 func(int) 型") case bool, string: fmt.Printf("x 是 bool 或 string 型") default: fmt.Printf("未知型")
for循环:
基本语法:
for 循环变量初始化 ;循环条件 ;循环变量迭代
//循环操作
注意:
- Go中没有while,do…while循环,但可以通过for循环和break实现其功能
- break 出现在多层嵌套循环中可以使用标签(label)表明要终止哪个循环
func main()
//for循环的第一种写法
//指定标签的形式来使用 break
for i := 0; i < 4; i++
lable1: // 设置一个标签
for j := 0; j < 10; j++
if j == 2
break lable1
fmt.Println("j=", j)
//for循环的第二种写法
j := 1 //循环变量初始化
for j <= 10 //循环条件
fmt.Println("hello,world~", j)
j++ //循环变量迭代
//for循环的第三种写法, 这种写法通常会配合break使用
k := 1
for // 这里也等价 for ; ;
if k <= 10
fmt.Println("ok~~", k)
else
break //break就是跳出这个for循环
k++
for-range遍历:这是一种同时获取索引
和值或键值
的遍历方式
func main()
fmt.Println()
//字符串遍历方式2-for-range
str := "abc~ok上海"
for index, val := range str
fmt.Printf("index=%d, val=%c \\n", index, val)
跳转控制语句-goto:
- goto语句可以无条件地转移到程序中指定的行。
func main()
var n int = 30
// 演示goto的使用
fmt.Println("ok1")
if n > 20
goto label1
fmt.Println("ok2")
label1:
fmt.Println("ok3")
2.7 init函数
-
每一个源文件都可以包含一个
init函数
,该函数会在main函数执行前,被Go运行框架调用,也就是说init会在main函数前被调用
。 -
如果一个文件同时包含全局变量定义, init 函数和 main函数,则执行的流程:全局变量定义->init函数->main函数
var age int = test() //初始化全局变量的时候执行该函数 func test() int fmt.Println("test()...")//1 return 18 //init函数,通常可以在init函数中完成初始化工作 func init() fmt.Println("init()...")//2 func main() fmt.Println("main()...")//3
执行结果:
-
如果 main.go 文件中导入了utils.go而且都含有变量定义,init 函数时,执行的流程又是怎么样的呢?
三、数据类型
数据类型介绍:
值类型与引用类型:
-
值类型:基本数据类型int系列、float系列、bool、string、数组和结构体struct。变量直接存储值,内存通常在栈中分配
-
引用类型:指针、slice切片、map、管道chan、interface 等都是引用类型。变量存储的是一个地址,这个地址对应的空间才真正存储数据(值),内存通常在堆上分配。当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收
3.1 基本数据类型
3.1.1 整型
整数的各个类型:
类型 | 有无符号 | 占用存储空间 | 范围 |
---|---|---|---|
int8 | 有符号 | 1字节 | -128 ~ 127 |
int16 | 有符号 | 2字节 | -215 ~ 215-1 |
int32 | 有符号 | 4字节 | -231 ~ 231-1 |
int64 | 有符号 | 8字节 | -263 ~ 263-1 |
uint8 | 无符号 | 1字节 | 0 ~ 255 |
uint16 | 无符号 | 2字节 | 0 ~ 216-1 |
uint32 | 无符号 | 4字节 | 0 ~ 232-1 |
uint64 | 无符号 | 8字节 | 0 ~ 264-1 |
int | 有符号 | 32位系统4个字节 64位系统8个字节 | -231 ~ 231-1 -263 ~ 263-1 |
uint | 无符号 | 32位系统4个字节 64位系统8个字节 | 0 ~ 232-1 0 ~ 264-1 |
rune | 有符号 | 等价int32 表示一个Unicode码 | -231 ~ 231-1 |
byte | 无符号 | 当要存储字符时选用byte | 2 ~ 255 |
整型的使用注意:
-
int 和 unit 的大小和系统有关
-
Golang 中没有专门的字符类型,如果要存储单个字符(字母),一般使用byte来保存
-
Golang的整型默认声明为int型
-
unsafe.Sizeof()函数可以查看内存大小
func main() //查看变量的数据类型 var n1 = 100 fmt.Printf("n1的类型 %T \\n",n1) //n1的类型 int var a int32 = 10 fmt.Printf("a占用内存大小:%d", unsafe.Sizeof(a)) //a占用内存大小:4
-
就算在64位平台上 int 和 int64结构完全一致,也分属不同类型,须显式转换。
func main() var x int = 100 var y int64 = x //错误:cannot use x (type int) as type int64 in assignment
3.1.2 浮点型
浮点类型分类:
类型 | 有无符号 | 占用存储空间 | 范围 |
---|---|---|---|
float32 | 有符号 | 4字节 | -3.403E38 ~ 3.403E38 |
float64 | 有符号 | 8字节 | -1.798E308 ~ -1.798E308 |
浮点型的使用注意:
- Golang 的浮点型默认声明为
float64
类型。 - 浮点型常量有两种表示形式
- 十进制数形式:如:5.12 .512 (必须有小数点)
- 科学计数法形式:如:5.1234e2 = 5.1234 * 10的2次方
func main()
//Golang 的浮点型默认声明为float64 类型
var num5 = 1.1
fmt.Printf("num5的数据类型是 %T \\n", num5)
//十进制数形式:如:5.12 .512 (必须有小数点)
num6 := 5.12
num7 := .123 //=> 0.123
fmt.Println("num6=", num6, "num7=", num7)
//科学计数法形式
num8 := 5.1234e2 // ? 5.1234 * 10的2次方
num9 := 5.1234E2 // ? 5.1234 * 10的2次方 shift+alt+向下的箭头
num10 := 5.1234E-2 // ? 5.1234 / 10的2次方 0.051234
fmt.Println("num8=", num8, "num9=", num9,以上是关于Go基础之程序结构数据类型的主要内容,如果未能解决你的问题,请参考以下文章