您的位置:首页 > 编程语言 > Go语言

Go语言学习笔记1

2013-07-11 09:02 423 查看
参考:《学习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由
<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}]

[code]map
也是一种引用类型,如果两个
map
同时指向一个底层,那么一个改变,另一个也相应的改变:[/code]
m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut"  // 现在m["hello"]的值已经是Salut了
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: