Go(Golang) ——静态强类型、编译型语言

1. go的基本语法

1.1 变量声明

标准格式声明变量:var name type ,其中var是声明变量的关键字,name是变量名称,type是变量类型
eg: var a, b *int

批量格式声明变量:使用关键字 var 和括号,可以将一组变量定义放在一起。

var (
    a int
    b string
    c []float32
    d func() bool
    e struct {
        x int
    }
)

简短格式声明变量:名字 := 表达式
其有一下限制:

  1. 定义变量,同时显式初始化
  2. 不能提供数据类型
  3. 只能在函数内部使用
    eg: i, j := 0, 1

说明:简短变量声明被广泛用于大部分的局部变量的声明和初始化。var 形式的声明语句往往是用于需要显式指定变量类型地方,或者因为变量稍后会被重新赋值而初始值无关紧要的地方。

Go语言的基本类型有:

  • bool
  • string
  • int、int8、int16、int32、int64 // 分别对应 8、16、32、64 bit(二进制位)大小的有符号整数
  • uint、uint8、uint16、uint32、uint64、uintptr
  • byte // uint8 的别名
  • rune // int32 的别名 代表一个 Unicode 码
  • float32、float64 // 浮点数在声明的时候可以只写整数部分或者小数部分
  • complex64、complex128 // 复数

变量的命名采用小驼峰形式
函数命名采用大驼峰

1.2 变量初始化

所有的内存在 Go 中都是经过初始化的,每个变量会初始化其类型的默认值。

  • 整型和浮点型变量的默认值为 0 和 0.0。
  • 字符串变量的默认值为空字符串。
  • 布尔型变量默认为 bool。
  • 切片、函数、指针变量的默认为 nil。

变量初始化的标准格式var 变量名 类型 = 表达式
eg: var hp int = 100

编译器推导类型的格式: var hp = 100

短变量声明并初始化: hp := 100
说明:推导声明写法的左值变量必须是没有定义过的变量。若定义过,将会发生编译错误。

多个变量同时赋值、交换:

var a int = 100
var b int = 200
b, a = a, b
fmt.Println(a, b)

1.3 匿名变量

匿名变量的特点是一个下画线“”,“”本身就是一个特殊的标识符,被称为空白标识符。它可以像其他标识符那样用于变量的声明或赋值(任何类型都可以赋值给它),但任何赋给这个标识符的值都将被抛弃,因此这些值不能在后续的代码中使用,也不可以使用这个标识符作为变量对其它变量进行赋值或运算。使用匿名变量时,只需要在变量声明的地方使用下画线替换即可。例如:

func GetData() (int, int) {
    return 100, 200
}
func main(){
    a, _ := GetData()
    _, b := GetData()
    fmt.Println(a, b)
}

匿名变量不占用内存空间,不会分配内存。匿名变量与匿名变量之间也不会因为多次声明而无法使用。

1.4 复数

Go语言中复数的类型有两种,分别是 complex128(64 位实数和虚数)和 complex64(32 位实数和虚数),其中 complex128 为复数的默认类型

复数是由两个浮点数表示的,其中一个表示实部(real),一个表示虚部(imag)。

复数的值由三部分组成 RE + IMi,其中 RE 是实数部分,IM 是虚数部分,RE 和 IM 均为 float 类型,而最后的 i 是虚数单位。

声明复数的两种语法规则:
var name complex128 = complex(x, y)
name := complex(x, y)
举例:

var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x*y)                 // "(-5+10i)"
fmt.Println(real(x*y))           // "-5"
fmt.Println(imag(x*y))           // "10"

1.5 短路行为

布尔值可以和 &&(AND)和 ||(OR)操作符结合,并且有短路行为,如果运算符左边的值已经可以确定整个布尔表达式的值,那么运算符右边的值将不再被求值,因此下面的表达式总是安全的:
s != "" && s[0] == 'x'
其中 s[0] 操作如果应用于空字符串将会导致 panic 异常。

因为&&的优先级比||高(&& 对应逻辑乘法,|| 对应逻辑加法,乘法比加法优先级要高),所以下面的布尔表达式可以不加小括号:

if 'a' <= c && c <= 'z' ||
    'A' <= c && c <= 'Z' ||
    '0' <= c && c <= '9' {
    // ...ASCII字母或数字...
}

1.6 字符串

定义单行字符串

s := "hel" + "lo,"
s += "world!"
fmt.Println(s) //输出 “hello, world!”

在Go语言中,使用双引号书写字符串的方式是字符串常见表达方式之一,被称为字符串字面量(string literal),这种双引号字面量不能跨行,如果想要在源码中嵌入一个多行字符串时,就必须使用`反引号,代码如下:

const str = `第一行
第二行
第三行
\r\n
`
fmt.Println(str)

output :

第一行
第二行
第三行
\r\n

1.7 数据类型转换

Go语言不存在隐式类型转换,因此所有的类型转换都必须显式的声明:valueOfTypeB = typeB(valueOfTypeA)

只有相同底层类型的变量之间可以进行相互转换(如将 int16 类型转换成 int32 类型),不同底层类型的变量相互转换时会引发编译错误(如将 bool 类型转换为 int 类型)

举例示例:

func main() {
    fmt.Println("int8 range:", math.MinInt8, math.MaxInt8)
    fmt.Println("int16 range:", math.MinInt16, math.MaxInt16)
    fmt.Println("int32 range:", math.MinInt32, math.MaxInt32)
    fmt.Println("int64 range:", math.MinInt64, math.MaxInt64)

    // 初始化一个32位整型值
    var a int32 = 1047483647
    // 输出变量的十六进制形式和十进制值
    fmt.Printf("int32: 0x%x %d\n", a, a)
    // 将a变量数值转换为十六进制, 发生数值截断
    b := int16(a)
    // 输出变量的十六进制形式和十进制值
    fmt.Printf("int16: 0x%x %d\n", b, b)
    // 将常量保存为float32类型
    var c float32 = math.Pi
    // 转换为int类型, 浮点发生精度丢失
    fmt.Println(int(c))
}

输出:

int8 range: -128 127
int16 range: -32768 32767
int32 range: -2147483648 2147483647
int64 range: -9223372036854775808 9223372036854775807
int32: 0x3e6f54ff 1047483647
int16: 0x54ff 21759
3

1.8 指针

指针(pointer)在Go语言中可以被拆分为两个核心概念:

  • 类型指针,允许对这个指针类型的数据进行修改,传递数据可以直接使用指针,而无须拷贝数据,类型指针不能进行偏移和运算。
  • 切片,由指向起始元素的原始指针、元素数量和容量组成。

切片比原始指针具备更强大的特性,而且更为安全。切片在发生越界时,运行时会报出宕机,并打出堆栈,而原始指针只会崩溃。

给指针变量赋初值
ptr := &v // v 的类型为 T

* 操作符作为右值时,意义是取指针的值,作为左值时,也就是放在赋值操作符的左边时,表示 a 指针指向的变量。其实归纳起来, * 操作符的根本意义就是操作指针指向的变量。当操作在右值时,就是取指向变量的值,当操作在左值时,就是将值设置给指向的变量。

创建指针的另一种方法:
new(类型) ——new() 函数可以创建一个对应类型的指针,创建过程会分配内存,被创建的指针指向默认值。

str := new(string)
*str = "Go语言教程"
fmt.Println(*str)

1.9 特别说明:%组合

组合含义示意
%f打印浮点型
%s打印字符串fmt.Printf(“value: %s\n”, value)
%d打印十进制
%x打印十六进制fmt.Printf(“int16: 0x%x %d\n”, b, b)
%b打印二进制fmt.Printf("%b\n", FlagRed)
%p打印变量的内存地址fmt.Printf("%p %p", &cat, &str)
%T打印类型fmt.Printf(“ptr type: %T\n”, ptr)

1.10 常量 && const

常量的定义格式和变量的声明语法类似:const name [type] = value

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

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

常量的值必须是能够在编译时就能够确定的
eg: 错误的做法:const c2 = getNumber()

如果是批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式,对应的常量类型也是一样的。例如:

const (
    a = 1
    b
    c = 2
    d
)
fmt.Println(a, b, c, d) // "1 1 2 2"

iota 常量生成器可以模拟枚举
常量声明可以使用 iota 常量生成器初始化,它用于生成一组以相似规则初始化的常量,但是不用每行都写一遍初始化表达式。在一个 const 声明语句中,在第一个声明的常量所在的行,iota 将会被置为 0,然后在每一个有常量声明的行加一。

type Weekday int
const (
    Sunday Weekday = iota
    Monday
    Tuesday
    Wednesday
    Thursday
    Friday
    Saturday
)

周日将对应 0,周一为 1,以此类推。

生成标志位常量:

const (
    FlagNone = 1 << iota
    FlagRed
    FlagGreen
    FlagBlue
)
fmt.Printf("%d %d %d\n", FlagRed, FlagGreen, FlagBlue)
fmt.Printf("%b %b %b\n", FlagRed, FlagGreen, FlagBlue)

代码输出:

2 4 8
10 100 1000

1.11 特别说明:printf && println

  • Println : 直接输出变量或者直接输出字符串,默认换行
  • Printf : 打印出格式化的字符串,可以输出字符串类型的变量,需要自己换行
package main
import "fmt"
import "os"
type point struct {
    x, y int
}
func main() {
    //Go 为常规 Go 值的格式化设计提供了多种打印方式。例如,这里打印了 point 结构体的一个实例。
    p := point{1, 2}
    fmt.Printf("%v\n", p) // {1 2}
    //如果值是一个结构体,%+v 的格式化输出内容将包括结构体的字段名。
    fmt.Printf("%+v\n", p) // {x:1 y:2}
    //%#v 形式则输出这个值的 Go 语法表示。例如,值的运行源代码片段。
    fmt.Printf("%#v\n", p) // main.point{x:1, y:2}
    //需要打印值的类型,使用 %T。
    fmt.Printf("%T\n", p) // main.point
    //格式化布尔值是简单的。
    fmt.Printf("%t\n", true)
    //格式化整形数有多种方式,使用 %d进行标准的十进制格式化。
    fmt.Printf("%d\n", 123)
    //这个输出二进制表示形式。
    fmt.Printf("%b\n", 14)
    //这个输出给定整数的对应字符。
    fmt.Printf("%c\n", 33)
    //%x 提供十六进制编码。
    fmt.Printf("%x\n", 456)
    //对于浮点型同样有很多的格式化选项。使用 %f 进行最基本的十进制格式化。
    fmt.Printf("%f\n", 78.9)
    //%e 和 %E 将浮点型格式化为(稍微有一点不同的)科学技科学记数法表示形式。
    fmt.Printf("%e\n", 123400000.0)
    fmt.Printf("%E\n", 123400000.0)
    //使用 %s 进行基本的字符串输出。
    fmt.Printf("%s\n", "\"string\"")
    //像 Go 源代码中那样带有双引号的输出,使用 %q。
    fmt.Printf("%q\n", "\"string\"")
    //和上面的整形数一样,%x 输出使用 base-16 编码的字符串,每个字节使用 2 个字符表示。
    fmt.Printf("%x\n", "hex this")
    //要输出一个指针的值,使用 %p。
    fmt.Printf("%p\n", &p)
    //当输出数字的时候,你将经常想要控制输出结果的宽度和精度,可以使用在 % 后面使用数字来控制输出宽度。默认结果使用右对齐并且通过空格来填充空白部分。
    fmt.Printf("|%6d|%6d|\n", 12, 345)
    //你也可以指定浮点型的输出宽度,同时也可以通过 宽度.精度 的语法来指定输出的精度。
    fmt.Printf("|%6.2f|%6.2f|\n", 1.2, 3.45)
    //要最对齐,使用 - 标志。
    fmt.Printf("|%-6.2f|%-6.2f|\n", 1.2, 3.45)
    //你也许也想控制字符串输出时的宽度,特别是要确保他们在类表格输出时的对齐。这是基本的右对齐宽度表示。
    fmt.Printf("|%6s|%6s|\n", "foo", "b")
    //要左对齐,和数字一样,使用 - 标志。
    fmt.Printf("|%-6s|%-6s|\n", "foo", "b")
    //到目前为止,我们已经看过 Printf了,它通过 os.Stdout输出格式化的字符串。Sprintf 则格式化并返回一个字符串而不带任何输出。
    s := fmt.Sprintf("a %s", "string")
    fmt.Println(s)
    //你可以使用 Fprintf 来格式化并输出到 io.Writers而不是 os.Stdout。
    fmt.Fprintf(os.Stderr, "an %s\n", "error")
}

1.12 字符串和数值类型间的相互转换

strconv 包中常用的函数包括 Atoi()、Itia()、parse 系列函数、format 系列函数、append 系列函数等

类型含义示例
Itoa():整型转字符串func Itoa(i int) stringstr := strconv.Itoa(num)
Atoi():字符串转整型func Atoi(s string) (i int, err error)num1, err := strconv.Atoi(str1)
ParseBool() :字符串转换为 bool 类型的值func ParseBool(str string) (value bool, err error)
ParseInt() :返回字符串表示的整数值(可以包含正负号)func ParseInt(s string, base int, bitSize int) (i int64, err error)
ParseUint() :功能类似于 ParseInt() 函数,但 ParseUint() 函数不接受正负号,用于无符号整型func ParseUint(s string, base int, bitSize int) (n uint64, err error)
ParseFloat() :用于将一个表示浮点数的字符串转换为 float 类型func ParseFloat(s string, bitSize int) (f float64, err error)
FormatBool() 函数可以一个 bool 类型的值转换为对应的字符串类型func FormatBool(b bool) string
FormatInt() 函数用于将整型数据转换成指定进制并以字符串的形式返回func FormatInt(i int64, base int) string
FormatUint() 函数与 FormatInt() 函数的功能类似,但是参数 i 必须是无符号的 uint64 类型func FormatUint(i uint64, base int) string
FormatFloat() 函数用于将浮点数转换为字符串类型func FormatFloat(f float64, fmt byte, prec, bitSize int) string

Append 系列函数用于将指定类型转换成字符串后追加到一个切片中,其中包含 AppendBool()、AppendFloat()、AppendInt()、AppendUint()。

Append 系列函数和 Format 系列函数的使用方法类似,只不过是将转换后的结果追加到一个切片中。

package main
import (
    "fmt"
    "strconv"
)
func main() {
    // 声明一个slice
    b10 := []byte("int (base 10):")
  
    // 将转换为10进制的string,追加到slice中
    b10 = strconv.AppendInt(b10, -42, 10)
    fmt.Println(string(b10))
    b16 := []byte("int (base 16):")
    b16 = strconv.AppendInt(b16, -42, 16)
    fmt.Println(string(b16))
}

输出:

int (base 10):-42
int (base 16):-2a

2 语言容器

2.1 数组

数组的声明:var 数组变量名 [元素数量]Type
数组初始化示例:

var q [3]int = [3]int{1, 2, 3}
var r [3]int = [3]int{1, 2}  // 第三位默认为0
q := [...]int{1, 2, 3}  // 表示数组的长度是根据初始化值的个数来计算

遍历数组:

var team [3]string
team[0] = "hammer"
team[1] = "soldier"
team[2] = "mum"

for k, v := range team {  // 打印索引和元素
    fmt.Println(k, v)
}

// 仅打印元素
for _, v := range team {
    fmt.Printf("%s\n", v)
}

2.2 多维数组

数组的声明:var array_name [size1][size2]...[sizen] array_type
示例:

// 声明一个二维整型数组,两个维度的长度分别是 4 和 2
var array [4][2]int
// 使用数组字面量来声明并初始化一个二维整型数组
array = [4][2]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
// 声明并初始化数组中索引为 1 和 3 的元素
array = [4][2]int{1: {20, 21}, 3: {40, 41}}

2.3 切片

切片默认指向一段连续内存区域,可以是数组,也可以是切片本身。(python里面也有切片的概念,基本相同
格式如下:slice [开始位置 : 结束位置] // 取前不取后

切片类型声明格式:var name []Type
eg: var strList []string
var numListEmpty = []int{}

使用 make() 函数构造切片: make( []Type, size, cap )

  • Type 是指切片的元素类型
  • size 指的是为这个类型分配多少个元素
  • cap 为预分配的元素数量,这个值设定后不影响 size,只是能提前分配空间,降低多次分配空间造成的性能问题。
a := make([]int, 2)
b := make([]int, 2, 10)
fmt.Println(a, b)
fmt.Println(len(a), len(b))

output:

[0 0] [0 0]
2 2

注意:切片长度 len 并不等于切片的容量 cap。

切片相关函数的使用:

  • cap() 查看切片的容量情况
  • append() 为切片动态添加元素。如果空间不足以容纳足够多的元素,切片就会按2倍“扩容”
// 尾端追加元素,空间不足,会导致扩容
var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包

// 头部追加元素,会导致空间重新分配
var a = []int{1,2,3}
a = append([]int{0}, a...) // 在开头添加1个元素
a = append([]int{-3,-2,-1}, a...) // 在开头添加1个切片
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值