golang语法基础

golang语法基础

此文章只做总结,以记住语法为准,学习go的同学应该都不是第一次学习编程语言了,既然玩了go,就要以生产实践为主。

导入

import "fmt"

起别名

import f "fmt"

批量导入,使用()来表示

import ( "fmt" "math" )

只导入不调用

import ( "fmt" _ "math" )

在go中禁止循环导入,无论直接还是间接,编译无法通过

导出

该规则适用于整个Go语言:对外暴露,首字母大写;不对外暴露,首字母小写

私有

go中约定,一个包内名为internal包为私有包,其他的包将无法访问私有包中的任何东西

数据类型

bool

true & false

在go中,整数0并不代表假值,非零整数也不能代表真值,即数字无法代替布尔值进行逻辑判断,两者是完全不同的类型

整型

uint8、uint16、uint32、uint64

int8、int16、int32、int64

uint、int

uintptr 等价于无符号64位整型

浮点型

float32、float64

字符类型

byte 等价于uint8表示ANSCII字符

rune 等价于int32表示Unicode字符

string 字节序列,可以转换为\[\]byte类型即字节切片

派生类型

数组 5int,长度为5的整型数组

切片 \[\]float64,64位浮点数切片

映射表 mapstringint ,键为字符串类型、值为整型的映射表

结构体 type Gopher struct{} Gopher结构体

指针 *int 一个整型指针

函数 type f func(),一个参数,没有返回值的函数类型

接口 type Gopher interface{},Gopher接口

通道 chan int,整型通道

零值

数字类型 0

布尔类型 false

字符串类型 ""

数组 固定长度对应类型的零值集合

结构体 内部字段都是零值的结构体

切片、映射表、函数、接口、通道、指针 nil

常量

const 修饰的必须初始化的字面量、常量表达式

变量

var 修饰的

声明

只声明不赋值,则为零值

简单声明

var intNum int

同时声明相同的数据类型

var numA,numB,numC int

同时声明多个变量

var (
name string
age int
)

赋值

先声明再赋值

var name string
name = "jack"

声明直接赋值

var name string = "jack"

同时赋值多个变量

var name string
var age int
name,age = "jack",1

语法糖

a := 1

特殊情况,允许编译通过

go 复制代码
func main() {
	a := 1
	a, b := 2, 2
	fmt.Println(a)
	fmt.Println(b)
}

交换

go 复制代码
func main() {
	num1, num2 := 10, 20
	num1, num2 = num2, num1
	fmt.Println(num1)
	fmt.Println(num2)
}

匿名变量

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

比较

类型必须相同
minVal := min(1, 2, -1, 1.2)

数组

go中的数组是值类型,而非引用,并不是指向头部元素的指针

初始化

go 复制代码
// 声明
var nums [5]int
// 元素初始化
nums := [5]int{1,2,3}
// new函数获取一个指针
nums := new([5]int)

使用

go 复制代码
fmt.Println(nums[0])
nums[0] = 1
// 元素数量
len(nums)
// 数组容量
cap(nums)

切割

左闭右开

go 复制代码
nums :=[5]{1,2,3,4,5}
nums[1:]
nums[:5]
nums[2:3]

切片

初始化

go 复制代码
var nums []int // 值
nums := []int{1,2,3} // 值
nums := make([]int,0,0) // 值
nums := new([]int) // 指针

使用

切片的使用和数组完全一样

append

go 复制代码
package main

import "fmt"

func main() {
	nums := make([]int, 0, 0)
	nums = append(nums, 1, 2, 3, 4, 5, 6, 7, 8, 9)
	fmt.Println(len(nums), cap(nums))
}

插入元素

头插入

go 复制代码
func main() {
	nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	nums = append([]int{-1, 0}, nums...)
	fmt.Println(nums)
}

中间插入

例如在i的位置插入

nums = append(nums[:i+1], append([]int{999, 999}, nums[i+1:]...)...)

尾部插入
nums = append(nums,99,100)

删除元素

从头部删除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) // []

拷贝

copy

clear

clear会将切片内所有的值置为零值

清空切片

go 复制代码
func main() {
	s := []int{1, 2, 3, 4}
    s = s[:0:0]
	fmt.Println(s)
}

字符串

在Go中,字符串本质上是一个不可变的只读的字节数组,也是一片连续的内存空间

字面量

普通字符串

支持转义、不支持多行书写

go 复制代码
"这是一个普通字符串\n"
"abcdefghijlmn\nopqrst\t\\uvwxyz"

原生字符串

支持多行书写,不支持转义

访问

go 复制代码
func main() {
	str := "hello world"
	fmt.Println(str[0])
	fmt.Println(str[0:4])
}

不支持修改

str[0]='a'

转换

go 复制代码
func main() {
	str := "this is a string"
	// 字符串转切片
	bytes := []byte(str)
	fmt.Println(bytes)
	// 切片转字符串
	fmt.Println(string(bytes))
}

字符串的内容是只读的不可变的,无法修改,但是字节切片是可以修改的

go 复制代码
func main() {
	str := "this is a string"
	// 字符串转切片
	bytes := []byte(str)
	fmt.Println(bytes)
	bytes = append(bytes, 96, 97, 98)
	// 切片转字符串
	fmt.Println(string(bytes))
}

提示:两种类型之间的转换都需要进行数据拷贝,其性能损耗会随着长度的增加而增长。

长度

字符串的长度,并不是字面量的长度,而是字节数组的长度

go 复制代码
func main() {
	str := "中国人"
	fmt.Println(len(str))
}

拷贝

本质是字节切片的拷贝

go 复制代码
func main() {
	var dst, src string
	src = "this is a string"
	desBytes := make([]byte, len(src))
	copy(desBytes, src)
	dst = string(desBytes)
	fmt.Println(src)
	fmt.Println(dst)
}

也可以使用string.clone函数,本质内部实现差不多

go 复制代码
func main() {
	var dst, src string
	src = "this is a string"
	dst = strings.Clone(src)
	fmt.Println(src, dst)
}

拼接

+ 拼接

转为字节使用append添加

go 复制代码
package main

import "fmt"

func main() {
	var str string = "hello world"
	bytes := []byte(str)
	bytes = append(bytes, "您好 世界"...)
	fmt.Println(string(bytes))
}

strings.Builder

以上两种的性能都很差,如果对性能有要求,使用strings.Builder

go 复制代码
package main

import (
	"fmt"
	"strings"
)

func main() {
	builder := strings.Builder{}
	builder.WriteString("hello world")
	builder.WriteString("hello world")
	fmt.Println(builder.String())
}

遍历

go 复制代码
func main() {
	str := "hello world 您好世界"
	for _, r := range str {
		fmt.Printf("%c", r)
	}
}

rune 本质上是int32的别名

映射表

一般来说,映射表数据结构实现通常有两种,哈希表和搜索树,区别在于前者无序,后者有序,在Go中,map的实现是基于哈希通,所以也是无序的

初始化

字面量

go 复制代码
func main() {
	mp := map[int]string{
		0: "a",
		1: "b",
		2: "c",
		3: "d",
	}
	fmt.Println(mp)
}

make

go 复制代码
func main() {
	mp := make(map[string]int, 8)
	mp["a"] = 1
	fmt.Println(mp)
}

访问

不存在,返回对应的零值

go 复制代码
func main() {
	mp := make(map[string]int, 8)
	mp["a"] = 1
	fmt.Println(mp["b"])
}

访问方式

go 复制代码
func main() {
	mp := make(map[string]int, 8)
	//mp["a"] = 1
	fmt.Println(mp["b"])

	if val, exist := mp["a"]; exist {
		// 如果存在
		fmt.Println(val)
	} else {
		// 如果不存在
		fmt.Println("mp['a'] not exist")
	}
}

删除

go 复制代码
delete(mp,"a")

遍历

go 复制代码
func main() {
	mp := map[string]int{
		"a": 1,
		"b": 2,
		"c": 3,
		"d": 4,
	}
	for k, v := range mp {
		fmt.Println(k, v)
	}
}

清空

clear(mp)

Set

Set是一种无序的,不包含重复元素的集合,Go中并没有提供类似的数据结构的实现,但是map的键正是无序且不能重复的,所以也可以使用map来替代set

go 复制代码
func main() {
	set := make(map[int]struct{}, 10)
	for i := 0; i < 10; i++ {
		set[rand.Intn(100)] = struct{}{}
	}
	fmt.Println(set)
}

提示:一个空的结构体不会占用内存

并发安全

map并不是一个并发安全的数据结构 ,Go团队认为大多数情况下map的使用并不涉及高并发的场景,引入互斥锁会极大的降低性能,map内部有读写检测机制,如果冲突会触发fatal error

在这种情况下,需要使用sync.Map来替代。

指针

Go保留了指针,在一定程度上保证了性能,同时为了更好的GC和安全考虑,又限制了指针的使用

创建

go 复制代码
func main() {
	num := 2
	p := &num
	fmt.Println(p)
}

解引用符号

  1. 访问指针所指向元素
  2. 声明一个指针
go 复制代码
func main() {
	num := 2
	p := &num
	fmt.Println(*p)

	var numPtr *int
	numPtr = &num
	fmt.Println(*numPtr)

}

指针声明初始化

go 复制代码
func main() {
   var numPtr *int = new(int)
   fmt.Println(*numPtr)

   numPtr1 := new(int)
   fmt.Println(*numPtr1)

}

new 和 make

  • new
  1. 返回值是类型指针
  2. 接收参数是类型
  3. 专用于给指针分配内存空间
  • make
  1. 返回值是值,不是指针
  2. 接收的第一个参数是类型,不定长参数根据传入类型的不同而不同
  3. 专用于给切片,映射表,通道分配内存

结构体

Go中抛弃了类与继承,同时抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法可以模拟出一个类。结构体可以存储一组不同类型的数据,是一种复合类型

声明

go 复制代码
type Person struct {
	name string
	age int
}

对于相同类型的字段,如下声明

go 复制代码
type Rectangle struct {
	height, width float32
}

注意:在声明结构体字段时,字段名和方法名不应该重复

创建

go 复制代码
package main

import "fmt"

type Programmer struct {
	Name     string
	Age      int
	Job      string
	Language string
}
 
func main() {
	programmer := Programmer{
		Name:     "jack",
		Age:      42,
		Job:      "bash",
		Language: "python",
	}
	fmt.Println(programmer)

}

组合

在Go中,结构体之间的关系是通过组合来表示的,可以显式组合,也可以匿名组合,后者使用起来更类似于继承

go 复制代码
import "fmt"

type Person struct {
	name string
	age  int
}

type Student struct {
	p      Person
	school string
}

type Employee struct {
	p   Person
	job string
}

func main() {
	student := Student{
		p:      Person{name: "jack", age: 18},
		school: "lili school",
	}
	fmt.Println(student.p.name)
}
go 复制代码
type Person struct {
	name string
	age  int
}

type Student struct {
	Person
	school string
}

type Employee struct {
	Person
	job string
}

func main() {
	student := Student{
		Person: Person{name: "jack",age: 18},
		school: "lili school",
	}
	fmt.Println(student.name)
}

指针

对于结构体而言,不需要解引用就可以直接访问结构体的内容

go 复制代码
type Person struct {
	name string
	age  int
}

func main() {
	p := &Person{
		name: "jack",
		age:  18,
	}
	fmt.Println(p.age)
}

标签

结构体标签是一种元编程的形式,结合反射可以做出很多奇妙的功能

标签是一种键值对的形式,使用空格进行分隔。

结构体的容错性很低,如果没能按照正确的格式书写结构体,那么将导致无法正常读写,但是编译时不会有任何报错

go 复制代码
type Programmer struct {
	Name string `json:"name"`
	Age  int `json:"age"`
}

空间结构体

空间结构体没有字段,不占用内存空间,可以通过unsafe.SizeOf函数计算占用字节大小

go 复制代码
func main() {
	type Empty struct {
	}
	fmt.Println(unsafe.Sizeof(Empty{}))
}

应用:例如 将map作为set来使用

相关推荐
红尘散仙13 分钟前
我把终端小说阅读器接上了 AI Agent:TRNovel 现在能用 skill 生成书源了
人工智能·后端·rust
卷毛的技术笔记2 小时前
告别硬编码!Spring AI Alibaba 实现 AI Agent 智能工具调用(Tool Calling)
java·人工智能·后端·python·spring·ai编程
isyangli_blog2 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008112 小时前
FastAPI APIRouter
开发语言·python
Benszen2 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆2 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木2 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
喵个咪2 小时前
GoWind Toolkit Go后端代码生成 完整全流程实战
后端·go·orm
杨充2 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~2 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言