青训营X豆包MarsCode 技术训练营 - Golang 语法解析 - 1 | 豆包MarsCode AI 刷题

108 阅读7分钟

前言

本文记录训练营第一课的相关内容,对于课程中讲的不够充分的地方,结合了go入门指南中的详细介绍进行了补充。笔记同步更新在我的博客

本篇讲述了变量,控制结构, 值类型。

介绍

golang的特点

特点描述
高性能,高并发接近C++的性能,标准库原生高并发支持。
语法简单,学习曲线缓学习周期短至周计
丰富的标准库可以解决大部分需求
工具链编译,代码格式化,代码检查,测试
静态编译体积小,部署编译
快速编译支持增量编译
快平台交叉编译简单
GC原生GC支持

开发环境

这里我使用的是 gvmdebian12上进行开发。开发环境使用vscode + neovim

gvmgithub链接

我的实验代码仓库

数据类型

表达式是一种特定的类型的值,它可以由其它的值以及运算符组合而成。每个类型都定义了可以和自己结合的运算符集合,如果你使用了不在这个集合中的运算符,则会在编译时获得编译错误。

Bool

一个简单的例子:var b bool = true

布尔型的值只可以是常量 true 或者 false。

两个类型相同的值可以使用相等 == 或者不等 != 运算符来进行比较并获得一个布尔型的值。

当相等运算符两边的值是完全相同的值的时候会返回 true,否则返回 false,并且只有在两个的 值的类型相同的情况下才可以使用。

var aVar = 10
aVar == 5 -> false
aVar == 10 -> true

数字类型

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

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

这些类型的长度都是根据运行程序所在的操作系统类型所决定的:

  • int 和 uint 在 32 位操作系统上,它们均使用 32 位(4 个字节),在 64 位操作系统上,它们均使用 64 位(8 个字节)。
  • uintptr 的长度被设定为足够存放一个指针即可。

Go 语言中没有 float 类型。(Go语言中只有 float32 和 float64)没有double类型。

整数:

  • int8(-128 -> 127)
  • int16(-32768 -> 32767)
  • int32(-2,147,483,648 -> 2,147,483,647)
  • int64(-9,223,372,036,854,775,808 -> 9,223,372,036,854,775,807)

无符号整数:

  • uint8(0 -> 255)
  • uint16(0 -> 65,535)
  • uint32(0 -> 4,294,967,295)
  • uint64(0 -> 18,446,744,073,709,551,615)

浮点型(IEEE-754 标准):

  • float32(+- 1e-45 -> +- 3.4 * 1e38)
  • float64(+- 5 * 1e-324 -> 107 * 1e308)

int 型是计算最快的一种类型。

o 中不允许不同类型之间的混合使用,但是对于常量的类型限制非常少,因此允许常量之间的混合使用,下面这个程序很好地解释了这个现象(该程序无法通过编译):

package main

func main() {
	var a int
	var b int32
	a = 15
	b = a + a	 // 编译错误
	b = b + 5    // 因为 5 是常量,所以可以通过编译
}

如果尝试编译该程序,则将得到编译错误 cannot use a + a (type int) as type int32 in assignmentint的字节数取决于操作系统,32位系统中为4字节,64位系统中为8字节。可以使用unsafe.Sizeof(a)查看。不论ab的字节数是否相同,这种转换都是不允许的。

如要进行复制,可以进行显式类型转换。数值转换会造成精度丢失,这里不做详细论述。

数据类型的位运算和逻辑算数运算和C++一致。

字符类型

严格来说,这并不是 Go 语言的一个类型,字符只是整数的特殊用例。byte 类型是 uint8 的别名,对于只占用 1 个字节的传统 ASCII 编码的字符来说,完全没有问题。例如:var ch byte = 'A';字符使用单引号括起来。可以将数字赋值给 byte类型,这点和C++一致。

类型别名

当你在使用某个类型时,你可以给它起另一个名字,然后你就可以在你的代码中使用新的名字(用于简化名称或解决名称冲突)。

在 type TZ int 中,TZ 就是 int 类型的新名称(用于表示程序中的时区),然后就可以使用 TZ 来操作 int 类型的数据。

package main
import "fmt"

type TZ int

func main() {
	var a, b TZ = 3, 4
	c := a + b
	fmt.Printf("c has the value: %d", c) // 输出:c has the value: 7
}

实际上,类型别名得到的新类型并非和原类型完全相同,新类型不会拥有原类型所附带的方法。

常量

常量使用关键字 const 定义,用于存储不会改变的数据。

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

常量的定义格式:const identifier [type] = value,例如:

const Pi = 3.14159

在 Go 语言中,你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。

  • 显式类型定义: const b string = "abc"
  • 隐式类型定义: const b = "abc"

一个没有指定类型的常量被使用时,会根据其使用环境而推断出它所需要具备的类型。换句话说,未定义类型的常量会在必要时刻根据上下文来获得相关类型。

常量的值必须是能够在编译时就能够确定的;你可以在其赋值表达式中涉及计算过程,但是所有用于计算的值必须在编译期间就能获得。

  • 正确的做法:const c1 = 2/3
  • 错误的做法:const c2 = getNumber() // 引发构建错误: getNumber() used as value

因为在编译期间自定义函数均属于未知,因此无法用于常量的赋值,但内置函数可以使用,如:len()。

数字型的常量是没有大小和符号的,并且可以使用任何精度而不会导致溢出:

const Ln2 = 0.693147180559945309417232121458\
			176568075500134360255254120680009
const Log2E = 1/Ln2 // this is a precise reciprocal
const Billion = 1e9 // float constant
const hardEight = (1 << 100) >> 97

不过需要注意的是,当常量赋值给一个精度过小的数字型变量时,可能会因为无法正确表达常量所代表的数值而导致溢出,这会在编译期间就引发错误。

控制结构

IF 语句

if 可以包含一个初始化语句(如:给一个变量赋值)。这种写法具有固定的格式(在初始化语句后方必须加上分号):

例如:

if val := 10; val > max {
	// do something
}

For 语句

基本使用

package main

import "fmt"

func main() {
	for i := 0; i < 5; i++ {
		fmt.Printf("This is the %d iteration", i)
	}
}

for 初始化语句; 条件语句; 修饰语句 {}C++中的范式是一致的。

For-range

这是 Go 特有的一种的迭代结构,您会发现它在许多情况下都非常有用。它可以迭代任何一个集合。语法上很类似其它语言中 foreach 语句,但您依旧可以获得每次迭代所对应的索引。一般形式为:for ix, val := range coll { }

要注意的是,val 始终为集合中对应索引的值拷贝,因此它一般只具有只读性质,对它所做的任何修改都不会影响到集合中原有的值。

例子,迭代字符串

for pos, char := range str {
...
}

循环控制

breakcontinueC++一致。

Switch

相比较 C 和 Java 等其它语言而言,Go 语言中的 switch 结构使用上更加灵活。其有三种主要形式

形式一

值比较

switch var1 {
	case val1:
		...
	case val2:
		...
	default:
		...
}

形式二

直接判别条件

switch {
	case condition1:
		...
	case condition2:
		...
	default:
		...
}

形式三

包含初始化语句

switch result := calculate(); {
	case result < 0:
		...
	case result > 0:
		...
	default:
		// 0
}

Goto

go中有goto语句,但不鼓励在编程实践中使用。