package main
import (
"errors"
"fmt"
"math"
"time"
)
//通道关闭
func fibonacci1(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 say(s string) {
for i := 0; i < 10; i++ {
time.Sleep(1000 * time.Millisecond)
fmt.Println(s)
}
}
//两个 goroutine 来计算数字之和
func Sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // 把 sum 发送到通道 c
}
//Printf 可以格式化输出
//Println 直接输出
//只支持显示类型转换
//type point struct{
// x, y int
//}
//
这种不带声明格式的只能在函数体中出现
g, h := 123, "hello"
//var ( // 这种因式分解关键字的写法一般用于声明全局变量
// a int
// b bool
//)
//
常量可以用len(), cap(), unsafe.Sizeof()函数计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过
//const (
// c = "abc"
// d = len(c)
// g = unsafe.Sizeof(c)
//)
//
//var e, f = 123, "hello"
// 结构体
type Books struct {
title, author, subject string
book_id int
}
//切片
func printSlice(x []int) {
fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x)//切片用%v打印
}
//递归
func fibonacci(n int) int {
if n < 2 {
return n
}
return fibonacci(n-2) + fibonacci(n-1)
}
func jiecheng(n uint64) uint64 {
if n > 0 {
return n * jiecheng(n - 1)
}
return 1
}
//接口
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!")
}
//错误处理
type error interface {
Error() string
}
func sqrt(f float64) (float64, error) {
if f < 0 {
return 0, errors.New("sqrt root of negative number")
}
return math.Sqrt(f), nil
}
func main() {
//fmt.Println("Hello, World!")
//fmt.Println("菜鸟教程:runoob.com")
//fmt.Println("Google" + " Runoob")
//
变量声明
//var stockcode = 123
//var enddate="2022-04-07"
//var url= "Code=%d&endDate=%s"
//var strUrl = fmt.Sprintf(url, stockcode, enddate)
//fmt.Println(strUrl)
//
常量 常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
//const name, age = "laven", 30
//s := fmt.Sprintf("%s is %d years old\n", name,age)
//fmt.Println(s)
//io.WriteString(os.Stdout, s)//需要导入上边io os库
使用内置函数
//println(c, d, g)
//
默认值
//var i int
//var f float64
//var b bool
//var s string
//fmt.Println(i, f, b, s)//默认和别的没啥区别
//
/各自声明
//var b0 bool = true
//var b1 uint8 = 254
//var b2, b3 string = "256", "lisi"
//
var intVal int
//intVal :=1 // 这时候会产生编译错误,因为 intVal 已经声明,不需要重新声明 no new variables on left side of :=
//b4 := 15//根据值自行判定变量类型。
//fmt.Println(b0,b1,b2,b3, intVal, b4, a, b, e, f) //b declared and not used
//iota,特殊常量,可以认为是一个可以被编译器修改的常量。
//iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次
struct
//p := point{1, 2}
//fmt.Printf("%v\n", p)//不能用println
//fmt.Printf("%#v\n", p)
运算符
//g := true
//h := false
b2 := g && h
//if g && h {//if else 格式比较固定, 加不加括号都行
// fmt.Println("true")
//} else {
// fmt.Println("false")
//}
//if (!(g && h)) {
// fmt.Println("false")
//}
//a := 10
//if a < 20 {
// fmt.Printf("a 小于 20\n" )
//}
常规位运算
//a := 60
//b := 13
//fmt.Println(a&b)
//fmt.Println(a|b)
//fmt.Println(a >> 1)
//fmt.Println(b << 1)
switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case
如果我们需要执行后面的 case,可以使用 fallthrough
//var grade string = "B"
//marks := 90
//switch marks {
// case 90: grade = "A"
// case 80: grade = "B"
//default:
// grade = "C"
//}
//switch {//可以不加任何变量,相当于true false
// case grade == "A":
// fmt.Println("优秀")
// //fallthrough
// case grade == "B":
// fmt.Println("良好")
// //fallthrough
//default:
// fmt.Println("一般")
//}
//
//a := 3
//switch a {
//case 1,2,3,4: //支持多条件匹配
// fmt.Printf("case a %d\n", a)//Printf 格式化输出
//default:
// fmt.Println("default a %d", a) //Println 不能格式化输出
//}
//循环语句,不需要加括号
//sum := 0
//for i := 0; i <= 10; i++ {
// sum +=i
//}
//fmt.Println(sum)
//sum := 0
//for {//死循环
// sum +=1
//}
//fmt.Println(sum)
//for true {
// println("死循环")
//}
//str := []string{"zhang", "san", "li", "si"}
//for i, s := range str {
// fmt.Println(i, s)
//}
//num := [6]int{1,2,3,4,5}
//for i, s := range num {
// fmt.Println(i, s)
//}
数组
//n := [10]int{}
//var i, j int
//for i = 0; i < 10; i++ {
// n[i] = i + 100
//}
//for j = 0; j < 10; j++ {
// fmt.Println(n[j])
//}
map集合
//countryMap := make(map[string]string)
//countryMap [ "France" ] = "巴黎"
//countryMap [ "Italy" ] = "罗马"
//countryMap [ "Japan" ] = "东京"
//countryMap [ "India " ] = "新德里"
//for country := range countryMap { //获取的相当于index,实际是key, 默认获取index
// fmt.Println(country, countryMap[country])
//}
集合是否存在
//capital, ok := countryMap [ "India "]
//if ok {
// fmt.Println("India 首都:", capital)
//} else {
// fmt.Println("India 首都不存在")
//}
//delete(countryMap, "India ")
//fmt.Println("删除India")
//for country1 := range countryMap { //获取的相当于index,实际是key
// fmt.Println(country1, countryMap[country1])
//}
//for _, capital1 := range countryMap {//不获取index,直接获取值
// fmt.Println(capital1)
//}
///* 定义局部变量 质数或者素数*/
//var i, j int
//for i=2; i < 100; i++ {
// for j=2; j <= (i/j); j++ {
// if(i%j==0) {
// break; // 如果发现因子,则不是素数
// }
// }
// if(j > (i/j)) {
// fmt.Printf("%d 是素数\n", i);
// }
//}
range也可以用来枚举 Unicode 字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
//for i, c := range "Bingo\a" {
// fmt.Println(i, c)
//}
结构体
//book := Books{"Go 语言", "www.runoob.com", "Go 语言教程", 6495407}
//book1 := Books{title: "Go 语言", author: "www.runoob.com", subject: "Go 语言教程", book_id: 6495407}
//var book2 Books
//book2.title = "c++"
//var book3 *Books
book3 = &book//不同于C++ 好像不能创建
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x47e3cf]
//book3.title ="1689"//不同于C++ 指针用的还是. 必须有前边一行,不然会段错误
//book3.book_id =168
//fmt.Println(book)
//fmt.Println(book1)
//fmt.Println(book2) 忽略的字段为 0 或 空 {c++ 0}
//fmt.Println(*book3)
指针
//a := 20
//var ip * int
//ip = &a
//fmt.Printf("a 变量的地址是: 0x%x\n", &a )
///* 指针变量的存储地址 */
//fmt.Printf("ip 变量储存的指针地址: 0x%x\n", ip )
///* 使用指针访问值 */
//fmt.Printf("*ip 变量的值: %d\n", *ip )
//
nil 指针也称为空指针。
nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。
//var ptr * int
//fmt.Printf("ptr %x", ptr)//0
//if(ptr != nil) {fmt.Println(" 非空")}
//if(ptr == nil) {fmt.Println(" 空")}
与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大
可以理解成数组不指定长度
定义
var identifier []type
slice1 := make([]type, len)
make([]T, length, capacity)
初始化 通过内置函数 make() 初始化切片s,[]int 标识为其元素类型为 int 的切片
s :=[] int {1,2,3 }
s := arr[startIndex:endIndex]
//var nums = make([]int, 3, 5)
//printSlice(nums) //len=3 cap=5 slice=[0 0 0]
///* 创建切片 */
//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)
append copy
//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)
//递归
//for i := 1; i <= 10; i++ {
// //fmt.Println(fibonacci(i))
// fmt.Printf("%d ", fibonacci(i))
//}
//阶乘
//var i uint64 = 15
//fmt.Println(jiecheng(i))
//
// //接口
// var phone Phone
// phone = new(NokiaPhone)
// phone.call()
// phone = new(IPhone)
// phone.call()
错误处理
result, err := sqrt(-1) //传递的一个负数,然后就得到了non-nil的error对象
//result, err := sqrt(1.44)
//if err != nil {
// fmt.Println(err)
//} else {
// fmt.Println(result)
//}
类似线程
并发 Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。
//go say("hello")
//say("world")
//通道(channel)是用来传递数据的一个数据结构。
//通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
//默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据
//ch <- v // 把 v 发送到通道 ch
//v := <-ch // 从 ch 接收数据 并把值赋给 v
两个 goroutine 来计算数字之和,不加缓冲区的
// 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
// fmt.Println(x, y, x+y)
//如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。
//如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;
//如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。
//接收方在有值可以接收之前会一直阻塞。
// 缓冲区大小为2
//ch := make(chan int, 2)
//ch <- 1
//ch <- 2
ch <- 3
ch <- 3 //只能塞2个,塞多了就会 fatal error: all goroutines are asleep - deadlock!
fmt.Println(<-ch)
//fmt.Println(<-ch)
//fmt.Println(<-ch)
for {
fmt.Println(<-ch)
}
//c := make(chan int, 10)
//go fibonacci1(cap(c), c)
range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个
数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据
之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不
会结束,从而在接收第 11 个数据的时候就阻塞了,报错 fatal error: all goroutines are asleep - deadlock!
//for i := range c {
// fmt.Printf("%d ", i)
//}
}