Go语言学习笔记1

Mervyn1205 · · 1167 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

参考:《学习go语言》《GO WEB 编程》
所有的Go文件以package <something>开始,对于独立运行执行文件必须是package main;在这个main包中必定包含一个入口函数main,而这个函数既没有参数,也没有返回值
注释: // 和  /* */
Go变量的类型在变量名的后面。

如:
var a int    //声明一个变量a
a = 15           //给变量a赋值
var a, b int    //让a和b都是int类型变量
多个变量声明可以如下:
var(
    a int
    b bool
)

a := 15           //完成了声明和赋值。此时变量的类型由值推演出来。
a, b := 20, 16    //20赋值给a,16赋值给b
上述形式只可用在函数内,所以一般用var方式来定义全局变量。
或 var a int = 15 //定义变量并初始化值
特殊变量名 '_'(下划线)。任何赋值给它的值都会给舍弃。如
_, b := 34, 35  //将35赋值给b,同时丢弃34

Go编译器对声明却未使用的变量会报错。
例:
package main
import "fmt"
func main(){    // 注意大括号的位置
    var a int
    fmt.Printf("Hello, world!\n")
}

编译时报错,如下:
# command-line-arguments
./file.go:6: a declared and not used
变量 a 声明了却没有使用。

数据类型
布尔类型(bool)由预定义的常量true和false代表的布尔判定值,默认为false

数字类型
1、整型
由硬件决定最大长度。int是32或64位之一,不会定义成其他值。uint情况相同。
如果希望明确int的长度,可以使用例如:int32或uint32
完整的整数类型列表(符号和无符号)是int8,int16,int32,int64和byte,uint,uint16,uint32,uint64
2、浮点型
浮点类型的值有float32和float64(没有float类型),默认为float64

这些类型都是独立的,混合用这些类型向变量赋值会引起编译器错误。

常量 只能是数字、字符串或布尔值;
可以使用iota生成枚举值,如:
const (
    a = iota
    b = iota
)
第一个iota表示为0,因此a等于0,当iota再次在新的一行使用时,它的值增加了1,因此b的值是1
也可以
const (
    a = iota
    b        //隐含的意思是    b = iota
)

字符串在Go中是UTF-8的由双引号(")或反引号(``)包裹的字符序列。如果使用单引号则表示一个字符(UTF-8编码)
一旦给变量赋值,字符串就不能修改了
如果想要修改可使用如下方式:
package main
import "fmt"
func main(){
    s := "Hello, world!"
    c := []byte(s)            //将字符串转换为[]byte类型
    c[0] = 'c'
    s2 := string(c)            //在转换为string
    fmt.Printf("%s\n",s2)
}
package main
import "fmt"
func main() {
    s := "hello, world!"
    s = "c" + s[1:]        // 字符串虽不能更改,但可进行切片操作
    fmt.Printf("%s\n", s)      
}
输出结果:
cello, world!

小心使用多行字符串。
s := "Starting part"
    + "Ending part"
这是错误的。他会被转化为
s := "Starting part"
    + "Ending part"
应该按如下方式写:  + 可以连接两个字符串
s := "Starting part" + 
    "Ending part"

例:
package main
import "fmt"
func main(){
    s := "Hello,
    world!"
    c := []byte(s)
    c[0] = 'c'
    s2 := string(c)
    fmt.Printf("%s\n",s2)
}
上面的s那些写编译器会报错
应改为: 
package main
import "fmt"
func main(){
    s := "Hello," +
    "world!"
    c := []byte(s)
    c[0] = 'c'
    s2 := string(c)
    fmt.Printf("%s\n",s2)
}
输出结果:
cello,world!

或 使用反引号(`)
package main
import "fmt"
func main(){
    s := `Hello,
    world!`
    c := []byte(s)
    c[0] = 'c'
    s2 := string(c)
    fmt.Printf("%s\n",s2)
}
输出结果:
cello,
    world!
注意:反引号第二行world前面的空格会原样输出。

rune
Rune是int32的别名。用UTF-8编码。例如循环字符串获取实际字符时就需要使用rune类型

复数
Go支持原生复数
例:
var c complex64 = 5 + 5i;    //32位的虚数部分。 complex128是64位的虚数部分
fmt.Printf("Value is: %v", c);
打印结果为:5 + 5i;

错误
Go有了为了错误存在的内建类型error。例:va                                                                            r c error
package main
import (
    "fmt"
    "errors"
)
func main() {
    err := errors.New("emit macho dwarf: elf header corrupted")
    if err != nil {
        fmt.Print(err)
    }     
}
输出结果:
emit macho dwarf: elf header corrupted

大写字母开头的变量是可导出的,也就是其它包可以读取的,是公用变量;小写字母开头的就是不可导出的,是私有变量。
大写字母开头的函数也是一样,相当于class中的带public关键词的公有函数;小写字母开头的就是有private关键词的私有函数。

运算符

其中 &^ 表示位清除



Go控制结构

for循环(没有do或者while)
switch和if
select 类型选择和多路通讯转接器

语法与C不同:不需要圆括号,但是语句体必须总是包含在大括号内。
以if为例:
if x > 0 {        //注意大括号的位置
    return y
} else {
    return x
}

注意:在函数中这样的结束不会被编译.
例:
package main
import "fmt"
func main(){
    b := 2
    d := test(b)
    fmt.Printf("%d\n", d)
}
func test(x int) int {
    if x == 0 {
        return 5
    } else {
        return x
    }
    return 1        //如果没有这种结尾的话编译时会报错:./file.go:8: function ends without a return statement。注意:从go1.1起没有这句也可以。
}
输出结果:2
return 1只是举了个例子。
如果有返回值的话,最好返回值在if条件的外边,如上边的return 1;

Go的if还有个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示:
// 计算获取值x,然后根据x返回的大小,判断是否大于10。
if x := computedValue(); x > 10 {
    fmt.Println("x is greater than 10")
} else {
    fmt.Println("x is less than 10")
}

//这个地方如果这样调用就编译出错了,因为x是条件里面的变量
fmt.Println(x)
switch
例:
package main
func main(){
    i := 1
    switch i {
     case 1:
            println(1)
    fallthrough
case 2:
    println(2)
default: 
    println(0)
    }
}
输出结果:
1
2
注意:默认每个case后都带有break,匹配成功后不会自动向下执行其他case。
fallthrough关键字用来强制执行下一个case的值,如果fallthrough关键字后面显式的写break关键字会报错
例2:
package main
func main(){
    i := 1
    switch i {
     case 1,2:
            println(i)
default: 
    println(0)
    }
}
输出结果:
1    //这里如果上边的i为2 则输出2

goto
用goto跳转到一定是当前函数内定义的标签。标签名大小写敏感。
例:
package main
//import "fmt"
func main(){
    myfunc()
}
func myfunc(){
    i := 0
Here:                    //这个自定义的标签区分大小写,以分号":"结束,定义了标签就要使用
    println(i)
    i++
    if i < 5{
        goto Here
    }
}
输出结果:
0
1
2
3
4
注意:import "fmt"    //下面没有用到形如fmt.Printf();的地方所有如果上述代码有加这一行 就会编译出错

for的三种形式

for init; condition; post {}        //与C的for一样
for condition {}                    //和while一样
for {}                              //和c的for( ; ; )一样(死循环)

嵌套循环时,可以再break后面指定标签。用标签决定那个循环被终止。
例:
package main
func main(){
    J: for j := 0; j < 5; j++ {
        for i := 0; i < 10; i++ {
            if i > 5{
                break J;        //直接退出最外层循环J
            }
            println(i);
        }
    }
}
输出结果:
0
1
2
3
4
5
注意:如果标签定义了就一定要使用

range可用于循环。可用于slice、array、string、map和channel。当对slice或者array做循环时,range返回序号作为键,这个序号对应的内容作为值。
例:
package main
import "fmt"

func main(){
    list := [...]string{"a", "b", "c", "d"}
    for k, v := range list{
        fmt.Printf("%d %s\n", k, v)
    }
}
输出结果:
0 a
1 b
2 c
3 d

例2:
package main
import "fmt"
func main(){
    arr := [...]string{"a", "b", "c", "d"}
    for _, val := range arr{        //这个如果下面不想用到数组的序号,则要用 '_'
        fmt.Printf("%s",val)
    }
    fmt.Printf("\n")
}
输出结果:
abcd

也可以在字符串上直接使用range
例:
package main
import "fmt"
func main(){
    list := []string{"a", "b", "c", "d"}
    for k, v := range list{
        fmt.Printf("%d %s\n", k, v)
    }
    list1 := "e例f"
    for pos, char := range list1 {
        fmt.Printf("%d %c\n", pos, char)
    }
}
输出结果:
0 a
1 b
2 c
3 d
0 e
1 例        //注意:“例” 这个字占了3个字节,所以下面的序号变为了4
4 f

以上预定义函数无需引用任何包就可以使用它们。
print和println是底层函数,可以再不引入fmt包的情况下使用。
complex、real和imag全部用于处理复数。
len 和 cap 可用于不同的类型,len 用于返回字符串、slice 和数组的长度
delete 用于在 map 中删除实例
close 用于 channel 通讯。使用它来关闭 channel
new 用于各种类型的内存分配。
make 用于内建类型(map、slice 和 channel )的内存分配。
copy 用于复制 slice。
append 用于追加 slice。
panic 和 recover 用于异常处理机制。

array、slices和map
1、array
array由 [n]<type>定义,n为array的长度,<type>标示希望存储的内容的类型。
由于长度也是数组类型的一部分,因此[3]int[4]int是不同的类型,数组也就不能改变长度。数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。
例:
var arr [10]int    //声明一个int类型的数组
arr[0] = 42        //数组下标是从0开始的
arr[1] = 13        //赋值操作

由于长度也是数组类型的一部分,因此[3]int[4]int是不同的类型,数组也就不能改变长度。数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。如果要使用指针,那么就需要用到后面介绍的slice类型了。

声明数组也可以简写为: 
a := [3]int{1, 2, 3}
b := [10]int{1, 2, 3} //声明了一个长度为10的int数组,其中前3个元素初始化为1、2、3,其他默认为0
或a := [...]int{1, 2, 3}

二维数组
a := [2][2]int{ [2]int{1, 2}, [2]int{3, 4} }
或  a := [2][2]int{[...]int{1, 2}, [...]int{3, 4} }

从2010-10-27开始上面的例子可以改为如下:
a := [2][2]int{ {1, 2}, {3, 4} }

2、slice
slice 与 array 接近,但是在新的元素加入的时候可以增加长度.slice 是一个指向 array 的指针,这是其与 array 不同的地方;slice 是引用
类型,这意味着当赋值某个 slice 到另外一个变量,两个引用会指向同一个 array.
注意slice和数组在声明时的区别:声明数组时,方括号内写明了数组的长度或使用...自动计算长度,而声明slice时,方括号内没有任何字符。
// 声明一个含有10个元素元素类型为byte的数组
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}

// 声明两个含有byte的slice
var a, b []byte

// a指向数组的第3个元素开始,并到第五个元素结束,
a = ar[2:5]
//现在a含有的元素: ar[2]、ar[3]和ar[4]

// b是数组ar的另一个slice
b = ar[3:5]
// b的元素是:ar[3]和ar[4]
slice的一些简便操作:
    slice的默认开始位置是0,ar[:n]等价于ar[0:n]
    slice的第二个序列默认是数组的长度,ar[n:]等价于ar[n:len(ar)]
    如果从一个数组里面直接获取slice,可以这样ar[:],因为默认第一个序列是0,第二个是数组的长度,即等价于ar[0:len(ar)]

例:
a := [...]int{1, 2, 3, 4, 5}    //定义一个 5 个元素的 array,序号从 0 到 4;
s1 := a[2:4]                    //从序号 2 至 3 创建 slice,它包含元素 3, 4;
s2 := a[1:5]                    //从序号 1 至 4 创建,它包含元素 2, 3, 4, 5;
s3 := a[:]                      //用 array 中的所有元素创建 slice,这是 a[0:len(a)] 的简化写法;
s4 := a[:4]                     //从序号 0 至 3 创建,这是 a[0:4] 的简化写法,得到 1, 2, 3, 4;
s5 := s2[:]                     //从 slice s2 创建 slice,注意 s5 仍然指向 array a。a的值变了,slice的值也随之改变

例:
package main
import "fmt"
func main(){
    list := [...]string{"a", "b", "c", "d"}
    s1 := list[0:3]   //如果这个地方写成  s1 := list[0:6]; 编译时会报错。超出了范围
    fmt.Printf("%s\n", s1[1])
    s2 := append(s1, "tang")
    fmt.Printf("%s\n", s2[2])
    fmt.Printf("%s\n", s2[3])
    fmt.Printf("%d\n", cap(s2))
    fmt.Printf("%s\n", list[3])        //此时list数组的最后一个元素变成了 tang
}

输出结果:
b
c
tang
4
tang

对于slice几个有用的内置函数:
    len获取slice的长度。
    cap获取slice的最大容量。
    append向slice里面追加一个或者多个元素,然后返回一个和slice一样类型的slice。
    copy函数copy从源slice的src中复制元素到目标dst,并且返回复制的元素的个数。

函数 copy 从源 slice src 复制元素到目标 dst,并且返回复制的元素的个数。源和目标可能重叠。复制的数量是 len(src) 和 len(dst) 中的最小值。
注:append函数会改变slice所引用的数组的内容,从而影响到引用同一数组的其它slice。 但当slice中没有剩余空间(即(cap - len) == 0)时,此时将动态分配新的数组空间。返回的slice数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的slice则不受影响。

3、map
一般定义 map 的方法是:map[<from type>]<to type>
var numbers map[string] int 或 numbers := make(map[string] int)
当只需要声明一个 map 的时候,使用 make 的形式:monthdays := make(map[string]int)
例:
monthdays := map[string]int{
    "Jan": 31, "Feb": 28, "Mar": 31,
    "Apr": 30, "May": 31, "Jun": 30,
    "Jul": 31, "Aug": 31, "Sep": 30,
    "Oct": 31, "Nov": 30, "Dec": 31,   
}
package main
import "fmt"
func main(){
    monthdays := map[string]int{
        "Jan": 31, "Feb": 28, "Mar": 31,
"Apr": 30, "May": 31, "Jun": 30,
"Jul": 31, "Aug": 31, "Sep": 30,
"Oct": 31, "Nov": 30, "Dec": 31, //逗号是必须的
    }
    for key, val := range monthdays {
fmt.Printf("key = %s, val = %d\n", key, val)
    }
}
输出结果:
key = Sep, val = 30
key = May, val = 31
key = Oct, val = 31
key = Jun, val = 30
key = Apr, val = 30
key = Aug, val = 31
key = Mar, val = 31
key = Nov, val = 30
key = Jul, val = 31
key = Jan, val = 31
key = Feb, val = 28
key = Dec, val = 31

当在 map 中索引(搜索)时,使用方括号。例如打印出 12 月的天数:fmt.Printf("%d\n", monthdays["Dec"])

这个map就像我们平常看到的表格一样,左边列是key,右边列是值

使用map过程中需要注意的几点:

  • map是无序的,所以上述例子每次打印出来的结果顺序都不一样都会不一样,它不能通过index获取,而必须通过key获取
  • map的长度是不固定的,也就是和slice一样,也是一种引用类型
  • 内置的len函数同样适用于map,返回map拥有的key的数量
  • map的值可以很方便的修改,通过monthdays["Feb"] = 20可以很容易的把key为Feb的字典值改为20

map的初始化可以通过key:val的方式初始化值,同时map内置有判断是否存在key的方式

通过delete删除map的元素

例:
package main
func main(){
    monthdays := map[string]int{
    "Jan": 31, "Feb": 28, "Mar": 31,
    "Apr": 30, "May": 31, "Jun": 30,
    "Jul": 31, "Aug": 31, "Sep": 30,
    "Oct": 31, "Nov": 30, "Dec": 31,
}
    delete(monthdays, "Feb")    //删除map中Feb元素
    //map有两个返回值,第一个返回值为该key的值;第二个返回值,如果不存在key,那么ok为false,如果存在ok为true
    val, ok := monthdays["Feb"]
    //判断monthdays["Feb"]是否存在
    if ok {
        println("Feb is in the map and its value is", val)
    }else{
println("Feb isn't in the map")
    }
}
输出结果:
Feb isn't in the map

例2:
package main
import "fmt"
type Vertex struct {
Lat, Long float64
}
var m = map[string]Vertex{
"Bell Labs": {40.68433, -74.39967},
"Google":    {37.42202, -122.08408},
}
func main() {
fmt.Println(m)
}

输出结果:
map[Bell Labs:{40.68433 -74.39967} Google:{37.42202 -122.08408}]
例:
package main
import "fmt"
type Vertex struct {
	Lat, Long float64
}
var m = map[string]Vertex{
	"Bell Labs": Vertex{
		40.68433, -74.39967,
	},
	"Google": Vertex{
		37.42202, -122.08408,
	},
}
func main() {
	fmt.Println(m)
}
输出结果:
map[Google:{37.42202 -122.08408} Bell Labs:{40.68433 -74.39967}]

map也是一种引用类型,如果两个map同时指向一个底层,那么一个改变,另一个也相应的改变:
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut"  // 现在m["hello"]的值已经是Salut了


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:Mervyn1205

查看原文:Go语言学习笔记1

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1167 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传