2019-7-24

对常量的类型转换操作或以下函数调用都是返回常量结果:len、cap、real、imag、complex和unsafe.Sizeof(

const (

    a = 1

    b

    c = 2

    d

)

fmt.Println(a, b, c, d) // "1 1 2 2"

在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加一。

type Weekday int

const (

    Sunday Weekday = iota

    Monday

    Tuesday

    Wednesday

    Thursday

    Friday

    Saturday

)

type Flags uint

const (

    FlagUp Flags = 1 << iota // is up

    FlagBroadcast            // supports broadcast access capability

    FlagLoopback            // is a loopback interface

    FlagPointToPoint        // belongs to a point-to-point link

    FlagMulticast            // supports multicast access capability

)

随着iota的递增,每个常量对应表达式1 << iota,是连续的2的幂,分别对应一个bit位置。使用这些常量可以用于测试、设置或清除对应的bit位的值:

const (

    _ = 1 << (10 * iota)

    KiB // 1024

    MiB // 1048576

    GiB // 1073741824

    TiB // 1099511627776            (exceeds 1 << 32)

    PiB // 1125899906842624

    EiB // 1152921504606846976

    ZiB // 1180591620717411303424    (exceeds 1 << 64)

    YiB // 1208925819614629174706176

)

这里有六种未明确类型的常量类型,分别是无类型的布尔型、无类型的整数、无类型的字符、无类型的浮点数、无类型的复数、无类型的字符串。

数组是一个由固定长度的特定类型元素组成的序列,一个数组可以由零个或多个元素组成。因为数组的长度是固定的,因此在Go语言中很少直接使用数组

数组的每个元素可以通过索引下标来访问,索引下标的范围是从0开始到数组长度减1的位置。

内置的len函数将返回数组中元素的个数

var a [3]int            // array of 3 integers

fmt.Println(a[0])        // print the first element

fmt.Println(a[len(a)-1]) // print the last element, a[2]

var q [3]int = [3]int{1, 2, 3}

var r [3]int = [3]int{1, 2}

如果在数组的长度位置出现的是“...”省略号,则表示数组的长度是根据初始化值的个数来计算。

q := [...]int{1, 2, 3}

fmt.Printf("%T\n", q) // "[3]int"

const (

    USD Currency = iota // 美元

    EUR                // 欧元

    GBP                // 英镑

    RMB                // 人民币

)

symbol := [...]string{USD: "$", EUR: "€", GBP: "£", RMB: "¥"}

r := [...]int{99: -1}

定义了一个含有100个元素的数组r,最后一个元素被初始化为-1,其它元素都是用0初始化。

可以直接通过==比较运算符来比较两个数组,只有当两个数组的所有元素都是相等的时候数组才是相等的。不相等比较运算符!=遵循同样的规则。

import "crypto/sha256"

    c1 := sha256.Sum256([]byte("x"))

当调用一个函数的时候,函数的每个调用参数将会被赋值给函数内部的参数变量,所以函数参数变量接收的是一个复制的副本,并不是原始调用的变量。

函数参数传递的机制导致传递大的数组类型将是低效的,并且对数组参数的任何的修改都是发生在复制的数组上,并不能直接修改调用时原始的数组变量。在

以显式地传入一个数组指针,那样的话函数通过指针对数组的任何修改都可以直接反馈到调用者。

func zero(ptr *[32]byte) {

    for i := range ptr {

        ptr[i] = 0

    }

}

一个slice类型一般写作[]T,其中T代表slice中元素的类型;

一个slice由三个部分构成:指针、长度和容量。指针指向第一个slice元素对应的底层数组元素的地址,要注意的是slice的第一个元素并不一定就是数组的第一个元素。长度对应slice中元素的数目;长度不能超过容量,容量一般是从slice的开始位置到底层数据的结尾位置。内置的len和cap函数分别返回slice的长度和容量。

和数组不同的是,slice之间不能比较,因此我们不能使用==操作符来判断两个slice是否含有全部相等元素。不过标准库提供了高度优化的bytes.Equal函数来判断两个字节型slice是否相等([]byte),

slice唯一合法的比较操作是和nil比较,例如:

if summer == nil { /* ... */ }

一个零值的slice等于nil。一个nil值的slice并没有底层数组。一个nil值的slice的长度和容量都是0,但是也有非nil值的slice的长度和容量也是0的,例如[]int{}或make([]int, 3)[3:]

如果你需要测试一个slice是否是空的,使用len(s) == 0来判断,而不应该用s == nil来判断。除

make([]T, len)

make([]T, len, cap) // same as make([]T, cap)[:len]

在底层,make创建了一个匿名的数组变量,然后返回一个slice;

内置的append函数用于向slice追加元素:

    runes = append(runes, r)

先检测slice底层数组是否有足够的容量来保存新添加的元素。如果有足够空间的话,直接扩展slice(依然在原有的底层数组之上),将新添加的y元素复制到新扩展的空间,并返回slice。因此,输入的x和输出的z共享相同的底层数组。

如果没有足够的增长空间的话,appendInt函数则会先分配一个足够大的slice用于保存新的结果,先将输入的x复制到新的空间,然后添加y元素。结果z和输入的x引用的将是不同的底层数组。

copy函数可以方便地将一个slice复制另一个相同类型的slice。

两个slice可以共享同一个底层数组,甚至有重叠也没有问题。copy函数将返回成功复制的元素的个数

为了提高内存使用效率,新分配的数组一般略大于保存x和y所需要的最低大小。通过在每次扩展数组时直接将长度翻倍从而避免了多次内存分配,也确保了添加单个元素操的平均时间是一个常数时间。

一个map就是一个哈希表的引用,map类型可以写为map[K]V,其中K和V分别对应key和value。

ages := make(map[string]int) // mapping from strings to ints

ages := map[string]int{

    "alice":  31,

    "charlie": 34,

}

ages := make(map[string]int)

ages["alice"] = 31

ages["charlie"] = 34

因此,另一种创建空的map的表达式是map[string]int{}

使用内置的delete函数可以删除元素:

delete(ages, "alice") // remove element ages["alice"]

所有这些操作是安全的,即使这些元素不在map中也没有关系;如果一个查找失败将返回value类型对应的零值

但是map中的元素并不是一个变量,因此我们不能对map的元素进行取址操作:

_ = &ages["bob"] // compile error: cannot take address of map element

禁止对map元素取址的原因是map可能随着元素数量的增长而重新分配更大的内存空间,从而可能导致之前的地址无效。

Map的迭代顺序是不确定的,并且不同的哈希函数实现可能导致不同的遍历顺序。在实践中,遍历的顺序是随机的,每一次遍历的顺序都不相同。

sort.Strings(names)

和slice一样,map之间也不能进行相等比较;唯一的例外是和nil进行比较。

Map的value类型也可以是一个聚合类型,比如是一个map或slice。

type Employee struct {

    ID        int

    Name      string

    Address  string

    DoB      time.Time

    Position  string

    Salary    int

    ManagerID int

}

var dilbert Employee

dilbert.Salary -= 5000 // demoted, for writing too few lines of code

或者是对成员取地址,然后通过指针访问:

position := &dilbert.Position

*position = "Senior " + *position // promoted, for outsourcing to Elbonia

如果结构体没有任何成员的话就是空结构体,写作struct{}。它的大小为0,也不包含任何信息,

type Point struct{ X, Y int }

p := Point{1, 2}

anim := gif.GIF{LoopCount: nframes}

var _ = p.T{a: 1, b: 2} // compile error: can't reference a, b

var _ = p.T{1, 2}      // compile error: can't reference a, b

如果要在函数内部修改结构体成员的话,用指针传入是必须的;因为在Go语言中,所有的函数参数都是值拷贝传入的,函数参数将不再是函数调用时的原始变量。

func AwardAnnualRaise(e *Employee) {

    e.Salary = e.Salary * 105 / 100

pp := &Point{1, 2}

pp := new(Point)

*pp = Point{1, 2}

只声明一个成员对应的数据类型而不指名成员的名字;这类成员就叫匿名成员。

w = Wheel{Circle{Point{8, 8}, 5}, 20}

w = Wheel{

    Circle: Circle{

        Point:  Point{X: 8, Y: 8},

        Radius: 5,

    },

    Spokes: 20, // NOTE: trailing comma necessary here (and at Radius)

}

因为匿名成员也有一个隐式的名字,因此不能同时包含两个类型相同的匿名成员,这会导致名字冲突。

encoding/json

type Movie struct {

    Title  string

    Year  int  `json:"released"`

    Color  bool `json:"color,omitempty"`

。将一个Go语言中类似movies的结构体slice转为JSON的过程叫编组(marshaling)。编组通过调用json.Marshal函数完成:

data, err := json.Marshal(movies)

if err != nil {

    log.Fatalf("JSON marshaling failed: %s", err)

}

fmt.Printf("%s\n", data)

Marshal函数返还一个编码后的字节slice,包含很长的字符串,并且没有空白缩进

为了生成便于阅读的格式,另一个json.MarshalIndent函数将产生整齐缩进的输出。该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进:

data, err := json.MarshalIndent(movies, "", "    ")

if err != nil {

    log.Fatalf("JSON marshaling failed: %s", err)

}

fmt.Printf("%s\n", data)

只有导出的结构体成员才会被编码,这也就是我们为什么选择用大写字母开头的成员名称。

编码的逆操作是解码,对应将JSON数据解码为Go语言的数据结构,Go语言中一般叫unmarshaling,通过json.Unmarshal函数完成。

var titles []struct{ Title string }

if err := json.Unmarshal(data, &titles); err != nil {

    log.Fatalf("JSON unmarshaling failed: %s", err)

}

fmt.Println(titles) // "[{Casablanca} {Cool Hand Luke} {Bullitt}]"

func hypot(x, y float64) float64 {

func add(x int, y int) int  {return x + y}

func sub(x, y int) (z int)  { z = x - y; return}

func first(x int, _ int) int { return x }

func zero(int, int) int      { return 0 }

实参通过值的方式传递,因此函数的形参是实参的拷贝。对形参进行修改不会影响实参

如果实参包括引用类型,如指针,slice(切片)、map、function、channel等类型,实参可能会由于函数的间接引用被修改。

你可能会偶尔遇到没有函数体的函数声明,这表示该函数不是以Go实现的。这样的声明定义了函数标识符。

package math

func Sin(x float64) float //implemented in assembly language

如果一个函数将所有的返回值都显示的变量名,那么该函数的return语句可以省略操作数。这称之为bare return。

nil意味着函数运行成功,non-nil表示失败。对于non-nil的error类型,我们可以通过调用error的Error函数或者输出函数获得字符串类型的错误信息。

fmt.Println(err)

fmt.Printf("%v", err)

fmt.Errorf("parsing %s as HTML: %v", url,err)

}

fmt.Errorf函数使用fmt.Sprintf格式化错误信息并返回。

    const timeout = 1 * time.Minute

    deadline := time.Now().Add(timeout)

time.Now().Before(deadline)

        time.Sleep(time.Second << uint(tries)) // exponential back-off

log.Fatalf可以更简洁的代码达到与上文相同的效果。log中的所有函数,都默认会在错误信息之前输出时间信息。

    log.Fatalf("Site is down: %v\n", err)

package io

import "errors"

// EOF is the error returned by Read when no more input is available.

var EOF = errors.New("EOF")

    r, _, err := in.ReadRune()

    if err == io.EOF

在Go中,函数被看作第一类值(first-class values):函数像其他值一样,拥有类型,可以被赋值给其他变量,传递给函数,从函数返回。

    func square(n int) int { return n * n }

    func negative(n int) int { return -n }

    func product(m, n int) int { return m * n }

    f := square

    fmt.Println(f(3)) // "9"

    f = negative

    fmt.Println(f(3))    // "-3"

    fmt.Printf("%T\n", f) // "func(int) int"

    f = product // compile error: can't assign func(int, int) int to func(int) int

函数类型的零值是nil。调用值为nil的函数值会引起panic错误:

函数值可以与nil比较:

    var f func(int) int

    if f != nil {

        f(3)

    }

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,752评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,100评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,244评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,099评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,210评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,307评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,346评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,133评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,546评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,849评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,019评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,702评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,331评论 3 319
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,030评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,260评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,871评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,898评论 2 351

推荐阅读更多精彩内容

  • 使用定时器t := time.NewTicker(1 * time.Second)// 第一种方式for { ...
    qishuai阅读 913评论 0 2
  • fmt格式化字符串 格式:%[旗标][宽度][.精度][arg索引]动词旗标有以下几种:+: 对于数值类型总是输出...
    皮皮v阅读 1,092评论 0 3
  • 一、数据类型转换 https://studygolang.com/articles/10838 package m...
    蓓蓓的万能男友阅读 1,069评论 0 1
  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,774评论 0 38
  • Golang是我最喜欢的一门语言,它简洁、高效、易学习、开发效率高、还可以编译成机器码… 虽然它一出世,就饱受关注...
    盘木阅读 3,548评论 0 7