GO 语言基础学习记录

一:声明变量

在golang语言中声明变量的方式

package main

import "fmt"

func main() {

var a int = 3 //关键字 var + 变量名 + 变量指定类型 = 变量值

var b int //关键字 var + 变量名 + 变量指定类型(注意:当变量没赋值时是按照变量指定类型的默认值进行赋值,如 【var b int】最后输出的就是int类型的0 而【var b bool】则会输出bool类型的false )

var c = 4 //关键字 var + 变量名 = 变量值(注意:当变量未设置类型则会根据变量值自行补充,如【var c = 4】则c的类型为int 而【var c = "hello"】c的类型则为string)

var d, e int = 1, 2 //这是在第一个声明的基础上进行多个变量声明

var f, g int //这是在第二个声明基础上进行多个变量声明

var h, i = 7, 8 //这是在第三个声明基础上进行多个变量声明

//输出

fmt.Println(a, b, c, d, e, f, g, h, i)

//输出的值为 3 0 4 1 2 0 0 7 8

}

注意:在方法中设置变量的时候,一定要在方法体中用到,否则会报错(如果 var c int 在方法中却没有使用,则运行程序会报错!)!! -- go的全局变量不限制,仅校验方法中的变量

go中除了用 var 关键字声明的变量外,还提供了 := 的方式声明未声明过的变量。

package main

import "fmt"

func main() {

a := 3

b := "hellow"

c, d := 8, "word"

//输出

fmt.Println(a, b, c, d)

//输出的值为 3 hellow 8 word

}

注意::= 只能用于声明 未被声明过的变量( 如声明 var a int = 3 后再 a := 4 就会报错)

二:声明常量

常量的声明可以参考变量声明的第一部分-此处省略

const a int = 3 //关键字 const + 常量名 + 常量类型 = 常量值

注意:常量在方法中声明不校验是否调用、

除了上面的常量声明方式外,常量还可用作枚举

复制代码
package main

import "fmt"

func main() {
    const (
        Unknown = 0
        Female = 1
        Male = 2
    )
    //输出
    fmt.Println(Unknown, Female, Male)
    //输出值为 0 1 2
}

在常量声明时,可以引入"unsafe" 包使用 函数计算表达式,注意:必须是系统内置函数

package main

import "fmt"

import "unsafe"

func main() {

const (

a = "abc"

b = len(a)

c = unsafe.Sizeof(a)

)

const e string = "abg"

const f int = len(a)

const g = unsafe.Sizeof(a)

//输出

fmt.Println(a, b, c, e, f, g)

//输出值为 abc 3 16 abg 3 16

}

相对于常量声明中 枚举类型 有个 iota 用法 ,就是 从0开始依次递增

package main

import "fmt"

func main() {

const (

a = iota //0

b //1

c //2

d = "ha" //独立值,iota += 1

e //"ha" iota += 1

f = 100 //iota +=1

g //100 iota +=1

h = iota //7,恢复计数

i //8

)

fmt.Println(a,b,c,d,e,f,g,h,i)

}

三、运算符

go的运算符和其他编程语言基本一致

+(加)、-(减)、*(乘)、/(除)、%(取余数)、++(自增)、--(自减)、==(相等)、!=(不等)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)、&&(与)、||(或)、!(非)等等

这里着重记录一下 位运算 和 其他运算符 (了解即可,日常业务中很少用到)

位运算符:&(二进制相与)、|(二进制相或)、^(二进制相异)、<<(二进制左移)、>>(二进制右移)

package main

import "fmt"

func main() {

var a uint = 60 /* 60 = 0011 1100 */

var b uint = 13 /* 13 = 0000 1101 */

var c uint = 0

c = a & b /* 12 = 0000 1100 */ //二进制相与,当二进制码对应位都为1时记录为1

fmt.Printf("第一行 - c 的值为 %d\n", c )

c = a | b /* 61 = 0011 1101 */ //二进制相或,当二进制码对应位有一位为1时记录为1

fmt.Printf("第二行 - c 的值为 %d\n", c )

c = a ^ b /* 49 = 0011 0001 */ //二进制相异,当二进制码对应位值不同时记录为1

fmt.Printf("第三行 - c 的值为 %d\n", c )

c = a << 2 /* 240 = 1111 0000 */ //二进制左移,将左侧丢弃,右侧填补为0,<<2就是丢弃两位

fmt.Printf("第四行 - c 的值为 %d\n", c )

c = a >> 2 /* 15 = 0000 1111 */ //二进制右移,将右侧丢弃,左侧填补为0

fmt.Printf("第五行 - c 的值为 %d\n", c )

}

其他运算符:&(获取变量存储地址)、*(指针变量)

package main

import "fmt"

func main() {

var a int = 4

var ptr *int

/* & 和 * 运算符实例 */

ptr = &a /* 'ptr' 包含了 'a' 变量的地址 */

fmt.Printf("a 的值为 %d\n", a); //输出a变量的值 4

fmt.Printf("a 的值为 %d\n", &a); //输出a变量的变量存储地址,能粗浅理解为数据库的索引

fmt.Printf("*ptr 为 %d\n", *ptr); //输出的是指针变量下的值 4

fmt.Printf("ptr 为 %d\n", ptr); //输出指针变量的存储地址

}

在go中也有其他语言的 if、if...else..、switch、等。

也有:for 循环和嵌套,有 break、continue、goto语句。

注意:在go中没有while循环

四、函数、方法声明和调用

package main

import "fmt"

func main() {

var a, b int = 4,5

var c int

var s string

c = sum(a,b)

s = fmt.Sprintf("获取a+b=%d",c)

fmt.Println(s)

}

func sum(x int,y int) int{ //声明函数 关键字func + 函数名称 + 函数参数和参数类型 + 返回类型

return x+y

}

注意:声明函数的时候,必须设定参数类型和返回值类型!

这里记录一下 函数多个返回值 和 函数闭包(匿名函数) 的用法

package main

import "fmt"

func main() {

a, b := swap("Google", "Runoob")

fmt.Println(a, b)

}

func swap(x, y string) (string, string) { //设定两个string类型的返回值

return y, x

}
package main

import "fmt"

func main() {

// 定义一个匿名函数并将其赋值给变量add

add := func(a, b int) int {

return a + b

}

// 调用匿名函数

result := add(3, 5)

fmt.Println("3 + 5 =", result)

// 在函数内部使用匿名函数

multiply := func(x, y int) int {

return x * y

}

product := multiply(4, 6)

fmt.Println("4 * 6 =", product)

// 将匿名函数作为参数传递给其他函数

calculate := func(operation func(int, int) int, x, y int) int {

return operation(x, y)

}

sum := calculate(add, 2, 8)

fmt.Println("2 + 8 =", sum)

// 也可以直接在函数调用中定义匿名函数

difference := calculate(func(a, b int) int {

return a - b

}, 10, 4)

fmt.Println("10 - 4 =", difference)

}

再记录一下 go 中的方法

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

}

注意:从上面的例子中我们可以看出,在go中 方法和函数是不同的。方法在定义的时候会在 func 关键字后面 方法名的前面 设置一个接收器( c Circle) 该接收器可在方法中调用

五、变量作用域

函数方法外面的是全局变量,方法或函数内的则是局部变量,局部变量优先级大于全局变量。局部变量除非作为参数传入另一个方法或函数中,否则两个方法或函数中的局部变量不能共用。

六、数组

在go中初始化数组,需要明确个数和类型

var a [10]int //初始化数组 关键字 var + 数组名 + [数组元素个数]数组元素类型

var a = [5]int{1,2,3,4,5} //初始化数组 a 并赋值

a := [5]int{1,2,3,4,5} //省略关键字 var 初始化数组 a 并赋值

var a = [...]int{1,2,3} //省略数组元素个数,通过"..."程序会自动通过赋值确定元素个数

a := [...]int{1,2,3} //和上面同理

var a []int //设置空切片a 注意:不指定长度的数组都是切片,第七点记录切片

a := []int{} //设置空切片a

多维数组也同理 var a [][]int 初始化即可

package main

import "fmt"

func main() {

// Step 1: 创建数组

values := [][]int{} //创建二维空数组

// Step 2: 使用 append() 函数向空的二维数组添加两行一维数组

row1 := []int{1, 2, 3}

row2 := []int{4, 5, 6}

values = append(values, row1)

values = append(values, row2)

// Step 3: 显示两行数据

fmt.Println("Row 1")

fmt.Println(values[0])

fmt.Println("Row 2")

fmt.Println(values[1])

// Step 4: 访问第一个元素

fmt.Println("第一个元素为:")

fmt.Println(values[0][0])

}

七、切片

相较于数组的长度不可改变,go还提供了切片的类型也可以称之为 动态数组,即它的长度不固定,可以直接追加元素提升切片的元素数

通过**make()**函数来创建切片

var slice []type = make([]type, len) //注意:len只是定义切片的初始长度 可以为0

slice := make([]type, len)

如:

slice1 := make([]int,0) //这行代码创建了个长度为0 的slice1 切片

除了用make() 函数来创建切片外

slice2 := []int{} 和 var slice2 []int 也是初始化了一个切片

对于数组来说,切片除了不用固定长度这一个优点外,还有一个切片赋值的功能

package main

import "fmt"

func main() {

arr :=[]int {1,2,3 } //初始化一个切片 a 里面有三个值

s := arr[0:2] //将切片下标0-2 的值 赋值给s

fmt.Println(s) //输出 [1 2]

fmt.Println(len(arr)) //

fmt.Println(cap(arr)) //

}

切片除了通过[:]来获取指定下标的值,由于切片有索引,所以go很多内置函数都可作用于切片上

如:len()//获取切片长度 cap() //计算容量获得切片最大长度

八、range的用法

当需要循环数组、切片、集合等数据时,可以用for + range 的关键字来遍历。

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {

for i, v := range pow { //应用range时,会直接将循环中的key值提供给i,value值提供给v(i,v是变量类型,可以设置成你熟悉的变量名 如:k,v 也可以)

fmt.Printf("2**%d = %d\n", i, v)

}

}

除了将 pow 的值完全按照 key-value 的形式完全赋值给 i,v 外,如果只想要key,则可以省略v 如:for i := range pow{ 或者用 _ 代替 如 for i,_ := range pow {

当然,如果只想要value 不要key时也可以用 _ 代替 如: for _,v := range pow{

注意://range也可以用来枚举 字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身

九、map的用法

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,遍历 Map 时返回的键值对的顺序是不确定的。

在获取 Map 的值时,如果键不存在,返回该类型的零值,例如 int 类型的零值是 0,string 类型的零值是 ""。

Map 是引用类型,如果将一个 Map 传递给一个函数或赋值给另一个变量,它们都指向同一个底层数据结构,因此对 Map 的修改会影响到所有引用它的变量。

// 创建一个空的 Map

m := make(map[string]int)

// 创建一个初始容量为 10 的 Map

m := make(map[string]int, 10)

复制代码
// 使用字面量创建 Map
m := map[string]int{
    "apple": 1,
    "banana": 2,
    "orange": 3,
}
复制代码
// 获取键值对
v1 := m["apple"]     //map 就和 php中的数组对象类似,用值的时候直接 变量[key值] 即可
v2, ok := m["pear"]  // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值

Map很像php 中的数组对象类型,但是Map是引用类型,要注意改变其中一个值时 其他引用过Map的变量都会更改

package main

import "fmt"

func main() {

s := map[string]string{ //初始化一个map数据

"one" : "one",

"two" : "two",

"three" : "three",

}

c := s //将 s 赋值给 c

a := s["two"]

b := s["two"]

a = "for"

fmt.Println(a, b, s, c) //输出值 for two map[one:one three:three two:two] map[one:one three:three two:two]

s["two"] = "fiv"

fmt.Println(a, b, s, c) //for two map[one:one three:three two:fiv] map[one:one three:three two:fiv]

}

从上面的示例可以看出,当将整个map类型的s赋值给c时,修改s 的值,c同样会变化!

delete() 函数可以删除map中的值 delete(map变量,需要删除的key值)

十、变量类型转换

1、数值类型转换

var a float64 = 3.7415926 //定义float64类型a为3.7415926

var b int = int(a) //将a转变成int类型赋值给b

fmt.Println(a, b) //输出结果 a=3.7415926 b=3 从float转化为int时,会直接舍去小数点后的值

注意:go 不支持隐式转换类型,比如 :

复制代码
package main
import "fmt"

func main() {  
    var a int64 = 3
    var b int32
    b = a
    fmt.Printf("b 为 : %d", b)
}

会报错

复制代码
cannot use a (type int64) as type int32 in assignment
cannot use b (type int32) as type string in argument to fmt.Printf

但是如果改成 b = int32(a) 就不会报错了:

复制代码
package main
import "fmt"

func main() {  
    var a int64 = 3
    var b int32
    b = int32(a)
    fmt.Printf("b 为 : %d", b)
}
2、字符串类型转换
复制代码
var str string = "10"        //定义字符串str
var num int                  //初始化int类型num
num, _ = strconv.Atoi(str)   //将字符串转换为num的数据类型,其中 _ 位置取的是转换时的错误信息,如果想查看错误信息则需

var msg error //定义error类型 msg

num,msg = strconv.Atoi(str) //通过 msg 获取转换失败的信息

或者可以用下面方法直接获取:

number, msg = strconv.Atoi(str) //没有错误时 msg为nil

注意:使用strconv.Atoi() 函数时,要引入 strconv 包

3.接口类型转换

接口类型转换有两种情况**:类型断言** 和类型转换

类型断言用于将接口类型转换为指定类型:

value.(T)

其中 value 是接口类型的变量, T 是要转换成的类型。

如果类型断言成功,它将返回转换后的值和一个布尔值,这个布尔值表示转换是否成功。

package main

import "fmt"

func main() {

var i interface{} = "Hello, World" //定义接口变量i

str, ok := i.(string) //将i类型断言为字符串类型 返回转换后的值和断言结果

if ok { //类型断言成功

fmt.Printf("'%s' is a string\n", str)

} else { //类型断言失败

fmt.Println("conversion failed")

}

}

类型转换用于将一个接口类型的值转换为另一个接口类型:

T(value)

T 是目标接口类型,value 是要转换的值。

注意:在类型转换中,我们必须保证要转换的值和目标接口类型之间是兼容的,否则编译器会报错。

package main

import "fmt"

type Write interface { //定义接口Write 接口参数为 []byte类型数据 返回值为 int 和 error类型

Write([]byte)(int,error)

}

type WriteString struct { //定义结构体WriteString 结构体属性 str 为字符串类型

str string

}

func (ws WriteString) Write(data []byte)(int, error){

ws.str += string(data)

return len(ws.str) , nil

}

func main(){

var w WriteString

ss := "hellow_Word!"

sb := []byte(ss)

fmt.Println(w.Write(sb))

}

十一、go实现并发

Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 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() {

go say("world")

say("hello")

}

//输出的 world 和 hello 出现的顺序并不固定,也从侧面证明了 使用go关键字后确实 两次方法调用确实没有先后,是处于两条并发线程中

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

package main

import "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)

}

目前我认为通道的作用 就是用于等待时常 可以同时获取到 两个goroutine 的值。

注意:x和y的值不一定是按照执行代码先后时间来分配的,也就是说x有可能获得第二个结果

如下面的代码,只要多执行几次,总会获得 *d, *e, *f 依旧会输出 1, 2, 3 的情况 或者 其中几个值是sum 运行后的值,从而证明了 并发并不是执行完goroutine 之后再进行下面代码的执行。

package main

import "fmt"

func sum(s []int, c *int) {

sum := 0

for _, v := range s {

sum += v

}

*c = sum

fmt.Println(" sum中:",*c,"------\n")

}

func main() {

s := []int{7, 2, 8, -9, 4, 0}

a,b,c := 1,2,3

d, e, f := &a, &b, &c

go sum(s[:len(s)/2], d)

go sum(s[len(s)/2:], e)

go sum(s[2:len(s)-2], f)

fmt.Println(" 变量值 ",*d, *e, *f,"------\n")

}

或者我提供另一个佐证 执行这段代码时,会沉睡,直至x和y 接收到结果

package main

import (

"fmt"

"time"

)

func sum(s []int, c chan int) {

sum := 0

time.Sleep(10 * time.Second) //沉睡十秒

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)

}

学习时借助查看 菜鸟教程

网址:Go 语言教程 | 菜鸟教程

相关推荐
西岸行者6 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意6 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习
im_AMBER6 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode
CodeJourney_J6 天前
从“Hello World“ 开始 C++
c语言·c++·学习