我把go的接口和并发放在了进阶篇,因为这是go的核心,不能浅显地去理解它地语法,需要深入。而基础篇注重一些语法地表达
go
1. GoLang的应用方向及基本特性
应用方向:
- 区块链开发工程师
- GO服务器端、游戏软件工程师
- golang分布式,云计算
1.1 learning way:now how to know why? 整体框架到细节
go语言设计者设计的初衷:
1.当前硬件发展很迅速,软件的编程没有跟上发展速度,不能发挥多核多CPU的优势
2.软件系统复杂度越来越高,维护成本很高不够,需要简洁高效的语言(现有的编程语言风格不统一,计算能力不够,处理大并发不够好)
3.企业运行和维护c、c++的项目虽然运行速度快,但是编译速度慢,而且还有内存泄漏的问题存在
1.2 go语言特点
既有静态编译语言的安全和性能,有达到动态语言开发维护的效率
1.弱化的指针
2.引入包的概念,每一个文件都要归属于一个包而不能单独存在
3.类似于java 的垃圾回收机制
4.天然并发,从语言层面支持并发,实现简单,goroutine,轻量级线程,可实现大并发处理,高效利用多核,基于CPS并发模型实现
5.吸收了管道通信机制
6.函数可以返回多个值
func getSumAndSub(n1 int,n2 int)(int,int){
sum:=n1+n2
sub:=n1-n2
return sum,sub
}
复制代码
7.类似java语言的集合,有切片,延时执行defer等
8.与java不同的是,导入的包如果不使用编译不通过,在多人开发的时候,冗余的代码包和变量就会不参与编译,提高代码性能
1.3 go的执行流程
注意编译的时候,编译器会把执行程序的一些依赖文件都添加到可执行文件中,所以可执行文件会变大
package main
import "fmt"
func main(){
fmt.Println("tomcat")
}
复制代码
2. Golang的类型
2.1注释
- 行注释://注释内容
- 块注释:/*跨行注释内容*/
复制代码
2.2go语言的代码规范要求
1.尽量使用行注释
2.要有合适的缩进和空白,函数体的缩进,以及运算符左右两边都要添加空格
3.可以使用goformat指令修复格式 :gofmt -w 文件名(-w写入文件的操作)
4.花括号的位置不能调换
5.如果单行长度很长可以用,隔开
6.golang每行末会自行添加;字符串拼接和python等同+
2.3go语言的变量
变量的命名建议:
- 驼峰命名
- 局部变量优先使用短名
- 专有名词大写
首先理解go语言中的变量和内存中的关系
如果一个变量a=b就是把b的值赋值给a,而不是引用或者指针
但是使用&a=&b的时候,就是把b的地址赋给a的地址,这时候修改a的值的时候b的值就改变了
2.3.1变量类型
标准库math定义了个数字类型的取值范围
四大基本类型
序号 | 类型和描述 |
---|---|
1 | 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
2 | 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
3 | 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
4 | 派生类型: 包括:(a) 指针类型(Pointer)(b) 数组类型(c) 结构化类型(struct)(d) Channel 类型(e) 函数类型(f) 切片类型(g) 接口类型(interface)(h) Map 类型 |
数字类型的分支
序号 | 类型和描述 |
---|---|
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32 位实数和虚数 |
4 | complex128 64 位实数和虚数 |
2.3.2变量赋值方式
变量代表一个存储区域,该区域有自己的名称(变量名)和类型(数据类型)
golang中三种变量的声明方式:
//第一种,指定变量类型,声明后不赋值,使用默认值
var myVal1 string
//第二种,根据数值自行判断变量类型(类型推导)
var myVal2 = happy
//第三种,省略var,注意:=左边的变量不应该是已经声明过的,否则会产生编译错误,:=又被称为初始化声明
myVal3 := 1
//第四种,多变量声明:会先计算等号右边的表达式,然后在一起赋值给左边的变量
// 类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断
vname1, vname2, vname3 := v1, v2, v3
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
//不同类型的初始化声明
a, b, c := 5, 7, "abc"
复制代码
//初始化声明也可以退化成赋值声明,在已经赋值过的变量的基础上对它在赋值,但是有一个限制,变量的作用域必须相同
package main
import (
"fmt"
)
func main(){
x:=100
fmt.println(x)
{
x:=200
fmt.println(x)
}
}
//这里的两个变量的地址不一样,因为他们的作用域不同,所以变成了新变量定义
复制代码
变量类型 | 默认值 |
---|---|
int | 0 |
bool | false |
string | “” |
*int | nil |
[]int | nil |
map[string] int | nil |
chan int | nil |
func(string) int | nil |
error(error是接口) | nil |
2.3.3变量使用的注意事项
局部变量定义不使用,编译会直接不通过,而全局变量不会
由于上面这条定义,在一些函数返回的时候,有些返回值不会使用到,所以我不能用一个有名字的变量去接受它,后续会有很多麻烦。
package main
import "fmt"
func main() {
_,numb,strs := numbers() //只获取函数返回值的后两个
fmt.Println(numb,strs)
}
//一个可以返回多个值的函数
func numbers()(int,int,string){
a , b , c := 1 , 2 , "str"
return a,b,c
}
复制代码
2.3.4空标识符_
—空标识符通常作为忽略占位符只用,可用作表达式左值,用于代表无法读取的内容或者不需要知道的内容,避免后续变量命名但却没有使用的语法错误。用来临死规避编译器对未使用变量和导入包的错误检查
import "strconv"
import "fmt"
func main(){
x,_:=strconv.Atoi("12")
fmt.println(x)
}
复制代码
2.3.5变量的类型转换
go语言中总是要求我们使用显式 的类型转化,
标准库strconv可以在不同进制(字符串)见转换
import "strconv"
func main(){
a,_:=strconv.ParseInt("110110",2,32)
println("0b"+strconv.FormatInt(a,2))
}
复制代码
有两个别名,别名类型无需转换,可以直接赋值
byte alias for uint8
rune alias for int32
但是并不是底层结构相同的就属于别名,例如64位平台上的int 和int64结构完全一致,也分属不同的类型,需显式转化
常见语法歧义:如果转换的是指针,单向通道或者没有返回值的函数(即使有返回值也推荐用括号),那么必须使用括号,以避免造成的语法分解错误
func main(){ x:=100 p:=*int(&x) //cannot convert &x(type *int) to type int //正确的做法时 p:=(*int)(&x)//让编译器解析为指针类型 类似于(func ())(x) println(p)}
复制代码
2.3.6自定义类型
使用关键字type定义用户自定义类型,可以是结构体类型,函数类型等
package main
import "fmt"
type (
user struct {
name string
age uint8
}
event func(string) bool
)
func main() {
u := user{"tom", 12}//注意这里是大括号
fmt.Println(u)
var f event = func(s string) bool {
fmt.Println(s)
return s != ""
}
f("abc")
}
复制代码
注意即便制定了基础类型,也只表明他们有相同的底层结构,二者之间没有任何关系,不能用作别名,不能隐式转化,不能直接用于比较表达式
func main(){
type data int
var d data=10
var x int=d
println(x) //cannot use d as type int in assignment
println(d==x) //invalid operation:d==x(mismatched types data and int)
}
复制代码
2.4go语言的常量与枚举(enum)
常量形式:
const identifier [type] =value
- 显式类型定义:
const b string = "abc"
- 隐式类型定义:
const b = "abc"
const也能用作枚举,常量能够使用一些内置函数比如len,cap,unsafe.sizeof等
package mainimport "unsafe"const ( a = "abc" b = len(a) c = unsafe.Sizeof(a))func main(){ println(a, b, c)}
复制代码
iota是一个特殊常量,用在枚举的时候可以自增的情况
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
//iota只能在同一个常量组内递增
复制代码
在定义常量组时,如果不提供初始值,则表示将使用上行的表达式。
package main
import "fmt"
const (
a = 1
b
c
d
)
func main() {
fmt.Println(a)
// b、c、d没有初始化,使用上一行(即a)的值
fmt.Println(b) // 输出1
fmt.Println(c) // 输出1
fmt.Println(d) // 输出1
}
复制代码
常量与变量的不同之处?
除了常量只读以外,还有什么地方和变量不同呢?不同于变量在运行期间分配存储内存,常量在go在预处理阶段直接展开,作为指令数据使用,所以找不到它的地址。常量即使不适用也不会报错
var x=0x100
const y=0x200
func main(){
println(&x,x)
println(&y,y)
}
//error:can't not take place address of y
复制代码
2.5 复合类型的初始化
对符合类型的变量的初始化时,有一些语法限制
- 初始化表达式必须含类型标签
- 多个成员以逗号分隔
- 允许多行,但是每行必须以都好或者花括号结束
type data struct{ x int s string}//正确的表达var a data=data{ 1, "abc",}//错误的表达方式var a data ={ 1, "abc",}
复制代码
3.golang的表达式和流程控制
3.1go语言的运算符
3.1.1算术运算符
下表列出了所有Go语言的算术运算符。假定 A 值为 10,B 值为 20。
运算符 | 描述 | 实例 |
---|---|---|
+ | 相加 | A + B 输出结果 30 |
– | 相减 | A – B 输出结果 -10 |
* | 相乘 | A * B 输出结果 200 |
/ | 相除 | B / A 输出结果 2 |
% | 求余 | B % A 输出结果 0 |
++ | 自增 | A++ 输出结果 11 |
— | 自减 | A– 输出结果 9 |
运算符 | 描述 | 实例 |
---|---|---|
== | 检查两个值是否相等,如果相等返回 True 否则返回 False。 | (A == B) 为 False |
!= | 检查两个值是否不相等,如果不相等返回 True 否则返回 False。 | (A != B) 为 True |
> | 检查左边值是否大于右边值,如果是返回 True 否则返回 False。 | (A > B) 为 False |
< | 检查左边值是否小于右边值,如果是返回 True 否则返回 False。 | (A < B) 为 True |
>= | 检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。 | (A >= B) 为 False |
<= | 检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。 | (A <= B) 为 True |
3.1.2逻辑运算符
下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 | (A && B) 为 False |
|| | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 | (A || B) 为 True |
! | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 | !(A && B) 为 True |
3.1.3位运算符
左移和右移都需要将该变量转化成无符号形式的
运算符 | 描述 | 实例 |
---|---|---|
& | 按位与运算符”&”是双目运算符。 其功能是参与运算的两数各对应的二进位相与。 | (A & B) 结果为 12, 二进制为 0000 1100 |
| | 按位或运算符”|”是双目运算符。 其功能是参与运算的两数各对应的二进位相或 | (A | B) 结果为 61, 二进制为 0011 1101 |
^ | 按位异或运算符”^”是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。 | (A ^ B) 结果为 49, 二进制为 0011 0001 |
<< | 左移运算符”<<“是双目运算符。左移n位就是乘以2的n次方。 其功能把”<<“左边的运算数的各二进位全部左移若干位,由”<<“右边的数指定移动的位数,高位丢弃,低位补0。 | A << 2 结果为 240 ,二进制为 1111 0000 |
>> | 右移运算符”>>”是双目运算符。右移n位就是除以2的n次方。 其功能是把”>>”左边的运算数的各二进位全部右移若干位,”>>”右边的数指定移动的位数。 | A >> 2 结果为 15 ,二进制为 0000 1111 |
和python一样可以组合运算符和赋值符号
//一个特殊的bit clear,将左右操作数对用二进制位都是1的重置为0,以达到一次清楚多个标记位的目的
const{
read byte = 1 << iota
write
exec
freeze
}
func main(){
a := read | write | freeze
b := read | freeze | exec
c := a &^ b
fmt.Printf("%04b &^ %04b =%04b\n", a, b, c)
}
//1011 &^ 1101 =0010输出???
复制代码
3.1.4其他
运算符 | 描述 | 实例 |
---|---|---|
& | 返回变量存储地址 | &a; 将给出变量的实际地址。 |
* | 指针变量。 | *a; 是一个指针变量 |
3.1.5运算符的优先级
优先级 | 运算符 |
---|---|
5 | * / % << >> & &^ |
4 | + – | ^ |
3 | == != < <= > >= |
2 | && |
1 | || |
别忘记括号
3.1.6自增不再是表达式,为独立语句
func main(){ a:=1 a++ //现在++不能后置,只能前置 if (++a)>1 { } p:=&a *p++//相当于(*p)++ println(a)}
复制代码
表达式通常是求值代码,可作为右值或者参数使用,而语句完成一个行为,比如if,for代码块,表达式可以当作语句用,而语句不能当作表达式
3.1.7指针
不要搞混指针和内存地址,指针是一个保存地址的整型变量,需要内存空间。
- 指针不能用于运算+-,并且不能类型转换
- &获取地址,*引用对象
- **T二级指针,如包含包名则写成 *package.T
unsafe.Pointer可以将指针转化成unintptr后进行加减法运算,但是可能会造成非法访问
Pointer类似于C语言的*void万能指针,用来转换指针类型,它能安全持有对象或对象成员,但uintptr不行,它仅仅是一种特殊整形对象,并没有引用目标对象,无法阻止垃圾回收器回收对象内存
指针没有专门指向成员的->运算符,和python一样用.
3.2go语言的条件语句
go语言没有三目运算符,不能用 ? : 这样的形式
语句 | 描述 |
---|---|
if 语句 | if 语句 由一个布尔表达式后紧跟一个或多个语句组成。 |
if…else 语句 | if 语句 后可以使用可选的 else 语句, else 语句中的表达式在布尔表达式为 false 时执行。 |
if 嵌套语句 | 你可以在 if 或 else if 语句中嵌入一个或多个 if 或 else if 语句。 |
switch 语句 | switch 语句用于基于不同条件执行不同动作。 |
select 语句 | select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。 |
前几种都是一样的注意switch没有括号,case和select中的不一样,会逐个测试如果不写break的话
switch {
case grade == "A" :
fmt.Printf("优秀!\n" )
case grade == "B", grade == "C" :
fmt.Printf("良好\n" )
case grade == "D" :
fmt.Printf("及格\n" )
case grade == "F":
fmt.Printf("不及格\n" )
default:
fmt.Printf("差\n" );
}
特别注意最后一种select语句
//select 是 Go 中的一个控制结构,类似于用于通信的 switch 语句。每个 case 必须是一个通信操作,要么是发送要么是接收。
//select 随机执行一个可运行的 case。如果没有 case 可运行,它将阻塞,直到有 case 可运行。一个默认的子句应该总是可运行的。
select {
case communication clause :
statement(s);
case communication clause :
statement(s);
/* 你可以定义任意数量的 case */
default : /* 可选 */
statement(s);
}
复制代码
//fallthrough语句switch x :=5;x{ default: println(x) case 5: x+=10 println(x) fallthrough//继续执行下一case,但是不再匹配条件表达式 case 6: x+=20 println(x) //fallthrough //如果在此继续fallthrough,不会执行default,完全按源码顺序}
复制代码
以下描述了 select 语句的语法:
-每个 case 都必须是一个通信
-所有 channel 表达式都会被求值
-所有被发送的表达式都会被求值
-如果任意某个通信可以进行,它就执行,其他被忽略。
-如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。
-如果有 default 子句,则执行该语句。
-如果没有 default 子句,select 将阻塞,直到某个通信可以运行;Go 不会重新对 channel 或值进行求值。
3.3go语言的循环语句
func main() {
/* 定义局部变量 */
var i, j int
for i=2; i < 100; i++ {
for j=2; j <= (i/j); j++ {
if(i%j==0) {
break; // 如果发现因子,则不是素数
}
}
if(j > (i/j)) {
fmt.Printf("%d 是素数\n", i);
}
}
}
复制代码
不过就是java的去掉了括号,continue可以添加标记,break也是
fmt.Println("---- continue label ----")
re:
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
continue re
}
}
----------------------------------------------------
re:
for i := 1; i <= 3; i++ {
fmt.Printf("i: %d\n", i)
for i2 := 11; i2 <= 13; i2++ {
fmt.Printf("i2: %d\n", i2)
break re
}
}
复制代码
range迭代
range类似于一个迭代器,可以获得索引和键值数据,注意range会复制目标数据,这是因为相关的数据类型中,字符串和切片基本结构都是很小的结构体,而字典,通道本身是指针封装,复制成本都很小,无需专门优化
package main
import "fmt"
func main() {
data := [3]string{"a", "b", "c"}
for i, s := range data {
fmt.Println(i, s)
}
data1 :=[]int{10,20,30}
for i,x :=range data1[:]{//这里拿到的x是复制出来的值
if i==0 {
data[0]+=100
data[1]+=200
data[2]+=300
}
fmt.Printf("x:%d,data:%d\n",x,data[i])
}
}
复制代码
没有相关的接口可以实现自定义类型的迭代,除非基础类型是可迭代类型,例如字符串,数组,数组指针,切片,字典,通道类型
goto语句
goto不能跳转到其他函数或者代码块内
4.go语言的函数
函数属于第一对象(指在运行期创建,可以用作函数参数或这返回值,可存入变量的实体,最常见的就是匿名函数),具备相同签名(参数及返回值列表) 的视作统一类型
- 无需前置声明
- 不支持命名嵌套定义
- 不支持同名函数重载!!!
- 不支持默认参数!!!
- 不支持不定长变参!!!!
- 支持匿名函数和闭包
- 不支持有默认值的可选参数,不支持命名实参
func test(x,y int,s string,_ bool) *int{
return nil
}
func main(){
test(1,2,"abc")//错误,即便是_命名的参数也不能忽略
}
复制代码
func function_name( [parameter list] ) [return_types] { 函数体}
复制代码
函数定义解析:
- func:函数由 func 开始声明
- function_name:函数名称,函数名和参数列表一起构成了函数签名。
- parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
- 函数体:函数定义的代码集合。
函数的参数调用有两种方式,一种是值传递,值传递不会改变原来函数的参数,一种是引用传递,下面是引用传递的例子
package main
import "fmt"
func main() {
/* 定义局部变量 */
var a int = 100
var b int= 200
fmt.Printf("交换前,a 的值 : %d\n", a )
fmt.Printf("交换前,b 的值 : %d\n", b )
/* 调用 swap() 函数
* &a 指向 a 指针,a 变量的地址
* &b 指向 b 指针,b 变量的地址
*/
swap(&a, &b)
fmt.Printf("交换后,a 的值 : %d\n", a )
fmt.Printf("交换后,b 的值 : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* 保存 x 地址上的值 */
*x = *y /* 将 y 值赋给 x */
*y = temp /* 将 temp 值赋给 y */
}
复制代码
2.8.1函数作为参数传入
package main
import (
"fmt"
"math"
)
func main(){
/* 声明函数变量 */
getSquareRoot := func(x float64) float64 {
return math.Sqrt(x)
}
/* 使用函数 */
fmt.Println(getSquareRoot(9))
}
复制代码
2.8.2函数的递归
//用函数递归写出斐波那契数列
func fibornachi(i int) int {
if i < 2 {
return i
}
return fibornachi(i-1) + fibornachi(i-2)
}
fmt.Println(fibornachi(8))
复制代码
5.go数据结构
5.1字符串
字符串是不可变字节序列,本身是一个复合结构,和python中的字符串一样,可以切片和索引,因为底层是数组
//头部指针指向字节数组,但没有NULL结尾
type stringStruct struct{
str unsafe.Pointer
len int
}
//使用`定义不做转义处理的字符串可以跨行,类似于python的r
s:=`line1\r\n,
line2`
复制代码
5.1.1字符串的两种遍历方式
\\byte
s1:="山东大学"
for i := 0; i < len(s1); i++ {
fmt.Printf("%d:[%c]\n", i, s1[i])
}
/*输出结果
0:[å]
1:[±]
2:[±]
3:[ä]
4:[¸]
5:[]
6:[å]
7:[¤]
8:[§]
9:[å]
10:[]
11:[¦]
*/
//rune
for i,item:=range s1 {
fmt.Printf("%d:[%c]\n", i, s1[i])
}
复制代码
要修改字符串,必须将其转化成可变类型[]rune,[]byte,待完成后再转化回来,需要重新分配内存,复制数据
5.2数组
数组声明
var variable_name [SIZE] variable_type
var array1 [5] int //这时候默认是 00000
var array2:= [5] int {1,2,3,4,5}//
//错误
array1={1,2,3,4,5}
复制代码
-数组的初始化与声明是同时的,不能先声明然后初始化
-数组是固定长度的,并且如果[]中没有数字就不是数组,而是切片slide
package main
import "fmt"
func main() {
var n [10]int /* n 是一个长度为 10 的数组 */
var i,j int
/* 为数组 n 初始化元素 */
for i = 0; i < 10; i++ {
n[i] = i + 100 /* 设置元素为 i + 100 */
}
/* 输出每个数组元素的值 */
for j = 0; j < 10; j++ {
fmt.Printf("Element[%d] = %d\n", j, n[j] )
}
}//数组初始化的方法
复制代码
5.3切片
5.3.1切片的声明和初始化
切片其实是没有定长的数组,切片和数组十分相似,如果试图使用{}来进行赋值就只能在初始化的时候才能使用
var identifier []type
复制代码
切片的初始化有两种形式
make也有两种方式
s:=[]int{1,2,3}
s:=make([] int,3)//这里即是在声明同时也初始化了,只不过每个元素都是默认值
//length是初始化的时候的长度,capacity是数组的最大容量
make([]T,length)
make([]T, length, capacity)
//len()可以获取长度,cap()可以获取容量
复制代码
那么切片如果只是声明了没有初始化是怎么样的呢
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
if(numbers == nil){
fmt.Printf("切片是空的")
}
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
//切片声明了就默认是是nil,长度和容量默认都是0
//切片的截取同python
复制代码
5.3.2切片的copy和append
package main
import "fmt"
func main() {
var numbers []int
printSlice(numbers)
/* 允许追加空切片 */
numbers = append(numbers, 0)
printSlice(numbers)
/* 向切片添加一个元素 */
numbers = append(numbers, 1)
printSlice(numbers)
/* 同时添加多个元素 */
numbers = append(numbers, 2,3,4)
printSlice(numbers)
/* 创建切片 numbers1 是之前切片的两倍容量*/
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
/* 拷贝 numbers 的内容到 numbers1 */
copy(numbers1,numbers)
printSlice(numbers1)
}
func printSlice(x []int){
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)
}
/*
len=0 cap=0 slice=[]
len=1 cap=1 slice=[0]
len=2 cap=2 slice=[0 1]
len=5 cap=6 slice=[0 1 2 3 4]
len=5 cap=12 slice=[0 1 2 3 4]*/
复制代码
5.4map
map是无序的键值对的集合,可以迭代
var country map[string]string
country = make(map[string]string)
country["france"] = "法国"
country["english"] = "英国"
for key, item := range country {
fmt.Println(key, item)
}
for key := range country {
fmt.Println(key)
}
item, ok := country["china"]
fmt.Println(item, ok)
//delete 函数用于删除对应的键值对
delete(country,"france")
//输出
france 法国
english 英国
france
english
false
复制代码
5.5结构体
结构体的定义和声明
type struct_variable_type struct {
member definition
member definition
...
member definition
}
声明的时候需要注意一点,必须在花括号前加上结构体的名字
variable_name := structure_variable_type {value1, value2...valuen}
或
variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}
复制代码
type mystruct struct{
name string
age int
}
var myself =mystruct {age: 11,name: "uhu"}
fmt.Println(myself)
//错误的改变数据的方式
myself=mystruct{12,"hhh"}
//正确的
myself.age=12
复制代码