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

基本语法

  • 注释// /**/

  • 行分隔符,换行即可,不需要;

关键字

breakdefaultfuncinterfaceselect
casedefergomapstruct
chanelsegotopackageswitch
constfallthroughifrangetype
continueforimportreturnvar

变量类型

编号类型和说明
1布尔类型 - 它们是布尔类型,由两个预定义常量组成:(a)true(b)false
2数字类型 - 它们是算术类型,在整个程序中表示:a)整数类型或 b)浮点值。
3字符串类型 - 字符串类型表示字符串值的集合。它的值是一个字节序列。 字符串是不可变的类型,一旦创建后,就不可能改变字符串的内容。预先声明的字符串类型是string
4派生类型: - 包括(a)指针类型,(b)数组类型,©结构类型,(d)联合类型和(e)函数类型(f)切片类型(g)函数类型(h)接口类型(i) 类型
编号类型和说明
1uint8 - 无符号8位整数(0到255)
2uint16 - 无符号16位整数(0到65535)
3uint32 - 无符号32位整数(0至4294967295)
4uint64 - 无符号64位整数(0至18446744073709551615)
5int8 - 带符号的8位整数(-128到127)
6int16 - 带符号的16位整数(-32768到32767)
7int32 - 带符号的32位整数(-2147483648至2147483647)
8int64 - 带符号的64位整数(-9223372036854775808至9223372036854775807)
编号类型和说明
1float32 - IEEE-754 32位浮点数
2float64 - IEEE-754 64位浮点数
3complex64 - 复数带有float32实部和虚部
4complex128 - 复数带有float64实部和虚部
1byte - 与uint8相同
2rune - 与int32相同
3uint - 32或64位
4int - 与uint大小相同
5uintptr - 无符号整数,用于存储指针值的未解释位

变量定义

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)
}
  1. 直接调用:square(1)
  2. 把函数当成变量一样赋值:s := square;接着可以调用这个函数变量:s(1)
    注意:这里 square 后面没有圆括号,调用才有。
  • 调用 nil 的函数变量会导致 panic。
  • 函数变量的零值是 nil,这意味着它可以跟 nil 比较,但两个函数变量之间不能比较。
func incr() func() int {
    var x int
    return func() int {
        x++
        return x
    }
}

调用这个函数会返回一个函数变量。

i := incr():通过把这个函数变量赋值给 ii 就成为了一个闭包

所以 i 保存着对 x 的引用,可以想象 i 中有着一个指针指向 xi 中有 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代码

《Go语言精进之路》读书笔记 | 理解Go语言代码块与作用域

Go基础--笔记

《Go语言精进之路》读书笔记 | 使用无类型常量简化代码