Golang 学习笔记一 fmt、变量、分支、循环

一、golang fmt格式“占位符”
%d 十进制整数
%x, %o, %b 十六进制,八进制,二进制整数。
%f, %g, %e 浮点数: 3.141593 3.141592653589793 3.141593e+00
%t 布尔:true或false
%c 字符(rune) (Unicode码点)
%s 字符串
%q 带双引号的字符串"abc"或带单引号的字符'c'
%v 变量的自然形式(natural format)
%T 变量的类型
%% 字面上的百分号标志(无操作数)

比如fmt.Printf("%d,strig value:%s",value,string(value))

二、变量

参考快学 Go 语言》第 2 课 —— 变量基础
如果一个名字是在函数内部定义,那么它就只在函数内部有效。如果是在函数外部定义,那么将在当前包的所有文件中都可以访问。名字的开头字母的大小写决定了名字在包外的可见性。如果一个名字是大写字母开头的(译注:必须是在函数外部定义的包级名字;包级函数名本身也是包级名字),那么它将是导出的,也就是说可以被外部的包访问,例如fmt包的Printf函数就是导出的,可以在fmt包外部访问。包本身的名字一般总是用小写字母。

名字的长度没有逻辑限制,但是Go语言的风格是尽量使用短小的名字,对于局部变量尤其是这样;你会经常看到i之类的短名字,而不是冗长的theLoopIndex命名。通常来说,如果一个名字的作用域比较大,生命周期也比较长,那么用长的名字将会更有意义。

1.变量三种写法

var s int = 42
var s = 42//类型推导
s := 42//简短变量声明 自动类型推导 + 赋值

如果一个变量很重要,建议使用第一种显示声明类型的方式来定义,比如全局变量的定义就比较偏好第一种定义方式。如果要使用一个不那么重要的局部变量,就可以使用第三种。比如循环下标变量

for i:=0; i<10; i++ {
  doSomething()
}

那第二种方式能不能用在上面的循环下标中呢,答案是不可以,你无法将 var 声明直接写进循环条件中的初始化语句中,而必须提前声明变量,像下面这样,这时就很明显不如上面的形式了

var i = 0
for ; i<10; i++ {
  doSomething()
}

也可以在一个声明语句中同时声明一组变量,或用一组初始化表达式声明并初始化一组变量。

var i, j, k int // int, int, int
var b, f, s = true, 2.3, "four" // bool, float64, string

一组变量也可以通过调用一个函数,由函数返回的多个返回值初始化:var f, err = os.Open(name) // os.Open returns a file and an error

根据经验,如果需要声明初始值为零值的变量,应该 使用var关键字声明变量;如果提供确切的非零值初始化变量或者使用函数返回值创建变量,应该 使用简化变量声明运算符。

2.go没有静态变量

3.常量
Go 语言还提供了常量关键字 const,用于定义常量。常量可以是全局常量也可以是局部常量。

const globali int = 24

如果是批量声明的常量,除了第一个外其它的常量右边的初始化表达式都可以省略,如果省略初始化表达式则表示使用前面常量的初始化表达式写法,对应的常量类型也一样的。例如:

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

如果只是简单地复制右边的常量表达式,其实并没有太实用的价值。但是它可以带来其它的特性,那就是iota常量生成器语法。在一个const声明语句中,在第一个声明的常量所在的行,iota将会被置为0,然后在每一个有常量声明的行加1。iota每出现一次,自身会加1

以下例子参考奇怪的go语言iota

const (
        TestMin = -1//-1
        TestA//-1
        TestB = iota//2
        TestC//3
)

对以上数值,可以这样理解:TestA省略了初始化表达式,所以使用前面的-1。然后TestB是第一个出现iota的,它的值就是行号了,也就是2。然后TestC省略了初始化表达式,还是使用前面的iota,所以iota又出现一次,再加1。TestC就变成3了。

const (
    i=1<<iota//1<<0=1
    j=3<<iota//3<<1=6
    k//3<<iota=3<<2=12
    l//3<<iota=3<<3=24
)

再看一个:

const (
_ = 1 << (10 * iota)
KiB // 1<<(10 * iota) = 1<< (10 * 1) = 1024
MiB // 1<<(10 * iota) = 1<< (10 * 2) = 1048576
GiB // 1073741824
TiB // 1099511627776 (exceeds 1 << 32)
PiB // 1125899906842624
EiB // 1152921504606846976
ZiB // 1180591620717411303424 (exceeds 1 << 64)
YiB // 1208925819614629174706176
)

4.GO语言总结(5)——类型转换和类型断言

var var1 int = 7
fmt.Printf("%T->%v\n", var1, var1)
var2 := float32(var1)
var3 := int64(var1)

类型断言的本质,跟类型转换类似,都是类型之间进行转换,不同之处在于,类型断言实在接口之间进行,相当于Java中,对于一个对象,把一种接口的引用转换成另一种。

func test6() {
    var i interface{} = "kk"
    j := i.(int)
    fmt.Printf("%T->%d\n", j, j)
}

把这个 i 转换成 int 类型,系统内部检测到这种不匹配,就会调用内置的panic()函数,抛出一个异常。改一下,把 i 的定义改为:var i interface{} = 99,就没问题了。输出为:int->99
【golang】类型转换和类型断言中,解释了为什么要类型断言:
我们思考一个问题,为什么Golang需要类型断言,golang中对类型的要求十分严格,而且golang中也没有Typescript中的联合类型,好像一切类型都是固定不变的,有了强制转换类型,为什么还需要类型断言呢?在Golang中,接口类型是能够隐式转换的。看一个具体的例子:

var w io.Writer = os.Stdout

w的类型为io.Writer,但是它被赋值了File,这是Golang帮助我们做了一次类型转换。这次类型转换是在运行时的,编译时并不能确定下来。在运行时,这个接口值的类型被赋值为了File,与此同时,值也被赋值为了os.Stdout。

上述说明了一个问题,接口值的类型是不固定的!因为它的类型是要在运行时才能确定下来,这需要看它的动态值的类型才能确定。这就是需要类型断言的原因了。

当T为一个接口时,首先会判断x的动态值是否符合T这个接口,如果符合的话,断言成功,反之断言失败,断言失败时会抛出一个panic异常。但是如果类型断言出现在一个预期有两个结果的赋值操作中,那么断言失败不会抛出panic异常,而是用一个bool值标识是否断言成功。

var w io.Writer = os.Stdout 
b, ok := w.(*bytes.Buffer) 

为了健壮性,我们应该对ok返回的结果进行处理。标识是否断言成功。

var w io.Writer = os.Stdout 
if b, ok := w.(*bytes.Buffer);!ok {
    fmt.Fprintf(os.Stderr, "断言失败")
} else {
    //TODO
}

golang: 类型转换和类型断言还有一种转换方式是switch测试。既然称之为switch测试,也就是说这种转换方式只能出现在switch语句中。可以很轻松的将刚才用Comma-ok断言的例子换成由switch测试来实现:


package main
 
import (
    "fmt"
)
 
type Html []interface{}
 
func main() {
    html := make(Html, 5)
    html[0] = "div"
    html[1] = "span"
    html[2] = []byte("script")
    html[3] = "style"
    html[4] = "head"
    for index, element := range html {
        switch value := element.(type) {
        case string:
            fmt.Printf("html[%d] is a string and its value is %s\n", index, value)
        case []byte:
            fmt.Printf("html[%d] is a []byte and its value is %s\n", index, string(value))
        case int:
            fmt.Printf("error type\n")
        default:
            fmt.Printf("unknown type\n")
        }
    }
}

关于类型的switch测试,更多参考golang关于.(type)的使用
5.数据类型

uint8
无符号 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
有符号 64 位整型 (-9223372036854775808 到 9223372036854775807)

float32
IEEE-754 32位浮点型数
float64
IEEE-754 64位浮点型数
complex64
32 位实数和虚数
complex128
64 位实数和虚数

常量math.MaxFloat32表示float32能表示的最大数值,大约是 3.4e38;对应的math.MaxFloat64常量大约是1.8e308。它们分别能表示的最小值近似为1.4e-454.9e-324

(1)int和uint,与CPU位数相关,可以参考Golang int 和 uint 天天用,那么问题来了,它多大?
(2)尽管Go语言提供了无符号数和运算,即使数值本身不可能出现负数我们还是倾向于使用有符号的int类型,就像数组的长度那样,虽然使用uint无符号类型似乎是一个更合理的选择。事实上,内置的len函数返回一个有符号的int,我们可以像下面例子那样处理逆序循环。

medals := []string{"gold", "silver", "bronze"}
for i := len(medals) - 1; i >= 0; i-- {
fmt.Println(medals[i]) // "bronze", "silver", "gold"
}

另一个选择对于上面的例子来说将是灾难性的。如果len函数返回一个无符号数,那么i也将是无符号的uint类型,然后条件 i >= 0 则永远为真。在三次迭代之后,也就是 i == 0 时,i--语句将不会产生-1,而是变成一个uint类型的最大值,然后medals[i]表达式将发生运行时panic异常,也就是试图访问一个slice范围以外的元素。

出于这个原因,无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合、分析二进制文件格式或者是哈希和加密操作等。它们通常并不用于仅仅是表达非负数量的场合。

(3)参考能不能用 double 去取代 float?,浮点数建议直接使用float64。

(4)关于精度丢失,可以参考js 小数的精度损失

三、分支与循环

《快学 Go 语言》第 3 课 —— 分支与循环

  1. Go 语言没有三元操作符
func sign(a int) int {
    if a > 0 {
        return 1
    } else if a < 0 {
        return -1
    } else {
        return 0
    }
}

2.switch

func prize1(score int) string {
    switch score / 10 {
    case 0, 1, 2, 3, 4, 5:
        return "差"
    case 6, 7:
        return "及格"
    case 8:
        return "良"
    default:
        return "优"
    }
}

要注意,Go语言并不需要显式地在每一个case后写break,语言默认执行完case后的逻辑语句会自动退出。当然了,如果你想要相邻的几个case都执行同一逻辑的话,需要自己显式地写上一个fallthrough语句来覆盖这种默认行为。

3.Go 语言虽然没有提供 while 和 do while 语句,不过这两个语句都可以使用 for 循环的形式来模拟。

//输出10次hello,world(使用类似while循环形式,先判断后做)
func jobWhileMoni() {
    var count = 0
    for {
        if count >= 10 {
            break //如果count>=10则退出
        }
        fmt.Println("hello,world", count)
        count++
    }
}
//模拟do……while实现输出10次hello,world(先做后判断)
func jobDowhileMoni(){
    var i = 0
    for{
        fmt.Println("hello,world",i)
        i++
        if(i>=10){
            break
        }
    }
}
//生成count个[start,end)结束的不重复的随机数
func generateRandomNumber(start int, end int, count int) []int {
    //范围检查
    if end < start || (end-start) < count {
        return nil
    }

    //存放结果的slice
    nums := make([]int, 0)
    for len(nums) < count {
        //生成随机数
        num := utils.Rand(end-start) + start

        //查重
        exist := false
        for _, v := range nums {
            if v == num {
                exist = true
                break
            }
        }

        if !exist {
            nums = append(nums, num)
        }
    }

    return nums
}

4.使用if合并判断

if err := r.ParseForm(); err != nil {
log.Print(err)
}

Go语言允许这样的一个简单的语句结果作为循环的变量声明出现在if语句的最前面,这一点对错误处理很有用处。我们还可以像下面这样写(当然看起来就长了一些):

err := r.ParseForm()
if err != nil {
log.Print(err)
}

用if和ParseForm结合可以让代码更加简单,并且可以限制err这个变量的作用域,这么做是很不错的。

5.参考【GoLang】GoLang for 中有多个循环变量怎么处理?
由于Go没有逗号表达式,而++和--是语句而不是表达式,如果想在for中执行多个变量,需要使用平行赋值

for i, j := 1, 10; i < j; i,j=i+1,j+1 {  //死循环
    fmt.Println(i)
}

而不能写成

for i, j := 1, 10; i < j; i++,j++ {
    fmt.Println(i)
}

for的condition在每执行一次循环体时便会执行一次,因此在实际开发过程中需要注意不要让condition中计算简单而不是复杂。

for i,j :=0,len(str); i<j ; i++ {
    fmt.Println(str[i])
}

而不要写成(这仅是一个演示而已)

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

推荐阅读更多精彩内容

  • 环境搭建 Golang在Mac OS上的环境配置 使用Visual Studio Code辅助Go源码编写 VS ...
    陨石坠灭阅读 5,774评论 0 5
  • 1.安装 https://studygolang.com/dl 2.使用vscode编辑器安装go插件 3.go语...
    go含羞草阅读 1,548评论 0 6
  • 首先,iText 一定是款需要付费才能使用的产品: 在线 OCR 服务是需要付费的,通常是按次收费注:腾讯 OCR...
    ITJason阅读 3,568评论 0 0
  • 遥想善林小伙。 飞乘二八云朵。 飘逸俏佳人, 偷走心儿魂魄。 忐忑。 忐忑。 没事偷偷傻乐。 (新韵·二波)
    不惑而歌阅读 1,204评论 20 38
  • [问答02/如何做一个正常的时间买卖人?2017.03.08留言] 持续成长,从小步迈起! 以前无论是学英语,还是...
    早知今日阅读 155评论 0 0