Go编程结构
// 当前程序的包名
package main
// 导入其他的包
// import "fmt"
// 导入其他的包,并设置别名
import std "fmt"
// 定义常量
const PI = 3.14
// 全局变量的声明与赋值
var name = "gopher"
// 一般类型声明:为类型设置别名
type newType int
// 结构的声明
type gopher string{}
// 接口的声明
type golang interface {}
// 由 main 函数作为程序入口点启动
func main() {
std.Println("Hello world!")
}
Tips:
.
作为包的别名,不过,尽量不要使用,避免降低代码可读性;类型和变量都能批量声明,变量组只能用于全局变量声明,不能在函数体重使用。
// 变量的声明
var a int
// 变量的赋值
a = 123
// 声明的同时赋值
var a int = 123
// 多个变量的声明
var a, b, c int
// 多个变量的复制
a, b, c, = 111, 222,333
// 多个变量声明的同时赋值
var d, e, f int = 444, 555, 666
// 省略变量类型,由系统推断
var i, j, k = 777, 888, 999
// 多个变量声明与赋值的最简写法
m, n, o := 1, 2, 3
var (
// 使用常规方式
aaa = "hello"
// 使用并行方式
sss, bbb = 1, 2
)
// 函数体内只能使用并行方式声明多个变量
func main() {
var x, y, z int = 444, 555, 666
// var aa, _, bb, cc = 11, 22, 33, 44 // _ 为空白符号,即忽略对应位置的值
}
// 只能在相互兼容的类型之间转换
func main() {
var a float32 = 1.1
b := int(a)
fmt.Println(a)
fmt.Println(b)
}
const (
B float64 = 1 << (iota * 10)
KB
MB
GB
TB
PB
)
func main() {
fmt.Println(B)
fmt.Println(KB)
fmt.Println(MB)
fmt.Println(GB)
fmt.Println(TB)
fmt.Println(PB)
}
go保留了指针,但不支持指针运算以及 ->
运算符,而直接采用 .
选择符来操作指针目标对象的成员。
&
取变量地址,使用 *
通过指针间接访问目标对象nil
,而非 NULL
go语言中(递增、递减),++
与 --
是作为语句而并不是作为表达式(即,只能作为单独的语句存在)。
// 指针
func main() {
a := 1
var p *int = &a // 指针 p 指向变量 a 的地址
fmt.Println(p) // 打印指针
fmt.Println(*p) // 打印指针对应的目标对象
}
// ++ 和 --
func main() {
// a := a++ // 不允许这样
a := 1
a++
var p *int = &a
fmt.Println(p)
fmt.Println(*p)
}
func main() {
a := 10
if a, b := 1, 3; a > 0 {
fmt.Println(a)
fmt.Println(b)
}
fmt.Println(a)
}
go语言中只有 for 循环。
// 普通 for 循环
func main() {
a := 1
for {
a++
if a > 3 {
break
}
fmt.Println(a)
}
fmt.Println("Over!")
}
// for 循环本身带条件
func main() {
a := 1
for a <= 3 {
a++
fmt.Println(a)
}
fmt.Println("Over!")
}
// 经典用法:计数器(i)
func main() {
a := 1
for i := 0; i <= 3; i++ {
a++
fmt.Println(a)
}
fmt.Println("Over!")
}
默认满足某个 case 后自动 break,如果想继续执行后面的 case,需要使用 fallthrough 语句。
类似 python 中的 if...elif...else...
switch 中有变量 swithc a {}
,则case中默认使用该变量创建表达式( case 0:
);如果switch中没有变量 switch {}
,则需要在case中使用变量创建表达式(case a==0:
)。
// 普通用法
func main() {
a := 1
switch a {
case 0:
fmt.Println("a=0")
case 1:
fmt.Println("a=1")
default:
fmt.Println("None")
}
}
// fallthrough
func main() {
a := 1
switch {
case a <= 0:
fmt.Println("a=0")
case a >= 1:
fmt.Println("a=1")
fallthrough
default:
fmt.Println("None")
}
}
// 带一个初始化语句
func main() {
switch a := 1; {
case a >= 10:
fmt.Println("a=0")
case a >= 1:
fmt.Println("a=1")
fallthrough
default:
fmt.Println("None")
}
}
跳转语句都可以配合标签使用。标签区分大小写。
goto用于调整执行位置;break和continue配合标签可以跳出多层循环。
// 跳出 LABEL1 循环
func main() {
LABEL1:
for {
for i := 0; i < 10; i++ {
if i > 3 {
break LABEL1
}
fmt.Println(i)
}
}
fmt.Println("Over")
}
// continue 本身可以是一个无限循环;但是 continue 对应的标签必须有限循环,不然会造成死循环;
func main() {
LABEL1:
for i := 0; i < 10; i++ { // 有限循环,即 LABEL1 对应有限循环
fmt.Println(i)
for { // 无限循环
continue LABEL1 // 有限循环标签
fmt.Println("???")
}
}
fmt.Println("Over")
}
func main() {
a := 1
for {
switch {
case a <= 5:
fmt.Println("a=", a)
case a > 5 && a <= 10:
fmt.Println("a=", a)
fallthrough
default:
fmt.Println("None")
}
a++
if a > 10 {
fmt.Println("Over...", "a=", a)
break
}
}
}
Tips: goto 结合标签使用的时候,标签放在 goto 语句的后面,避免造成死循环。
var <varName> [n]<type>
,n>=0 。
==
或 !=
比较,但是不可以使用 <
或 >
[...]
来让系统自动计算数组长度(注意:该方法只能用于顶级数组)
// 普通
func main() {
var a [2]int
fmt.Println(a)
a[0] = 5
a[1] = 6
fmt.Println(a)
}
// 最简方式
func main() {
a := [2]int{7, 8}
fmt.Println(a)
}
// 为数组的某一位赋值
func main() {
a := [2]int{7, 8}
fmt.Println(a)
b := [10]int{5: 5}
fmt.Println(b)
}
// 数组间的比较
func main() {
a := [2]int{1, 2}
b := [2]int{1, 2}
fmt.Println("a=", a)
fmt.Println("b=", b)
fmt.Println(a == b)
fmt.Println(a != b)
}
// 使用new方法创建数组
func main() {
a := [10]int{}
a[1] = 2
fmt.Println(a)
p := new([10]int)
p[1] = 2 // 通过指针对数组中单个元素进行操作
fmt.Println(p)
}
func main() {
a := [10]int{9, 1}
var p *[10]int = &a // 指向数组的指针
fmt.Println(p)
x, y := 1, 2
b := [...]*int{&x, &y} // 指针数组
fmt.Println(b)
}
在go语言中数组是值类型。
func main() {
a := [2][3]int{ // 含义是:数组a包含2个元素,每个元素为长度为3的int型数组
{1, 1, 1},
{2, 2, 2}}
fmt.Println(a)
}
func main() {
a := [...]int{5, 3, 6, 7, 9}
fmt.Println(a)
num := len(a)
for i := 0; i < num; i++ {
for j := i + 1; j < num; j++ {
if a[i] < a[j] {
temp := a[i]
a[i] = a[j]
a[j] = temp
}
}
}
fmt.Println(a)
}
make()
创建
make([]Type, len, cap)
//
func main() {
var s1 []int // 声明slice,结果为一个空的slice
fmt.Println(s1)
a := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9}
s2 := a[0:5] // 从已有数组中取值
fmt.Println(s2)
s3 := a[5:]
fmt.Println(s3)
}
// make中的 len 和 cap
func main() {
s1 := make([]int, 5, 10)
fmt.Println(s1)
fmt.Println(len(s1), cap(s1))
}
slice与底层数组的对应关系:
func main() {
a := []byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n'}
fmt.Println("================= aa ===================")
fmt.Println("a=", string(a))
fmt.Println("len(a)=", len(a), "cap(a)=", cap(a))
fmt.Println("================= sa ===================")
sa := a[2:5]
fmt.Println("sa=", string(sa))
fmt.Println("len(sa)=", len(sa), "cap(sa)=", cap(sa))
fmt.Println("================= sb ===================")
sb := sa[3:5]
fmt.Println("sb=", string(sb))
fmt.Println("len(sb)=", len(sb), "cap(sb)=", cap(sb))
}
func main() {
s1 := make([]int, 3, 6)
fmt.Printf("%p %v\n", s1, s1)
s1 = append(s1, 1, 2, 3) // 未超出原始slice容量时,内存地址不变
fmt.Printf("%p %v\n", s1, s1)
s1 = append(s1, 1, 2, 3) // 超出原始slice容量时,会重新分配内存地址
fmt.Printf("%p %v\n", s1, s1)
}
func main() {
a := []int{1, 2, 3, 4, 5}
s1 := a[2:5]
s2 := a[1:3]
fmt.Println(s1, s2) // 此时 s1,s2 共用 a[2]
s1[0] = 9 // 多个切片(slice)指向相同底层数组,其中一个值的改变会影响全部
fmt.Println(s1, s2)
fmt.Println(a)
s2 = append(s2, 1, 2, 3, 4, 5, 6, 7)
s1[0] = 10
fmt.Println(s1, s2) // 超出原始切片(s1)容量的部分不受影响,因为超出原始切片容量后,已分配新的内存地址
fmt.Println(a)
}
// copy时,以短的slice中元素长度为准进行copy
func main() {
s1 := []int{1, 2, 3, 4, 5, 6}
s2 := []int{7, 8, 9}
fmt.Println(s1, s2)
copy(s1, s2)
fmt.Println("new:: ", s1, s2)
s3 := []int{1, 2, 3, 4, 5, 6}
s4 := []int{7, 8, 9}
fmt.Println(s3, s4)
copy(s4, s3)
fmt.Println("new:: ", s3, s4)
}
// 拷贝到目标切片的指定位置
func main() {
s1 := []int{1, 2, 3, 4, 5, 6}
s2 := []int{7, 8, 9}
fmt.Println(s1, s2)
copy(s1[2:5], s2)
fmt.Println("new:: ", s1, s2)
}