go语言基础语法
运行第一个Go程序
go
package main
import "fmt"
func main(){
fmt.Println("Hello, World!")
}
运行go程序:
bash
# 方法1:
go run hello.go

这种方式运行go程序,不会生成二进制文件,而是直接运行!
bash
# 方法2
go build hello.go
./hello

这种方式是先通过go build命令将hello.go编译成一个可执行二进制文件,然后再通过运行这个二进制文件来运行我们的go程序。
go语言目录结构
Go 社区推荐并遵循一个被称为 "Standard Go Project Layout" 的结构。虽然这不是 Go 官方工具链强制要求的,但绝大多数 Go 项目和工具(如包管理、构建脚本)都默认为这种结构。
核心目录结构:
| 目录名 | 作用&描述 | 强制性 |
|---|---|---|
| cmd/ | 主应用 (Main Applications):存放所有可执行应用程序的源代码。每个子目录通常对应一个独立的可执行文件(即一个 package main)。 | 约定 |
| pkg/ | 公共库代码 (Library Code):存放可以被外部应用导入使用的库代码。它不应该包含任何 package main 的代码。 | 约定 |
| internal/ | 内部库代码 (Private Library Code):存放只供当前项目内部使用,禁止被外部项目导入的代码。Go 工具链会强制阻止外部导入这个目录下的代码。 | 强制执行 (Go 1.4+) |
| api/ | **API 规范:**存放 API 规范文件,如 OpenAPI/Swagger 规范、Protocol Buffers 定义文件等。 | 约定 |
建议工程项目按照这样的文件目录结构来进行维护,否则你的项目将难以被其他 Go 开发者理解和维护,也可能与标准的 Go 工具链、IDE 或第三方工具(如 Linters)集成不佳。因此,强烈建议遵循。
基础语法
行分隔符
在go语言程序中,每一句程序,无须手动添加;来表示结尾,编译器会手动为你添加,当让你非要手动添加,编译器也不会报错;
但是如果你在一行中写了多条语句,那么这时候必须强制为每一条语句加上;表示结尾,不要编译会报错;
go
# 编译不会报错
fmt.Println("hello");
# 编译不会报错
fmt.Println("hello")
# 编译不会报错
fmt.Println("hello"); fmt.Println("hello"); fmt.Println("hello");
# 编译会报错
fmt.Println("hello") fmt.Println("hello") fmt.Println("hello")
注释
go语言注释和C/C++注释风格一致;
go
// 这表示一个单行注释
/*
这是一个注释块
*/

标识符
go语言的标识符命名规则与其他语言一致,允许使用字母、数字、下划线;
- 但是第一个字符必须是字母!
- 如果想要一个全局变量、函数、类被外部包访问,则首字母必需大写!首字母小写表示不可被外部包导入!
字符串拼接
go语言支持使用+来拼接两个字符串:
go
package main
import "fmt"
func main() {
// 输出结果helloworld
fmt.Println("hello" + "world")
}
go
// 输出结果:
helloworld
关键字

go语言数据类型
在 Go 编程语言中,数据类型用于声明函数和变量。
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
Go 语言按类别有以下几种数据类型:
| 类型 | 描述 |
|---|---|
| 布尔型 | 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
| 数字类型 | 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
| 字符串类型: | 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
| 派生类型: | 包括:指针类型、 数组类型 、 结构化类型(struct) 、 Channel 类型 、函数类型 、切片类型 、接口类型(interface)、Map 类型 |
数字类型:
| 类型 | 描述 |
|---|---|
| uint8 | 无符号 8 位整型 (0 到 255) |
| int8 | 有符号 8 位整型 (-128 到 127) |
| uint16 | 无符号 16 位整型 (0 到 65535) |
| int16 | 有符号 16 位整型 (-32768 到 32767) |
| uint32 | 无符号 32 位整型 (0 到 4294967295) |
| int32 | 有符号 32 位整型 (-2147483648 到 2147483647) |
| uint64 | 无符号 64 位整型 (0 到 18446744073709551615) |
| int64 | 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
| int | 与平台架构相关,32位平台:4字节;64位平台:8字节 |
| uint | 与平台架构相关,32位平台:4字节;64位平台:8字节 |
| uintptr | 与平台架构相关,32位平台:4字节;64位平台:8字节;用来存储任何指针(地址)的值,但它本身是一个整数,不携带任何指针的语义。 |
| byte | 1字节,用于存储ASCII表中字符的字符类型 ,底层是uint8,byte是uint8的一个别名 |
| rune | 4字节,用于存储多字节字符的类型,比如:中文字符、韩文字符、日文字符等 ,底层是int32,rune是int32的一个别名 |
浮点类型:
| 类型 | 描述 |
|---|---|
| float32 | IEEE-754 32位浮点型数 |
| float64 | IEEE-754 64位浮点型数 |
| complex64 | 32 位实数和虚数 |
| complex128 | 64 位实数和虚数 |
其中:布尔类型、数字类型、浮点类型、字符串类型是go语言的基本数据类型
go语言变量
go语言中变量需要使用var关键字来定义
在go语言中定义变量的方式有以下几种:
1. 对于局部变量来说:
go
// 完整定义+初始化
var name string = "hello"
// 短声明(编译器会自动根据`=`的复制内容类确定变量类型),
// 相当于编译器自动完成了声明+赋值
sex := "man"
// 不完整初始化,编译器会根据赋值内容,自动推导变量的类型
var age = 28
// 仅声明,未赋值,默认值为声明类型的默认值,string类型默认值就是空串;
var word string
对于局部变量来说,定义出来就必须要使用,不然会报错!
2. 对于全局变量来说:
go
// 允许
var global string = "sssssss"
// 允许
var global3 = "aaaaaa"
// 允许
var global2 string
// 对于全局变量的声明不允许使用短声明!!!
global4 :="dddddd"
对于全局变量来说,定义出来可以不使用,编译器不会报错!
- 一次性声明多个变量
go
// 显示初始化
var a, b, c, d int = 1, 2, 3, 4
// 根据赋值内容自动推导变量类型
var (
e = 3
f = 5
h = 7
)
// 显示初始化
var (
sh int = 29
dcdh bool = true
cnjd string = "hello"
)
go语言常量
在go语言中,常量使用const来进行修饰
常量的定义有以下几种方式:
go
// 显示指定类型初始化
const a int = 10
const b string = "hello"
const c bool = false
// 通赋值内容自动推导
const (
d = 3.45
e = '你'
f = 's'
)
对于常量来说不必像变量那样定义了就必须使用,在go语言中无论是全局常量还是局部常量定义了都可以不必使用。
iota:iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
- 在一个新的const初始化块中,iota会被初始化为0,之后const初始化块中,每声明一个常量,iota就会自动+1,由编译器自动进行维护;
go
const (
a = iota // 0
b = iota // 1
c = iota // 2
d = iota // 3
)
//在新的const块中iota会被重新初始化为0
const (
a1 = iota // 0
b1 = iota // 1
c1 = iota // 2
d1 = iota // 3
)
- 在const块中,如果常量没有进行显示的赋值,那么它会沿用前一个有赋值表达式的值
go
package main
import "fmt"
func main() {
const (
a = iota //0
b //1 沿用a = iota的赋值,也就是: b= iota
c //2 沿用a = iota的赋值,也就是: c= iota
d = "ha" //独立值
e //"ha" ,沿用d= "ha"的赋值,也就是: e="ha"
f = 100 //独立值
g //100 沿用f=100的赋值,也就是: g=100
h = iota //7,恢复计数
i //8 沿用h=iota,也就是: i= iota
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
go语言运算符
go语言的运算符与其他语言的运算符大差不差:
算术运算符:
| 运算符 | 描述 |
|---|---|
| + | 相加 |
| - | 相减 |
| * | 相乘 |
| / | 相除 |
| % | 取模 |
| ++ | 后置++ |
| -- | 后置- - |
关系运算符:
| 运算符 | 描述 |
|---|---|
| == | 检查两个值是否相等,如果相等返回 True 否则返回 False。 |
| != | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 |
| > | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 |
| < | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 |
| >= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 |
| <= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 |
逻辑运算符:
| 运算符 | 描述 |
|---|---|
| && | 如果两边的操作数都是 True,则条件 True,否则为 False。 |
| || | 如果两边的操作数有一个 True,则条件 True,否则为 False。 |
| ! | 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 |
位运算符:
| 运算符 | 描述 |
|---|---|
| & | 按位与;1 &1 =1 ,1 & 0=0, 0&1=0,0&0=0 |
| | | 按位或;1|1=1,1|0=1,0|1=1,0|0=0 |
| ^ | 按位异或;1^1=0,1^0=1,0^1=1,0^0=0 |
| << | 左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。 |
| >> | 右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。 |
赋值运算符:
| 运算符 | 描述 |
|---|---|
| = | 简单的赋值运算符,将一个表达式的值赋给一个左值 |
| += | 相加后再赋值 |
| -= | 相减后再赋值 |
| *= | 相乘后再赋值 |
| /= | 相除后再赋值 |
| %= | 求余后再赋值 |
| <<= | 左移后赋值 |
| >>= | 右移后赋值 |
| |= | 按位或后赋值 |
| &= | 按位与后赋值 |
| ^= | 按位异或后赋值 |
指针运算符:
| 运算符 | 描述 |
|---|---|
| & | 返回变量存储地址 |
| * | 指针变量。 |
go语言条件语句
- if语句
go
// 语法
if 布尔表达式{
// to do...
}
- if...else语句
go
if 布尔表达式{
//todo....
}else{
// todo...
}
- if语句嵌套
go
if 布尔表达式1{
// todo...
if 布尔表达式2{
// tood...
}
}
- elif语句
go
if 布尔表达式1{
//todo...
}else if 布尔表达式2{
// todo...
}else{
// todo...
}
- switch语句
switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。
switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough 。
变量 varnum可以是任何类型,而 val1 和 val2 则可以是同类型的任意值。类型不被局限于常量或整数,但必须是相同的类型;或者最终结果为相同类型的表达式。
go
// switch 第一种用法
switch varnum{
case var1:
case var2:
case var3:
.....
case var6:
case var7:
default:
}
// 当varnum匹配case var4的时候,case var6也会被执行!
switch varnum{
case var1:
case var2:
case var3:
case var4:
fallthrough
case var6:
case var7:
default:
}
//switch 的第二种用法,switch可以不用写需要判定的变量
var age int
fmt.Scanf("%d", &age)
switch {
case age < 18:
fmt.Println("未成年")
case age >= 18 && age < 65:
fmt.Println("成年人")
case age >= 65:
fmt.Println("老年人")
default:
fmt.Println("输入有误")
}
//switch第三种用法,type switch
//注意想要使用这种类型的switch,var2必须是接口类型,也就是interface{}类型
var var2 interface{} = age
switch var2.(type) {
case int:
fmt.Println("var2是int类型")
case string:
fmt.Println("var2是string类型")
default:
fmt.Println("var2是其他类型")
}
go语言循环语句
在go语言中只有for循环,没有while循环、do while循环;
- 标准for循环
go
for init; condition; post {
}
for i:=0;i<=10;i++{
// todo...
}
- 使用for循环仿照while循环
go
for 布尔表达式{
// todo
}
for i<=100{
sum+=i;
i++;
}
- 死循环
go
// 方式1
for true {
// todo
}
// 方式2
for {
// todo
}
- 配合range使用
go
//for 循环的 range 格式可以对 slice、map、数组、字符串等进行迭代循环。格式如下:
// 1. 遍历map
for key,value:= range map1{
//todo...
}
//只读key,不读value
for key:=range map1{
//todo..
}
//只读valuee,不读key
for _,value:=range map1{
// todo...
}
// 2. 遍历数组
for index,val range arr{
//todo...
}
- break语句
经常用于中断当前 for 循环或跳出 switch 语句 - continue语句
跳过当前循环的剩余语句,然后继续进行下一轮循环。 - goto语句
将控制转移到被标记的语句。
在go语言中,标记一旦被定义就必须被使用,不然会报错!
go语言函数
语法:
go
func function_name( [parameter list] ) [return_types] {
函数体
}
//eg:
func Add(x,int,y int)(z int){
return x+y
}
函数定义解析:
func:函数由 func 开始声明
function_name:函数名称,参数列表和返回值类型构成了函数签名。
parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。语法糖:当有多个连续的参数具有相同的类型时,可以将参数的类型声明写在最后一个,eg:
go
func show(a string, b int, c int, d bool) {
fmt.Println(a, b, c, d)
}
// 根据语法糖,可以写成下面这种形式:
// 参数b的类型声明可以和参数c的类型声明合并在一起
func show(a string, b, c int, d bool) {
fmt.Println(a, b, c, d)
}
return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
函数的用法
- 将函数作为另一个函数的实参
go
func test3() {
getSqurtNum := func(x float64) float64 {
return math.Sqrt(x)
}
fmt.Println(getSqurtNum(92845))
}
- 闭包(匿名函数)
匿名函数是一种没有函数名的函数,通常用于在函数内部定义函数,或者作为函数参数进行传递。
类似C++的lamda表达式。
go
func getSequence() func() int {
i := 0
func1 := func() int {
i++
return i
}
return func1
}
func test3() {
nextNumber := getSequence()
fmt.Println(nextNumber()) // 1
fmt.Println(nextNumber()) // 2
fmt.Println(nextNumber()) // 3
newSeq := getSequence()
fmt.Println(newSeq()) // 1
fmt.Println(newSeq()) // 2
fmt.Println(newSeq()) // 3
}
- 方法
Go 语言中同时有函数和方法。一个方法就是一个包含了接受者的函数,接受者可以是命名类型 或者结构体类型 的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。语法格式如下:
类似于其它语言的成员函数
go
func (variable_name variable_data_type) function_name() [return_type]{
/* 函数体*/
}
//eg:
type Student struct {
name string
age int
}
func (s Student) printStudentInfo() {
fmt.Printf("学生姓名:%s,年龄:%d\n", s.name, s.age)
}
func test4() {
var s1 Student = Student{age: 20, name: "李四"}
//输出: 学生姓名:李四,年龄:20
s1.printStudentInfo()
}