您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > Go语言

golang基本数据类型讲解

时间:2023-05-20 16:49:19  来源:  作者:干饭人小羽

Go语言中,数据类型分为**静态类型**和**底层类型**,感觉底层类型是golang实现的时候所使用的C语言的类型,而静态类型仅仅是针对于go语言本身而言所定义好的类型。具体的信息可以查看$
GOROOT/src/runtime/runtime.h 可以看到golang中的byte类型,通过`typedef uint8 byte`来定义,这样在golang中直接使用反射



package mAIn

import (

	"fmt"

	"reflect"

)

func main() {

	var b byte = 'D'

	fmt.Println(reflect.TypeOf(b))

}

//output: uint8
 

这里通过反射得到的是uint8,8个bit表示的无符号整数类型。

string

可以看到string类型的结构体就是



struct String

{

    byte* str;

    intgo len;

}
 

字符数组存储实际的数据,len存储长度。通过结构体就可以看到,定义好string变量之后是不能动态增加的,因为len已经写入进去了。

strconv golang中string与基本数据类型之间的转化
fmt.sprintf 函数也可以将interface类型转化为string类型

http://blog.csdn.NET/siddontang/article/details/23541587
string之间的类型的比较 strings.Equal??

slice

slice为了实现动态增长,又多添加了一个元素cap,这个元素表示已经分配的元素,就是相当于总的空间,len表示对于用户使用而言这个slice的实际长度。这个和JAVA中的Arraylist感觉有点类似,就是先分配一个空间,之后看空间不够的话,再动态扩展,但是扩展的过程对使用者来说是不可见的:



struct Slice

{

    byte* array;

    uintgo len;

    uintgo cap;

}
 

注意slice的第一个参数是一个byte指针,实际上指向的就是底层数组的对应的位置。
这里补充一点,关于初始化的问题,make函数可以用于对slice、map 以及 channel对象进行初始化操作,这三个对象的背后使用了必须要初始化的结构,比如对于slice , 要是不初始化的话,各个值都是nil 显然是没没法使用的。关于make与new的区别可以参考这个博客

下面是一个slice和string转化的例子,可以用来更好地了解slice与string以及底层数组的关系,不同的生成slice的方式,内部的操作是不同的。



package main

import (

	"fmt"

	"unsafe"

)

func main() {

	//example 1

	var slice []int32 = make([]int32, 5, 10)

	//p是指向slice类型的一个指针

	//这里是模拟一个slice类型 然后转化过来

	p := (*struct {

		array uintptr

		len   int

		cap   int

	})(unsafe.Pointer(&slice))

	//para v will print the go type

	//If the value is a struct,

	//the %+v variant will include the struct’s field names

	fmt.Printf("output:%+vn", p)

	//example 2

	//声明数组的时候 没有显式指定数组的长度 而是通过特殊的标记 ...

	//告诉编译器 让编译器去自动地计算

	var array = [...]int32{1, 2, 3, 4, 5}

	//数组进行截取 转化为slice 此时自动给len 和 cap 赋了值

	var sliceb = array[2:4]

	fmt.Printf("before modify: array=%+v, slice = %+vn", array, sliceb)

	//此时 slice 中用的底层数组和 array 是同一个

	//此时 slice[0]的位置 指向的实际是底层数组中 元素3所在的位置

	sliceb[0] = 678

	fmt.Printf("after modify: array=%+v, slice = %+vn", array, sliceb)

	//example 3

	//注意 如果使用的是Append方式生成新的slice

	//就不会有类似的效果 因为采用append方式会分配新的底层数组

	array = [...]int32{1, 2, 3, 4, 5}

	var slicec = array[1:3]

	slicec = append(slicec, 6, 7, 8)

	//可以对比这次结果 发现 两次array的输出是一样的 并没有因为 slice的修改而对array造成影响

	fmt.Printf("before modify: array=%+v, slice = %+vn", array, sliceb)

	slicec[0] = 123

	fmt.Printf("after modify: array=%+v, slice = %+vn", array, sliceb)

	//注意 从slice 生成slice 的时候原理也是类似的 直接用slicea=sliceb[para1:para2]的语法

	//使用的是同一个底层的数组 要是通过append方式 则会重新分配底层数组

}

/* 输出:

output:before modify: array=[1 2 3 4 5], slice = [3 4]

after modify: array=[1 2 678 4 5], slice = [678 4]

before modify: array=[1 2 3 4 5], slice = [3 4]

after modify: array=[1 2 3 4 5], slice = [3 4]

*/
 

map

map类型、slice类型、以及通道类型,都是引用类型,引用类型就是说这个结构总会持有一个指针,保持对底层某个结构的引用。作为对比,数组类型就是值类型。对于引用类型,在使用的时候,具体声明了之后,还要进行初始化,只有这样才能建立起和底层数据结构之间的联系。使用字典的时候,要不就直接声明+初始化,要不就声明之后使用make初始化。

//直接声明加赋值

mb := map[int]string{1: "hello", 2: "go"}

//直接声明并初始化

m := make(map[string]int)

之后就可以直接使用了。

array

array定义的时候,通过[n]类型进行定义,可以在定义的时候就把值赋进去,比如[3]strng{"a","b","c"} 还可以在[]中使用...标记,这样编译器会自动识别数组的长度,并且进行初始化。

interface以及类型转化

接口类型的实现比较复杂,整理出几条关于接口的要点,实际中,关于断言表达式的地方总是用的不太好。这个帖子关于断言说的比较好。这个文章介绍也比较好。

go 语言是编译型的,有一次写了这样的代码 :

containerpid = fmt.Sprintf("%d", containerpid)

之后是对containerpid进行一些字符串的操作,比如字符串的拼接,在编译的时候就会报如下的错误:

invalid operation: "teststring" + containerpid (mismatched types string and interface {})

主要原因是因为go并不是那种动态执行的语言,因为是要先编译好再执行,在编译器看来,这个contaierid还是没有转化成strig类型(??) 除非不使用同名的变量,另外新起一个变量名字,这样编译时才能通过。

(1)普通类型向接口类型的转化是隐式的,接口类型向普通类型转换需要类型断言。由于interface{}包含了0个方法,所以任何类型都实现了interface{}接口,这就是为什么可以将任意类型值赋值给interface{}类型的变量,包括nil,下面是隐式转化的例子:

package main

import (

	"fmt"

	"reflect"

)

func main() {

	var val interface{} = "hello"

	fmt.Println(reflect.TypeOf(val)) //output: string

	fmt.Println(val)

	val = []byte{'a', 'b', 'c'}

	fmt.Println(reflect.TypeOf(val)) //output: string

	fmt.Println(val)

}

(2)在显式的类型转化中,一种方式是使用comma-ok的断言语法,由于golang中不像java哪有那样采用泛型声明的方式,因此好多时候不能确定这个类型到底实现了哪些接口,于是可以采用断言表达式,注意 x.(T) 这样的断言类型转化,x 必须是一个接口类型的变量,T 是一个对应的实际的类型,比如下面这个程序,生成一个interface{}类型的数组,之后通过隐式转化,可以往里面放入任何类型的变量,之后还可以再使用用断言表达式检测对应的类型。

package main

import (

	"fmt"

)

type Test []interface{}

func main() {

	test := make(Test, 5)

	test[0] = "a"

	test[1] = 2

	test[2] = true

	test[3] = []byte("test")

	for index, element := range test {

		if value, ok := element.(string); ok {

			fmt.Printf("test[%d] is type of string the value is %vn", index, value)

		} else if value, ok := element.([]byte); ok {

			fmt.Printf("test[%d] is type of string the []byte is %vn", index, value)

		}

	}

}

(3)在使用实例实现interface的时候,还要注意值方法和指针方法的区别。interface这里的使用,多多少少可以看出一些动态语言的设计风格,就是所谓的"鸭子类型",即不是通过显式的指明继承自某个接口或者类,而是由当前类型实现方法的属性集合来决定。理解上就是,感觉比静态的类型逼格高一点,总之我的interface就写在那里,你要是想继承我,使用我的方法,就把这些方法实现了就行,并不要求你显示的声明,继承自我,怎样怎样。有些动态类型的语言在运行期才能进行类型的语法检测,go语言在编译期间就可以检测。比如下面这个例子:

package main

import (

	"fmt"

	"reflect"

	"sort"

)

type Sortable interface {

	Len() int

	Less(i, j int) bool

	Swap(i, j int)

}

type SortStringA [3]string

type SortStringB [3]string

func (self SortStringA) Len() int {

	return len(self)

}

func (self SortStringA) Less(i, j int) bool {

	return self[i] < self[j]

}

func (self SortStringA) Swap(i, j int) {

	self[i], self[j] = self[j], self[i]

}

func (self SortStringA) Sort() {

	sort.Sort(self)

}

func (self *SortStringB) Len() int {

	return len(self)

}

func (self *SortStringB) Less(i, j int) bool {

	return self[i] < self[j]

}

func (self *SortStringB) Swap(i, j int) {

	self[i], self[j] = self[j], self[i]

}

func (self *SortStringB) Sort() {

	//调用sort包中的 Sort方法 传入的参数只要是一个实现了 sort.interface的类型的实例即可

	sort.Sort(self)

}

func main() {

	sa := SortStringA{"2", "3", "1"}

	sb1 := &SortStringB{"2", "3", "1"}

	sb2 := SortStringB{"2", "3", "1"}

	fmt.Println(reflect.TypeOf(sa))

	sorta, ok := interface{}(sa).(Sortable)

	fmt.Println(reflect.TypeOf(sorta))

	fmt.Println(ok) //output:true

	sa.Sort()

	fmt.Printf("SortStringA after sort %v:n", sa)

	sort.Sort(sa)

	fmt.Printf("SortStringA after sort %v:n", sa)

	//在golang 的源码包中

	fmt.Println(reflect.TypeOf(sb1))

	sort.Sort(sb1)

	sorted := sort.IsSorted(sb1)

	fmt.Printf("sb1 (type:SortStringB) after sort %v :, is sorted : %v n", *sb1, sorted)

	sb2.Sort()

	fmt.Printf("sb2 (type:SortStringB) after sort : %vn", sb2)

}

/*output:

main.SortStringA

main.SortStringA

true

SortStringA after sort [2 3 1]:

SortStringA after sort [2 3 1]:

*main.SortStringB

sb1 (type:SortStringB) after sort [1 2 3] :, is sorted : true

sb2 (type:SortStringB) after sort : [1 2 3]

感觉这一段代码还是能够说明一些问题的:

  • 首先定义了一个sortstable的 接口 这个接口中有三个方法,这三个方法同sort包中的基本类型sort.interface中定义的三个方法是一样的,之后分别用值方法与指针方法对这三个方法进行了实现。
  • 可以看到,采用值方法时,因为方法调用的时候,采用的是指传递,原来的变量中sa的数据并没有排序。同时还测试了一下断言表达式,通过comma-ok的表达式,sa类型可以转化为sortable类型,这里体现了其动态性,但是采用反射输出的时候,还显示的是其本身声明时候的类型main.SortStringA,这里又体现了其强静态类型(类型一但确定就不再改变),两者结合起来使Go语言在保持强静态类型的安全和高效的同时,也能灵活安全地在不同相容类型之间转换。(这个文章讲一些interface原理性的内容,比较好)注意里面的典型的interface+switch+断言的使用方式。
  • 由于sortable中的三个方法和sort.interface中定义的三个方法一样,因此sa sb相当于都实现了sort.interface方法,可以直接调用sort包中的函数。但是要注意,具体实现的时候,前一个是SortStringA本身实现了三个方法,后一个是SortStringB指针实现了三个方法,因此 SortStringB指针类型实现了 sort.Interface 调用的时候传递过去的要是指针才行,否则编译报错。比如直接sort.Sort(sb2)会报错
SortStringB does not implement sort.Interface (Len method has pointer receiver)


Tags:golang   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
慢聊Golang协程池Ants实现原理
大家都知道goroutine 是 Go语言中的轻量级线程实现,由 Go 运行时(runtime)管理,Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。创建一个goroutine大小大概在2k左...【详细内容】
2023-12-27  Search: golang  点击:(94)  评论:(0)  加入收藏
基于Go-Kit的Golang整洁架构实践
简介Go是整洁架构(Clean Architecture)的完美选择。整洁架构本身只是一种方法,并没有告诉我们如何构建源代码,在尝试用新语言实现时,认识到这点非常重要。自从我有了使用Ruby o...【详细内容】
2023-12-25  Search: golang  点击:(156)  评论:(0)  加入收藏
Golang清晰代码指南
发挥易读和易维护软件的好处 - 第一部分嗨,开发者们,清晰的代码是指编写易于阅读、理解和维护的软件代码。它是遵循一组原则和实践,优先考虑清晰性、简单性和一致性的代码。清...【详细内容】
2023-12-18  Search: golang  点击:(131)  评论:(0)  加入收藏
如何优雅的组织Golang项目结构
一个Go项目的结构设计始终遵循Go语言的简洁高效理念。一个合理和良好的布局可以提高代码的可读性,简化依赖管理,并优化编译过程。像cmd、internal和docs这样的目录是标准Go项...【详细内容】
2023-12-06  Search: golang  点击:(169)  评论:(0)  加入收藏
从 Discord 的做法中学习 — 使用 Golang 进行请求合并
正如你可能之前看到的,Discord去年发布了一篇有价值的文章,讨论了他们成功存储了数万亿条消息。虽然有很多关于这篇文章的YouTube视频和文章,但我认为这篇文章中一个名为“数据...【详细内容】
2023-11-24  Search: golang  点击:(257)  评论:(0)  加入收藏
使用Golang进行自动化的20个库
Golang,也被称为Go,是一种静态类型的编译型编程语言,由Robert Griesemer,Rob Pike和Ken Thompson在Google设计。它在2009年推出,旨在解决其他编程语言在并发编程、垃圾回收和代码...【详细内容】
2023-11-23  Search: golang  点击:(206)  评论:(0)  加入收藏
Golang 中的 Bytes 包详解之 Bytes.Buffer
上篇文章详细讲解了一次性密码 OTP 相关的知识,基于时间的一次性密码 TOTP 是 OTP 的一种实现方式。这种方法的优点是不依赖网络,因此即使在没有网络的情况下,用户也可以生成密...【详细内容】
2023-11-07  Search: golang  点击:(261)  评论:(0)  加入收藏
聊聊Golang饱受争议的Error
一、error是什么?在C中,返回错误通过errno.h中的错误代码来表示,比如0代表No error,也就是没有错误;2代表No such file or directory,也就是找不到指定路径的文件或文件夹;5代表Inp...【详细内容】
2023-11-06  Search: golang  点击:(256)  评论:(0)  加入收藏
使用示例和应用程序全面了解高效数据管理的Golang MySQL数据库
Golang,也被称为Go,已经成为构建强大高性能应用程序的首选语言。在处理MySQL数据库时,Golang提供了一系列强大的库,简化了数据库交互并提高了效率。在本文中,我们将深入探讨一些...【详细内容】
2023-10-29  Search: golang  点击:(307)  评论:(0)  加入收藏
Golang中的强大Web框架
揭示Fiber在Go Web开发中的特点和优势在不断发展的Web开发领域中,选择正确的框架可以极大地影响项目的效率和成功。介绍一下Fiber,这是一款令人印象深刻的Golang(Go语言)Web框架...【详细内容】
2023-10-26  Search: golang  点击:(258)  评论:(0)  加入收藏
▌简易百科推荐
宝藏级Go语言开源项目——教你自己动手开发互联网搜索引擎
DIYSearchEngine 是一个能够高速采集海量互联网数据的开源搜索引擎,采用 Go 语言开发。Github 地址:https://github.com/johnlui/DIYSearchEngine运行方法首先,给自己准备一杯...【详细内容】
2024-03-12  OSC开源社区    Tags:Go语言   点击:(19)  评论:(0)  加入收藏
Go Gin框架实现优雅地重启和停止
在Web应用程序中,有时候我们需要重启或停止服务器,无论是因为更新代码还是进行例行维护。在这种情景下,我们需要保证应用程序的可用性和数据的一致性。这就需要优雅地关闭和重...【详细内容】
2024-01-30  源自开发者  微信公众号  Tags:Go   点击:(67)  评论:(0)  加入收藏
如何让Go程序以后台进程或daemon方式运行
本文探讨了如何通过Go代码实现在后台运行的程序。最近我用Go语言开发了一个WebSocket服务,我希望它能在后台运行,并在异常退出时自动重新启动。我的整体思路是将程序转为后台...【详细内容】
2024-01-26  Go语言圈  微信公众号  Tags:Go程序   点击:(60)  评论:(0)  加入收藏
深入Go底层原理,重写Redis中间件实战
Go语言以其简洁、高效和并发性能而闻名,深入了解其底层原理可以帮助我们更好地利用其优势。在本文中,我们将探讨如何深入Go底层原理,以及如何利用这些知识重新实现一个简单的Re...【详细内容】
2024-01-25  547蓝色星球    Tags:Go   点击:(67)  评论:(0)  加入收藏
Go 内存优化与垃圾收集
Go提供了自动化的内存管理机制,但在某些情况下需要更精细的微调从而避免发生OOM错误。本文将讨论Go的垃圾收集器、应用程序内存优化以及如何防止OOM(Out-Of-Memory)错误。Go...【详细内容】
2024-01-15  DeepNoMind  微信公众号  Tags:Go   点击:(63)  评论:(0)  加入收藏
Go函数指针是如何让你的程序变慢的?
导读Go 语言的常规优化手段无需赘述,相信大家也能找到大量的经典教程。但基于 Go 的函数值问题,业界还没有太多深度讨论的内容分享。本文作者根据自己对 Go 代码的使用与调优...【详细内容】
2024-01-15  腾讯云开发者  微信公众号  Tags:Go函数   点击:(88)  评论:(0)  加入收藏
Go编程中调用外部命令的几种场景
在很多场合, 使用Go语言需要调用外部命令来完成一些特定的任务, 例如: 使用Go语言调用Linux命令来获取执行的结果,又或者调用第三方程序执行来完成额外的任务。在go的标准库...【详细内容】
2024-01-09  suntiger    Tags:Go编程   点击:(107)  评论:(0)  加入收藏
Go 语言不支持并发读写 Map,为什么?
Go语言的map类型不支持并发读写的主要原因是并发读写会导致数据竞态(data race),这意味着多个 goroutine 可能同时访问并修改同一个 map,从而引发不确定的结果。在Go语言的设计...【详细内容】
2024-01-05  Go语言圈  微信公众号  Tags:Go 语言   点击:(81)  评论:(0)  加入收藏
Go微服务入门到容器化实践
Go微服务入门到容器化实践Go 是一门高效、现代化、快速增长的编程语言,非常适合构建 Web 应用程序。而 Docker 是一种轻量级的容器化技术,能够使得您的应用程序在任何地方运行...【详细内容】
2024-01-01  大雷家吃饭    Tags:Go微服务   点击:(63)  评论:(0)  加入收藏
你是否想知道如何应对高并发?Go语言为你提供了答案!
并发编程是当前软件领域中不可忽视的一个关键概念。随着CPU等硬件的不断发展,我们都渴望让我们的程序运行速度更快、更快。而Go语言在语言层面天生支持并发,充分利用现代CPU的...【详细内容】
2023-12-29  灵墨AI探索室  微信公众号  Tags:Go语言   点击:(110)  评论:(0)  加入收藏
站内最新
站内热门
站内头条