1. Go项目的构建
一个Go工程中主要包含以下三个目录:
- src:源代码文件,编写程序代码xxx.go,执行命令go build xxx.go会生成*.exe文件;执行go run xxx.go可以直接运行文件
- pkg:包文件,执行go install name会在此目录下生成*.a文件,用于import
- bin:相关bin文件,执行go install xxx会在此母名生成*.exe文件,可以直接运行
go的基本命令如下:
image.png
2. 变量和常量
Go的程序是保存在多个.go文件中,文件的第一行就是package XXX声明,用来说明该文件属于哪个包(package),package声明下来就是import声明,再下来是类型,变量,常量,函数的声明。Go语言的变量声明格式为:
var 变量名 变量类型 [ = 表达式或值]
变量声明以关键字var开头,变量类型放在变量的后面,行尾无需分号。举个例子:
var name string
var age int
//批量声明,一个var带多个不同类型的变量声明
var (
a string
b int
c bool
d float32
)
类型推断
- 我们可以将变量的类型省略,编译器会根据等号右边的值来推导变量的类型完成初始化
- 在函数内部,可以使用更简略的 := 方式(省略var和type)声明并初始化变量。但是有限制:
- 不能用在函数外
- := 操作符的左边至少有一个变量是尚未声明的
常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。const同时声明多个常量时,如果省略了值则表示和上面一行的值相同。例如:
const (
n1 = 100
n2
n3
)
3 内置数据类型
类型长度(字节)默认值说明bool1falsebyte10uint8rune40代表一个UTF8字符, int32int, uint4或8032 或 64 位int8, uint810-128 ~ 127, 0 ~ 255,byte是uint8 的别名int16, uint1620-32768 ~ 32767, 0 ~ 65535int32, uint3240-21亿~ 21亿, 0 ~ 42亿,rune是int32 的别名int64, uint6480float3240.0float6480.0complex648复数,实部和虚部为32位,创建方式:- 使用函数complex创建- a := 6 + 7icomplex12816复数,实部和虚部为64位uintptr4或8以存储指针的 uint32 或 uint64 整数array值类型struct值类型string""UTF-8 字符串slicenil引用类型mapnil引用类型channelnil引用类型interfacenil接口functionnil函数nil空指针
3.1 格式化打印
fmt包支持如下几种打印方式
- fmt.Println:打印一行内容,类似std::cout,难以设置格式
- fmt.Print:打印内容,并不换行
- fmt.Printf:格式化打印,与C语言printf同理
- fmt.Sprintf:格式化打印,不同之处使返回string类型,不是打印到屏幕
格式化打印支持的格式符:
image.png
fmt.Printf("type of a is %T, size of a is %d", a, unsafe.Sizeof(a)) // a 的类型和大小
3.2 类型转换
Go语言中只有强制类型转换,没有隐式类型转换。该语法只能在两个类型之间支持相互转换的时候使用。强制类型转换的基本语法如下:
T(表达式)
4 基本语句
4.1 if语句
//可省略条件表达式括号。
//持初始化语句,可定义代码块局部变量。
//代码块左 括号必须在条件表达式尾部。
if 布尔表达式 {
//。。。
} else { //else不能单独一行,golang的自动分号插入机制导致的
//。。。
}
//另一种格式,在条件判断前执行一条指令
if statement; condition {
}
4.2 switch语句
switch var1 {
case val1:
...
case val2,val3,val4://通过用逗号分隔,可以在一个 case 中包含多个表达式
...
default:
...
}
//可以看到每个case不需要break来分割
//switch 语句还可以被用于type-switch 来判断某个interface 变量中实际存储的变量类型
switch x.(type){
case type:
statement(s)
case type:
statement(s)
/* 你可以定义任意个数的case */
default: /* 可选 */
statement(s)
}
注意:
- case可以是字符、字符串,表达式,不一定是常量
- 每个case语句块自动结束退出switch,不需要使用break
- 如果需要接着执行下一个case的内容,需要使用**fallthrough **
4.3 for循环
三种形式
for init; condition; post { }
for condition { }
for { }
//init: 一般为赋值表达式,给控制变量赋初值;
//condition: 关系表达式或逻辑表达式,循环控制条件;
//post: 一般为赋值表达式,给控制变量增量或减量。
range循环语句:range类似迭代器操作,返回 (索引, 值) 或 (键, 值)
for key, value := range oldMap {
newMap[key] = value
}
5 函数
5.1 函数定义
在 Go 语言中,函数声明通用语法如下:
func functionname(parametername type) returntype {
// 函数体(具体实现的功能)
}
//如果有连续若干个函数参数,它们的类型一致,那么无须一一罗列,只需在最后一个参数后添加该类型。
Go 语言支持一个函数可以有多个返回值(也用括号包含),并且可以给返回值命名,这样可以不在return里添加需要返回的变量:
func rectProps(length, width float64)(float64, float64) {//两个括号,一个函数参数,一个返回列表
var area = length * width
var perimeter = (length + width) * 2
return area, perimeter//返回多返回值
}
//返回值命名
func rectProps(length, width float64)(area, perimeter float64) {
area = length * width
perimeter = (length + width) * 2
return // 不需要明确指定返回值,默认返回 area, perimeter 的值
}
_ 在 Go 中被用作空白符,可以用作表示任何类型的任何值,通常用在接收函数多返回值,过滤掉不需要的返回值:
area, _ := rectProps(10.8, 5.6) // 返回值周长被丢弃
5.2 可变参数
如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最后一个参数。可变参数函数的工作原理是把可变参数转换为一个新的切片。
func find(num int, nums ...int) {
fmt.Printf("type of nums is %T\n", nums)//nums相当于整型slice
found := false
for i, v := range nums {
if v == num {
fmt.Println(num, "found at index", i, "in", nums)
found = true
}
}
if !found {
fmt.Println(num, "not found in ", nums)
}
fmt.Printf("\n")
}
func main() {
find(89, 89, 90, 95)//传入数多个参数
nums := []int{89, 90, 95}
find(89, nums...)//传入一个slice
}
嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!
无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。
点击这里找小助理0元领取:加微信领取资料
5.3 返回error信息
我们可以使用errors包或fmt包来生成error类型的对象,用于返回函数的内部错误:
//实现自定义函数同时返回err和其他返回值
package main
import (
"errors"
"fmt"
)
func f1() (int, error) { //设置多返回值
err := errors.New("I am the error") //使用errors包生成error
return 1, err
}
func f2() (int, error) {
//使用fmt包生成error
err := fmt.Errorf("I am a error created by fmt")
return 2, err
}
func main() {
a, err := f1()
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(a)
b, err := f2()
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(b)
}
5.4 指针传址参数
对于需要在函数内部修改的参数,需要使用传址参数,GO中指针和C语言使一样的,基本符号也是***和&**。
//指针传址参数,和函数返回指针
package main
import "fmt"
func fun1(value *int) *float64 {
*value += 10
myFloat := 98.5
//虽然myFloat是局部变量,但GO并不会释放它,因为所有权被转移到函数外了
return &myFloat
}
func main() {
number := 10
ret := fun1(&number)
fmt.Println(number, " ", *ret)
}
6 数组
一个数组的表示形式为[n]T。n表示数组中元素的数量,T代表每个元素的类型。使用示例如下:
var a [3]int //所有元素有默认值0
a := [3]int{12, 78, 50}//简要声明,赋值
a := [3]int{12} //只给第一个元素赋值
var b = [...]int{1, 2, 3} // 定义长度为3的int型数组, 元素为 1, 2, 3
fmt.Println(a) //数组可以直接打印出来
fmt.Println(len(a)) //打印数组长度
//打印内容
for i := range a {
fmt.Printf("a[%d]: %d\n", i, a[i])
}
for i, v := range b {
fmt.Printf("b[%d]: %d\n", i, v)
}
Go中的数组是值类型而不是引用类型。一个数组变量即表示整个数组,它并不是隐式的指向第一个元素的指针(比如C语言的数组)。这意味着当数组赋值给一个新的变量时,该变量会得到一个原始数组的一个副本。如果对新变量进行更改,则不会影响原始数组。
a := [...]string{"USA", "China", "India", "Germany", "France"}
b := a // a copy of a is assigned to b
b[0] = "Singapore" //修改b,a不会改变,这不是C++的数组基地址指针
数组的长度是数组类型的一个部分,不同长度或不同类型的数据组成的数组都是不同的类型,因此在Go语言中很少直接使用数组(不同长度的数组因为类型不同无法直接赋值),因此推荐使用切片。
7 slice切片
切片是由数组建立的一种方便、灵活且功能强大的包装(Wrapper),切片本身不拥有任何数据。它们只是对现有数组的引用。可以理解为简化版的动态数组,slice才是C++的数组指针类似的存在,修改slice就是修改原数组。
7.1 创建slice
带有T类型元素的切片由[]T表示,切片的长度是切片中的元素数,切片的容量是从创建切片的索引开始算起到数组末尾的元素数。创建slice如下:
var (
a []int // nil切片, 和 nil 相等, 一般用来表示一个不存在的切片
b = []int{} // 空切片, 和 nil 不相等, 一般用来表示一个空的集合
c = []int{1, 2, 3} // 有3个元素的切片, len和cap都为3
d = c[:2] // 有2个元素的切片, len为2, cap为3
e = c[0:2:cap(c)] // 有2个元素的切片, len为2, cap为3
f = c[:0] // 有0个元素的切片, len为0, cap为3
g []int = a[1:4] // creates a slice from a[1] to a[3]
g = make([]int, 3) // 有3个元素的切片, len和cap都为3
i = make([]int, 2, 3) // 有2个元素的切片, len为2, cap为3
j = make([]int, 0, 3) // 有0个元素的切片, len为0, cap为3
)
7.2 修改slice
切片自己不拥有任何数据。它只是底层数组的一种表示。对切片所做的任何修改都会反映在底层数组中。当多个切片共用相同的底层数组时,每个切片所做的更改将反映在数组中。
func main() {
numa := [3]int{78, 79 ,80}
nums1 := numa[:] // creates a slice which contains all elements of the array
nums2 := numa[:]
fmt.Println("array before change 1", numa)
nums1[0] = 100
fmt.Println("array after modification to slice nums1", numa)
nums2[1] = 101
fmt.Println("array after modification to slice nums2", numa)
}
//输出
//array before change 1 [78 79 80]
//array after modification to slice nums1 [100 79 80]
//array after modification to slice nums2 [100 101 80]
append函数可以追加新元素,原数组长度会变化(不是不能改变长度吗??)。其原理是当新的元素被添加到slice时,会创建一个新的数组。现有数组的元素被复制到这个新数组中,并返回这个新数组的新切片引用,新切片的容量是旧切片的两倍。
//在切片尾部追加元素
var a []int
a = append(a, 1) // 追加1个元素
a = append(a, 1, 2, 3) // 追加多个元素, 手写解包方式
a = append(a, []int{1,2,3}...) // 追加一个切片, 切片需要解包
删除切片元素:
上一篇:概念游戏