GO语言常见数据类型
来源:互联网 发布:45号钢原料重量算法 编辑:程序博客网 时间:2024/05/22 13:39
GO语言常见数据类型
- GO语言常见数据类型
- 基础类型
- 布尔类型 bool
- 整型 int8byteint16intuintuintptr等
- 类型表示
- 数值运算
- 比较运算
- 位运算
- 浮点类型 float32float64
- 浮点数表示
- 浮点数比较
- 复数类型 complex64complex128
- 复数的表示
- 实部与虚部
- 字符串 string
- 字符串操作
- 字符串遍历
- 字符类型 rune
- 错误类型 error
- 复合类型
- 指针pointer
- 数组array
- 元素的访问
- 值类型
- 切片slice
- 创建数组切片
- 元素遍历
- 动态增减元素
- 基于数组切片创建数组切片
- 内容复制
- 字典map
- 变量声明
- 创建
- 元素赋值
- 元素删除
- 元素查找
- 通道channel
- 基本语法
- select
- 缓冲机制
- 超时机制
- channel的传递
- 单向channel
- 关闭channel
- 结构体struct
- 接口interface
- 基础类型
基础类型
布尔类型 bool
Go语言中的布尔类型与其他语言基本一致,关键字也为bool,可赋值为预定义的true和false,示例代码如下:
var v1 bool //声明后默认为falsev1 = true v2 := (1 == 2) // v2也会被推导为bool类型
布尔类型不能接受其他类型的赋值,不支持自动或强制的类型转换。以下的示例是一些错误的用法,会导致编译错误:
var b bool b = 1 // 编译错误 b = bool(1) // 编译错误 以下的用法才是正确的: var b bool b = (1!=0) // 编译正确 fmt.Println("Result:", b) // 打印结果为Result: true
布尔可以做3种逻辑运算,&&(逻辑且),||(逻辑或),!(逻辑非)。
比较操作符:<,>, ==,!=, <=, >=。
整型 int8/byte/int16/int/uint/uintptr等
- 种类
- 有符号(负号)
int8 int16 int32 int64
- 无符号(无符号)
uint8 uint16 uint32 uint64
- 架构特定(取决于系统位数)
int uint
- 类型别名
Unicode字符rune类型等价int32
byte等价uint8 - 特殊类型
uintptr,无符号整型,
由系统决定占用位大小,足够存放指针即可,和C库或者系统接口交互
取值范围
1.类型表示
需要注意的是,int和int32在Go语言里被认为是两种不同的类型,编译器也不会帮你做自动类型转换,比如以下的例子:
var value2 int32 //声明后默认为0value1 := 64 //value1将会被自动推导为int类型value2 = value1 //编译错误
使用强制类型转换可以解决这个编译错误:
value2 = int32(value1) //编译通过
2.数值运算
Go语言支持以下的常规整数运算:+、-、*、/、%。%是求余运算。
5 % 3 //结果为:2
3.比较运算
Go语言支持以下几种比较运算符:>、<、==、>=、<=、!=。
4.位运算
Go语言支持以下所示的位运算
浮点类型 float32/float64
浮点型用于表示包含小数点的数据,Go语言中的浮点类型采用IEEE-754标准的表达式。
取值范围
1.浮点数表示
在Go语言中,定义一个浮点数变量的代码如下:
var fvalue1 float32 //声明后默认为0fvalue2 := 12.0 //如果不加小数点,fvalue2会被推导为整型而不是浮点型
2.浮点数比较
因为浮点数不是一种精确的表达方式,所以像整型那样直接用==来判断两个浮点数是否相等是不可行的,这可能会导致不稳定的结果。下面是一种推荐的替代方案:
import "math"func IsEqual(f1,f2,p float64) bool { return math.Abs(f1-f2) < p}
复数类型 complex64/complex128
复数实际上由两个实数(在计算机中用浮点数表示)构成,一个表示实部(real),一个表示虚部(imag)。
1.复数的表示
func main() { var v1 complex64 //声明后默认为(0+0i) v1 = 3.2 + 12i v2 := 3.2 + 12i v3 := complex(3.2, 12) fmt.Println(v1, v2, v3)//输出结果为:(3.2+12i) (3.2+12i) (3.2+12i)}
2.实部与虚部
对于一个复数 z=complex(x,y),我们可以通过GO语言内置函数real(z)获取复数z的实部,imag(z)获取复数z的虚部。
func main() { var v1 complex64 v1 = 3.2 + 12i fmt.Println(real(v1), imag(v1))//输出结果为:3.2 12}
字符串 string
在所有编程语言中都涉及到大量的字符串操作,可见熟悉对字符串的操作是何等重要。 Go中的字符串和C#中的一样(java也是),字符串内容在初始化后不可修改。 需要注意的是在Go中字符串是有UTF-8编码的,请注意保存文件时将文件编码格式改成UTF-8(特别是在windows下)。
1.字符串操作
平时常用的字符串操作如表2-3所示。
更多的字符串操作,请参考标准库strings包。
2.字符串遍历
Go语言支持两种方式遍历字符串。一种是以字节数组的方式遍历:
var str string //声明后默认为"",注意Go语言中没有null的定义str ="Hello,世界"n := len(str)for i := 0; i< n; i ++ { ch := str[i] //依据下标取字符串中的字符,类型为byte fmt.Println(i,ch)}
输出结果为:
0 721 1012 1083 1084 1115 446 2287 1848 1509 23110 14911 140
这个字符串的长度为12,因为每个中文字符在UTF-8中占3个字节。
另一种是以Unicode字符遍历:
str:="Hello,世界"for i,ch := range str { fmt.Println(i,ch)//ch的类型为rune}
输出结果为:
0 721 1012 1083 1084 1115 446 199909 30028
以Unicode字符方式遍历时,每个字符的类型是rune,而不是byte。
字符类型 rune
在Go语言中支持两个字符类型,要给是byte(实际上是uint8的别名),代表UTF-8字符串的单个字节的值;另一个是rune,代表单个Unicode字符。
关于rune相关操作,可查阅Go标准库的unicode包。另外unicode/utf8包也提供了UTF-8和Unicode之间的转换。
错误类型 error
error类型本身就是一个预定义好的接口,里面定义了一个method。
type error interface { Error() string}
生成一个新的error并返回,一般有以下几种处理方式:
package mainimport ( "errors" "fmt")type Customerror struct { infoa string infob string Err error //声明后默认为nil}func (cerr Customerror) Error() string { errorinfo := fmt.Sprintf("infoa : %s , infob : %s , original err info : %s ", cerr.infoa, cerr.infob, cerr.Err.Error()) return errorinfo}func main() { //方法一: //采用errors包的New方法 返回一个err的类型 var err error = errors.New("this is a new error") //由于已经实现了error接口的方法 因此可以直接调用对应的方法 fmt.Println(err.Error()) //方法二: //采用fmt.Errof 将string信息转化为error信息 并返回 err = fmt.Errorf("%s", "the error test for fmt.Errorf") fmt.Println(err.Error()) //方法三: //采用自定义的方式实现一个error的 一个duck 类型 err = &Customerror{ infoa: "err info a", infob: "err info b", Err: errors.New("test custom err"), } fmt.Println(err.Error())}/*output:this is a new errorthe error test for fmt.Errorfinfoa : err info a , infob : err info b , original err info : test custom err */
golang中的 error package 内容也比较简单,这个package中实现了error中所声明的method(Error)相当于是一个error接口的duck类型。
// Package errors implements functions to manipulate errors.package errors// New returns an error that formats as the given text.func New(text string) error { return &errorString{text}}// errorString is a trivial implementation of error.type errorString struct { s string}func (e *errorString) Error() string { return e.s}
采用fmt.Errorf方法把string类型转化为error类型,在这个方法的内部,先调用fmt包中的Sprintf方法把格式化的输入转化为字符串,在使用 errors.New 方法返回error类型。
采用自定义的error类型可以先判断err的动态类型,再进行下一层的判断。
比如net.Error接口,是一个对原先error接口的再封装。在读取文件的时候判断读取器读取结束的时候的io.EOF。
//io.EOFvar EOF = errors.New("EOF")//net.Error type Error interface { error Timeout() bool // Is the error a timeout? Temporary() bool // Is the error temporary?}
复合类型
指针(pointer)
Go中的指针是很容易学习,也是比较有趣的。一些Go编程任务使用指针更容易执行,并且不使用指针不能执行其他任务,例如通过引用调用。 如众所知,每个变量都是一个内存位置,每个内存位置都有其定义的地址,可以使用”和”号(&)运算符来访问它,这个运算符表示内存中的地址。参考下面的例子,它将打印定义的变量的地址:
package mainimport "fmt"func main() { var a int = 10 var b *int //声明后默认为nil fmt.Printf("Address of a variable: %x\n", &a )//输出结果:Address of a variable: c42000e2b0}
如何使用指针?
有几个重要的操作,将非常频繁地使用指针来实现。
- 定义一个指针变量
- 将一个变量的地址赋值给一个指针
- 最后访问指针变量中可用地址的值
这是通过使用一元运算符*来返回位于操作数指定的地址的变量的值。下面的例子使用这些操作:
package mainimport "fmt"func main() { var a int= 20 // actual variable declaration var ip *int // pointer variable declaration ip = &a // store address of a in pointer variable fmt.Printf("Address of a variable: %x\n", &a) // address stored in pointer variable fmt.Printf("Address stored in ip variable: %x\n", ip )// access the value using the pointer fmt.Printf("Value of *ip variable: %d\n", *ip )}
当上面的代码编译和执行时,它产生结果如下:
Address of var variable: 10328000Address stored in ip variable: 10328000Value of *ip variable: 20
在Go语言中的nil指针
Go编译器为指针变量分配一个Nil值,以防指针没有确切的地址分配。这是在变量声明的时候完成的。指定为nil值的指针称为nil指针。
nil指针是在几个标准库中定义的值为零的常量。参考下面的程序:
package mainimport "fmt"func main() { var ptr *int fmt.Printf("The value of ptr is : %x\n", ptr )//输出结果为:The value of ptr is 0,但不能直接做比较}
在大多数操作系统上,程序不允许访问地址0处的内存,因为该内存是由操作系统保留的。 然而,存储器地址0具有特殊意义; 它表示指针不打算指向可访问的存储器位置。但是按照惯例,如果指针包含nil(零)值,则假设它不指向任何东西。
要检查是否为nil指针,可以使用if语句,如下所示:
if(ptr != nil) // succeeds if p is not nilif(ptr == nil) // succeeds if p is null
数组(array )
数组就是指一系列同一类型数据的集合。数组中包含的每个数据被称为数组元素(element),一个数组包含的元素的个数被称为数组的长度。
1.元素的访问
可以使用数组下标来访问数组中的元素。下面示例遍历数组并逐个打印。
for i := 0; i< len(array); i++ { fmt.Println("Element",i,"of array is",array[i])}
Go语言海提供了一个关键字range,用于便捷地遍历容器中的元素。上面的遍历可以简化如下:
for i,v := range array { fmt.Println("Array element[",i,"]=",v)//数组的range打印出来是有序的}
2.值类型
需要特别注意的是,在Go语言中数组是一个值类型(value type)。所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。
切片(slice)
数组的缺点有:数组的长度在定义之后无法再次修改且数组是值类型,每次传递都将产生一份副本。数组切片(slice)可以弥补数组的不足。初看起来,数组切片就像一个指向数组的指针,实际上它拥有自己的数据结构,而不仅仅是个指针。数组切片的数据结构可以抽象为以下3个变量:
- 一个指向原生数组的指针;
- 数组切片中的元素个数;
- 数组切片已分配的存储空间。
1.创建数组切片
基于数组
mySlice1 := myArray[:]//基于myArray的所有元素创建数组切片 mySlice2 := myArray[:5]//基于myArray的前5个元素创建数组切片 mySlice2 := myArray[5:]//基于myArray第5个元素开始的所有元素创建数组切片
直接创建
mySlice1:= make([]int,5)//创建一个初始元素个数为5的数组切片,元素初始值为0mySlice2:= make([]int,5,10)//创建一个初始元素个数为5的数组切片,元素初始值为0,并预留10个元素的存储空间mySlice3:= []int{1,2,3,4,5}//直接创建并初始化包含5个元素的数组切片
2.元素遍历
传统元素遍历方法如下:
for i := 0; i< len(slice); i++ { fmt.Println("Element",i,"of slice is",slice[i])}
使用range关键字遍历:
for i,v := range slice { fmt.Println("slice[",i,"]=",v)//数组切片的range打印出来是有序的}
3.动态增减元素
与数组相比,数组切片多了一个存储能力(capacity)的概率,即元素个数和分配的空间可以是两个不同的值。数组切片支持Go语言内置的cap()函数和len()函数分别返回数组切片分配的空间大小和存储的元素个数。例如:
mySlice := make([]int,5,10)//cap(mySlice)为10,len(mySlice)为5mySlice = append(mySlice,1,2,3)//在原mySlice的尾部添加3个元素mySlice2 := []int{8,9,10}mySlice = append(mySlice,mySlice2...)
需要注意的是,在的哥参数mySlice2后面加了三个点,即一个省略号,如果没有这个省略号的话,会有编译错误,因为按appand()的语义,从第二个参数起的所有参数都是待附加的元素。mySlice中的元素类型为int,直接传递mySlice2是行不通的。加上省略号相当于把mySlice2包含的所有元素打散后传入。
数组切片会自动处理存储空间不足的问题。如果追加的内容长度超过当前已分配的存储空间,数组切片会自动分配一块足够大的内存。
4.基于数组切片创建数组切片
oldSlice := []int{1,2,3,4,5}newSlice := oldSlice[:3] //基于oldSlice的前3个元素构建新数组切片
有意思的是,选择的oldSlice元素范围甚至可以超过所包含的元素个数,只要这个选择的范围不超过oldSlice存储能力,那么创建程序就是合法的。newSlice中超出oldSlice元素的部分都会填上0。
5.内容复制
slice1 := []int{1,2,3,4,5}slice2 := []int{5,4,3}copy(slice2,slice1)//只会复制slice1的前3个元素到slice2中copy(slice1,slice2)//只会复制slice2的前3个元素到slice1的前3个位置
字典(map )
map是一堆键值对的未排序集合。
1.变量声明
var myMap map[string]PersonInfo
myMap是声明的变量名,string是键的类型,PersonInfo是值的类型。
2.创建
myMap = make(map[string]PersonInfo)myMap = make(map[string]PersonInfo,100)//myMap初始存储能力100myMap = map[string]PersonInfo{"1234":PersonInfo{"1","Jack","Room 101,..."},}//创建并初始map
3.元素赋值
myMap["1234"]=PersonInfo{"1","Jack","Room 101,..."}
4.元素删除
delete(myMap,"1234")//从myMap中删除键为"1234"的键值对。
如果”1234”这个键不存在,那么这个调用将什么都不发生,也不会有什么副作用。但如果传入的map变量的值是nil,该调用将导致程序抛出异常。
5.元素查找
value,ok := myMap["1234"]if ok {//找到了 //处理找到的value}
或者
if value,ok := myMap["1234"];ok {//找到了 //处理找到的value}
通道(channel )
channel是Go语言在语言级别提供的goroutine间的通信方式。我们可以使用channel在两个或 多个goroutine之间传递消息。channel是进程内的通信方式,因此通过channel传递对象的过程和调用函数时的参数传递行为比较一致,比如也可以传递指针等。如果需要跨进程通信,我们建议用 分布式系统的方法来解决,比如使用Socket或者HTTP等通信协议。Go语言对于网络方面也有非常完善的支持。 channel是类型相关的。也就是说,一个channel只能传递一种类型的值,这个类型需要在声明channel时指定。如果对Unix管道有所了解的话,就不难理解channel,可以将其认为是一种类 型安全的管道。
1.基本语法
声明
//var chanName chan ElementTypevar ch chan intvar m map[string] chan bool
定义
ch := make(chan int)
写入
ch <- value
读取
value := <- ch
读取和写入都会导致程序阻塞。
2.select
Go语言直接在语言级别支持select关键字,用于处理异步IO问题。
select { case <- chan1: //如果chan1成功读到数据,则进行该case处理语句 case chan2 <- 1: //如果成功向chan2写入数据,则进行该case处理语句 default: //如果上面都没有成功,则进入default处理流程}
select类似switch语句,但不同在于,select后面不带判断条件,直接去查看case语句,且case语句里必须是一个channel操作。
3.缓冲机制
给channel带上缓冲,可达到消息队列的效果。
c := make(chan int,1024)
上面创建了一个大小为1024的int类型channel,即使没有读取方,写入方也可以一直往channel里写入,在缓冲区被填完之前都不会阻塞。
读取数据时候可以用range关键字实现简便的循环读取。
for i := range c { fmt.Println("Received:",i)}
4.超时机制
//首先,我们实现并执行一个匿名的超时等待函数timeout := make(chan bool,1)go func(){ time.sleep(1e9) timeout <- true}()//然后我们把timeout这个channel利用起来select { case <- ch: //从ch中读取数据 case <- timeout: //一直没有从ch中读取到数据,但从timeout中读取到了数据}
5.channel的传递
channel本身也是原生类型,与map之类的类型地位一样,也可以通过channel传递。
type PipeData struct { value int handler func(int) int next chan int}func handle(queue chan *PipeData){ for data := range queue { data.next <- data.handler(data.value) }}
流式处理
6.单向channel
var ch2 chan<- float64 //ch2是单向channel,只用于写float64数据var ch3 <-chan int //ch3是单向channel,只用于读取int数据
channel支持类型转换
ch4 := make(chan int)ch5 := <-chan int(ch4) //ch5就是一个单向的读取channelch6 := chan<- int(ch4) //ch6是一个单向的写入channel
基于ch4可以进行初始化,实现最小权限原则
7.关闭channel
close(ch)
如何判断channel是否已经关闭:
if x,ok := <- ch;!ok{ //关闭处理程序}
注意:以上判断如果ok为true,则会从ch中取出一个数据,可能会影响数据的连续性。
结构体(struct )
结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。
接口(interface)
接口是用来定义行为的类型。这些被定义的行为不由接口直接实现,而是通过方法由用户定义的类型实现。如果用户定义的类型实现了某个接口类型声明的一组方法,那么这个用户的类型的值就可以赋给这个接口类型的值。这个赋值会把用户定义的类型的值存入接口类型的值。
- GO语言常见数据类型
- [go语言]基本数据类型
- Go 语言数据类型
- go语言基本数据类型
- 4.Go 语言数据类型
- 3.Go语言数据类型
- Go 学习笔记:Go 语言数据类型
- Go语言学习之数据类型
- Go语言的复合数据类型
- Go 语言——数据类型
- c语言常见数据类型
- go语言的常见函数
- go语言学习-golang的基本数据类型
- Go语言 常用数据类型底层结构分析
- go语言学习-golang的基本数据类型
- go语言学习(2)-数据类型
- Go语言 常用数据类型底层结构分析
- Go语言 常用数据类型底层结构分析
- Faster rcnn 训练自己的数据—环境搭建
- Uboot的Makefil分析e
- xgboost参数详解
- NDI学习总结 JNDI数据源的配置
- Qt模块&头文件出现下划线问题
- GO语言常见数据类型
- python性能分析工具:cProfile使用
- 设计模式之CS和BS结构的区别
- AOJ 0033
- Go语言函数
- linux 文件基本属性ls -l
- 网易笔试题--跳石板
- Hello,未来网络 –SDN实战特训营(第X期)
- 顺序队列(循环队列)