运行
1 | go run hello.go |
1 | go build hello.go |
注释
注释和C++一样,都是/**/ 或者 //,可以用go doc指令来查看代码中的注释,比如我们想查找parse相关的内容:
1 | go doc -all regexp | grep -i parse |
变量
变量声明
一般形式
1 | var identifier type |
1 | var identifier1, identifier2 type |
根据值自动判断类型
1 | var v_name = value |
用:=替代var
用:=声明的变量可以被多次声明
1 | v_name := value |
初始化
可以在创建的时候初始化值:
1 | var a int = 0 |
不初始化就默认零:
数值类型(包括complex64/128)为 0
布尔类型为 false
字符串为 “”(空字符串)
以下几种类型为 nil:
1
2
3
4
5
6
7 > var a *int
> var a []int
> var a map[string] int
> var a chan int
> var a func(string) int
> var a error // error 是接口
>
new 和 make
Go语言中的 new 和 make 主要区别如下:
- make 只能用来分配及初始化类型为 slice、map、chan 的数据。new 可以分配任意类型的数据;
- new 分配返回的是指针,即类型 *Type。make 返回引用,即 Type;
- new 分配的空间被清零。make 分配空间后,会进行初始化;
包引入
fmt
个人理解:和iostream类似,都是格式化I/O的包,简单格式:
Printf
就是个输出,用法和C的printf还有python的format
有着微妙的相似性,都是第一个变量定义输出格式,后面填入输出变量。
%v
占位符可以打印任何 Go 的值,“加号”标记(%+v
)会添加字段名%T
可以打印出变量的类型%#v
相应值的Go语法表示%%
字母上的百分号,并非值的占位符
1 | import ( |
(抄一个例子),其他普通的%d
什么的就懒得写了。
其他特殊用法:
指定宽度
1 | fmt.Printf("%10d\n", 353) // will print " 353" |
多次引用一个变量
1 | // 如果你在一个格式化的字符串中多次引用一个变量,你可以使用 %[n],其中 n 是你的参数索引(位置,从 1 开始) |
Sprintf
Sprintf
则格式化并返回一个字 符串而不带任何输出。
1 | s := fmt.Sprintf("是字符串 %s ","string") |
Fprintf
和Printf的区别:格式化并输出到指定的地方
1 | fmt.Fprintf(os.Stdout, "%s\n", "hello world!") |
这样就是和Printf
一样从os.stdout
输出,看下面一个例子:
1 | buf := bufio.NewWriter(os.Stdout) |
就变成了把输出扔进队列里。
Println
输出但是无法格式化。
自定义包
抄博客时间:
- 文件名与包名没有直接关系,不一定要将文件名与包名定成同一个。
- 文件夹名与包名没有直接关系,并非需要一致。
- 同一个文件夹下的文件只能有一个包名,否则编译报错。
文件结构:
1 | Test |
测试代码:
1 | // helloworld.go |
常量
常量,也就是在运行时不会被修改的量,常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。常量可以用len()
, cap()
, unsafe.Sizeof()
函数计算表达式的值。
- 显式类型定义:
const b string = "abc"
- 隐式类型定义:
const b = "abc"
itoa
iota,特殊常量,可以认为是一个可以被编译器修改的常量。const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:
1 | const ( |
继续抄例子:
1 | package main |
运算符
go的运算符倒是和C的没什么太大区别,要用自己查。
语句
整体来讲和常用的语言没什么太大区别,说点不一样的吧
if
Go 对 if
语句做了稍微修改,支持在条件语句被求值之前先进行初始化:
1 | if err := process(); err != nil { |
这也是一种非常常见的 go 的编写方式,虽然 err
不能在 if
语句之外使用,但他可以在任何 else if
或者 else
之内使用。
这样的写法和下面的写法相比要简介方便非常非常多:
1 | f, err := os.Open(name) |
for
go语言没有while,也是for。。。
1 | for condition { } |
函数
格式
1 | func function_name( [parameter list] ) [return_types] { |
返回值
可以返回多个值,默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。
除了普通的返回值外,当给返回值命名时,它会被直接初始化,并且绑定给一个返回值,这样在函数内部使用的时候可以直接把它当作参数来用。
1 | func ReadFull(r Reader, buf []byte) (n int, err error) { |
返回地址问题:
因为go在执行返回的时候会先给返回内容分配地址,所以返回引用并不会有问题,也就是:return &f
数组
还是和C一样,go语言初始化数组时需要申明数组的长度:
1 | var 数组变量名 [元素数量]Type |
但是9102年了还用这么死板的语言肯定是不行的,所以出现了切片:
切片
切片(slice)是建立在数组之上的更方便,更灵活,更强大的数据结构。切片并不存储任何元素而只是对现有数组的引用。
切片个人感觉和动态数组差不多,具体的运算逻辑和原因就后面再说,这里只提使用方法。你可以声明一个未指定大小的数组来定义切片:
1 | var identifier []type |
切片不需要说明长度。
或使用make()函数来创建切片:
1 | var slice1 []type = make([]type, len) |
向切片中增加新元素:append(oldslice, newelement)
指针
和C的还是无比相似,累了放弃。
结构和方法
结构的定义方法
1 | type struct_variable_type struct { |
关于结构:(抄的)
- 用于定义复杂的数据结构
- struct里面可以包含多个字段(属性),字段可以是任意类型
- struct类型可以定义方法(注意和函数的区别)
- struct类型是值类型
- struct类型可以嵌套
- Go语言没有class类型,只有struct类型
初始化方法:
- 指针类型
var 变量 = new (结构体名称)
var 变量 = &结构体名称{}
var 变量 = &结构体名称{成员A:值,成员B:值} - 值类型
var 变量= 结构体{成员A:值,成员B:值}
1 | type Books struct { |
换个例子(还是抄的)
1 | type Student struct { |
方法
方法这种东西总算回到了我们熟悉的OO编程的世界。。。
1 | /* 实现接口方法 */ |
Go 没有面向对象,而我们知道常见的 Java,C++ 等语言中,实现类的方法做法都是编译器隐式的给函数加一个 this 指针,而在 Go 里,这个 this 指针需要明确的申明出来,其实和其它 OO 语言并没有很大的区别。
例:
1 | func (c Circle) getArea() float64 { |
Map
像Python的dict的键值对,但是还不知道内部存储方式,累了怎么用以后再说。
Interface
就是我们知道的interface。。。。但是使用的时候不是用implementation或者是继承,只要在interface里定义了会被继承的类元素就行。
继续抄例子:
1 | package main |
interface的具体实现详见https://www.jianshu.com/p/70003e0f49d1 (后面有空再研究)