go笔记
一.参考资料
https://golang.halfiisland.com/essential/base/0.ready.html
主要根据这两学的
感觉像C与pyhon的中间态,不过偏向python更多
二.包
1.package
每个Go程序都从包开始,同一个目录下的包名必须相同,如果要生成可执行文件,main函数所在的包必须得命名为main
如何引用自己本地的包?
假设你的文件结构是这样
text-->
main.go
hh-->
a.go
b.go
其中tools是你自己的工具包
你需要在text目录下创建mod,具体指令是go mod init text,你也可以变成go mod init 66.com/66/text反正只要你最后一个是你的根目录名字就行
如果要导入就得使用import
按这个结构第一种方法就是import"text/hh"
然后假如a,b的包名,也就是一开始声明的package为tools
直接在主函数里使用就行了tools.XXX()
注意工具包里的函数的首字母一定要大写,这是公有的体现,如果是小写就会报错
2.import
导入一个包就是导入这个包的所有公有的类型/变量/常量,导入的语法就是import加上包名
可以这样写
package main
import "example"
import "example1"
也可以这样写:
package main
import (
"example"
"example1"
)
起别名
package main
import (
e "example"
e1 "example1"
)
不过注意引用的包必须使用,不然会报错
但…………
我就简单写个hello,word
然后go build编译
注意到程序大小为什么那么大,用ida一看…………

??????
为什么什么包都编译上去了
不许我做
就许你做是吧,我本来还以为是减少储存占用才这样……
三.数值
1.声明:
声明一个变量的基本语法是 var,类型声明放在最后
var a,b,c,d int
也可以像这样
使用:=,go会根据后面的数据自动声明类型
a:=10//int
b:="a"//string
c:=True//bool
2.常量:
常量不能用 := 语法声明。
只能const
四,循环
只有for循环,for中条件不用加括号,其他和C都一样
1.for range
for range可以更加方便的遍历一些可迭代的数据结构,如数组,切片,字符串,映射表,通道。语句
用法如下:
index为可迭代数据结构的索引,value则是对应索引下的值,例如使用for range遍历一个字符串。
func main() {
sequence := "hello world"
for index, value := range sequence {
fmt.Println(index, value)
}
}
也可以当成python用
for range也可以迭代一个整型值,字面量,常量,变量都是有效的。
for i := range 10 {
fmt.Println(i)
}
n := 10
for i := range n {
fmt.Println(i)
}
const n = 10
for i := range n {
fmt.Println(i)
}
五,条件判断
1.if
条件不用加括号,其他和C一样
不过多了可以在条件之前可以执行一个简短语句
2.switch
Go 只会运行选定的 case,而非之后所有的 case。 在效果上,Go 的做法相当于这些语言中为每个 case 后面自动添加了所需的 break 语句。在 Go 中,除非以 fallthrough 语句结束,否则分支会自动终止。 Go 的另一点重要的不同在于 switch 的 case 无需为常量,且取值不限于整数。
并将case从上到下执行
package main
import (
"fmt"
"runtime"
)
func main() {
fmt.Print("Go 运行的系统环境:")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("macOS.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.\n", os)
}
}
六,输入输出
1.输出:
fmt.Println标准的
os.stdout用来输出文件,也可以用来输出字符串
2.输入
// 扫描从os.Stdin读入的文本,根据空格分隔,换行也被当作空格
func Scan(a ...any) (n int, err error)
// 与Scan类似,但是遇到换行停止扫描
func Scanln(a ...any) (n int, err error)
// 根据格式化的字符串扫描
func Scanf(format string, a ...any) (n int, err error)
读取两个数字
func main() {
var a, b int
fmt.Scanln(&a, &b)
fmt.Printf("%d + %d = %d\n", a, b, a+b)
}
读取固定长度的数组
func main() {
n := 10
s := make([]int, n)
for i := range n {
fmt.Scan(&s[i])
}
fmt.Println(s)
}
七,数组与切片
数组甚至不能动态定义大小,连C都不如
切片感觉没有python灵活好用,限制好多
1,数组
数值声明只能是一个常量不能是一个变量
// 正确示例
var a [5]int
nums := [5]int{1, 2, 3}
nums := [...]int{1, 2, 3, 4, 5} //等价于nums := [5]int{1, 2, 3, 4, 5},省略号必须存在,否则生成的是切片,不是数组
nums := new([5]int)//获取指针
// 错误示例
l := 1
var b [l]int
可以通过len()来看数组数据大小
用cap()来看数组容量
还有一些类python的性质,不过数组在切割后,就会变为切片类型
nums := [5]int{1, 2, 3, 4, 5}
nums[1:] // 子数组范围[1,5) -> [2 3 4 5]
nums[:5] // 子数组范围[0,5) -> [1 2 3 4 5]
nums[2:3] // 子数组范围[2,3) -> [3]
nums[1:3] // 子数组范围[1,3) -> [2 3]
2.切片
切片在 Go 中的应用范围要比数组广泛的多,它用于存放不知道长度的数据,且后续使用过程中可能会频繁的插入和删除元素。
类似于python中的列表
初始化
var nums []int // 值
nums := []int{1, 2, 3} // 值
nums := make([]int, 0, 0) // 值
nums := new([]int) // 指针
插入元素
切片元素的插入也是需要结合append函数来使用,现有切片如下,
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
从头部插入元素
nums = append([]int{-1, 0}, nums...)
fmt.Println(nums) // [-1 0 1 2 3 4 5 6 7 8 9 10]
从中间下标 i 插入元素
nums = append(nums[:i+1], append([]int{999, 999}, nums[i+1:]...)...)
fmt.Println(nums) // i=3,[1 2 3 4 999 999 5 6 7 8 9 10]
从尾部插入元素,就是append最原始的用法
nums = append(nums, 99, 100)
fmt.Println(nums) // [1 2 3 4 5 6 7 8 9 10 99 100]
这个比不上python里的列表,如果想切片插切片只能这样做,b后加...
a :=[5]int{1,2,3,4,5}
b := a[2:]
b = append(b,6)
c:=a[:]
c=append(c,b...)
删除元素
切片元素的删除需要结合append函数来使用,现有如下切片
nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
从头部删除 n 个元素
nums = nums[n:]
fmt.Println(nums) //n=3 [4 5 6 7 8 9 10]
从尾部删除 n 个元素
nums = nums[:len(nums)-n]
fmt.Println(nums) //n=3 [1 2 3 4 5 6 7]
从中间指定下标 i 位置开始删除 n 个元素
nums = append(nums[:i], nums[i+n:]...)
fmt.Println(nums)// i=2,n=3,[1 2 6 7 8 9 10]
删除所有元素
nums = nums[:0]
fmt.Println(nums) // []
拷贝
切片在拷贝时需要确保目标切片有足够的长度,例如
func main() {
dest := make([]int, 0)
src := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
fmt.Println(src, dest)
fmt.Println(copy(dest, src))
fmt.Println(src, dest)
}
[1 2 3 4 5 6 7 8 9] []
0
[1 2 3 4 5 6 7 8 9] []
将长度修改为 10,输出如下
[1 2 3 4 5 6 7 8 9] [0 0 0 0 0 0 0 0 0 0]
9
[1 2 3 4 5 6 7 8 9] [1 2 3 4 5 6 7 8 9 0]
八,字符串
和python一样无法修改,但可以转换为字节数组,然后修改
str := "this is a string"
// 显式类型转换为字节切片
bytes := []byte(str)
fmt.Println(bytes)
// 显式类型转换为字符串
fmt.Println(string(bytes))
然后其他的东西基本和python相同。
九,映射表?字典!
在 Go 中,map的实现是基于哈希桶(也是一种哈希表),所以是无序的
但比python差一点,不能镶嵌字典
初始化
map[keyType]valueType{}
例子
mp := map[int]string{
0: "a",
1: "a",
2: "a",
3: "a",
4: "a",
}
mp := map[string]int{
"a": 0,
"b": 22,
"c": 33,
}
或者make
map 是引用类型,零值或未初始化的 map 可以访问,但是无法存放元素,所以必须要为其分配内存。
mp := make(map[string]int, 8)
mp := make(map[string][]int, 10)
十,指针
灵活性比C差一点,其他和C一样
不能指针运算!
创建
关于指针有两个常用的操作符,一个是取地址符&,另一个是解引用符*。对一个变量进行取地址,会返回对应类型的指针
var p *int
a:=1
p=&a
fmt.Println(*p)
fmt.Println(p)
1
0xc00010c0f0
或者
p:=new(int)
a:=1
p=&a
fmt.Println(*p)
fmt.Println(p)
}
十一,函数
声明
func sum(a int, b int) int {
return a + b
}
//或
var sum = func(a int, b int) int {
return a + b
}
返回值可以像python一样有多个
匿名函数
匿名函数就是没有签名的函数,例如下面的函数func(a, b int) int,它没有名称,所以我们只能在它的函数体后紧跟括号来进行调用。
func main() {
func(a, b int) int {
return a + b
}(1, 2)
}
也可以这样,在参数里创建一个函数
slices.SortFunc(people, func(p1 Person, p2 Person) int {
if p1.Name > p2.Name {
return 1
} else if p1.Name < p2.Name {
return -1
}
return 0
}
)
闭包
和python里的Lamda差不多
不过我没有怎么用过
例子
func main() {
grow := Exp(2)
for i := range 10 {
fmt.Printf("2^%d=%d\n", i, grow())
}
}
func Exp(n int) func() int {
e := 1
return func() int {
temp := e
e *= n
return temp
}
}
输出
2^0=1
2^1=2
2^2=4
2^3=8
2^4=16
2^5=32
2^6=64
2^7=128
2^8=256
2^9=512
延迟调用defer
例子
func main() {
Do()
}
func Do() {
defer func() {
fmt.Println("1")
}()
fmt.Println("2")
}
输出
2
1
不过这个只能延迟返回值,其他的照旧,就像这样,猜猜有返回什么?
func main() {
defer fmt.Println(a())
fmt.Println("c")
}
func a()int{
fmt.Println("a")
fmt.Println("b")
return 1
}
输出
a
b
c
1
十二,结构体
Go没有类,但可以用结构体达成类似的效果
声明
type Programmer struct {
Name string
Age int
Job string
Language []string
}
//或者这样
type Rectangle struct {
height, width, area int
color string
}
实例化
go不存在构造函数
只能像字典一样实例化
programmer := Programmer{
Name: "jack",
Age: 19,
Job: "coder",
Language: []string{"Go", "C++"},
}
或者像这样
type Person struct {
Name string
Age int
Address string
Salary float64
}
func NewPerson(name string, age int, address string, salary float64) *Person {
return &Person{Name: name, Age: age, Address: address, Salary: salary}
}
func main() {
person := NewPerson("John", 30, "New York", 10000.0)
fmt.Println(*person)
}
选项模式
type PersonOptions func(p *Person)
type Person struct {
Name string
Age int
Address string
Salary float64
Birthday string
}
type PersonOptions func(p *Person)
func WithName(name string) PersonOptions {
return func(p *Person) {
p.Name = name
}
}
func WithAge(age int) PersonOptions {
return func(p *Person) {
p.Age = age
}
}
func WithAddress(address string) PersonOptions {
return func(p *Person) {
p.Address = address
}
}
func WithSalary(salary float64) PersonOptions {
return func(p *Person) {
p.Salary = salary
}
}
实际声明的构造函数签名如下,它接受一个可变长PersonOptions类型的参数。
func NewPerson(options ...PersonOptions) *Person {
// 优先应用options
p := &Person{}
for _, option := range options {
option(p)
}
// 默认值处理
if p.Age < 0 {
p.Age = 0
}
......
return p
}
这样一来对于不同实例化的需求只需要一个构造函数即可完成,只需要传入不同的 Options 函数即可
func main() {
pl := NewPerson(
WithName("John Doe"),
WithAge(25),
WithAddress("123 Main St"),
WithSalary(10000.00),
)
p2 := NewPerson(
WithName("Mike jane"),
WithAge(30),
)
}
十三,方法
方法与函数的区别在于,方法拥有接收者,而函数没有,且只有自定义类型能够拥有方法。先来看一个例子。
方法只有自定义类型才能使用
i可以换成其他的,和其他语言中的this,self差不多
package main
import ("fmt";
)
type IntSlice []int
func (i IntSlice) Get(index int) int {
return i[index]
}
func (i IntSlice) Set(index, val int) {
i[index] = val
}
func (i IntSlice) Len() int {
return len(i)
}
func main() {
var intSlice IntSlice
intSlice = []int{1, 2, 3, 4, 5}
fmt.Println(intSlice.Get(2))
intSlice.Set(0, 2)
fmt.Println(intSlice)
fmt.Println(intSlice.Len())
}
十四,泛型
泛型使得一个函数可以处理不同类型的数据
例如
我想实现两个数间的加法,对于不同类型得用不同的函数,太过于麻烦了
func Sum(a, b int) int {
return a + b
}
func SumFloat64(a, b float64) float64 {
return a + b
}
可以像这样实现
func Sum[T int | float64](a, b T) T {
return a + b
}
十五,接口
接口是方法的集合
就我理解的和面向对象的区别:
面向对象是现有一个对象,比如说人
人可以走,可以跳
而走,跳就是人的方法
而Go这种不是
是面向方法的
现有走与跳
然后想什么可以做这些事
人可以走,也能跳
动物也能走和跳
这就是我理解的不同
声明
使用 interface关键字
type Person interface {
Say(string)
}
方法的实现与应用
type XiaoMing struct{
world string
}
func (i XiaoMing)Say(s string){
i.world = s
fmt.Println(i.world)
}
type XiaoHong struct{
world string
}
func (i XiaoHong)Say(s string){
i.world = s
fmt.Println(i.world)
}
func main() {
people:=People(XiaoMing{})
people.Say("hello,XiaoHong")
people=People(XiaoHong{})
people.Say("hello,XiaoMing")
}
后记
暂时就这么多,以后遇到再添加,能看懂代码就行
本文来自博客园,作者:漫宿骄盛,转载请注明原文链接:https://www.cnblogs.com/msjs/p/18831549
都是顺手发的,写的时候可能有错误,如果发现了,望各位指出。

浙公网安备 33010602011771号