02-舞动数据类型:Golang 类型定义的奇妙之旅

这篇具有很好参考价值的文章主要介绍了02-舞动数据类型:Golang 类型定义的奇妙之旅。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

02-舞动数据类型:Golang 类型定义的奇妙之旅

📃个人主页:个人主页
🔥系列专栏:Golang基础
💬Go(又称Golang)是由Google开发的开源编程语言。它结合了静态类型的安全性和动态语言的灵活性,拥有高效的并发编程能力和简洁的语法。Go被设计用于构建可扩展、高性能的软件系统,具有优秀的内存管理和快速的编译速度,适用于Web开发、系统编程和云计算等领域。

变量

Go语言里面定义变量有多种方式。
使用 var 关键字是Go最基本的定义变量方式,与C语言不同的是Go把变量类型放在变量名后面:

//定义一个名称为“name”,类型为“type”的变量
var name type

//定义三个类型都是“type”的变量
var name1, name2, name3 type

定义变量并初始化值:

//初始化“name”的变量为“v”值,类型是“type”
var name type = v

/*
定义三个类型都是“type”的变量,并且分别初始化为相应的值
name1为v1,name2为v2,name3为v3
*/
var name1, name2, name3 type= v1, v2, v3

是不是觉得上面这样的定义有点繁琐?
有一种写法可以让它变得简单一点。
可以直接忽略类型声明,那么上面的代码变成这样了:

//Go会根据其相应值的类型来初始化它们
var name1, name2, name3 = v1, v2, v3

觉得上面的还是有些繁琐,继续简化:

//编译器会根据初始化的值自动推导出相应的类型
name1, name2, name3 := v1, v2, v3

现在是不是看上去非常简洁了?
:= 这个符号直接取代了 vartype ,这种形式叫做简短声明。
不过它有一个限制,那就是它只能用在函数内部,在函数外部使用则会无法编译通过,所以一般用 var 方式来定义全局变量。
_ (下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。
在这个例子中,将值 35 赋予 b ,并同时丢弃 34 :

_, b := 34, 35

Go对于已声明但未使用的变量会在编译阶段报错,比如下面的代码就会产生一个错误:声明了 i 但未使用。

package main
func main() {
	var i int
}

常量

所谓常量,也就是在程序编译阶段就确定下来的值,而程序在运行时无法改变该值。
在Go程序中,常量可定义为数值、布尔值或字符串等类型。
它的语法如下:

const Name = v

//如果需要,也可以明确指定常量的类型
const Pi float32 = 3.1415926

下面是一些常量声明的例子:

const Pi = 3.1415926
const i = 10000
const MaxThread = 10
const prefix = "astaxie_"

Go 常量和一般程序语言不同的是,可以指定相当多的小数位数(例如200位),若指定给 float32 自动缩短为 32bit ,指定给 float64 自动缩短为 64bit ,详情参考 Go编程语言规范 。

内置基础类型

Boolean

在Go中,布尔值的类型为 bool ,值是 true 或 false ,默认为 false 。

var flag bool // 全局变量声明
var flag1, flag2 = true, false // 忽略类型的声明
func test() {
	var flag3 bool // 一般声明
	flag4 := false // 简短声明
	flag4 = true // 赋值操作
}

数值类型

整数类型有无符号和有符号两种。
Go同时支持 intuint ,这两种类型的长度相同,但具体长度取决于不同编译器的实现。
Go里面也有直接定义好位数的类型: rune , int8 , int16 , int32 , int64byte , uint8 , uint16 , uint32 , uint64
其中 runeint32 的别称, byteuint8 的别称。
需要注意的一点是,这些类型的变量之间不允许互相赋值或操作,不然会在编译时引起编译器报错。
如下的代码会产生错误:invalid operation: a + b (mismatched types int8 and int32)

var a int8
var b int32
c := a + b

另外,尽管 int 的长度是 32bit , 但 intint32 并不可以互用。
浮点数的类型有 float32float64 两种(没有 float 类型),默认是 float64
Go还支持复数。它的默认类型是 complex128 (64位实数+64位虚数)。
如果需要小一些的,也有complex64 (32位实数+32位虚数)。复数的形式为 RE + IMi ,其中 RE 是实数部分, IM 是虚数部分,
而最后的 i 是虚数单位。下面是一个使用复数的例子:

var c complex64 = 5+5i
//output: (5+5i)
fmt.Printf("Value is: %v", c)

字符串

Go中的字符串都是采用 UTF-8 字符集编码。字符串是用一对双引号 " " 或反引号 ``括起来定义,
它的类型是 string 。

var s1 string // 声明变量为字符串的一般方法
var s2 string = "" // 声明了一个字符串变量,初始化为空字符串
func test() {
	no, yes, or := "no", "yes", "or" // 简短声明,同时声明多个变量
	s := "Konichiwa" // 同上
	s = "Bonjour" // 常规赋值
}

在Go中字符串是不可变的,例如下面的代码编译时会报错:cannot assign to s[0]

var s string = "hello"
s[0] = 'c'

但如果真的想要修改怎么办呢?下面的代码可以实现:

s := "hello"
c := []byte(s) // 将字符串 s 转换为 []byte 类型
c[0] = 'c'
s2 := string(c) // 再转换回 string 类型
fmt.Printf("%s\n", s2)

Go中可以使用 + 操作符来连接两个字符串:

s := "hello,"
m := " world"
a := s + m
fmt.Printf("%s\n", a)

修改字符串也可写为:

s := "hello"
s = "c" + s[1:] // 字符串虽不能更改,但可进行切片操作
fmt.Printf("%s\n", s)

如果要声明一个多行的字符串怎么办?可以通过``来声明:

m := `hello
    world`

``括起的字符串为 Raw 字符串,即字符串在代码中的形式就是打印时的形式,它没有字符转义,换行也将原样输出。例如本例中会输出:

hello
    world

错误类型

Go内置有一个 error 类型,专门用来处理错误信息,Go的 package 里面还专门有一个包 errors 来处理错误:

err := errors.New("error")
if err != nil {
	fmt.Print(err)
}

分组声明

在Go语言中,同时声明多个常量、变量,或者导入多个包时,可采用分组的方式进行声明。
例如下面的代码:

import "fmt"
import "os"
const I = 100
const Pi = 3.1415
const Prefix = "Go_"
var i int
var pi float32
var prefix string

可以分组写成如下形式:

import(
	"fmt"
	"os"
)
const(
	I = 100
	Pi = 3.1415
	Prefix = "Go_"
)
var(
	i int
	pi float32
	prefix string
)

iota枚举

Go里面有一个关键字 iota ,这个关键字用来声明 enum 的时候采用,它默认开始值是 0const 中每增加一行加 1

package main
import "fmt"
const (
	x = iota // x == 0
	y = iota // y == 1
	z = iota // z == 2
	w // 常量声明省略值时,默认和之前一个值的字面相同。这里隐式地说w = iota,因此w== 3。其实上面y和z可同样不用"= iota"
)
const v = iota // 每遇到一个const关键字,iota就会重置,此时v == 0
const h, i, j = iota, iota, iota //h=0,i=0,j=0 iota在同一行值相同
const (
	a = iota //a=0
	b = "B"
	c = iota //c=2
	d, e, f = iota, iota, iota //d=3,e=3,f=3
	g = iota //g = 4
)
func main() {
	fmt.Println(a, b, c, d, e, f, g, h, i, j, x, y, z, w, v)
}

除非被显式设置为其它值或 iota ,每个 const 分组的第一个常量被默认设置为它的0值,第二及后续的
常量被默认设置为它前面那个常量的值,如果前面那个常量的值是 iota ,则它也被设置为 iota

array 、slice 、map

array

array 就是数组,它的定义方式如下:

var arr [n]type

[n]type 中, n 表示数组的长度, type 表示存储元素的类型。
对数组的操作和其它语言类似,都是通过 [ ] 来进行读取或赋值:

var arr [10]int // 声明了一个int类型的数组
arr[0] = 42 // 数组下标是从0开始的
arr[1] = 13 // 赋值操作
fmt.Printf("The first element is %d\n", arr[0]) // 获取数据,返回42
fmt.Printf("The last element is %d\n", arr[9]) //返回未赋值的最后一个元素,默认返回0

由于长度也是数组类型的一部分,因此 [3]int[4]int 是不同的类型,数组也就不能改变长度。
数组之间的赋值是值的赋值,即当把一个数组作为参数传入函数的时候,传入的其实是该数组的副本,而不是它的指针。如果要使用指针,那么就需要用到后面介绍的 slice 类型了。
数组可以使用另一种 := 来声明:

a := [3]int{1, 2, 3} // 声明了一个长度为3的int数组
b := [10]int{1, 2, 3} // 声明了一个长度为10的int数组,其中前三个元素初始化为1、2、3,其它默认为0
c := [...]int{4, 5, 6} // 可以省略长度而采用...的方式,Go会自动根据元素个数来计算长度

Go支持嵌套数组,即多维数组。比如下面的代码就声明了一个二维数组:

// 声明了一个二维数组,该数组以两个数组作为元素,其中每个数组中又有4个int类型的元素
doubleArray := [2][4]int{[4]int{1, 2, 3, 4}, [4]int{5, 6, 7, 8}}
// 上面的声明可以简化,直接忽略内部的类型
easyArray := [2][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}}

slice

在很多应用场景中,数组并不能满足需求。
在初始定义数组时,并不知道需要多大的数组,因此就需要“动态数组”,在Go里面这种数据结构叫 slice
slice 并不是真正意义上的动态数组,而是一个引用类型。
slice 总是指向一个底层 arrayslice的声明也可以像 array 一样,只是不需要长度。

// 和声明array一样,只是少了长度
var fslice []int

接下来可以声明一个 slice ,并初始化数据,如下所示:

slice := []byte {'a', 'b', 'c', 'd'}

slice 可以从一个数组或一个已经存在的 slice 中再次声明。
slice 通过 array[i:j] 来获取,其中 i 是数组的开始位置, j 是结束位置,但不包含 array[j] ,它的长度是 j-i

// 声明一个含有10个元素元素类型为byte的数组
var ar = [10]byte {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// 声明两个含有byte的slice
var a, b []byte
// a指向数组的第3个元素开始,并到第五个元素结束,
a = ar[2:5]
//现在a含有的元素: ar[2]、ar[3]和ar[4]
// b是数组ar的另一个slice
b = ar[3:5]
// b的元素是:ar[3]和ar[4]

注意 slice数组 在声明时的区别:声明数组时,方括号内写明了数组的长度或使用 … 自动计算长度,而声明 slice 时,方括号内没有任何字符。
slice 有一些简便的操作:

  • slice 的默认开始位置是0, ar[:n] 等价于 ar[0:n]
  • slice 的第二个序列默认是数组的长度, ar[n:] 等价于 ar[n:len(ar)]
  • 如果从一个数组里面直接获取 slice ,可以这样 ar[:] ,因为默认第一个序列是0,第二个是数组
    的长度,即等价于 ar[0:len(ar)]

下面这个例子展示了更多关于 slice 的操作:

// 声明一个数组
var array = [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
// 声明两个slice
var aSlice, bSlice []byte
// 演示一些简便操作
aSlice = array[:3] // 等价于aSlice = array[0:3] aSlice包含元素: a,b,c
aSlice = array[5:] // 等价于aSlice = array[5:10] aSlice包含元素: f,g,h,i,j
aSlice = array[:] // 等价于aSlice = array[0:10] 这样aSlice包含了全部的元素
// 从slice中获取slice
aSlice = array[3:7] // aSlice包含元素: d,e,f,g,len=4,cap=7
bSlice = aSlice[1:3] // bSlice 包含aSlice[1], aSlice[2] 也就是含有: e,f
bSlice = aSlice[:3] // bSlice 包含 aSlice[0], aSlice[1], aSlice[2] 也就是含有:
d,e,f
bSlice = aSlice[0:5] // 对slice的slice可以在cap范围内扩展,此时bSlice包含:d,e,f,g,h
bSlice = aSlice[:] // bSlice包含所有aSlice的元素: d,e,f,g

slice 是引用类型,所以当引用改变其中元素的值时,其它的所有引用都会改变该值,例如上面的 aSlicebSlice ,如果修改了 aSlice 中元素的值,那么 bSlice 相对应的值也会改变。
从概念上面来说 slice 像一个结构体,这个结构体包含了三个元素:一个指针,指向数组中 slice 指定的开始位置,长度,即 slice 的长度最大长度,也就是 slice 开始位置到数组的最后位置的长度

Array_a := [10]byte{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}
Slice_a := Array_a[2:5]

slice 有几个有用的内置函数:

  • len 获取 slice 的长度
  • cap 获取 slice 的最大容量
  • append 向 slice 里面追加一个或者多个元素,然后返回一个和 slice 一样类型的 slice
  • copy 函数 copy 从源 slice 的 src 中复制元素到目标 dst ,并且返回复制的元素的个数

注: append 函数会改变 slice 所引用的数组的内容,从而影响到引用同一数组的其它 slice
但当 slice 中没有剩余空间(即 (cap-len) == 0 )时,此时将动态分配新的数组空间。返回的 slice 数组指针将指向这个空间,而原数组的内容将保持不变;其它引用此数组的 slice 则不受影响。
从Go1.2开始 slice 支持了三个参数的 slice ,之前一直采用这种方式在 slice 或者 array 基础上来获取一个 slice

var array [10]int
slice := array[2:4]

这个例子里面slice的容量是 8,新版本里面可以指定这个容量:

slice = array[2:4:7]

上面这个的容量就是 7-2 ,即5。这样这个产生的新的 slice 就没办法访问最后的三个元素。
如果 slice 是这样的形式 array[:i:j] ,即第一个参数为空,默认值就是0

map

map 也就是Python中字典的概念,它的格式为:

map[keyType]valueType

看下面的代码, map 的读取和设置也类似 slice 一样,通过 key 来操作,只是 sliceindex 只能是int 类型,而 map 多了很多类型,可以是 int ,可以是 string 及所有完全定义了 ==!= 操作的类型。

// 声明一个key是字符串,值为int的字典,这种方式的声明需要在使用之前使用make初始化
var numbers map[string]int
// 另一种map的声明方式
numbers = make(map[string]int)
numbers["one"] = 1 //赋值
numbers["ten"] = 10 //赋值
numbers["three"] = 3
fmt.Println("第三个数字是: ", numbers["three"]) // 读取数据
// 打印出来如:第三个数字是: 3

这个 map 就像平常看到的表格一样,左边列是 key ,右边列是值
使用 map 过程中需要注意的几点:

  • map 是无序的,每次打印出来的 map 都会不一样,它不能通过 index 获取,而必须通过 key 获取
  • map 的长度是不固定的,也就是和 slice 一样,也是一种引用类型
  • 内置的 len 函数同样适用于 map ,返回 map 拥有的 key 的数量
    map 的值可以很方便的修改,通过 numbers[“one”]=11 可以很容易的把key为 one 的字典值改为11
  • map 和其他基本型别不同,它不是thread-safe,在多个go-routine存取时,必须使用mutex lock机制

map 的初始化可以通过 key:val 的方式初始化值,同时 map 内置有判断是否存在 key 的方式。
通过 delete 删除 map 的元素:

// 初始化一个字典
rating := map[string]float32{"C":5, "Go":4.5, "Python":4.5, "C++":2 }
// map有两个返回值,第二个返回值,如果不存在key,那么ok为false,如果存在ok为true
csharpRating, ok := rating["C#"]
if ok {
	fmt.Println("C# is in the map and its rating is ", csharpRating)
} else {
	fmt.Println("We have no rating associated with C# in the map")
}
delete(rating, "C") // 删除key为C的元素

上面说过了, map 也是一种引用类型,如果两个 map 同时指向一个底层,那么一个改变,另一个也相应的改变:

m := make(map[string]string)
m["Hello"] = "Bonjour"
m1 := m
m1["Hello"] = "Salut" // 现在m["hello"]的值已经是Salut了

make 、new 操作

make 用于内建类型( mapslicechannel )的内存分配。
new 用于各种类型的内存分配。
内建函数 new 本质上说跟其它语言中的同名函数功能一样: new(T) 分配了零值填充的 T 类型的内存空间,并且返回其地址,即一个 *T 类型的值。用Go的术语说,它返回了一个指针,指向新分配的类型 T 的零值。有一点非常重要:

new 返回指针

内建函数 make(T, args)new(T) 有着不同的功能,make 只能创建 slicemapchannel ,并且返回一个有初始值(非零)的 T 类型,而不是 *T
本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。
例如,一个 slice ,是一个包含指向数据(内部 array )的指针、长度和容量的三项描述符;在这些项目被初始化之前, slicenil 。对于 slicemapchannel 来说, make 初始化了内部的数据结构,填充适当的值。
make 返回初始化后的(非零)值

零值

关于“零值”,所指并非是空值,而是一种“变量未填充前”的默认值,通常为 0
此处罗列部分类型的“零值”:

int 0
int8 0
int32 0
int64 0
uint 0x0
rune 0 //rune的实际类型是 int32
byte 0x0 // byte的实际类型是 uint8
float32 0 //长度为 4 byte
float64 0 //长度为 8 byte
bool false
string ""

Go程序设计的一些规则

Go之所以会那么简洁,是因为它有一些默认的行为:文章来源地址https://www.toymoban.com/news/detail-489385.html

  • 大写字母开头的变量是可导出的,也就是其它包可以读取的,是公有变量;小写字母开头的就是不
    可导出的,是私有变量。
  • 大写字母开头的函数也是一样,相当于 class 中的带 public 关键词的公有函数;小写字母开头的
    就是有 private 关键词的私有函数。

到了这里,关于02-舞动数据类型:Golang 类型定义的奇妙之旅的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 探索图像数据中的隐藏信息:语义实体识别和关系抽取的奇妙之旅

    关键信息抽取 (Key Information Extraction, KIE)指的是是从文本或者图像中,抽取出关键的信息。针对文档图像的关键信息抽取任务作为OCR的下游任务,存在非常多的实际应用场景,如表单识别、车票信息抽取、身份证信息抽取等。然而,使用人力从这些文档图像中提取或者收集关键

    2024年02月10日
    浏览(47)
  • Golang自定义类型与类型别名

    type myInt int32 与 type myInt = int32 ,概念并不相同 自定义类型: type myInt int32 通过这种方式定义的类型是一个 全新的类型 ,这个新类型与 int32 有相同的底层结构,但是却与 int32 类型不兼容。 尽管 myInt 的底层类型是 int32 ,但它们在类型系统中是不同的。 类型别名: type myInt

    2024年02月13日
    浏览(32)
  • Redis九种数据类型及其持久化机制:探索数据存储的奇妙世界

    目录 一、9种数据类型 3.1 Key操作 3.1.1 相关命令 练习: 3.2 String 3.2.1 结构图 3.2.2 相关命令  练习: 3.3 List(双向的链表) 3.3.1 结构图 3.3.2 相关命令 练习: 3.4 Set(无序集合) 3.4.1 结构图 3.4.2 相关命令 练习: 3.5 Zset(有序集合) 3.5.1 结构图 3.5.2 相关命令 练习 3.6 Hash 3.6.1 结构

    2024年02月16日
    浏览(31)
  • 球幕影院气膜:未来娱乐的奇妙之旅

    球幕影院气膜:未来娱乐的奇妙之旅 在科技日新月异的时代,娱乐体验的创新与演变从未停歇。气膜球幕影院,作为一项领航未来的前沿科技,正以其沉浸感和颠覆性的观影体验,吸引着人们驻足体验。 创新科技的巅峰之作 气膜球幕影院采用了一种独特的气膜技术,将轻薄

    2024年01月19日
    浏览(28)
  • 图灵日记之java奇妙历险记--数据类型与变量&&运算符

    看下面这个栗子,宝子们 输出的\\\"cheer up!!!\\\"就是字面变量 字面常量的值在程序的整个生命周期中是不变的 字面常量的分类: 字符串常量 整形常量 浮点数常量 字符常量 布尔常量:只有两种true和false 空常量 注意:字符串,整形,浮点型,字符型以及布尔型,在java中都被叫作数据类型

    2024年02月04日
    浏览(35)
  • 畅想未来感汽车HMI设计的奇妙之旅!

    当下智能电动汽车的发展势头越来越高涨,与智能电动汽车相关的汽车HMI设计也成为各个品牌重点发力的地方,汽车HMI设计正在前所未有的新高度,本篇文章就来聊聊HMI设计的那些事 ⬇⬇⬇点击获取更多设计资源 https://js.design/community?category=designsource=csdnplan=bbqcsdn777   HMI的是

    2024年02月16日
    浏览(31)
  • AI创作之旅:探索提示工程的奇妙世界

    💂 个人网站:【 海拥】【神级代码资源网站】【办公神器】 🤟 基于Web端打造的:👉轻量化工具创作平台 💅 想寻找共同学习交流的小伙伴,请点击【全栈技术交流群】 在当今信息爆炸的时代,人工智能的发展为创作者们提供了全新的可能性。本文将引导你从零开始,利用

    2024年01月23日
    浏览(48)
  • 解锁JDK 12的奇妙之旅:新特性详解

    欢迎来到我的博客,代码的世界里,每一行都是一个故事 在Java的发展历程中,每个新版本都带来了令人兴奋的功能和性能改进。JDK 12作为Java发展的最新一环,不仅引入了一系列新特性,还为开发者提供了更多工具来提高代码的可读性和性能。让我们一起踏上JDK 12的奇妙之旅

    2024年02月03日
    浏览(33)
  • Buf 教程 - 使用 Protobuf 生成 Golang 代码和 Typescript 类型定义

    Buf 是一款更高效、开发者友好的 Protobuf API 管理工具,不仅支持代码生成,还支持插件和 Protobuf 格式化。 我们可以使用 Buf 替代原本基于 Protoc 的代码生成流程,一方面可以统一管理团队 Protoc 插件的版本、代码生成配置,另一方面可以简化项目开发配置。 本文将会用两部分

    2024年02月08日
    浏览(68)
  • 渐进式编程之旅:探寻PHP函数的奇妙世界

    目录 前言 一、函数的定义和调用 1.1 初识函数 1.1.1 函数分类 1.1.2 自定义函数 1.1.3 return 1.2 参数设置 1.2.1 无参函数 1.2.2 按值传递参数 1.2.3 引用传参 1.2.4 设置参数默认值 1.2.5 指定参数类型(弱) 1.3 变量的作用域 1.3.1 变量分类 1.3.2 全局变量的使用 1.3.3 global关键

    2024年02月08日
    浏览(50)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包