Hello world
package main // 包名与文件名没有关系。main 函数所在包名必须是 mainimport ( // 导包"fmt")// main 函数,程序入口func main() { // 左大括号必须和函数名在同一行fmt.Println("Hello, golang!") // 分号可加可不加,和 JS 一样,一般不加fmt.Printf("Hello, %s!", "golang") // Printf 格式化输出}
Go 程序的入口是main函数,包含main函数的包名必须是main。
Go 中很多代码规范是在编译器层面限定死的,比如“函数左大括号必须和函数名在同一行”,否则编译都无法通过。
变量
四种声明方式
// 方式一:声明一个变量,不赋值,会有默认值var a intfmt.Println("a = ", a) // 字符串用双引号,单个字符用单引号fmt.Printf("type of a: %T\n", a) // 打印数据类型用 %T// 方式二:声明一个变量,初始化一个值var b float64 = 10 // go 没有 double,float64 就相当于 doublefmt.Println("b = ", b)fmt.Printf("type of b: %T\n", b)// 方式三:初始化时省略数据类型,编译器会根据初始化值自动判断类型var c = 100.0fmt.Println("c = ", c)fmt.Printf("type of c: %T\n", c)// 方式四:省略 var 关键词和数据类型(最常用)d := "go"fmt.Println("d = ", d)fmt.Printf("type of d: %T\n", d)
:::warning 注:声明全局变量时不能使用方式四。 :::
不能重复声明
var c = 100.0//var c = 10.0 // 不可重复声明d := "go"//d := "golang" // 这种方式同样不能重复声明变量
声明多个变量
var e, f int = 1, 2var g, h = 3, "4"var (i int = 5j = "6")fmt.Println(e, f, g, h, i, j)
注意
变量类型不可修改。
Go 中局部变量声明后必须要使用,否则编译会报错。全局变量只声明不使用不会报错,但 IDE 一般也会提醒。
匿名变量
_在 Go 中表示匿名变量(和 Python 相同),但在 Python 中只是一个约定,而在 Go 中变成了语法。
Python 中匿名变量可以当普通变量使用,而在 Go 中不行,比如 fmt.Println(_)会报错。
匿名变量的作用主要是接收不需要用的数据。比如下面的例子:
a, b := func() // 这个函数返回两个值
假如a的值后面的程序会用到,而b用不到。其他语言这样写无所谓,但 Go 不行,因为 Go 的变量定义后必须要使用,此时就可以使用匿名变量:
a, _ := func()
常量
定义常量
const a int = 10 // 定义常量const ( // 定义多个常量,可以分别初始化b = 1c = 2d = 3)const ( // 若没有指定初始化的值,会复用 *上一行* 的 *表达式*e = 1f // 1g = 2h // 2)
定义枚举类型
枚举类型本质上就是个常量组,不过我们只关注常量的名字,而不关注其值。
const ( // 定义枚举类型A = iota // 初始化为 iota,每行的 iota 自增 1,第一行的 iota 默认为 0BC)const (D = iota * 10 // D = 0 * 10 = 0E // E = 1 * 10 = 10F // F = 2 * 10 = 20)const (AA, AB = iota, iota * 2 // 0, 0BA, BB // 1, 2CA, CB // 2, 4DA, DB = iota, iota * 3 // 公式变了,但 iota 仍然累加。所以:3, 9)const (G = 2H = iota // 1。iota 可以认为是行数(从 0 开始的))
:::warning
iota只能在const ()中使用。
:::
基本数据类型
布尔
var _ bool // 1 byte
整数
var _ int8 // 1 byte, -128 ~ 127var _ int16var _ int32var _ int64var _ int // 在 32 位操作系统上相当于 int32,在 64 位操作系统上相当于 int64var _ uint8 // 1 byte, 0 ~ 257var _ uint16var _ uint32var _ uint64var _ uint // 在 32 位操作系统上相当于 uint32,在 64 位操作系统上相当于 uint64var _ = 0 // 不指定类型,默认为 intfmt.Println("int64 能表示的最大值 =", math.MaxInt64, "; 能表示的最小值 =", math.MinInt64)
浮点数
var _ float32var _ float64fmt.Println("float32 能表示的最大值 =", math.MaxFloat32)var flt = 0.0 // 默认为 float64,暂不知道是否与操作系统位数有关
byte 和 rune
byte其实是uint8的别称。专门再定义一个byte是为了表示强调,因为uint8比较特殊,刚好等于一个字节的大小。
下面是源码中byte的定义:
// 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是int32的别称。常用来处理中文字符。
下面是源码中rune的定义:
// 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
字符
Go 中字符和字符串有区别,单引号表示字符,双引号表示字符串。
var c = 'c'fmt.Printf("%T\n", c) // 结果:int32。所以说 Go 中字符实际上是 int32 类型,没有专门的 char 类型fmt.Println(c) // 结果:99。直接打印会打印出 ascii 码值fmt.Printf("%c\n", c) // 结果:c。这样才能打印出字符形式
复数
TODO
类型转换
基本类型的隐式类型转换
:::info
静态类型:变量定义后无法改变类型。
强类型:不支持隐式类型转换。
:::
Go 语言是强类型、静态类型语言。
Python 是强类型、动态类型语言。
C/C++ 是弱类型、静态类型语言。
Go 语言不支持 变量间 的隐式类型转换:
var a int = 1var b float64b = a // 报错。不支持(C/C++ 中这种情况是支持的)var c int16 = 1var d int32d = c // 报错。连两种整型之间的转换都不支持
但常量转变量的隐式类型转换是简单支持的:
var e int = 5.0 // 简单浮点型 隐式转换为 整型var f int = 5.1 // 报错。这种是不支持的var g float32 = 5 // 整型 隐式转换为 浮点型var h bool = 1 // 报错。不支持(C/C++ 中是支持的)const A int = 1var i float32 = A // 当然这种常量转变量也是不支持的
基本类型的强制类型转换
Go 允许底层结构相似的两个类型强制转换。
跟 C/C++ 类似,支持浮点型和整型互转,支持字符转换为整型、浮点型(因为字符底层实际上是int32),不支持字符串和其他类型之间的转换。
与 C/C++ 不同的是,不支持布尔型和整型、浮点型之间的转换。
低精度转高精度是安全的,高精度转低精度会丢失精度。例如int->float64、int64->int32会丢失精度,这个很好理解。
var a2 int32 = 1var b2 int64 = int64(a2)var c2 float64 = float64(b2)var e2 string = "2"var f2 int = int(e2) // 报错。不支持var g2 bool = bool(1) // 报错。不支持var h2 int = int(g2) // 报错。不支持type myInt int // 自定义一种类型,myInt 相当于 int 的别名var i2 myInt = 1var j2 int = i2 // 报错。即使是别名,也不支持隐式类型转换var j2 int = int(i2) // 但由于底层结构相同,所以支持强制类型转换var k2 string = "hello"var l2 []rune = []rune(k2) // 字符串和切片的底层结构是相似的,所以可以强制转换
字符串与基本类型之间的转换
运算符
算数运算符、关系运算符、逻辑运算符、位运算符、赋值运算符等都和 C/C++ 差不多,只是不支持
++a、--a的写法(但支持a++、a--)。a := 1a++a--++a // 不支持--a // 不支持fmt.Println(a++) // 不能和别的语句嵌套。其实这样确实更符合逻辑,因为 a++ 实际上是一条语句而非一个表达式
&取地址、*取内容 也和 C/C++ 相同。运算符优先级也和 C/C++ 差不多。
:::warning 运算符这一方面细节很多很多,没必要全搞清楚,平时用到的时候再查或者自己试试就知道了。 :::
控制台输入输出
输出:fmt.Printf()、fmt.Println()
指定格式的方式部分与 C 的printf相似,具体看老师笔记。
输入:fmt.Scanf()、fmt.Scanln()
var name stringvar age int_, _ = fmt.Scanf("%s, %d", &name, &age) // 跟 C 的 scanf 基本相同_, _ = fmt.Scanln(&name, &age) // 相当于 fmt.Scanf("%s %d", &name, &age)
流程控制
if语句
基本写法:
num := 10if num < 0 {// some code} else if num == 0 {// some code} else {// some code}
简化写法:
if n := 10; n < 0 {// some code} else if n == 0 {// some code} else {// some code}
注意:这种写法中,n的作用域仅仅是if语句块,不能在if语句块外使用。
for循环
Go 中没有while和do while,只有for。
但for的用法很多,功能很强大。
用法一(与 C/C++ 相似):
sum := 0for i := 0; i < 10; i++ {sum += i}
用法二(相当于while):
sum := 0i := 0for i < 10 {sum += ii++}
用法三(相当于for(;;),即无限循环):
sum := 0i = 0for {if i >= 10 {break}sum += ii++}
用法四(for-range,专门用来遍历字符串、数组、切片、map 等):
for index, char := range "hello,世界" { // 遍历字符串fmt.Printf("index = %d, char = %c\n", index, char)}
:::info 注:使用这种方式遍历字符串时,是可以自动处理中文的。 :::
break、continue
不用说了,都一样。
goto语句
Go 中是有goto语句的,用法和 C/C++ 一样。
goto语句能不用则不用,因为会影响程序结构,导致代码可读性差,难以维护。
goto语句常见的两个使用场景:
- 跳出多重循环。
 - 集中处理错误。
 
switch语句
有些情况下使用switch代替if可以增强代码可读性。
由于 Go 中的switch做了扩展,所以在 Go 中使用switch比在 C/C++ 中更频繁。
做接口类型判断时经常用到switch。
这种用法和 C/C++ 相似:
sex := 0switch sex {case 0: fmt.Println('女') // 只有一条语句的话可以写在一行里case 1: // 多条语句可以这样写fmt.Println('男')//fallthrough // Go 的 switch 中默认不用在每个 case 最后写 break。如果要用到 C/C++ 中不写 break 时的那种继续执行下一个 case 的效果,可以用 fallthrough 关键字default: { // 可以加大括号fmt.Println("未知")}}
这个写法与 C/C++ 不一样:
sex := 0switch sex {case 0, 1:fmt.Println("已知")default:fmt.Println("未知")}
这是 C/C++ 中没有的扩展写法:
score := 90switch {case score >= 90: // case 不再只能是一个值,而可以是一个逻辑表达式fmt.Println('A')case 80 <= score && score < 90:fmt.Println('B')case 70 <= score && score < 80:fmt.Println('C')case 60 <= score && score < 70:fmt.Println('D')default:fmt.Println('E')}
