【轻知识】Go入门学习整理——第一节

2019-01-06  本文已影响81人  言十年

准备工作:你要科学上网。不然一些东西下载不下来。Go的官网可能无法访问。
我英语不好。但尽量在官方文档找知识源。然后看看翻译。不看翻译阅读吃力。部分中文文档studygolang.com上有。
声明:知识点非原创,大都是我组合各个书,文章。所以雷同是正常滴。毕竟是整理。

golang 环境搭建

Downloads 页面,我们选择最新的吧。那就go1.11.4。我想我们机器都是64位。就下载amd64。

安装参考官网windows的msi一路装下去即可。当然mac的pkg也一样。当然yum、apt-get、brew,都中(虽然我没这么装过,我搜了下有这样的例子)。当然我还是建议你编译安装。这样比较funy。

windows

安装完msi文件,去设置系统的环境变量。

linux

第一种,直接使用编译好的。那种 Archive。当你下载的时候列表有一列是kind,选择Archive。

$ wget https://dl.google.com/go/go1.11.4.linux-amd64.tar.gz
$ tar xf go1.11.4.linux-amd64.tar.gz -C /usr/local/
$ /usr/local/go/bin/go version
go version go1.11.4 linux/amd64

第二种,用Go1.4编译 大于1.4的版本的。也就是源码编译安装。

1.下载 1.4.3的源码

$ wget  https://dl.google.com/go/go1.5.src.tar.gz

2. 放到/usr/local下面
 
$ tar xf go1.4.3.src.tar.gz -C /usr/local/

3. 因为要装其他版本,所以改名成go1.4.3

$ mv /usr/local/go /usr/local/go1.4.3

4.进入源码目录,编译(为节省时间,直接用make.bash)

$ cd /usr/local/go1.4.3 && ./make.bash
 
5.测试

$ /usr/local/go1.4.3/bin/go version
go version go1.4.3 linux/amd64

接着下载1.11.4,用1.4编译

1. 下载 1.11.4
$ wget https://dl.google.com/go/go1.11.4.src.tar.gz

2.解压
$ tar xf go1.11.4.src.tar.gz -C /usr/local

3.进入源码目录,使用1.4编译
$ cd / /usr/local/go/src
$ GOROOT_BOOTSTRAP=/usr/local/go1.4.3 ./make.bash

4.测试
$ /usr/local/go/bin/go version # 直接go version 也可以
go version go1.11.4 linux/amd64

windows也可以编译安装。就不弄了。

设置环境变量

上面不管是windows 跟linux都需要设置三个环境变量

GOPATH 也就是 你写代码的工作区。
GOROOT 也就是 go的安装目录。
GOBIN 也就是GO安装目录下的bin目录。

hello world

在cmd命令行,用notepad调起记事本。我还记得我的第一个html跟php是用的记事本。我们创建一个文件叫做hello.go。保存的时候一定要选utf-8。否则你打印中文会报错(Go语言的源代码要求是UTF8编码,导致Go源代码中出现的字符串面值常量一般也是UTF8编码的)。

package main

func main() {
    println("hello world")
}

我看这段代码。

package main。在官方文档中有一段Package names。源文件开头第一句必须是package 包名称,可执行命令必须始终使用package main。关于package 之后的文章还会再讲下。

func main 是程序入口函数。 学过其他强类型语言能够想到。

println 是内建的打印函数。下面我们会再次讲到。

vscode编辑器

参看《【轻知识】go开发,装个beego,安装下vscode》

import 、println、fmt.Println

上面我们打印了一下hello world。有看出什么特别之处么?你是否看过如下形式?

package main

import "fmt"

func main() {
    fmt.Println("hello world")
}

那么println跟fmt.Println 有什么区别?import是做什么的?fmt.Printlnt 太长了可以简写吗?

1.println是内建函数(go的内建),fmt.Println 是fmt包中的方法。

println 不保证一直会留在语言中(builtin/#println)。而且println跟print也只能简单的打印(基本类型,可以打印多个,请看文档)。

所以打印,建议使用fmt包,打印比较丰富,比如fmt.Printf。是不是想起C的printf格式化打印。

2.import 是引入包的,你用啥引啥。你想起了php的 include 、require,于是你想起了面试官问你两者不同之处。ok,扯远了。

import 有两种书写形式(而孔乙己的回字的有四种写法)。如下:

import "fmt"
import  (
    "fmt"
)

下面那种肯定是推荐的,引入多个的时候直接换行写。

第三个问题,在这里一并回答。可以简写。

import  (
    m "fmt"
)
m.Println()

还想简单点,可以,请注意那个.

import  (
    .  "fmt"
)
Println()

我把调试打印删了,我不想用了,但是保不齐我还会用。但是我放哪里会报错(not used)不能编译。可以使用_,这个符号加上之后包中只有init函数(初始化变量之类,之后会说)可以执行,其他方法不能调用。或者你可以用包的副作用(effective_go中Import for side effect)。

import  (
    _  "fmt"
)

变量

我们单独再创建一个文件var.go,跟hello.go在同一个目录下。

package main

import (
    "fmt"
)

func main() {
    var name string
    name = "野原新之助"
    fmt.Printf("Hello, %s\n", name)
}

我们看到在var.go这个文件,func main 的f下面有红色的~,鼠标放上去。

main redeclared in this block previous declaration at .\hello.go:3:6go

当然在你写完程序保存的时候。问题视窗就会把所有的问题打印出来了。

image.png

意思是前面的hello.go人家用了func main。当你按下F5调试运行。也跑不起来。hello.go用到了我不能用。难道这个提示是因为hello.go先创建的么?其实,当你创建一个a.go,a.go也用到func main,在hello.go文件里面也会报红。这么一看,其实跟文件创建时间没关系。跟首字母排序有关。当然这个不重要。重要的是,我们不能在一个文件目录下多个文件用main函数。就好比一个软件不能有多个入口一样。

那我还是想跑起来。三个办法:

1.把其他文件的main改成其他名字比如main01。当然下次我要运行其他文件又得反复改,当然这是馊主意。

2.我不用调试工具了。我手动在命令行里go run var.go。这个可以。但是

3.建子目录,把文件放进去。

这里思考一个问题。单独 go run var.go是没问题的。单独go build var.go也没问题。那么调试做了啥呢?其实我猜想vscode 调试,当你调试某一个文件的时候是在本文件所属的目录下。执行了go build,而不是单独build 一个文件。比如,把其他文件的main删掉。或者改成init。然后调试,你发下init是跑了的。也就是,当成一个项目来编译。

定义

var name string
var age int

还可以这样写

var (
    name string
    age int
)

可以一次定义多个变量

var x, y int

上面那种只声明不赋值,系统会默认该类型的零值:int 为 0,float 为 0.0,bool 为 false,string 为空字符串,指针为 nil。所有的内存在 Go 中都是经过初始化的。也是避免出现不可预测的行为。

紧接着就可以给声明的变量赋值了。

name = "野原新之助"

也可以声明跟赋值一起

var name string =  "野原新之助"

go支持自动推断类型,Go 是在编译时就已经完成推断过程。

var name =  "野原新之助"
var name, age = "野原新之助", 5

简短模式(Short variable declarations

name := "野原向日葵"
name, age := "野原新之助", 5

注意的是简短模式有些限制

简短变量声明可能仅出现在函数内部。在某些上下文中,例如“if”, “for”或 “switch”语句的初始化程序 ,它们可用于声明本地临时变量。

关于定义变量:先猜下,下面代码的输出结果

var age int
{
    age := 12
    print(age)
}
print(age)

答案是:12,0。{}代码块中的age是重新定义的一个age,作用域限制在代码块中。那么下面的结果就好猜了,打印的是 12 0

var age int
{
    age = 12
    print(age)
}
print(age)

只能用在函数内部,也就意味着不能用于包级变量。也就下面这样是不行的。会报语法错误syntax error: non-declaration statement outside function body

illegal := 42
func foo() {
  legal := 42
}

因为,在包级别,每个声明都应该以var,const,func,type和import之类的关键字开头。

可以在作用域语句上下文中重用它们,例如if (比如:Comma-ok断言),for,switch:

if foo := someFunc(); foo == 314 {
  // foo is scoped to 314 here
  // ...
}

还有一个注意点是,下面这两种都会报no new variables on left side of :=

var age = 5
age := 5

或者

age := 5
age := 6

但是,下面这样写就没问题。官网是这么说的(纯谷歌翻译哈):与常规变量声明不同,短变量声明可以重新声明 变量,前提是它们最初在同一个块中被声明(或者如果块是函数体的参数列表)具有相同的类型,并且至少有一个非变量是新的。因此,重新声明只能出现在多变量简短声明中。重新声明不会引入新变量; 它只是为原始分配一个新值。

age := 5
age, height := 5, 105.9 // 这里用字面量举例子。你可以想象有两个返回值的函数也可以。

什么是非空变量(non-blank),blank就是blank identifier即_。表示舍弃不用。也就是age, _ := 5,105.9 (代码中,有时候函数返回两个变量,有时候我们可以忽略变量)这样写是不行的。

《go语言学习笔记》说这样的叫做退化赋值。另外我还参考了一篇文章《Short variable declarations rulebook》

总结一下,了解了变量定义的方式。一是了解了一些细节后之后,能看懂别人代码,二是灵活运用。

思考

1.类型推断哪些好处?

Go 语言的类型推断可以明显提升程序的灵活性,使得代码重构变得更加容易(调用函数,返回值类型变了,接收返回值的变量不用改动声明,因为有类型推断),同时又不会给代码的维护带来额外负担(实际上,它恰恰可以避免散弹式的代码修改),更不会损失程序的运行效率。

作用域范围

官方文档Declarations_and_scope。或者自己搜吧。我觉得作用域没什么好说的。

数据类型

int、bool、float、字符类型

上面这几个没什么好说的。看《the way to go》中文版的基本类型和运算符了解下。

当你看到int有那么一堆。我觉得也不用纠结。类型选择我觉得吧,就跟你用数据库的选择数据库的类型那样。就可以了。

我觉得有两个知识点需要有印象。

fmt.Printf("%#v\n", []byte("世界")) // 打印 []byte{0xe4, 0xb8, 0x96, 0xe7, 0x95, 0x8c}
fmt.Printf("%#v\n", []rune("世界")) // 打印出 []int32{19990, 30028}

先看 []byte前三个0xe4, 0xb8, 0x96这个字。可以这样验证下——fmt.Println("\xe4\xb8\x96")。[]rune 中的19990也是,可以这样验证下——fmt.Println("\u4e16"),以后看到\u开头的字符串首先想到是unicode编码,4e16是19990的十六进制表示,你还可以这样验证fmt.Println(string(19990)) (int to string 不是string(int),请用strconv包,strconv.Itoa(19990)转字符串"19990")。

扩展点:在实际工作中,我们在排查问题时可能会遇到�,字符'�'的 Unicode 代码点是U+FFFD。。它是 Unicode 标准中定义的 Replacement Character,专用于替换那些未知的、不被认可的以及无法展示的字符。比如上面3个byte表示一个世。万一你丢掉了一个byte,比如fmt.Println("\xb8\x96"),那就��。虽然直接把一个整数值转换为一个,类型的值是可行的,但值得关注的是,被转换的整数值应该可以代表一个有效的 Unicode 代码点,否则转换的结果将会是"�"。(这个点,组装于郝林极客时间专栏《Go语言核心36讲》之《06 | 程序实体的那些事儿 (下)》)。

在整理资料的时候,我想到的一些问题

1.package main 中的这个main是什么?

  1. 同一个目录下多个文件里不允许有多个main吗?
    3.vscode 调试究竟干了些什么?
    3.什么是远程调试?go如何远程调试?

参考资料:

上一篇下一篇

猜你喜欢

热点阅读