golang学习(基础)

我把go的接口和并发放在了进阶篇,因为这是go的核心,不能浅显地去理解它地语法,需要深入。而基础篇注重一些语法地表达
go

1. GoLang的应用方向及基本特性

应用方向:

  • 区块链开发工程师
  • GO服务器端、游戏软件工程师
  • golang分布式,云计算

1.1 learning way:now how to know why? 整体框架到细节

image-20210518130942746.png

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的执行流程

image-20210518143245230.png

image-20210518143424528.png
注意编译的时候,编译器会把执行程序的一些依赖文件都添加到可执行文件中,所以可执行文件会变大

image-20210518144027128.png

package main
import  "fmt"
func main(){
	fmt.Println("tomcat")
}
复制代码

2. Golang的类型

2.1注释

- 行注释://注释内容
- 块注释:/*跨行注释内容*/
复制代码

2.2go语言的代码规范要求

1.尽量使用行注释

2.要有合适的缩进和空白,函数体的缩进,以及运算符左右两边都要添加空格

3.可以使用goformat指令修复格式 :gofmt -w 文件名(-w写入文件的操作)

4.花括号的位置不能调换

image-20210518155112011.png
5.如果单行长度很长可以用,隔开

image-20210518155415504.png

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 嵌套语句 你可以在 ifelse if 语句中嵌入一个或多个 ifelse if 语句。
switch 语句 switch 语句用于基于不同条件执行不同动作。
select 语句 select 语句类似于 switch 语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
前几种都是一样的注意switch没有括号,caseselect中的不一样,会逐个测试如果不写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
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享