Go语言笔记
Posted 云灬沙
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Go语言笔记相关的知识,希望对你有一定的参考价值。
文章目录
HelloWorld
package main//应该包含的包名。它是一个必须的语句,因为Go程序在包中运行
import "fmt"
func main(){
fmt.Println("Hello World!")//P是大写的,在Go语言中,如果以大写字母开头,则是导出的名称,导出意味着相应包装的输入者可以访问函数或变量/常数
}
运行go程序
go run hello.go
基本语法
-
注释
// /**/
-
行分隔符,换行即可,不需要
;
关键字
break | default | func | interface | select |
---|---|---|---|---|
case | defer | go | map | struct |
chan | else | goto | package | switch |
const | fallthrough | if | range | type |
continue | for | import | return | var |
变量类型
编号 | 类型和说明 |
---|---|
1 | 布尔类型 - 它们是布尔类型,由两个预定义常量组成:(a)true (b)false |
2 | 数字类型 - 它们是算术类型,在整个程序中表示:a)整数类型或 b)浮点值。 |
3 | 字符串类型 - 字符串类型表示字符串值的集合。它的值是一个字节序列。 字符串是不可变的类型,一旦创建后,就不可能改变字符串的内容。预先声明的字符串类型是string 。 |
4 | 派生类型: - 包括(a)指针类型,(b)数组类型,©结构类型,(d)联合类型和(e)函数类型(f)切片类型(g)函数类型(h)接口类型(i) 类型 |
编号 | 类型和说明 |
---|---|
1 | uint8 - 无符号8位整数(0到255) |
2 | uint16 - 无符号16位整数(0到65535) |
3 | uint32 - 无符号32位整数(0至4294967295) |
4 | uint64 - 无符号64位整数(0至18446744073709551615) |
5 | int8 - 带符号的8位整数(-128到127) |
6 | int16 - 带符号的16位整数(-32768到32767) |
7 | int32 - 带符号的32位整数(-2147483648至2147483647) |
8 | int64 - 带符号的64位整数(-9223372036854775808至9223372036854775807) |
编号 | 类型和说明 |
---|---|
1 | float32 - IEEE-754 32位浮点数 |
2 | float64 - IEEE-754 64位浮点数 |
3 | complex64 - 复数带有float32 实部和虚部 |
4 | complex128 - 复数带有float64实部和虚部 |
1 | byte - 与uint8 相同 |
---|---|
2 | rune - 与int32 相同 |
3 | uint - 32或64位 |
4 | int - 与uint 大小相同 |
5 | uintptr - 无符号整数,用于存储指针值的未解释位 |
变量定义
var i, j int;
var c, byte;
var f, salary float32;
d = 42;
//先写var,之后写变量名,最后写变量类型名称
自动判别
variable_name = value;
d = 3, f = 5; // declaration of d and f. Here d and f are int
var x = "go language"
var y = 100
动态声明变量:=
func main() {
var x float64 = 20.0
y := 42
fmt.Println(x)
fmt.Println(y)
:=
使用时必须是在函数内部,不能作为静态声明,即全局变量
左值与右值
左值 | 可寻址 |
---|---|
右值 | 不可寻址 |
可寻址:可以通过&取地址符,获取内存地址; 可寻址,也就是分配了内存;不可寻址:根本没有分配内存;
转义字符
转义序列 | 含义 |
---|---|
\\\\ | \\ 字符 |
\\' | ' 字符 |
\\" | " 字符 |
\\? | ? 字符 |
\\a | 警报或响铃 |
\\b | 退格 |
\\f | 换页 |
\\n | 新行 |
\\r | 回车 |
\\t | 水平制表格 |
\\v | 水直制表格 |
\\ooo | 八位数字一到三位数 |
\\xhh... | 一位或多位的十六进制数 |
const
关键字
用来声明常量
const LENGTH int = 10
const WIDTH int = 5
注:以大写字母来定义常量是一个很好的编程习惯。
逻辑运算符
运算符 | 描述 | 示例 |
---|---|---|
&& | 逻辑AND 运算符。如果两个操作数都不为零,则条件为真。 | (A && B)结果为真 |
|| | 逻辑OR 运算符。如果两个操作数中的任何一个非零,则条件变为真。 | (A || B) 结果为真 |
! | 逻辑非运算符。用于反转其操作数的逻辑状态。如果条件为真,则逻辑非运算符将为假。 | !(A && B) 结果为真 |
运算符 | 描述 | 示例 |
---|---|---|
& | 返回变量的地址 | &a 将给出变量a 的实际地址。 |
* | 指向变量的指针 | *a 是指向变量a 的指针。 |
逻辑判断
switch语句
在Go编程中,switch有两种类型。
- 表达式开关(switch) - 在表达式开关(switch)中,
case
包含与开关(switch)表达式的值进行比较的表达式。 - 类型开关(switch) - 在类型开关(switch)中,
case
包含与特殊注释的开关(switch)表达式的类型进行比较的类型
以下规则适用于switch
语句:
- 在
switch
语句中使用的表达式必须具有整数或布尔表达式, 或者是一个具有单个转换函数为整数或布尔值的类类型。如果未传递表达式,则默认值为true
。 - 在
switch
语句中可以有任意数量的case
语句。 每个case
后面都跟要比较的值和冒号。 case
的常量表达式必须是与switch
语句的变量是相同的数据类型,并且它必须是常量或文字。- 当被打开的变量等于一个
case
中的值,那么将执行case
之后的语句。在case
语句中可不需要break
语句。(与其他语言的区别,但是也可以加) switch
语句可有一个可选的default
,它必须出现在switch
语句的末尾。default
可用于在没有任何case
为真时执行任务。default
之后可不需要break
语句。
switch marks {
case 90: grade = "A"
case 80: grade = "B"
case 50,60,70 : grade = "C"
default: grade = "D"
}
switch {
case grade == "A" :
fmt.Printf("Excellent!\\n" )
case grade == "B", grade == "C" :
fmt.Printf("Well done\\n" )
case grade == "D" :
fmt.Printf("You passed\\n" )
case grade == "F":
fmt.Printf("Better try again\\n" )
default:
fmt.Printf("Invalid grade\\n" );
}
循环控制
无限循环
for true{}
函数
func function_name( [parameter list] ) [return_types]
{
body of the function
}
func max(num1, num2 int) int//形参的类型为int,返回值类型也为int
{
/* local variable declaration */
result int
if (num1 > num2) {
result = num1
} else {
result = num2
}
return result
}
返回多个值
func swap(x, y string) (string, string) {
return y, x
}
传入地址进行交换
swap(&a, &b)
func swap(x *int, y *int) {
var temp int
temp = *x /* save the value at address x */
*x = *y /* put y into x */
*y = temp /* put temp into y */
}
使用math函数
import "math"
return math.Sqrt(x)
闭包
在 Go 语言中,函数被看作是第一类值,这意味着函数像变量一样,有类型、有值,其他普通变量能做的事它也可以。
func square(x int) {
println(x * x)
}
- 直接调用:
square(1)
- 把函数当成变量一样赋值:
s := square
;接着可以调用这个函数变量:s(1)
。
注意:这里square
后面没有圆括号,调用才有。
- 调用
nil
的函数变量会导致 panic。 - 函数变量的零值是
nil
,这意味着它可以跟nil
比较,但两个函数变量之间不能比较。
func incr() func() int {
var x int
return func() int {
x++
return x
}
}
调用这个函数会返回一个函数变量。
i := incr()
:通过把这个函数变量赋值给 i
,i
就成为了一个闭包。
所以 i
保存着对 x
的引用,可以想象 i 中有着一个指针指向 x 或 i 中有 x 的地址。
由于 i
有着指向 x
的指针,所以可以修改 x
,且保持着状态:
println(i()) // 1
println(i()) // 2
println(i()) // 3
也就是说,x
逃逸了,它的生命周期没有随着它的作用域结束而结束。
但是这段代码却不会递增:
println(incr()()) // 1
println(incr()()) // 1
println(incr()()) // 1
这是因为这里调用了三次 incr()
,返回了三个闭包,这三个闭包引用着三个不同的 x
,它们的状态是各自独立的。
方法(method)
一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。所有给定类型的方法属于该类型的方法集。语法格式如下:
func (variable_name variable_data_type) function_name() [return_type]{
/* 函数体*/
}
package main
import (
"fmt"
)
/* 定义结构体 */
type Circle struct {
radius float64
}
func main() {
var c1 Circle
c1.radius = 10.00
fmt.Println("圆的面积 = ", c1.getArea())
}
//该 method 属于 Circle 类型对象中的方法
func (c Circle) getArea() float64 {
//c.radius 即为 Circle 类型对象中的属性
return 3.14 * c.radius * c.radius
}
字符串
字符串文字是不可变的,因此一旦创建后,字符串文字就不能更改了
字符串长度
len(str)
连接字符串
strings.Joins()
Joins
连接数组的元素以创建单个字符串。第二个参数是分隔符,放置在数组的元素之间。
func main() {
greetings := []string{"Hello","world!"}
fmt.Println(strings.Join(greetings, " "))//中间用” “进行连接
}
数组
声明数组
var balance [10] float32
//var variable_name [SIZE] variable_type
初始化数组
定长数组
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
变长数组
var balance = []float32{1000.0, 2.0, 3.4, 7.0, 50.0}
访问数组元素
balance[i]
在go语言中声明了变量却不使用会被判定为错误
多维数组
多维数组声明
var threedim [5][10][4]int
初始化二维数组
a = [3][4]int{
{0, 1, 2, 3} , /* initializers for row indexed by 0 */
{4, 5, 6, 7} , /* initializers for row indexed by 1 */
{8, 9, 10, 11} /* initializers for row indexed by 2 */
}
数组作为形参传入
func getAverage(arr []int, size int) float32 {}//非定长数组
func getAverage(arr [10]int, size int) float32 {}//定长数组
指针
取地址符&
指针声明
var ip *int /* pointer to an integer */
var fp *float32 /* pointer to a float */
nil
指针
Go编译器为指针变量分配一个Nil
值,以防指针没有确切的地址分配。这是在变量声明的时候完成的。指定为nil
值的指针称为nil
指针。
在大多数操作系统上,程序不允许访问地址0
处的内存,因为该内存是由操作系统保留的。 然而,存储器地址0
具有特殊意义; 它表示指针不打算指向可访问的存储器位置。
但是按照惯例,如果指针包含nil
(零)值,则假设它不指向任何东西。
结构体
定义结构体
type struct_variable_type struct {
member definition;
member definition;
...
member definition;
}
type Books struct {
title string
author string
subject string
book_id int
}
声明结构体
variable_name:=structure_variable_type {value1, value2...valuen}
var Book1 Books /* Declare Book1 of type Book */
var Book2 Books /* Declare Book2 of type Book */
结构体作为函数参数
func printBook( book Books ) {
fmt.Printf( "Book title : %s\\n", book.title);
fmt.Printf( "Book author : %s\\n", book.author);
fmt.Printf( "Book subject : %s\\n", book.subject);
fmt.Printf( "Book book_id : %d\\n", book.book_id);
}
指针指向结构体
var struct_pointer *Books
struct_pointer = &Book1;
//要使用指向该结构体的指针来访问结构的成员,必须使用“.”
struct_pointer.title;
调用
printBook(&Book1)
printBook(&Book2)
func printBook( book *Books ) {}
range
numbers := []int{0,1,2,3,4,5,6,7,8}
/* print the numbers */
for i:= range numbers {
fmt.Println("Slice item",i,"is",numbers[i])
}
countryCapitalMap := map[string] string //map中的用法
for country := range countryCapitalMap {}
for country,capital := range countryCapitalMap {}
//两个值都可以查到
切片
Go切片(Slice)是Go数组的一个抽象。 由于Go数组允许定义类型的变量,可以容纳相同数据类型的几个数据项,但它不提供任何内置的方法来动态增加其大小或获取自己的子数组。切片就没有这样的限制。 它提供了数组所需的许多实用功能,并广泛用于Go编程。可以理解为更灵活的动态数组
定义切片
要定义切片,可以将其声明为数组,而不指定大小或使用make
函数创建一个。
var numbers []int /* a slice of unspecified size */
/* numbers == []int{0,0,0,0,0}*/
numbers = make([]int,5,5) /* a slice of length 5 and capacity 5*/
容量和长度
因为切片(Slice)是数组上的抽象。 它实际上使用数组作为底层结构体.len()
函数返回切片中存在的元素数量,其中cap()
函数返回切片(Slice)的容量(大小),即可容纳多少个元素。 以下是解释切片(Slice)的用法的示例:
var numbers = make([]int,3,5)
nil切片
如果缺省情况下声明没有输入切片,则将其初始化为nil
。 其长度和容量为零。
var numbers []int
numbers==nil
子切片
切片(Slice)允许指定下界和上界,以使用[lower-bound:upper-bound]
获取它的子切片。
numbers := []int{0,1,2,3,4,5,6,7,8}
numbers[:3]
//左闭右开
append()和copy()函数
切片(Slice)允许使用append()
函数增加切片的容量(大小)。使用copy()
函数,将源切片的内容复制到目标切片。
var numbers []int
numbers = append(numbers, 1)
numbers = append(numbers, 2,3,4)
numbers1 := make([]int, len(numbers), (cap(numbers))*2)
copy(numbers1,numbers)
映射
Go提供了另一个重要的数据类型:映射(Map
),它将唯一键映射到值。 键是用于在检索值的对象。 给定一个键和一个值就可以在Map
对象中设置值。设置存储值后,就可以使用其键检索它对应的值了。
定义映射
必须要使用make
函数来创建映射。
var map_variable map[key_data_type]value_data_type
/* define the map as nil map can not be assigned any value*/
map_variable = make(map[key_data_type]value_data_type)
var countryCapitalMap map[string]string
/* create a map*/
countryCapitalMap = make(map[string]string)
delete()函数
delete()
函数用于从映射中删除项目。它需要映射以及指定要删除的相应键。
countryCapitalMap := map[string] string{
"France":"Paris","Italy":"Rome","Japan":"Tokyo","India":"New Delhi"}
delete(countryCapitalMap,"France")
函数递归
Go语言支持递归,即函数调用其自身
通道(Channel)
通道是连接并发goroutine
的管道。可以从一个goroutine
向通道发送值,并在另一个goroutine
中接收到这些值。
使用make(chan val-type)
创建一个新通道,通道由输入的值传入。使用通道 <-
语法将值发送到通道。 这里从一个新的goroutine
发送“ping
”到在上面的消息通道。
<-channel
语法从通道接收值。在这里,将收到上面发送的“ping
”消息并打印出来。当运行程序时,“ping
”消息通过通道成功地从一个goroutine
传递到另一个goroutine
。默认情况下发送和接收块,直到发送方和接收方都准备好。此属性允许在程序结束时等待“ping
”消息,而不必使用任何其他同步。
package main
import "fmt"
func main() {
// Create a new channel with `make(chan val-type)`.
// Channels are typed by the values they convey.
messages := make(chan string)
// _Send_ a value into a channel using the `channel <-`
// syntax. Here we send `"ping"` to the `messages`
// channel we made above, from a new goroutine.
go func() { messages <- "ping" }()
// The `<-channel` syntax _receives_ a value from the
// channel. Here we'll receive the `"ping"` message
// we sent above and print it out.
msg := <-messages
fmt.Println(msg)
}
信号(signal)
有时我们希望Go程序能够智能地处理Unix信号。 例如,可能希望服务器在接收到SIGTERM
时正常关闭,或者在收到SIGINT
时使用命令行工具停止处理输入。下面介绍如何使用Go语言处理信号。
Go信号通知通过在通道上发送os.Signal
值来工作。创建一个通道来接收这些通知(还会在程序退出时通知我们)。
package main
import "fmt"
import "os"
import "os/signal"
import "syscall"
func main() {
// Go signal notification works by sending `os.Signal`
// values on a channel. We'll create a channel to
// receive these notifications (we'll also make one to
// notify us when the program can exit).
sigs := make(chan os.Signal, 1)
done := make(chan bool, 1)
// `signal.Notify` registers the given channel to
// receive notifications of the specified signals.
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
// This goroutine executes a blocking receive for
// signals. When it gets one it'll print it out
// and then notify the program that it can finish.
go func() {
sig := <-sigs
fmt.Println()
fmt.Println(sig)
done <- true
}()
// The program will wait here until it gets the
// expected signal (as indicated by the goroutine
// above sending a value on `done`) and then exit.
fmt.Println(Go语言技巧之正确高效使用slice(听课笔记总结--简单易懂)
《Go语言精进之路》读书笔记 | 使用Go语言原生编码思维来写Go代码
《Go语言精进之路》读书笔记 | 使用Go语言原生编码思维来写Go代码