拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Build Web Application with Golang(第 2.2 节):Go 语言基础——变量、常量、基础类型与 array/slice/map 实战详解

文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载本指南对应开源电子书《Build Web Application with Golang》葡萄牙语译本第 2.2 节「Fundamentos em Go」系统讲解 Go 语言的核心基础语法变量的多种声明方式、编译期常量、布尔/数值/字符串/error 等基础类型、iota枚举、分组定义以及array、slice、map三种容器与make/new内存分配的区别。文中所有代码与结论均可在仓库 pt-br/code/src/apps/ch.2.2/main.go 的配套示例中得到验证读完本文你将具备独立编写 Go 基础程序、正确选择容器类型与内存分配方式的能力为后续章节构建 Web 应用打下地基。一、变量的定义方式Go 是一门强调简洁的编译型语言变量定义有四种逐步简化的写法核心特点是类型放在变量名之后。1.var关键字最基础的声明// 定义一个名为 variableName、类型为 type 的变量 var variableName type // 一次定义三个同类型变量 var vname1, vname2, vname3 type // 定义并初始化 var variableName type value // 一次定义三个变量并分别赋初值 var vname1, vname2, vname3 type v1, v2, v32. 省略类型由初始值自动推断当定义变量时同时给出初始值Go 允许省略类型由编译器从初值推断var vname1, vname2, vname3 v1, v2, v33. 短声明:函数内的最佳实践连var都省掉直接用:同时完成声明与赋值vname1, vname2, vname3 : v1, v2, v3关键限制:只能在函数体内使用在函数体外使用会触发编译错误。因此全局变量通常用var或var()分组形式声明函数内部则优先使用短声明。仓库配套示例 main.go 中的show_multiple_assignments函数完整演示了这一用法var v1 int 42 var v2, v3 int 2, 3 // : 只能在函数内使用省略 var 和类型直接初始化 vname1, vname2, vname3 : v1, v2, v3 // _ 空白标识符丢弃第一个返回值 34b 得到 35 _, b : 34, 35 fmt.Printf(vname1 %v, vname2 %v, vname3 %v\n, vname1, vname2, vname3) fmt.Println(b , b)4. 空白标识符__是 Go 的特殊变量名赋值给它的任何值都会被丢弃。上例中_, b : 34, 35即丢弃34、保留35。它的主要场景是接收函数返回值中不需要的部分例如后续章节中db.Query的多返回值而不仅仅用于这种简单赋值演示。5. 未使用的变量会编译报错Go 编译器对「声明了却不用」零容忍以下代码无法编译通过package main func main() { var i int }仓库在 pt-br/code/src/apps/ch.2.2/what_is_wrong_with_this/main.go 中专门保留了这个「有问题」的程序作为练习——注释明确写着「Tente corrigir este programa」试着修正这个程序并提示在终端执行go run main.go查看报错。你可以亲自运行验证go run pt-br/code/src/apps/ch.2.2/what_is_wrong_with_this/main.go二、常量常量const在编译期就被确定运行期不可修改。Go 中常量可以是数字、布尔值或字符串。const constantName value // 需要时也可以显式指定类型 const Pi float32 3.1415926更多示例const Pi 3.1415926 const i 10000 const MaxThread 10 const prefix astaxie_配套示例 main.go 中还演示了常量与全局变量的组合写法例如const Pi 3.1415926、const (i 1e4; MaxThread 10; prefix astaxie_)。三、基础类型详解1. 布尔类型boolbool只能取true或false默认值为false。注意Go 不允许在数字与布尔值之间做类型转换二者是截然不同的类型。var isActive bool // 全局变量默认 false var enabled, disabled true, false // 省略类型 func test() { var available bool // 局部变量 valid : false // 短声明 available true // 赋值 }2. 数值类型整型分为有符号与无符号两类int、uint长度取决于操作系统32 位系统上 32 位64 位系统上 64 位定长类型rune、int8、int16、int32、int64、byte、uint8、uint16、uint32、uint64别名关系rune是int32的别名用于表示 Unicode 码点byte是uint8的别名。重要规则这些类型之间不能互相赋值即使int32与int8同属 int 家族且长度不同混用也会产生编译错误var a int8 var b int32 c : a b // 编译错误类型不匹配浮点类型只有float32与float64不存在名为float的类型短声明:推断出的浮点默认类型是float64。复数类型Go 原生支持复数。complex12864 位实部 64 位虚部是默认类型complex6432 位实部 32 位虚部占用更小。复数形如REIMi其中RE为实部、IM为虚部、末尾的i表示虚数单位var c complex64 55i // 输出Value is: (55i) fmt.Printf(Value is: %v, c)配套示例 show_different_types 集中声明了rune、int8~int64、byte、uint8~uint64以及complex64并打印所有整型默认值是验证上述规则的可运行样板。3. 字符串stringGo 使用 UTF-8 字符集字符串可用双引号或反引号 表示。var frenchHello string // 基本定义方式 var emptyString string // 空字符串 func test() { no, yes, maybe : no, yes, maybe // 短声明 japaneseHello : Ohaiou frenchHello Bonjour // 赋值 }字符串不可变无法按下标修改字符s[0] c会编译报错。想要修改单个字符标准做法是转换为[]byte改完再转回字符串s : hello c : []byte(s) // 字符串 → []byte c[0] c s2 : string(c) // []byte → 字符串 fmt.Printf(%s\n, s2) // 输出 cello拼接使用操作符也可以借助切片技巧下标可读不可写s : hello, m : world a : s m fmt.Printf(%s\n, a) // 输出 hello, world s : hello s c s[1:] // 用 s[1:] 取子串再拼接输出 cello fmt.Printf(%s\n, s)多行字符串用反引号包裹且反引号不会转义任何字符保留原始内容m : hello world配套示例 show_strings 与 show_string_manipulation 分别演示了多语言字符串、多行反引号字符串、[]byte转换与拼接的完整运行效果。4. error 类型Go 用内置的error类型承载错误信息配套的errors包负责构造错误对象err : errors.New(emit macho dwarf: elf header corrupted) if err ! nil { fmt.Print(err) }四、基础类型的底层数据结构Go 变量在内存中以连续内存块word存储不同类型的长度不同。下图源自 Russ Cox 关于 Go 数据结构的分析直观展示了int、int32、float32、[5]byte、[4]int在内存中的排布方式——这是理解后面slice指针语义的基础Figure 2.1 Go 中的底层数据结构五、分组定义与 iota 枚举1. 分组定义grouping当需要同时声明多个常量、变量或导入多个包时可以用分组形式替代逐条声明使代码更紧凑// 基本形式 import fmt import os const i 100 const pi 3.1415 const prefix Go_ var i int var pi float32 var prefix string // 分组形式 import( fmt os ) const( i 100 pi 3.1415 prefix Go_ ) var( i int pi float32 prefix string )2.iota枚举iota是 Go 的枚举关键字在const()分组中从0开始、逐行递增 1。规则要点const()中第一个常量默认值为iota即0后续常量若未显式赋值会沿用上一条表达式每遇到一次const关键字iota重置为0同一行内的多个iota取值相同。const( x iota // x 0 y iota // y 1 z iota // z 2 w // 省略表达式则沿用上一条w iota因此 w 3 ) const v iota // 遇到新 constiota 重置为 0所以 v 0 const ( e, f, g iota, iota, iota // 同一行内 iota 值相同e0, f0, g0 )配套示例 show_iota 原样实现了上述代码并打印验证x0, y1, z2, w3, v0, efg0。3. 命名导出规则Go 之所以简洁是因为有一组默认行为规则以大写字母开头的变量/函数/常量会被导出对其他包可见否则为包内私有Go没有public/private关键字可见性完全由首字母大小写决定。配套示例中This_is_public()与this_is_private()正是对这一规则的注释说明见 main.go。六、array、slice、map1. array数组var arr [n]typen是数组长度type是元素类型。数组长度属于类型的一部分——[3]int与[4]int是不同的类型因此数组长度不可变。数组按值传递作为函数参数时传递的是副本而非引用需要引用语义时使用slice。var arr [10]int // 类型为 [10]int 的数组 arr[0] 42 // 下标从 0 开始 arr[1] 13 fmt.Printf(The first element is %d\n, arr[0]) // 42 fmt.Printf(The last element is %d\n, arr[9]) // 未赋值元素取零值 0数组也可以使用:定义Go 还支持用...让编译器推断长度a : [3]int{1, 2, 3} // 3 个元素的整型数组 b : [10]int{1, 2, 3} // 10 个元素前 3 个赋值其余为零值 0 c : [...]int{4, 5, 6} // ... 由编译器计算长度多维数组——数组中嵌套数组doubleArray : [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}} // 更简洁的写法可省略内层类型 easyArray : [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}下图展示二维数组的映射关系A[0][0]指向第一个子数组的首元素1A[1][3]指向第二个子数组的末元素8Figure 2.2 多维数组的映射关系2. slice切片当我们事先不知道数组长度时array就不合适了此时需要 Go 的slice「动态数组」。slice并不是真正的动态数组而是一种引用类型——它指向某个底层数组的一段声明方式与数组类似但省略长度var fslice []int slice : []byte{a, b, c, d}切片操作array[i:j]i是起始下标j是结束下标array[j]不包含在结果中切片长度为j-ivar ar [10]byte{a, b, c, d, e, f, g, h, i, j} var a, b []byte a ar[2:5] // a 指向 ar[2]、ar[3]、ar[4]即 c、d、e b ar[3:5] // b 指向 ar[3]、ar[4]即 d、e便捷操作规则ar[:n]等价于ar[0:n]ar[n:]等价于ar[n:len(ar)]ar[:]等价于ar[0:len(ar)]即整个数组。slice 的 slice可基于既有 slice 继续切片且能在容量范围内扩展var array [10]byte{a, b, c, d, e, f, g, h, i, j} var aSlice, bSlice []byte aSlice array[:3] // aSlice array[0:3]包含 a,b,c aSlice array[5:] // aSlice array[5:10]包含 f,g,h,i,j aSlice array[:] // aSlice array[0:10]包含全部元素 aSlice array[3:7] // aSlice 包含 d,e,f,glen4cap7 bSlice aSlice[1:3] // bSlice 包含 e,f bSlice aSlice[:3] // bSlice 包含 d,e,f bSlice aSlice[0:5] // 可在 cap 范围内扩展bSlice 包含 d,e,f,g,h bSlice aSlice[:] // bSlice 与 aSlice 元素相同d,e,f,g引用语义slice是引用类型修改任一 slice 指向的元素所有共享同一底层数组的 slice 都会同步变化。下图展示了Slice_a与Slice_b对同一底层数组Array_ori不同区间的引用关系Figure 2.3 slice 与 array 的关系slice 的底层结构由三部分组成指向 slice 起始位置的指针slice 的长度len容量cap即从起始位置到底层数组末尾的长度。Array_a : [10]byte{a, b, c, d, e, f, g, h, i, j} Slice_a : Array_a[2:5]Slice_a指向c,d,e三个元素len 3、cap 8Figure 2.4 slice 的信息结构slice 的内置函数函数作用len获取 slice 当前长度cap获取 slice 最大容量append追加一个或多个元素并返回新 slicecopy将元素从一个 slice 复制到另一个返回复制的元素个数注意append的陷阱当(cap-len) 0即容量耗尽时append会分配一个全新的底层数组并返回指向它的新 slice此时旧数组上的其他 slice 不受影响反之如果容量足够append会直接修改原底层数组共享该数组的其他 slice 也会随之改变。3. map字典map行为类似 Python 的字典以map[keyType]valueType声明。与 slice 只能使用int下标不同map 的键可以是任何可用/!比较的类型如int、stringvar numbers map[string]int numbers : make(map[string]int) // 另一种声明方式必须用 make 初始化 numbers[one] 1 // 按键赋值 numbers[ten] 10 numbers[three] 3 fmt.Println(The third number is: , numbers[three]) // 输出 3使用要点map无序每次打印顺序都可能不同只能按键取值不能按下标取map没有固定长度是引用类型与 slice 相同len同样适用于map返回键的数量修改值很方便numbers[one] 11即可更新。键存在性检查与删除map取值有两个返回值第二个值ok标识键是否存在delete用于删除元素rating : map[string]float32{C: 5, Go: 4.5, Python: 4.5, C: 2} csharpRating, ok : rating[C#] if ok { fmt.Println(C# is in the map and its rating is , csharpRating) } else { fmt.Println(We have no rating associated with C# in the map) } delete(rating, C) // 删除键 C引用语义两个 map 指向同一份底层数据时任一方的修改都会互相影响m : make(map[string]string) m[Hello] Bonjour m1 : m m1[Hello] Salut // 此时 m[Hello] 也变为 Salut配套示例 show_map 完整覆盖了上述make初始化、键存在性检查、delete与引用共享的全部场景。七、make 与 new两种内存分配make专门用于map、slice、channel这三类**引用类型内建模型**的内存分配new用于普通类型的内存分配。new(T)为类型T的零值分配内存并返回其内存地址即类型*T——返回的是一个指向零值的指针。make(T, args)同样用于分配内存但只适用于slice、map、channel返回的是初始化后的类型T本身非指针。原因是这三类数据必须先初始化底层数据结构才能使用例如slice内部包含指向底层数组的指针、长度和容量若不做初始化其值为nil因此make会初始化底层数据并赋予合适的初始值。// new返回零值指针 p : new(Point) // *Point指向零值结构体 // make返回已初始化的引用类型 s : make([]int, 5, 10) // 长度 5、容量 10 的切片 m : make(map[string]int) ch : make(chan int)下图对比了两者的差异new分配的是单个对象的零值内存结构体、数组等make则会申请一段底层数组内存并初始化出指定长度/容量的引用类型本身Figure 2.5 make 与 new 的内存分配零值zero value不等于空值它通常是变量未显式初始化时的默认值类型零值int0int80int320int640uint0x0rune0真实类型是 int32byte0x0真实类型是 uint8float3204 字节float6408 字节boolfalsestring配套示例 set_default_values 以常量形式逐项印证了这张零值表。八、配套示例的运行方式本章节的全部代码示例集中在仓库 pt-br/code/src/apps/ch.2.2/main.go覆盖多变量赋值、布尔、数值类型、字符串与拼接、error、iota、数组、slice、map 共 11 个演示函数。将pt-br/code设置为工作目录参见 pt-br/code/readme.md 中关于 GOPATH 的说明后即可运行go run src/apps/ch.2.2/main.go练习程序 what_is_wrong_with_this/main.go 则故意保留了一个「声明变量但未使用」的编译错误供读者亲手验证 Go 编译器的严格性go run src/apps/ch.2.2/what_is_wrong_with_this/main.go九、小结本章覆盖了编写任何 Go 程序都绕不开的基础语法从var/:变量声明与空白标识符到编译期常量与iota枚举从bool、整型、浮点、复数、字符串、error六大基础类型到array、slice、map三种容器的声明、操作与引用语义再到make/new的内存分配差异与零值表。掌握这些要素后可以继续学习下一节「控制语句与函数」或者回看上一节「Hello, Go」与第 2 章总览逐步构建完整的 Go Web 开发知识体系完整目录见 pt-br/preface.md。赞分享文档教程【免费下载链接】build-web-application-with-golangA golang ebook intro how to build a web with golang项目地址https://gitcode.com/gh_mirrors/bu/build-web-application-with-golang点击查看免费下载相关推荐Go 语言基础变量、常量、内置类型与 array/slice/map 详解《Build Web Application with Golang》2.2 节Go 语言基础变量、常量、内置类型与 array/slice/map 详解《Build Web Application with Golang》2.2 节文档教程Go 语言基础深入变量、常量、内置类型与 array/slice/mapBuild Web Application with Golang 2.2 节实战详解Go 语言基础深入变量、常量、内置类型与 array/slice/mapBuild Web Application with Golang 2.2 节实战详文档教程Go 语言基础实战变量、常量、基本类型与数组、切片、Map——来自《Build Web Application with Golang》第 2.2 章Go 语言基础实战变量、常量、基本类型与数组、切片、Map——来自《Build Web Application with Golang》第 2.2 章 本篇技文档教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门