zoukankan      html  css  js  c++  java
  • GO开发[二]:golang语言基础

    一.变量

    1.声明变量

    变量相当于是对一块数据存储空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后可以通过引用变量名来使用这块存储空间。

    Go语言引入了关键字var,而类型信息放在变量名之后,变量声明语句不需要使用分号作为结束符,示例如下:
    var v1 int
    var v2 string
    var v3 [10]int // 数组
    var v4 []int // 数组切片
    var v5 struct {
    f int
    }
    var v6 *int // 指针
    var v7 map[string]int // map, key为string类型, value为int类型
    var v8 func(a int) int
    

    var关键字的另一种用法是可以将若干个需要声明的变量放置在一起,免得程序员需要重复
    写var关键字,如下所示:

    var (
    v1 int
    v2 string
    )
    

    对于声明变量时需要进行初始化的场景, var关键字可以保留,但不再是必要的元素:

    var v1 int = 10 // 正确的使用方式1
    var v2 = 10 // 正确的使用方式2,编译器可以自动推导出v2的类型
    v3 := 10 // 正确的使用方式3,编译器可以自动推导出v3的类型
    

    冒号和等号的组合:=,用于明确表达同时进行变量声明和初始化的工作。

    不能重复声明:

    var i int
    i := 2
    会导致类似如下的编译错误:
    no new variables on left side of :=
    

    2.匿名变量

    _是特殊标识符,用来忽略结果

    package main
    
    import(
    	"fmt"
    )
    
    func cal(a int, b int)(int,int) {
    	sum := a + b
    	avg := (a+b)/2
    	return sum, avg
    }
    
    func main() {
    	_,avg := cal(100,200)
    	fmt.Println(avg)
    }
    

    二、常量

    在Go语言中,常量是指编译期间就已知且不可改变的值。常量可以是数值类型(包括整型、浮点型和复数类型)、布尔类型、字符串类型等。

    const Pi float64 = 3.14159265358979323846
    const zero = 0.0 // 无类型浮点常量
    const (
    	size int64 = 1024
    	eof = -1 // 无类型整型常量
    )
    const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重赋值
    const a, b, c = 3, 4, "foo"
    // a = 3, b = 4, c = "foo", 无类型整型和字符串常量
    

    定义两个常量a=1和b=2,获取当前时间的秒数,如果能被b整除,则在终端打印b,否则打印a。

    package main
    
    import(
    	"fmt"
    	"time"
    )
    
    const (
    	a = 1
    	b = 2
    )
    
    func main() {
    	for {
    		second := time.Now().Unix()
    		fmt.Print(second," ")
    		if (second % b == 0) {
    			fmt.Println("b")
    		} else {
    			fmt.Println("a")
    		}
    		time.Sleep(1000 * time.Millisecond)
    	}
    }
    

    三、数据类型

    1.整型

    类 型 长度(字节) 值 范 围
    int8 1 -128 ~ 127
    uint8(即byte) 1 0 ~ 255
    int16 2 -32768 ~ 32767
    uint16 2 0 ~ 65535
    int32 4 -2147483648 ~ 2147483647
    uint32 4 0 ~ 4294967295
    int64 8 -9223372036854775808 ~ 9223372036854775807
    uint64 8 0 ~ 18446744073709551615
    int 平台相关 平台相关
    uint 平台相关 平台相关
    uintptr 同指针 在32位平台下为4字节, 64位平台下为8字节
    

    对于常规的开发来说,用int和uint就可以了,没必要用int8之类明确指定长度的类型,以免导致移植困难。

    var value2 int32
    value1 := 64 // value1将会被自动推导为int类型
    value2 = value1 // 编译错误
    编译错误类似于:
    cannot use value1 (type int) as type int32 in assignment。
    使用强制类型转换可以解决这个编译错误:
    value2 = int32(value1) // 编译通过
    

    eg:

    package main
    
    import "fmt"
    
    func main()  {
    	var n int16 = 16
    	var m int32
    	//m=n
    	m= int32(n)
    	fmt.Printf("32 bit int:%d
    ",m)
    	fmt.Printf("16 bit int:%d
    ",n)
    }
    

    2.bool

    var v1 bool
    v1 = true
    v2 := (1 == 2) // v2也会被推导为bool类型
    
    var b bool
    b = (1!=0) // 编译正确
    fmt.Println("Result:", b) // 打印结果为Result: true
    

    3.数值运算

    Go语言支持下面的常规整数运算: +、 -、 *、 /和%。 % 和在C语言中一样是求余运算,比如:
    5 % 3 // 结果为: 2
    

    4.浮点型

    Go语言定义了两个类型float32和float64,其中float32等价于C语言的float类型,float64等价于C语言的double类型。

    var f1 float32
    f1 = 12
    f2 := 12.0 // 如果不加小数点, f2会被推导为整型而不是浮点型
    f1 = float32(f2)//强制类型转换
    

    因为浮点数不是一种精确的表达方式,所以像整型那样直接用==来判断两个浮点数是否相等是不可行的,这可能会导致不稳定的结果。
    下面是一种推荐的替代方案:

    import "math"
    // p为用户自定义的比较精度,比如0.00001
    func IsEqual(f1, f2, p float64) bool {
    	return math.Fdim(f1, f2) < p
    }
    

    练习:使用math/rand生成10个随机整数,10个小于100的随机整数以及10个随机浮点数

    package main
    
    import (
       "fmt"
       "math/rand"
       "time"
    )
    
    func init() {
       rand.Seed(time.Now().UnixNano())
    }
    
    func main() {
       for i := 0; i < 10; i++ {
          a := rand.Int()
          fmt.Println(a)
       }
    
       for i := 0; i < 10; i++ {
          a := rand.Intn(100)
          fmt.Println(a)
       }
    
       for i := 0; i < 10; i++ {
          a := rand.Float32()
          fmt.Println(a)
       }
    }
    

    5.字符

    在Go语言中支持两个字符类型,一个是byte(实际上是uint8的别名),代表UTF-8字符串,的单个字节的值;另一个是rune,代表单个Unicode字符。 出于简化语言的考虑, Go语言的多数API都假设字符串为UTF-8编码。尽管Unicode字符在标准库中有支持,但实际上较少使用。

    byte.go

    package main
    
    import "fmt"
    
    func main() {
       var b byte
       for b =0;b<177;b++{
          fmt.Printf("%d %c
    ",b,b)
       }
    }
    

    rune.go

    package main
    
    import "fmt"
    
    func main() {
    	// byte => uint8
    	// rune => int32
    	s := "golang你好"
    	fmt.Println(len(s))
    	cnt := 0
    	for _, r := range s {
    		cnt += 1
    		fmt.Printf("%c
    ", r)
    	}
    	fmt.Println("cnt", cnt)
    	ss := []rune("hello")
    	fmt.Println(ss)
    }
    

    6.字符串类型

    package main
    
    import "fmt"
    
    func main()  {
    	str := "Hello,世界"
    	n := len(str)
    	fmt.Println(n)
    	for i := 0; i < n; i++ {
    		ch := str[i] // 依据下标取字符串中的字符,类型为byte
    		fmt.Println(i, ch)
    	}
    	fmt.Println("///////////////////////////")
    	str1 := "Hello,世界"
    	for i, ch := range str1 {
    		fmt.Println(i, ch)//ch的类型为rune
    	}
    }
    /*
    12
    0 72
    1 101
    2 108
    3 108
    4 111
    5 44
    6 228
    7 184
    8 150
    9 231
    10 149
    11 140
    ///////////////////////////
    0 72
    1 101
    2 108
    3 108
    4 111
    5 44
    6 19990
    9 30028
    */
    

    以字节数组的方式遍历 :这个字符串长度为12。尽管从直观上来说,这个字符串应该只有10个字符。这是因为每个中文字符在UTF-8中占3个字节,而不是1个字节。

    以Unicode字符遍历: 以Unicode字符方式遍历时,每个字符的类型是rune(早期的Go语言用int类型表示Unicode字符),而不是byte。

    四、值类型和引用类型

    值类型:基本数据类型int、float、bool、string以及数组和struct。

    引用类型:指针、slice、map、chan等都是引用类型。

    值语义和引用语义的差别在于赋值,比如下面的例子:
    b = a
    b.Modify()
    如果b的修改不会影响a的值,那么此类型属于值类型。如果会影响a的值,那么此类型是引用类型。
    Go语言中的大多数类型都基于值语义,包括:
    基本类型,如byte、 int、 bool、 float32、 float64和string等;
    复合类型,如数组(array)、结构体(struct)和指针(pointer)等。

    package main
    
    import "fmt"
    
    func main(){
       var a = [3]int{1, 2, 3}
       var b = a
       b[1]++
       fmt.Println(a)// [1 2 3]
       fmt.Println(b)//[1 3 3]
    }
    

    b=a赋值语句是数组内容的完整复制。要想表达引用,需要用指针

    package main
    
    import "fmt"
    
    func main(){
       var a = [3]int{1, 2, 3}
       var b = a
       var c = &a
       b[1]++
       c[1]++
       fmt.Println(a)
       fmt.Println(b)
       fmt.Println(*c)
       /*
          [1 3 3]
          [1 3 3]
          [1 3 3]
       */
    }
    

    c=&a赋值语句是数组内容的引用。变量c的类型不是[3]int,而是*[3]int类型

    练习:写一个程序用来打印值类型和引用类型变量到终端,并观察输出结果

    package main
    
    import (
       "fmt"
    )
    
    func modify(a int) {
       a = 50
       return
    }
    
    func modify1(a *int) {
       *a = 500
    }
    
    func main() {
       a := 5
       b := make(chan int, 1)
    
       fmt.Println("a=", a)
       fmt.Println("b=", b)
    
       modify(a)
       fmt.Println("a=", a)
       modify1(&a)
       fmt.Println("a=", a)
    }
    

    练习:写一个程序,交换两个整数的值。

    package main
    
    import "fmt"
    
    func swap(a int, b int) {
       tmp := a
       a = b
       b = tmp
       return
    }
    
    func main() {
       one := 1
       two := 2
       swap(one,two)
       fmt.Println("one=",one)
       fmt.Println("two=",two)
    }
    

    傻眼了,不变!加星号!

    package main
    
    import "fmt"
    
    func swap(a *int, b *int) {
    	tmp := *a
    	*a = *b
    	*b = tmp
    	return
    }
    
    func swap2(a int,b int) (int,int){
    	return b,a
    }
    
    func main() {
    	one := 1
    	two := 2
    	swap(&one,&two)
    	//one,two=swap2(one,two)
    	//one,two=two,one
    	fmt.Println(one,two)
    }
    

    五、变量的作用域

    1、在函数内部声明的变量叫做局部变量,生命周期仅限于函数内部。

    2、在函数外部声明的变量叫做全局变量,生命周期作用于整个包,如果是大写的,则作用于整个程序。

    package main
    
    import "fmt"
    
    var a = "Greg"
    
    func aa()  {
    	fmt.Println(a)
    }
    
    func ab()  {
    	a = "ningxin"
    	fmt.Println(a)
    }
    
    func ac() {
    	a := 2
    	fmt.Println(a)
    }
    
    func main() {
    	aa()
    	ac()
    	aa()
    	ab()
    	aa()
    }
    

    a := 2不可能是全局变量,因为这不是声明,而是执行代码,代码块要在函数里,go程序从上到下代码块外的部分只能是声明。

    var a int 
    a=2
    

    输出什么?

    package main
    
    import "fmt"
    
    var a string
    
    func f1() {
       a := "ningxin"
       fmt.Println(a)
       f2()
    }
    
    func f2() {
       fmt.Println(a)
    }
    
    func main() {
       a = "greg"
       fmt.Println(a)
       f1()
    }
    

    数组

    1.数组:是同一种数据类型的固定长度的序列。

    2.数组定义:var a [len]int,比如:var a[5]int

    package main
    
    import (
       "fmt"
       "unsafe"
    )
    
    func main() {
       a1:=[3]int{1,2,3}
       fmt.Println(a1)
    
       var a2 [3]int
       a2=a1
       fmt.Println(a2)
    
       fmt.Println(a1==a2)
       fmt.Println(&a1[0],&a2[0])
       fmt.Println(unsafe.Sizeof(a1))
    
       var n1,n2 int
       n2=n1
       fmt.Println(n1,n2)
       fmt.Println(&n1,&n2)
    
       var x1,x2 *int
       x2=x1
       fmt.Println(x1,x2)
    
       fmt.Printf("%x
    ",255)
    
       fmt.Println("/////////////数组初始化/////////////////")
       var q [3]int = [3]int{1,2,3}
       var r [3]int = [3]int{1,2}
       fmt.Println(q)
       fmt.Println(r)
       fmt.Println(r[2])
    
       q1 := [...]int{1,2,3,4}
       fmt.Println(q1)
    
       q2 := [...]int{4:2,10:-1}
       fmt.Println(q2)
    }
    

    3.长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型

    4.数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1

    package main
    
    import "fmt"
    
    func main(){
    	i:=2
    	var a [3]int
    	fmt.Println(a)
    	fmt.Println(a[len(a)-1])
    	fmt.Println(a[i])
    	fmt.Println("###############")
    	for i :=range a {
    		fmt.Println(i)
    	}
    	fmt.Println("###############")
    	for i,v :=range a {
    		fmt.Printf("%d %d
    ",i,v)
    	}
    	fmt.Println("###############")
    	for _,v := range a{
    		fmt.Printf("%d
    ",v)
    	}
      
    
    }
    

    数组的应用--md5

    package main
    
    import (
       "fmt"
       "crypto/md5"
    )
    
    func main() {
       data :=[]byte("hello")
       fmt.Println(data)
       md5sum :=md5.Sum(data)
       fmt.Printf("%v
    ",md5sum)
       fmt.Printf("%x
    ",md5sum)
    }
    

    数组初始化

    package main
    
    import "fmt"
    //数组初始化
    func testArray() {
       var a [5]int = [5]int{1, 2, 3, 4, 5}
       var a1 = [5]int{1, 2, 3, 4, 5}
       var a2 = [...]int{1,2,3,4,5,6,7,0,10}
       var a3 = [...]int{1: 100, 3: 200}
       var a4 = [...]string{1: "hello", 3: "world"}
    
       fmt.Println(a)
       fmt.Println(a1)
       fmt.Println(a2)
       fmt.Println(a3)
       fmt.Println(a4)
    }
    //多维数组
    func testArray2() {
       var a [2][5]int = [...][5]int{{1, 2, 3, 4, 5}, {6, 7, 8, 9, 10}}
       for row, v := range a {
          for col, v1 := range v {
             fmt.Printf("(%d,%d)=%d ", row, col, v1)
          }
          fmt.Println()
       }
    }
    
    func main() {
       testArray()
       fmt.Println("///////////////////////////")
       testArray2()
    }
    

    切片

    数组的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本。显然这种数据结构无法完全满足开发者的真实需求。

    数组切片就像一个指向数组的指针,实际上它拥有自己的数据结构,而不仅仅是个指针。数组切片的数据结构可以抽象为以下3个变量:
    一个指向原生数组的指针;
    数组切片中的元素个数;
    数组切片已分配的存储空间。

    切片的内存布局

    package main
    
    import "fmt"
    
    func main(){
       primes := [6]int{10,9,8,7,6,5}
       var s []int = primes[1:4]
       fmt.Println(primes)
       fmt.Println(s)
       fmt.Println(&s[0])
       fmt.Println(&primes[1])
    
       var s1 []int
       s1=s
       fmt.Println(&s1[0])
       fmt.Println(&s1[0]==&s[0])
       /*
          [10 9 8 7 6 5]
          [9 8 7]
          0xc042066038
          0xc042066038
          0xc042066038
          true
       */
    }
    

    通过make来创建切片

    var slice []type = make([]type, len)

    slice := make([]type, len)

    slice := make([]type, len, cap)

    package main
    
    import "fmt"
    
    func main()  {
       s :=make([]int,5,10)
       //创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间
       //cap()函数返回的是数组切片分配的空间大小,而len()函数返回的是数组切片中当前所存储的元素个数
       fmt.Println(len(s),cap(s))
       s=append(s,1)
       fmt.Println(s)
       s = append(s,2,3,4)
       fmt.Println(s)
       s1 := []int{13,14,15}
       s = append(s,s1...)
       fmt.Println(s)
       fmt.Println(len(s),cap(s))
    }
    

    用append内置函数操作切片

    package main
    
    import "fmt"
    
    func testSlice() {
       var a [5]int = [...]int{1, 2, 3, 4, 5}
       s := a[1:]
       fmt.Printf("before cap len[%d] cap[%d]
    ", len(s), cap(s))
       s[1] = 100
       fmt.Println(s,a[1])
       fmt.Printf("s=%p a[1]=%p
    ", s, &a[1])
       fmt.Println("before a:", a)
       s = append(s, 10)
       s = append(s, 10)
       fmt.Printf("after cap len[%d] cap[%d]
    ", len(s), cap(s))
       s = append(s, 10)
       s = append(s, 10)
       s = append(s, 10)
    
       s[1] = 1000
       fmt.Println("after a:", a)
       fmt.Println(s)
       fmt.Printf("s=%p a[1]=%p
    ", s, &a[1])
    }
    
    func main() {
       testSlice()
    }
    /*
      before cap len[4] cap[4]
      [2 100 4 5] 2
      s=0xc04200a2d8 a[1]=0xc04200a2d8
      before cap a: [1 2 100 4 5]
      after len[6] cap[8]
      after a: [1 2 100 4 5]
      [2 1000 4 5 10 10 10 10 10]
      s=0xc04206e000 a[1]=0xc04200a2d8
    */
    

    For range 遍历切片

    for index, val := range slice {}
    

    切片拷贝

    package main
    
    import "fmt"
    
    func main() {
       s1 := []int{1,2,3,4,5}
       s2 := make([]int, 10)
       copy(s2, s1)
       fmt.Println(s2)
       s3 := make([]int,1)
       copy(s3,s1)
       fmt.Println(s3)
    }
    

    string与slice

    string是不可变的

    string底层就是一个byte的数组,因此,也可以进行切片操作

    package main
    
    import "fmt"
    
    func main(){
       names := [4]string{
          "A",
          "B",
          "C",
          "D",
       }
       fmt.Println(names)
       a := names[0:2]
       b := names[1:3]
       fmt.Println("a=",a,"b=",b)
    
       b[0]="XXX"
       fmt.Println("a=",a,"b=",b)
       fmt.Println(names)
    
       //切片对切片进行切片
       c := a[1:2]
       fmt.Println("c=",c)
       c[0]="YYY"
       fmt.Println(c[0])
    
       var p [2]*string
       p[0]=&names[0]
       fmt.Println(p[0])
       x :=&names[1]
       fmt.Println(x)
       *p[0]="AAA"
       fmt.Println(p[0])
    }
    

    排序和查找操作

    sort.Ints对整数进行排序, sort.Strings对字符串进行排序, sort.Float64s对浮点数进行排序.

    sort.SearchInts(a []int, b int) 从数组a中查找b,前提是a必须有序

    sort.SearchFloats(a []float64, b float64) 从数组a中查找b,前提是a必须有序

    sort.SearchStrings(a []string, b string) 从数组a中查找b,前提是a必须有序

    package main
    
    import (
       "fmt"
       "sort"
    )
    
    func intS() {
       var a = [...]int{1, 8, 3, 2, 12338, 12}
       sort.Ints(a[:])
       fmt.Println(a)
    }
    
    func stringS() {
       var a = [...]string{"abc", "B", "b", "A", "eeee"}
       sort.Strings(a[:])
       fmt.Println(a)
    }
    
    func floatS() {
       var a = [...]float64{2.3, 0.8, 28.2, 392342.2, 0.6}
       sort.Float64s(a[:])
       fmt.Println(a)
    }
    
    func intSearch() {
       var a = [...]int{1, 8, 38, 2, 348, 484}
       sort.Ints(a[:])
       index := sort.SearchInts(a[:], 348)
       fmt.Println(index)
    }
    
    func main() {
       intS()
       stringS()
       floatS()
       intSearch()
    }
    

    map

    map是一堆键值对的未排序集合。

    key-value的数据结构,又叫字典或关联数组。

    变量声明

    map的声明基本上没有多余的元素,比如:var myMap map[string] PersonInfo
    其中, myMap是声明的map变量名, string是键的类型, PersonInfo则是其中所存放的值类型。

    声明是不会分配内存的,初始化需要make

    package main
    
    import "fmt"
    
    func main(){
    	ages := make(map[string]int)
    	ages["greg"]=18
    	ages["ningxin"]=25
    	fmt.Println(ages)
    	//or
    	ages1 := map[string]int{
    		"greg":18,
    		"ningxin":25,
    	}
    	fmt.Println(ages1)
    
    	var m1 map[string]int
    	fmt.Println(m1 == nil)
    	m1=make(map[string]int)
    	fmt.Println(m1 == nil)
    }
    

    map相关操作

    package main
    
    import "fmt"
    
    func main(){
    	ages :=map[string]int{
    		"greg":18,
    		"ningxin":25,
    	}
    
    	//插入和更新
    	fmt.Println(ages["greg"])
    	ages["greg"]=ages["ningxin"]+2
    	fmt.Println(ages["greg"])
    
    	//查找
    	g,gr := ages["greg"]
    	fmt.Println(g,gr)
    	c,ok := ages["c"]
    	if ok {
    		fmt.Println(c)
    	}else{
    		fmt.Println("not found")
    	}
    	if c,ok :=ages["c"];ok {
    		fmt.Println(c)
    	}
    
    	//遍历
    	for name,age := range ages {
    		fmt.Printf("name:%v age:%v
    ",name,age)
    	}
    	for name := range ages {
    		fmt.Println(name)
    	}
    	//删除
    	delete(ages,"greg")
    	fmt.Println(ages["greg"])
    }
    

    map排序

    a. 先获取所有key,把key进行排序

    b. 按照排序好的key,进行遍历

    Map反转

    初始化另外一个map,把key、value互换即可

    package main
    
    import (
       "fmt"
    )
    
    func trans() {
       var a map[string]int
       var b map[int]string
    
       a = make(map[string]int, 5)
       b = make(map[int]string, 5)
    
       a["abc"] = 101
       a["efg"] = 10
    
       for k, v := range a {
          b[v] = k
       }
       fmt.Println(b)
    }
    
    func main() {
       trans()
    }
    
  • 相关阅读:
    关于近期对于移动端开发的一些看法
    前端加密
    移动开发小知识大全
    介绍下京东的(选项卡中的选项卡)是怎么实现的
    一样的代码,一样的逻辑,不一样的效果(选项卡和轮播图)
    总结一下meta标签
    cookie的使用
    移动端常用代码
    上拉加载实现
    关于jQuery出现的新添加元素点击事件无效
  • 原文地址:https://www.cnblogs.com/ningxin18/p/8280040.html
Copyright © 2011-2022 走看看