-
这篇文章主要介绍了Golang极简入门教程(三):并发支持,本文讲解了goroutine线程、channel 操作符等内容,需要的朋友可以参考下Golang 运行时(runtime)管理了一种轻量级线程,被叫做 goroutine。创建数十万级的 goroutine 是没有问题的。范例:复制代码代码如下:package main import ( "fmt" "time") func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) }} func main() { // 开启一个 goroutine 执行 say 函数 go say("world") say("hello")}我们使用 channel 和 goroutine 通讯。channel 中是一种带有类型的通道,被用于接收和发送特定类型的值。操作符 <- 被叫做 channel 操作符(这个操作符中箭头表明了值的流向):复制代码代码如下:// 发送 v 到 channel chch <- v// 接收 channel ch 中的值并赋值给 vv := <-ch使用 channel 和 goroutine 通讯能够避免显式使用锁机制,通过 channel 发送和接收值时默认是阻塞的。通过 make 函数创建 channel:复制代码代码如下:// int 指定 channel 收发值的类型为 intch := make(chan int)一个完整的例子:复制代码代码如下:package main import "fmt" // 计算数组 a 中所有元素值之和func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } // 计算结果发送到 channel c c <- sum} func main() { a := []int{7, 2, 8, -9, 4, 0} // 创建 channel c c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) // 接收两个 goroutine 发送的计算结果 x, y := <-c, <-c fmt.Println(x, y, x+y)}package main import "fmt" // 计算数组 a 中所有元素值之和func sum(a []int, c chan int) { sum := 0 for _, v := range a { sum += v } // 计算结果发送到 channel c c <- sum} func main() { a := []int{7, 2, 8, -9, 4, 0} // 创建 channel c c := make(chan int) go sum(a[:len(a)/2], c) go sum(a[len(a)/2:], c) // 接收两个 goroutine 发送的计算结果 x, y := <-c, <-c fmt.Println(x, y, x+y)}channel 可以带有一个缓冲区(buffer)来缓存被传递的值,向 channel 中发送时只有缓冲区满的情况下会阻塞,接收 channel 中的值时只有在缓冲区空的情况下阻塞:复制代码代码如下:package main import "fmt" func main() { // 创建 channel,缓冲区长度为 2 c := make(chan int, 2) // 由于 channel 的缓冲区长度为 2 // 因此发送不会阻塞 c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c)}发送者可以调用 close 来关闭 channel,接收者可以检测到 channel 是否被关闭:复制代码代码如下:// 这里的 ok 为 false 表示已经没有值可以接收了,并且 channel 被关闭了v, ok := <-ch不要向已经关闭的 channel 发送值了(will cause a panic)。我们可以使用 for range 来接收 channel 中的值:复制代码代码如下:package main import "fmt" func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } // 必须要关闭 c close(c)} func main() { c := make(chan int, 10) go fibonacci(cap(c), c) // 这里 for 和 range 组合使用 // 不断的接收 c 中的值一直到它被关闭 for i := range c { fmt.Println(i) }}通常来说,我们不需要主动的关闭 channel。但有时候接收者必须被告知已经没有值可以接收了,这时候主动关闭是必要的,例如终止 for range 循环。使用 select 语句可以让一个 goroutine 等待多个通讯操作。select 会阻塞直到某个 case 能够运行,如果同时存在多个可执行的,那么将随机选择一个:复制代码代码如下:package main import "fmt" func fibonacci(c, quit chan int) { x, y := 0, 1 for { select { case c <- x: x, y = y, x+y // 控制此线程退出 case <-quit: fmt.Println("quit") return } }} func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit)}select 中的 default 会在没有任何 case 可执行时执行(类似于 switch):复制代码代码如下:package main import ( "fmt" "time") func main() { // 创建一个 tick channel // 在 100 毫秒后会向 tick channel 中发送当前时间 tick := time.Tick(100 * time.Millisecond) // 创建一个 boom channel // 在 500 毫秒后会向 boom channel 中发送当前时间 boom := time.After(500 * time.Millisecond) for { select { case <-tick: fmt.Println("tick.") case <-boom: fmt.Println("BOOM!") return default: fmt.Println(" .") time.Sleep(50 * time.Millisecond) } }}
-
这篇文章主要介绍了Golang极简入门教程(二):方法和接口,本文同时讲解了错误、匿名域等内容,需要的朋友可以参考下方法在 Golang 中没有类,不过我们可以为结构体定义方法。我们看一个例子:复制代码代码如下:package main import ( "fmt" "math") type Vertex struct { X, Y float64} // 结构体 Vertex 的方法// 这里的方法接收者(method receiver)v 的类型为 *Vertexfunc (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y)} func main() { v := &Vertex{3, 4} fmt.Println(v.Abs())}在这里方法的接收者使用指针类型而非值类型主要出于以下几点考虑(类似 C/C++ 等语言):1.避免方法每次调用时,对接收者的不必要的拷贝2.在方法内可以修改接收者的值我们可以为任意类型定义方法,但以下情况除外:1.如果类型定义在其他包中,不能为其定义方法2.如果类型是基础类型,不能为其定义方法复制代码代码如下:package main import ( "fmt" "math") // 定义一个类型 MyFloattype MyFloat float64 // 注意此方法关联的类型是 MyFloat 而不是 *MyFloatfunc (f MyFloat) Abs() float64 { if f < 0 { return float64(-f) } return float64(f)} func main() { f := MyFloat(-math.Sqrt2) fmt.Println(f.Abs())}接口(interface)接口也是一种类型(就像结构体一样)。一个接口类型包含了一组方法,一个接口类型能够持有那些实现了这些方法的值。范例:复制代码代码如下:// 定义接口 Absertype Abser interface { Abs() float64} // 定义结构体 Vertextype Vertex struct { X, Y float64} // 实现方法 Absfunc (v *Vertex) Abs() float64 { return math.Sqrt(v.X*v.X + v.Y*v.Y)} func main() { v := Vertex{3, 4} // 成功,能够持有 *Vertex 类型的值 var a Abser = &v // 出错,不能持有 Vertex 类型的值 // 因为在 *Vertex 上定义了方法 Abs,而未在 Vertex 上定义 var b Abser = v}错误Golang 提供了一个 error 接口:复制代码代码如下:type error interface { Error() string}我们通过 os.Open 函数来了解一下 error 的用法:复制代码代码如下:// 此函数用于打开一个文件// 返回的第二个值为 error 类型func Open(name string) (file *File, err error)简单的例子:复制代码代码如下:package main import ( "fmt" "os") func main() { _, err := os.Open("test.txt") // 如果 err 不为 nil 表示存在错误 if err != nil { fmt.Println(err) }}创建一个 error 值的最简单方式是使用 errors.New 函数:复制代码代码如下:func Sqrt(f float64) (float64, error) { if f < 0 { // 出错时返回一个错误 return 0, errors.New("math: square root of negative number") } // ...}我们也可以定义一个新的 error 的实现(也就是实现接口 error):复制代码代码如下:type NegativeSqrtError float64 func (f NegativeSqrtError) Error() string { return fmt.Sprintf("math: square root of negative number %g", float64(f))}匿名域结构体中可以存在只有类型而没有名字的域,它们被叫做匿名域。例如:复制代码代码如下:struct { T1 *T2}一个结构体的匿名域中的域或者方法可以被此结构体实例直接访问:复制代码代码如下:package main import "fmt" type Car struct { wheelCount int} func (car *Car) numberOfWheels() int { return car.wheelCount} type Ferrari struct { Car} func main() { f := Ferrari{Car{4}} fmt.Println("A Ferrari has this many wheels: ", f.numberOfWheels())}
-
项目准备使用AOS服务,请教下有没有go语言的SDK可以用?感谢
-
Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。goroutine 语法格式:go 函数名( 参数列表 )例如:go f(x, y, z)开启一个新的 goroutine:f(x, y, z)Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。实例package mainimport ( "fmt" "time")func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) }}func main() { go say("world") say("hello")}执行以上代码,你会看到输出的 hello 和 world 是没有固定先后顺序。因为它们是两个 goroutine 在执行:worldhellohelloworldworldhellohelloworldworldhello通道(channel)通道(channel)是用来传递数据的一个数据结构。通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。ch <- v // 把 v 发送到通道 chv := <-ch // 从 ch 接收数据 // 并把值赋给 v声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:ch := make(chan int)注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。以下实例通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:实例package mainimport "fmt"func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v } c <- sum // 把 sum 发送到通道 c}func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) x, y := <-c, <-c // 从通道 c 中接收 fmt.Println(x, y, x+y)}输出结果为:-5 17 12通道缓冲区通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:ch := make(chan int, 100)带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。实例package mainimport "fmt"func main() { // 这里我们定义了一个可以存储整数类型的带缓冲通道 // 缓冲区大小为2 ch := make(chan int, 2) // 因为 ch 是带缓冲的通道,我们可以同时发送两个数据 // 而不用立刻需要去同步读取数据 ch <- 1 ch <- 2 // 获取这两个数据 fmt.Println(<-ch) fmt.Println(<-ch)}执行输出结果为:Go 遍历通道与关闭通道Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:v, ok := <-ch如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。实例package mainimport ( "fmt")func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y } close(c)}func main() { c := make(chan int, 10) go fibonacci(cap(c), c) // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个 // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据 // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不 // 会结束,从而在接收第 11 个数据的时候就阻塞了。 for i := range c { fmt.Println(i) }}执行输出结果为:0112358132134
-
Go 错误处理Go 语言通过内置的错误接口提供了非常简单的错误处理机制。error类型是一个接口类型,这是它的定义:type error interface { Error() string}我们可以在编码中通过实现 error 接口类型来生成错误信息。函数通常在最后的返回值中返回错误信息。使用errors.New 可返回一个错误信息:func Sqrt(f float64) (float64, error) { if f < 0 { return 0, errors.New("math: square root of negative number") } // 实现}在下面的例子中,我们在调用Sqrt的时候传递的一个负数,然后就得到了non-nil的error对象,将此对象与nil比较,结果为true,所以fmt.Println(fmt包在处理error时会调用Error方法)被调用,以输出错误,请看下面调用的示例代码:result, err:= Sqrt(-1)if err != nil { fmt.Println(err)}实例实例package mainimport ( "fmt")// 定义一个 DivideError 结构type DivideError struct { dividee int divider int}// 实现 `error` 接口func (de *DivideError) Error() string { strFormat := ` Cannot proceed, the divider is zero. dividee: %d divider: 0` return fmt.Sprintf(strFormat, de.dividee)}// 定义 `int` 类型除法运算的函数func Divide(varDividee int, varDivider int) (result int, errorMsg string) { if varDivider == 0 { dData := DivideError{ dividee: varDividee, divider: varDivider, } errorMsg = dData.Error() return } else { return varDividee / varDivider, "" }}func main() { // 正常情况 if result, errorMsg := Divide(100, 10); errorMsg == "" { fmt.Println("100/10 = ", result) } // 当除数为零的时候会返回错误信息 if _, errorMsg := Divide(100, 0); errorMsg != "" { fmt.Println("errorMsg is: ", errorMsg) }}执行以上程序,输出结果为:100/10 = 10errorMsg is: Cannot proceed, the divider is zero. dividee: 100 divider: 0
-
Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。实例实例/* 定义接口 */type interface_name interface { method_name1 [return_type] method_name2 [return_type] method_name3 [return_type] ... method_namen [return_type]}/* 定义结构体 */type struct_name struct { /* variables */}/* 实现接口方法 */func (struct_name_variable struct_name) method_name1() [return_type] { /* 方法实现 */}...func (struct_name_variable struct_name) method_namen() [return_type] { /* 方法实现*/}实例实例package mainimport ( "fmt")type Phone interface { call()}type NokiaPhone struct {}func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!")}type IPhone struct {}func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!")}func main() { var phone Phone phone = new(NokiaPhone) phone.call() phone = new(IPhone) phone.call()}在上面的例子中,我们定义了一个接口Phone,接口里面有一个方法call()。然后我们在main函数里面定义了一个Phone类型变量,并分别为之赋值为NokiaPhone和IPhone。然后调用call()方法,输出结果如下:I am Nokia, I can call you!I am iPhone, I can call you!
-
递归,就是在运行的过程中调用自己。语法格式如下:func recursion() { recursion() /* 函数调用自身 */}func main() { recursion()}Go 语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。阶乘以下实例通过 Go 语言的递归函数实例阶乘:实例package mainimport "fmt"func Factorial(n uint64)(result uint64) { if (n > 0) { result = n * Factorial(n-1) return result } return 1}func main() { var i int = 15 fmt.Printf("%d 的阶乘是 %d\n", i, Factorial(uint64(i)))}以上实例执行输出结果为:15 的阶乘是 1307674368000斐波那契数列以下实例通过 Go 语言的递归函数实现斐波那契数列:实例package mainimport "fmt"func fibonacci(n int) int { if n < 2 { return n } return fibonacci(n-2) + fibonacci(n-1)}func main() { var i int for i = 0; i < 10; i++ { fmt.Printf("%d\t", fibonacci(i)) }}以上实例执行输出结果为:0 1 1 2 3 5 8 13 21 34
-
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。定义 Map可以使用内建函数 make 也可以使用 map 关键字来定义 Map:/* 声明变量,默认 map 是 nil */var map_variable map[key_data_type]value_data_type/* 使用 make 函数 */map_variable := make(map[key_data_type]value_data_type)如果不初始化 map,那么就会创建一个 nil map。nil map 不能用来存放键值对实例下面实例演示了创建和使用map:实例package mainimport "fmt"func main() { var countryCapitalMap map[string]string /*创建集合 */ countryCapitalMap = make(map[string]string) /* map插入key - value对,各个国家对应的首都 */ countryCapitalMap [ "France" ] = "巴黎" countryCapitalMap [ "Italy" ] = "罗马" countryCapitalMap [ "Japan" ] = "东京" countryCapitalMap [ "India " ] = "新德里" /*使用键输出地图值 */ for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap [country]) } /*查看元素在集合中是否存在 */ capital, ok := countryCapitalMap [ "American" ] /*如果确定是真实的,则存在,否则不存在 */ /*fmt.Println(capital) */ /*fmt.Println(ok) */ if (ok) { fmt.Println("American 的首都是", capital) } else { fmt.Println("American 的首都不存在") }}以上实例运行结果为:France 首都是 巴黎Italy 首都是 罗马Japan 首都是 东京India 首都是 新德里American 的首都不存在delete() 函数delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。实例如下:实例package mainimport "fmt"func main() { /* 创建map */ countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"} fmt.Println("原始地图") /* 打印地图 */ for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap [ country ]) } /*删除元素*/ delete(countryCapitalMap, "France") fmt.Println("法国条目被删除") fmt.Println("删除元素后地图") /*打印地图*/ for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap [ country ]) }}以上实例运行结果为:原始地图India 首都是 New delhiFrance 首都是 ParisItaly 首都是 RomeJapan 首都是 Tokyo法国条目被删除删除元素后地图Italy 首都是 RomeJapan 首都是 TokyoIndia 首都是 New delhi
-
Go 语言范围(Range)Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。实例实例package mainimport "fmt"func main() { //这是我们使用range去求一个slice的和。使用数组跟这个很类似 nums := []int{2, 3, 4} sum := 0 for _, num := range nums { sum += num } fmt.Println("sum:", sum) //在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。 for i, num := range nums { if num == 3 { fmt.Println("index:", i) } } //range也可以用在map的键值对上。 kvs := map[string]string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s -> %s\n", k, v) } //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。 for i, c := range "go" { fmt.Println(i, c) }}以上实例运行输出结果为:sum: 9index: 1a -> appleb -> banana0 1031 111
-
Go 语言切片是对数组的抽象。Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go 中提供了一种灵活,功能强悍的内置类型切片("动态数组"),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。定义切片你可以声明一个未指定大小的数组来定义切片:var identifier []type切片不需要说明长度。或使用 make() 函数来创建切片:var slice1 []type = make([]type, len)也可以简写为slice1 := make([]type, len)也可以指定容量,其中 capacity 为可选参数。make([]T, length, capacity)这里 len 是数组的长度并且也是切片的初始长度。切片初始化s :=[] int {1,2,3 } 直接初始化切片,[] 表示是切片类型,{1,2,3} 初始化值依次是 1,2,3,其 cap=len=3。s := arr[:] 初始化切片 s,是数组 arr 的引用。s := arr[startIndex:endIndex] 将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。s := arr[startIndex:] 默认 endIndex 时将表示一直到arr的最后一个元素。s := arr[:endIndex] 默认 startIndex 时将表示从 arr 的第一个元素开始。s1 := s[startIndex:endIndex] 通过切片 s 初始化切片 s1。s :=make([]int,len,cap) 通过内置函数 make() 初始化切片s,[]int 标识为其元素类型为 int 的切片。len() 和 cap() 函数切片是可索引的,并且可以由 len() 方法获取长度。切片提供了计算容量的方法 cap() 可以测量切片最长可以达到多少。以下为具体实例:实例package mainimport "fmt"func main() { var numbers = make([]int,3,5) printSlice(numbers)}func printSlice(x []int){ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)}以上实例运行输出结果为:len=3 cap=5 slice=[0 0 0]空(nil)切片一个切片在未初始化之前默认为 nil,长度为 0,实例如下:实例package mainimport "fmt"func main() { var numbers []int printSlice(numbers) if(numbers == nil){ fmt.Printf("切片是空的") }}func printSlice(x []int){ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)}以上实例运行输出结果为:len=0 cap=0 slice=[]切片是空的切片截取可以通过设置下限及上限来设置截取切片 [lower-bound:upper-bound],实例如下:实例package mainimport "fmt"func main() { /* 创建切片 */ numbers := []int{0,1,2,3,4,5,6,7,8} printSlice(numbers) /* 打印原始切片 */ fmt.Println("numbers ==", numbers) /* 打印子切片从索引1(包含) 到索引4(不包含)*/ fmt.Println("numbers[1:4] ==", numbers[1:4]) /* 默认下限为 0*/ fmt.Println("numbers[:3] ==", numbers[:3]) /* 默认上限为 len(s)*/ fmt.Println("numbers[4:] ==", numbers[4:]) numbers1 := make([]int,0,5) printSlice(numbers1) /* 打印子切片从索引 0(包含) 到索引 2(不包含) */ number2 := numbers[:2] printSlice(number2) /* 打印子切片从索引 2(包含) 到索引 5(不包含) */ number3 := numbers[2:5] printSlice(number3)}func printSlice(x []int){ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)}执行以上代码输出结果为:len=9 cap=9 slice=[0 1 2 3 4 5 6 7 8]numbers == [0 1 2 3 4 5 6 7 8]numbers[1:4] == [1 2 3]numbers[:3] == [0 1 2]numbers[4:] == [4 5 6 7 8]len=0 cap=5 slice=[]len=2 cap=9 slice=[0 1]len=3 cap=7 slice=[2 3 4]append() 和 copy() 函数如果想增加切片的容量,我们必须创建一个新的更大的切片并把原分片的内容都拷贝过来。下面的代码描述了从拷贝切片的 copy 方法和向切片追加新元素的 append 方法。实例package mainimport "fmt"func main() { var numbers []int printSlice(numbers) /* 允许追加空切片 */ numbers = append(numbers, 0) printSlice(numbers) /* 向切片添加一个元素 */ numbers = append(numbers, 1) printSlice(numbers) /* 同时添加多个元素 */ numbers = append(numbers, 2,3,4) printSlice(numbers) /* 创建切片 numbers1 是之前切片的两倍容量*/ numbers1 := make([]int, len(numbers), (cap(numbers))*2) /* 拷贝 numbers 的内容到 numbers1 */ copy(numbers1,numbers) printSlice(numbers1) }func printSlice(x []int){ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)}以上代码执行输出结果为:len=0 cap=0 slice=[]len=1 cap=1 slice=[0]len=2 cap=2 slice=[0 1]len=5 cap=6 slice=[0 1 2 3 4]len=5 cap=12 slice=[0 1 2 3 4]
-
Go 语言中数组可以存储同一类型的数据,但在结构体中我们可以为不同项定义不同的数据类型。结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。结构体表示一项记录,比如保存图书馆的书籍记录,每本书有以下属性:Title :标题Author : 作者Subject:学科ID:书籍ID定义结构体结构体定义需要使用 type 和 struct 语句。struct 语句定义一个新的数据类型,结构体中有一个或多个成员。type 语句设定了结构体的名称。结构体的格式如下:type struct_variable_type struct { member definition member definition ... member definition}一旦定义了结构体类型,它就能用于变量的声明,语法格式如下:variable_name := structure_variable_type {value1, value2...valuen}或variable_name := structure_variable_type { key1: value1, key2: value2..., keyn: valuen}实例如下:实例package mainimport "fmt"type Books struct { title string author string subject string book_id int}func main() { // 创建一个新的结构体 fmt.Println(Books{"Go 语言", "www.run.com", "Go 语言教程", 6495407}) // 也可以使用 key => value 格式 fmt.Println(Books{title: "Go 语言", author: "www.run.com", subject: "Go 语言教程", book_id: 6495407}) // 忽略的字段为 0 或 空 fmt.Println(Books{title: "Go 语言", author: "www.run.com"})}输出结果为:{Go 语言 www.run.com Go 语言教程 6495407}{Go 语言 www.run.com Go 语言教程 6495407}{Go 语言 www.run.com 0}访问结构体成员如果要访问结构体成员,需要使用点号 . 操作符,格式为:结构体.成员名"结构体类型变量使用 struct 关键字定义,实例如下:实例package mainimport "fmt"type Books struct { title string author string subject string book_id int}func main() { var Book1 Books /* 声明 Book1 为 Books 类型 */ var Book2 Books /* 声明 Book2 为 Books 类型 */ /* book 1 描述 */ Book1.title = "Go 语言" Book1.author = "www.runoob.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 /* book 2 描述 */ Book2.title = "Python 教程" Book2.author = "www.run.com" Book2.subject = "Python 语言教程" Book2.book_id = 649570 /* 打印 Book1 信息 */ fmt.Printf( "Book 1 title : %s\n", Book1.title) fmt.Printf( "Book 1 author : %s\n", Book1.author) fmt.Printf( "Book 1 subject : %s\n", Book1.subject) fmt.Printf( "Book 1 book_id : %d\n", Book1.book_id) /* 打印 Book2 信息 */ fmt.Printf( "Book 2 title : %s\n", Book2.title) fmt.Printf( "Book 2 author : %s\n", Book2.author) fmt.Printf( "Book 2 subject : %s\n", Book2.subject) fmt.Printf( "Book 2 book_id : %d\n", Book2.book_id)}以上实例执行运行结果为:Book 1 title : Go 语言Book 1 author : www.run.comBook 1 subject : Go 语言教程Book 1 book_id : 6495407Book 2 title : Python 教程Book 2 author : www.runcomBook 2 subject : Python 语言教程Book 2 book_id : 6495700结构体作为函数参数你可以像其他数据类型一样将结构体类型作为参数传递给函数。并以以上实例的方式访问结构体变量:实例package mainimport "fmt"type Books struct { title string author string subject string book_id int}func main() { var Book1 Books /* 声明 Book1 为 Books 类型 */ var Book2 Books /* 声明 Book2 为 Books 类型 */ /* book 1 描述 */ Book1.title = "Go 语言" Book1.author = "www.run.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 /* book 2 描述 */ Book2.title = "Python 教程" Book2.author = "www.run.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 /* 打印 Book1 信息 */ printBook(Book1) /* 打印 Book2 信息 */ printBook(Book2)}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)}以上实例执行运行结果为:Book title : Go 语言Book author : www.run.comBook subject : Go 语言教程Book book_id : 6495407Book title : Python 教程Book author : www.run.comBook subject : Python 语言教程Book book_id : 6495700结构体指针你可以定义指向结构体的指针类似于其他指针变量,格式如下:var struct_pointer *Books以上定义的指针变量可以存储结构体变量的地址。查看结构体变量地址,可以将 & 符号放置于结构体变量前:struct_pointer = &Book1使用结构体指针访问结构体成员,使用 "." 操作符:struct_pointer.title接下来让我们使用结构体指针重写以上实例,代码如下:实例package mainimport "fmt"type Books struct { title string author string subject string book_id int}func main() { var Book1 Books /* 声明 Book1 为 Books 类型 */ var Book2 Books /* 声明 Book2 为 Books 类型 */ /* book 1 描述 */ Book1.title = "Go 语言" Book1.author = "www.run.com" Book1.subject = "Go 语言教程" Book1.book_id = 6495407 /* book 2 描述 */ Book2.title = "Python 教程" Book2.author = "www.run.com" Book2.subject = "Python 语言教程" Book2.book_id = 6495700 /* 打印 Book1 信息 */ printBook(&Book1) /* 打印 Book2 信息 */ printBook(&Book2)}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)}以上实例执行运行结果为:Book title : Go 语言Book author : www.runoob.comBook subject : Go 语言教程Book book_id : 6495407Book title : Python 教程Book author : www.runoob.comBook subject : Python 语言教程Book book_id : 6495700
-
Go 语言指针Go 语言中指针是很容易学习的,Go 语言中使用指针可以更简单的执行一些任务。接下来让我们来一步步学习 Go 语言指针。我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址。以下实例演示了变量在内存中地址:实例package mainimport "fmt"func main() { var a int = 10 fmt.Printf("变量的地址: %x\n", &a )}执行以上代码输出结果为:变量的地址: 20818a220现在我们已经了解了什么是内存地址和如何去访问它。接下来我们将具体介绍指针。什么是指针一个指针变量指向了一个值的内存地址。类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:var var_name *var-typevar-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。以下是有效的指针声明:var ip *int /* 指向整型*/var fp *float32 /* 指向浮点型 */本例中这是一个指向 int 和 float32 的指针。如何使用指针指针使用流程:定义指针变量。为指针变量赋值。访问指针变量中指向地址的值。在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。实例package mainimport "fmt"func main() { var a int= 20 /* 声明实际变量 */ var ip *int /* 声明指针变量 */ ip = &a /* 指针变量的存储地址 */ fmt.Printf("a 变量的地址是: %x\n", &a ) /* 指针变量的存储地址 */ fmt.Printf("ip 变量储存的指针地址: %x\n", ip ) /* 使用指针访问值 */ fmt.Printf("*ip 变量的值: %d\n", *ip )}以上实例执行输出结果为:a 变量的地址是: 20818a220ip 变量储存的指针地址: 20818a220*ip 变量的值: 20Go 空指针当一个指针被定义后没有分配到任何变量时,它的值为 nil。nil 指针也称为空指针。nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。一个指针变量通常缩写为 ptr。查看以下实例:实例package mainimport "fmt"func main() { var ptr *int fmt.Printf("ptr 的值为 : %x\n", ptr )}以上实例输出结果为:ptr 的值为 : 0空指针判断:if(ptr != nil) /* ptr 不是空指针 */if(ptr == nil) /* ptr 是空指针 */
-
Go 语言数组Go 语言提供了数组类型的数据结构。数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。相对于去声明 number0, number1, ..., number99 的变量,使用数组形式 numbers[0], numbers[1] ..., numbers[99] 更加方便且易于扩展。数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。声明数组Go 语言数组声明需要指定元素类型及元素个数,语法格式如下:var variable_name [SIZE] variable_type以上为一维数组的定义方式。例如以下定义了数组 balance 长度为 10 类型为 float32:var balance [10] float32初始化数组以下演示了数组初始化:var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}我们也可以通过字面量在声明数组的同时快速初始化数组: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 := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}如果设置了数组的长度,我们还可以通过指定下标来初始化元素:// 将索引为 1 和 3 的元素初始化balance := [5]float32{1:2.0,3:7.0}初始化数组中 {} 中的元素个数不能大于 [] 中的数字。如果忽略 [] 中的数字不设置数组大小,Go 语言会根据元素的个数来设置数组的大小: balance[4] = 50.0以上实例读取了第五个元素。数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。访问数组元素数组元素可以通过索引(位置)来读取。格式为数组名后加中括号,中括号中为索引的值。例如:var salary float32 = balance[9]以上实例读取了数组 balance 第 10 个元素的值。以下演示了数组完整操作(声明、赋值、访问)的实例:实例 1package mainimport "fmt"func main() { var n [10]int /* n 是一个长度为 10 的数组 */ var i,j int /* 为数组 n 初始化元素 */ for i = 0; i < 10; i++ { n[i] = i + 100 /* 设置元素为 i + 100 */ } /* 输出每个数组元素的值 */ for j = 0; j < 10; j++ { fmt.Printf("Element[%d] = %d\n", j, n[j] ) }}以上实例执行结果如下:Element[0] = 100Element[1] = 101Element[2] = 102Element[3] = 103Element[4] = 104Element[5] = 105Element[6] = 106Element[7] = 107Element[8] = 108Element[9] = 109实例 2package mainimport "fmt"func main() { var i,j,k int // 声明数组的同时快速初始化数组 balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0} /* 输出数组元素 */ ... for i = 0; i < 5; i++ { fmt.Printf("balance[%d] = %f\n", i, balance[i] ) } balance2 := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0} /* 输出每个数组元素的值 */ for j = 0; j < 5; j++ { fmt.Printf("balance2[%d] = %f\n", j, balance2[j] ) } // 将索引为 1 和 3 的元素初始化 balance3 := [5]float32{1:2.0,3:7.0} for k = 0; k < 5; k++ { fmt.Printf("balance3[%d] = %f\n", k, balance3[k] ) }}以上实例执行结果如下:balance[0] = 1000.000000balance[1] = 2.000000balance[2] = 3.400000balance[3] = 7.000000balance[4] = 50.000000balance2[0] = 1000.000000balance2[1] = 2.000000balance2[2] = 3.400000balance2[3] = 7.000000balance2[4] = 50.000000balance3[0] = 0.000000balance3[1] = 2.000000balance3[2] = 0.000000balance3[3] = 7.000000balance3[4] = 0.000000
-
Go 语言变量作用域作用域为已声明标识符所表示的常量、类型、变量、函数或包在源代码中的作用范围。Go 语言中变量可以在三个地方声明:函数内定义的变量称为局部变量函数外定义的变量称为全局变量函数定义中的变量称为形式参数接下来让我们具体了解局部变量、全局变量和形式参数。局部变量在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。以下实例中 main() 函数使用了局部变量 a, b, c:实例package mainimport "fmt"func main() { /* 声明局部变量 */ var a, b, c int /* 初始化参数 */ a = 10 b = 20 c = a + b fmt.Printf ("结果: a = %d, b = %d and c = %d\n", a, b, c)}以上实例执行输出结果为:结果: a = 10, b = 20 and c = 30全局变量在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出后)使用。全局变量可以在任何函数中使用,以下实例演示了如何使用全局变量:实例package mainimport "fmt"/* 声明全局变量 */var g intfunc main() { /* 声明局部变量 */ var a, b int /* 初始化参数 */ a = 10 b = 20 g = a + b fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g)}以上实例执行输出结果为:结果: a = 10, b = 20 and g = 30Go 语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑。实例如下:实例package mainimport "fmt"/* 声明全局变量 */var g int = 20func main() { /* 声明局部变量 */ var g int = 10 fmt.Printf ("结果: g = %d\n", g)}以上实例执行输出结果为:结果: g = 10形式参数形式参数会作为函数的局部变量来使用。实例如下:实例package mainimport "fmt"/* 声明全局变量 */var a int = 20;func main() { /* main 函数中声明局部变量 */ var a int = 10 var b int = 20 var c int = 0 fmt.Printf("main()函数中 a = %d\n", a); c = sum( a, b); fmt.Printf("main()函数中 c = %d\n", c);}/* 函数定义-两数相加 */func sum(a, b int) int { fmt.Printf("sum() 函数中 a = %d\n", a); fmt.Printf("sum() 函数中 b = %d\n", b); return a + b;}以上实例执行输出结果为:main()函数中 a = 10sum() 函数中 a = 10sum() 函数中 b = 20main()函数中 c = 30
-
Go 语言函数函数是基本的代码块,用于执行一个任务。Go 语言最少有个 main() 函数。你可以通过函数来划分不同功能,逻辑上每个函数执行的是指定的任务。函数声明告诉了编译器函数的名称,返回类型,和参数。Go 语言标准库提供了多种可动用的内置的函数。例如,len() 函数可以接受不同类型参数并返回该类型的长度。如果我们传入的是字符串则返回字符串的长度,如果传入的是数组,则返回数组中包含的元素个数。函数定义Go 语言函数定义格式如下:func function_name( [parameter list] ) [return_types] { 函数体}函数定义解析:func:函数由 func 开始声明function_name:函数名称,参数列表和返回值类型构成了函数签名。parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。函数体:函数定义的代码集合。实例以下实例为 max() 函数的代码,该函数传入两个整型参数 num1 和 num2,并返回这两个参数的最大值:实例/* 函数返回两个数的最大值 */func max(num1, num2 int) int { /* 声明局部变量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }函数调用当创建函数时,你定义了函数需要做什么,通过调用该函数来执行指定任务。调用函数,向函数传递参数,并返回值,例如:实例package mainimport "fmt"func main() { /* 定义局部变量 */ var a int = 100 var b int = 200 var ret int /* 调用函数并返回最大值 */ ret = max(a, b) fmt.Printf( "最大值是 : %d\n", ret )}/* 函数返回两个数的最大值 */func max(num1, num2 int) int { /* 定义局部变量 */ var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }以上实例在 main() 函数中调用 max()函数,执行结果为:最大值是 : 200函数返回多个值Go 函数可以返回多个值,例如:实例package mainimport "fmt"func swap(x, y string) (string, string) { return y, x}func main() { a, b := swap("Google", "Runoob") fmt.Println(a, b)}以上实例执行结果为:Runoob Google
上滑加载中
推荐直播
-
空中宣讲会 2025年华为软件精英挑战赛
2025/03/10 周一 18:00-19:00
宸睿 华为云存储技术专家、ACM-ICPC WorldFinal经验 晖哥
2025华为软挑赛空中宣讲会重磅来袭!完整赛程首曝+命题天团硬核拆题+三轮幸运抽奖赢参赛助力礼包,与全国优秀高校开发者同台竞技,直通顶尖赛事起跑线!
回顾中 -
华为开发者空间玩转DeepSeek
2025/03/13 周四 19:00-20:30
马欣 华为开发者布道师
同学们,想知道如何利用华为开发者空间部署自己的DeepSeek模型吗?想了解如何用DeepSeek在云主机上探索好玩的应用吗?想探讨如何利用DeepSeek在自己的专有云主机上辅助编程吗?让我们来一场云和AI的盛宴。
即将直播 -
华为云Metastudio×DeepSeek与RAG检索优化分享
2025/03/14 周五 16:00-17:30
大海 华为云学堂技术讲师 Cocl 华为云学堂技术讲师
本次直播将带来DeepSeek数字人解决方案,以及如何使用Embedding与Rerank实现检索优化实践,为开发者与企业提供参考,助力场景落地。
去报名
热门标签