golang 第一章 go简单用法

Simple Example

package main

import (
    "fmt"
)

func main() {
    fmt.Println("hello world ")
}

简单数据类型

整形类型

类型符号范围
uint无符号8位(0~255)
uint16无符号16位(0~65535)
uint32无符号32位(0~4294967295)
uint64无符号64位(0~18446744073709551615)
int8有符号8位(-128~127)
int16有符号16位(-32768 ~32767)
int32有符号32位(-2147483648~2147483647)
int64有符号6464位(-9223372036854775808~9223372036854775807
package main

import (
    "fmt"
)

func main() {
    //二进制表示所有位都是0
    var UINT_MIN uint64 = uint64(0)
    //最大值取反就是最大值
    const UINT_MAX = ^uint64(0)
    fmt.Println(UINT_MIN)
    fmt.Println(UINT_MAX)

    //无符号最大值右移一位就是二分
    const INT_MAX = int(^uint(0) >> 1)
    const INT_MIN = ^(INT_MAX)

    fmt.Println(INT_MIN)
    fmt.Println(INT_MAX)
    //输出二进制编码
    fmt.Printf("%08b %d \n", INT_MIN, INT_MIN)
    fmt.Printf("%08b %d \n", INT_MAX, INT_MAX)
}

浮点类型

类型长度
float32math.MaxFloat32
float64math.MaxFloat64

float32进度在6位,float64进度在15位

package main
import (
    "fmt"
    "math"
)
func main() {

    const MAX_FLOAT = math.MaxFloat32
    fmt.Println(MAX_FLOAT)
}

字符串

string底层是由byte数组构成,且是不可改变的字符数组,go默认使用的是utf-8格式的unicode字符,字符代表着一个rune类型,rune类型对应的是type rune = int32 ,几乎在所有方面都等同于int32,字符串复变量赋值后就变成了byte数组,英文为一个字节,中文是三个字节。


package main

import (
    "fmt"
    "log"
    "strconv"
)

func main() {
    str := "go语言"
    //go字符串底层使用byte数组,且是不可改变
    fmt.Println(len(str))
    fmt.Println(len([]rune(str)))
    //使用string 描述字符串,默认编码为UTF-8
    for _, val := range str {
        fmt.Print(val, " ")
    }
    //将string转为字节,一个英文字符一个字节,中文3个字节
    fmt.Println()
    for _, val := range []byte(str) {
        fmt.Print(val, " ")
    }
    fmt.Println()

    //string 转 int
    ii, err := strconv.Atoi("1")
    if err != nil {
        log.Fatal("strconv atoi panic", err)
    }
    fmt.Println(ii)

    i_64, err := strconv.ParseInt("111", 10, 64)
    fmt.Println(i_64)

    //string 转 float
    f_32, err := strconv.ParseFloat("1.2", 32)
    fmt.Println(f_32)

    // int 转 string
    str = strconv.Itoa(1)
    fmt.Println(str)

    /*
        float 转 string
        f 参数可以是e,E,g,G
        -1 代表输出的精度小数点后的位数,如果是<0的值,则返回最少的位数来表示该数,如果是大于0的则返回对应位数的值
        64 为float的类型,go中float分为32和64位,因此就需要传入32或者64
    */
    str = strconv.FormatFloat(1.23456, 'E', 10, 32)
    fmt.Println(str)
}

结果输出

8
4
103 111 35821 35328 
103 111 232 175 173 232 168 128 
1
111
1.2000000476837158
1
1.2345600128E+00

复数

类型长度
conplex6432位实数和虚数
conplex12864位实数和虚数

使用方式是 实部 + 虚部,函数 real© 和 imag© 可以分别获得相应的实数和虚数部分。

package main

import (
    "fmt"
)

func main() {
    var c1 = 5 + 5i
    var c2 = 10 + 10i
    fmt.Printf("%v\n", c1)
    fmt.Printf("%v\n", c2)
    fmt.Printf("%v\n", c1*c2)
    fmt.Printf("%f %f\n", real(c1), imag(c1))
    cc := complex(20, 20)
    fmt.Printf("%v\n", cc)
}

输出

package main

import (
    "fmt"
)

func main() {
    var c1 = 5 + 5i
    var c2 = 10 + 10i
    fmt.Printf("%v\n", c1)
    fmt.Printf("%v\n", c2)
    fmt.Printf("%v\n", c1*c2)
    fmt.Printf("%f %f\n", real(c1), imag(c1))
    cc := complex(20, 20)
    fmt.Printf("%v\n", cc)
}

变量

声明变量

var(
    a int
    b bool
    s string
    f float32
)
//显示声明
var a int = 10
//隐示声明
a,b,c := 1,true,"hello world","1,2"

变量声明后会进行赋值,常用类型被复制为0,0.0,“”,自定义类型或者指针为nil

全局变量定义和局部变量定义名称尽量不要重复,全局变量影响的范围不包含子模块里面

func main() {  
    x := 1
    fmt.Println(x)     // prints 1
    {
        fmt.Println(x) // prints 1
        x := 2
        fmt.Println(x) // prints 2
    }
    fmt.Println(x)     // prints 1 (不是2)
}

使用下划线可以忽略一些值,如循环的时候,忽视err,包导入



package main

import (  
    _ "fmt"
    "log"
    "time"
)
func main(){
    ii, _ := strconv.Atoi("1")
    for _, val := range "hello go"{
        fmt.Print(val, " ")
    }    
}

在golang中,使用的是引用传递,常用的引用类型有指针,slices,maps,channel

package main

import (
    "fmt"
)

func Test(arr []int) {
    arr[0] = 10
}

func TestPt(arr *[]int) {
    (*arr)[0] = 20
}
func main() {
    data := []int{1, 2, 3}
    Test(data)
    fmt.Println(data)
    TestPt(&data)
    fmt.Println(data)
}

golang中默认会将对象转为指针引用,效果如Test()和TestPt()

空指针nil

nil 标志符用于表示interface、函数、maps、slices和channels的“零值”,在一个 nil 的slice中添加元素是没问题的,但对一个map做同样的事将会生成一个运行时的panic:

package main
func main(){
    var m map[string]int
    m["test"] = 1 //panic error
}

常量和iota

package main

import (
    "fmt"
)

func main() {

    const PI = 3.14
    var ff float32 = PI
    var ff64 float64 = PI
    //const 泛化 可以赋值给所有同类型的变量
    fmt.Println(PI, " ", ff, " ", ff64)
    const (
        a = iota
        b
        c
        d = 8 * iota
        e
        f = 10
        g
        h
        i = 20
        j = iota
        k
    )
    //新的常量b声明后,iota 不再向下赋值
    fmt.Println(a, b, c, d, e, f, g, h, i, j, k)
    //遇到const重置值
    const (
        aa = iota
        bb
        cc
    )
    fmt.Println(aa, bb, cc)
}

常用容器使用

1.数组

数组是一系列数据项组成的一个固定大小的数据列表,其子项类型可以是任何数据类型,其长度是一个常量,并且必须是非负数。不同长度的数组是不同的数据结构,因此在使用数组的时候注意判断数据类型

package main

import (
    "fmt"
)

func main() {
    //定义常用的数据类型
    var arr = [3]int{1, 2, 3}
    //给数组大小为5的数组进行复制,在下表1,3分别赋值,其他位置默认为空
    var arrstr = [5]string{1: "test", 3: "simple"}
    //按顺序赋值,开始时按一定顺序赋值,指定循序的时候,后续的赋值在下标为3的地址
    var arrStr = [6]string{"data", 2: "dsdaf", "fasdfsafa"}
    //动态大小,和上面的情况相同
    var arrCount = [...]string{"1", "2", "3"}
    var arrCount1 = [...]int{10: 1}
    //定义一个固定大小的数组
    var array [10]int
    //通过new形式获取数组对象的指针,通过*获取对象数据
    var newArray = new([3]float32)
    fmt.Println(arr, arrstr, arrStr, arrCount, arrCount1, array, *newArray)
}

值引用和指针引用

对象数组和指针数组,对象数组在赋值的时候是值传递,指针数组是引用传递

package main

import (
    "fmt"
)
//参数的数据类型是[3]int
func Test(arr [3]int) {
    //值传递
    arr[2] = 100
}
//参数的数据类型是*[3]int
func Test1(arr *[3]int) {
    //指针传递
    arr[2] = 200
}
func main() {
    //值传递
    var arr = [3]int{1, 2, 3}
    var arrCopy = arr
    arrCopy[1] = 100
    fmt.Println(arr, arrCopy)
    Test(arr)
    fmt.Println(arr)
    //指针传递
    var arrPt = new([3]int)
    var arrPtCopy = arrPt
    arrPtCopy[1] = 100
    Test1(arrPt)
    fmt.Println(arrPt, arrPtCopy)
}

一个大的数组会造成函数小号大量内存(值传递),经常使用是切片

传递数组的指针
使用数组的切片

多维数组

数组只能在第一维度使用…动态获取数据大小,同时可以使用len和cap来获取数组长度

    //定义多维数组,长度为3,类型是[2]int的数组
    var multiArr [3][2]int
    //长度为2的多维数据
    var multiArr1 = [...][2]int{{1, 2}, {2, 3}}
    //长度为3的多维数组
    var multiArr2 = [...][2][2]int{{{1}, {2}}, {{3}, {4}}}
    fmt.Println(multiArr, multiArr1, multiArr2)
    fmt.Println(len(multiArr), len(multiArr1), len(multiArr2))
    fmt.Println(cap(multiArr), cap(multiArr1), cap(multiArr2))

数组遍历

    var arr = [3]int{1, 2, 3}
    for i := 0; i < len(arr); i++ {

    }
    for index, value := range arr {

    }

数组类型比较,数组在go中是对象的形式存在的,因此

var arr1 [3]int
var arr2 [3]int
arr1 == arr2 //   ----> true
var arr3 [4]int
arr1 == arr3 // ----> 编译错误 类型不同不能进行比较
arr2[1] = 1
arr1 == arr2 // ----> false

2.slice切片

切片是对数组的引用,len(slice)表示当前切片的长度,cap(slice)表示当前引用数组的长度,因此0<=len(slice)<=len(slice)恒成立。

切片是引用,因此在使用的使用不需要耗费其他内存空间,所有比数组效率更高。切片是引用,因此不能用指针去指向切片,引用类型在使用的使用会自动去判断,比如在函数参数传递的时候。定义切片的形式如下:

var identifier []type
//初始化后默认是nil ,长度是0,cap也是0
//切片引用,实际获取到的数据是下标start,end-1的数据
var slice []type = array[0:len(array)]

初始化切片

make函数原型 func make([]T, len, cap),len 是数组的长度并且也是 slice 的初始长度,cap是容量

var x = []int{2, 3, 5, 7, 11}
var xx = make([]int,2,10)
xxx := make([]int, 10, 50)

从数组或者切片中生成一个新的切片,我们可以使用下面的表达式, max-low的结果表示容量

a[low : high : max]


a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]
//结果[2 3] 2 4 ,t的容量(capacity)是5-1=4 ,长度是2

切片重组(reslice)

slice := make([]int,1,1)

切片重组的好处可以动态进行数据扩容,当len(slice) == cap(slice)的时候,如果还想继续添加数据,那么就会使得新的cap(slice)的容量*2。

    var aaa = make([]int, 1, 1)
    fmt.Println(aaa, &aaa[0], len(aaa), cap(aaa))
    aaa = append(aaa, 2)
    fmt.Println(aaa, &aaa[0], len(aaa), cap(aaa))
    //结果,容器的大小翻倍了,并且引用的底层数组变化了
    [0] 0xc042068ff8 1 1
    [0 2] 0xc042069020 2 2

当你重新划分一个slice时,新的slice将引用原有slice的数组。意思是原先数组数据太大,而引用的数据只是一小部分,这里就会导致一直会引用到底层的大数组,好比一个长度为1的切片引用了一个容器大小10000的底层数组,这里就不会释放资源。

package main

import "fmt"

func get() []byte {  
    raw := make([]byte, 10000)
    fmt.Println(len(raw), cap(raw), &raw[0]) // prints: 10000 10000 
    return raw[:3]  // 10000个字节实际只需要引用3个,其他空间浪费
}

func main() {  
    data := get()
    fmt.Println(len(data), cap(data), &data[0]) // prints: 3 10000 
}

从临时的切片中拷贝数据使用copy代替下标返回切片对象

package main

import "fmt"

func get() []byte {  
    raw := make([]byte, 10000)
    fmt.Println(len(raw), cap(raw), &raw[0]) // prints: 10000 10000 
    res := make([]byte, 3)
    copy(res, raw[:3]) // 利用copy 函数复制,raw 可被GC释放
    return res
}

func main() {  
    data := get()
    fmt.Println(len(data), cap(data), &data[0]) // prints: 3 3 
}

多个切片引用一个相同的底层数据,当切片引用的范围在0-cap(slice)之内的时候可以修改底层数组的数据,当引用的数据长度超过原先的数组长度的时候会导致扩容,这时候就会产生新数组。

package main

import "fmt"

func main() {
    s1 := []int{1, 2, 3}
    fmt.Println(len(s1), cap(s1), s1)
    s2 := s1[1:]
    fmt.Println(len(s2), cap(s2), s2)
    for i := range s2 {
        s2[i] += 20
    }
    // s2的修改会影响到数组数据,s1输出新数据
    fmt.Println(s1)
    fmt.Println(s2)
    // append  导致了slice 扩容
    s2 = append(s2, 4)

    for i := range s2 {
        s2[i] += 10
    }
    // s1 s2引用的底层数组发生变化。
    fmt.Println(s1, &s1[0])
    fmt.Println(s2, &s2[0])
}
程序输出:
3 3 [1 2 3]
2 2 [2 3]
[1 22 23]
[22 23]
[1 22 23] 0xc042066100    //地址发生了变化

[32 33 14] 0xc042066120

3.Map

map在go中也是引用类型,在创建和声明的时候不需要知道长度,定义之后就是nil类型,同类型的map是可以进行比较超值,map的key只能是内建类型,比如int,string,float,因此数组,切片,结构体是无法作为key,value可以是任意类型,包含空接口。map在参数传递的时候,内存花销小,32位机器占4个字节,64位机器上占8位,和slice不同的是,不会去管底层存储了多少数据。

定义map

//var map1 map[keytype]valuetype
var mymap  map[string]int
//make(map[keytype]valuetype,cap),cap指的是容器大小,可省略,在已知容器大小的时候必须申明出来
var mymap1 = make(map[string]int)
var mymap2 = make(map[string]int,10)
mymap3 := map[string]int{}
mymap4 := map[string]int{"one":1,"secend":2}

一个slice添加数据没问题,但是为初始化的map添加数据的时候会抛出异常

var mymap map[int]string
mymap[1] = "one"        //会抛出运行时panic异常

对容器访问的时候通过类似数组取下标的形式获取数据 value = map【key】

判断key是否存在,使用下面的方式,和python3相似

var testmap = make(map[int]string,10)
testmap[1] = "one"
testmap[2] = "secend"
if _,ok := testmap[1]{
    
}

删除map中的数据使用Delete(map,key),假设key不存在也不会异常

mymap4 := map[string]int{"one": 1, "secend": 2}

delete(mymap4,"one")

使用range遍历map或者slice通过下标的形式可以修改值,使用range是数据的拷贝,使用这些值不会修改到原始容器,在使用slice的时候推荐使用下标的形式,能忽略value就忽略,减少内存开销

package main



import (

	"fmt"

)



func main() {

	mymap4 := map[string]int{"one": 1, "secend": 2}



	for key, value := range mymap4 {



		fmt.Println(key, value)

		value = 100

	}

	fmt.Println(mymap4)



	for key, value := range mymap4 {

		mymap4[key] = value * 10//使用数据的原始地址

	}

	fmt.Println(mymap4)

	
	data := []int{1, 2, 3, 4, 5}

	for i, _ := range data { //忽略第二个参数降低内存拷贝

		var t_val = data[i]

		data[i] = data[i] * 10

		fmt.Println(t_val)

	}
}


更新日期:20190113

本文档持续更新中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值