Go语言数据类型

1、布尔型

布尔型的值只可以是常量 true 或者 false。

var ok bool  = true

布尔型数据和整型数据不能进行相互转换;
声明的布尔型变量如不指定初始化值,默认是false。

var a bool //a is false

2、 数字类型

整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。

整型:
Go语言内置了12种整数类型,如byte、int、int8、int16、int32(-2147483648 到 2147483647)、int64、uint、uint8、uint16、uint32、uint64、uintptr,这里特别注意不同类型的整型必须进行强制类型转换,其中byte相当于uint8。

var a int = 10
var b int64 = 20
b = a //错误

整型支持算术运算和位操作,算术表达式和位操作表达式的结果依旧是整型,如:

var a int = (2+3)*4
var b int = 1000>>3 

浮点型:
Go语言内置了两种浮点数类型,为float32、float64

var b := 10.00  //浮点数字面量会被自动推断为float64类型

注意:计算机很难进行浮点数的精确表示和存储,不应该使用==或者!=进行比较操作,应该使用math标准库进行高精度科学计算。

复数类型:
Go语言内置的复数类型有两种,为complex64和complex128,其中前者是由两个float32构成的,后者是由两个float64构成的,复数在计算机里面使用两个浮点数表示,一个表示实部,一个表示虚部。

var valuel complex64 = 3.1 + 5i

//三个内置函数处理函数
var v = complex(2.2,4)   //构造一个复数
a := real(v)    //返回复数实部 
b := image(v)   //返回复数虚部

3、字符串类型:

字符串就是一串固定长度的字符连接起来的字符序列,是一个不可改变的字节序列,创建后不能再次修改。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。

Go语言中,字符串(string类型)是 UTF-8 字符的序列(当字符为 ASCII 码表上的字符时则占用 1 个字节,其它字符根据需要占用 2-4 个字节,汉字是三个字节)。

字符串数据结构
字符串在 Go 语言中的接口其实非常简单,每一个字符串在运行时都会使用如下的 reflect.StringHeader 表示,其中包含指向字节数组的指针和数组的大小:

type StringHeader struct {
	Data uintptr
	Len  int
}

与切片的结构体相比,字符串只少了一个表示容量的 Cap 字段,而正是因为切片在 Go 语言的运行时表示与字符串高度相似,所以我们经常会说字符串是一个只读的切片类型。

type SliceHeader struct {
	Data uintptr
	Len  int
	Cap  int
}

因为字符串作为只读的类型,我们并不会直接向字符串直接追加元素改变其本身的内存空间,所有在字符串上的写入操作都是通过拷贝实现的。

注意:

字符串尾部不包含NULL字符,这一点与C/C++不一样;

基于字符串创建的切片和原字符串指向相同的底层字符数组,一样不能修改,对字符串的切片操作返回的子串仍然是string,而非slice。

字符串和切片的转换:字符串可以转换为字节数组,也可以转换为Unicode的字节数组。如下:

a := "你好,世界!"
b := []byte(a)
c := []rune(a)

字符串修改的方法:

/*
修改字符串
注意:字符串是无法被修改的,只能复制原字符串,在复制的版本上修改,修改完成之后可以使用 string() 函数将其转为字符串即可
方法1:转换为[]byte()
方法2:转换为[]rune()
方法3:新字符串代替原字符串的子字符串,用strings包中的strings.Replace()
*/
func main() {
	//方法1
	s1 := "abcdefgabc"
	s2 := []byte(s1)
	s2[1] = 'B'
	fmt.Println(string(s2)) //aBcdefgabc
	//方法2
	s3 := []rune(s1)
	s3[1] = 'B'
	fmt.Println(string(s3)) //aBcdefgabc
	//方法3
	new := "ABC"
	old := "abc"
	s4 := strings.Replace(s1, old, new, 2)
	fmt.Println(s4) //ABCdefgABC
}


字符串遍历:

func main(){
	strChinese :="你好"
	for i:=0;i<len(strChinese) ;i++  {
		fmt.Printf("%d:[%c]\n",i,strChinese[i])
	}//fmt.Println(utf8.RuneCountInString(strChinese)) 输出长度

	//会遍历每一个字节,一个汉字为3个字节,相当于遍历字节数组
	
	for i,c:=range strChinese{
		fmt.Printf("%d:[%c]\n",i,c)
	}
	//相当于遍历rune数组
}

将字符串转为一个整数:

  1. 调用 strconv 包中的相关函数
  2. 可以使用字符串底层为 uint8 类型整数的这个特点通过 int,int64 或者其他函数将其转为整数,通过遍历整数类型的字符串可以发现得到的是对应字符的 ascii 值,将 ascii 值与 15 进行与运算就可以得到当前位对应的整数值,最终就可以得到字符串 s 对应的整数值:
package main
 
import "fmt"
 
func main() {
	// 在声明字符串s的时候使用":="快速初始化s
	s := "0123456789"
	// 使用索引遍历字符串
	for i := 0; i < len(s); i++ {
		// 使用Printf格式化输出
		fmt.Printf("%v ", s[i])
	}
	fmt.Println()
	s1 := "392932"
	n := 0
    // 将s1转位int类型整数
	for i := 0; i < len(s1); i++ {
		n = n*10 + int(s1[i]&15)
	}
	fmt.Printf("%v", n)
}

字符串常用操作:
主要是分隔字符串,判断是否存在某个子串,是否存在某个前缀或者某个后缀,找到某个子串第一次出现的位置,拼接字符串等操作,可以使用 Sprintf 来拼接字符串等

package main
 
import (
	"fmt"
	"strings"
)
 
func main() {
	// 1. len(str)获取字符串的长度
	s := "Hello World!"
	fmt.Println(len(s))
 
	// 2. strings.Split()按照分隔符切分字符串, 函数的返回值是按照分隔符切分之后的字符串切片
	s1 := "I am a student"
	s2 := strings.Split(s1, " ")
	for i := 0; i < len(s2); i++ {
		fmt.Print(s2[i], " ")
	}
	fmt.Println()
 
	// 3. Contains()判断字符串中是否包含子串substr
	fmt.Println(strings.Contains(s1, "am"))
 
	// 4. HasPrefix(),HasSuffix()判断是否包含前缀与后缀
	fmt.Println(strings.HasPrefix("abbbcas", "abb"))
	fmt.Println(strings.HasSuffix("abbbcas", "cas"))
 
	// 5. strings.Index()返回子串第一次出现的位置, 不存在返回-1
	// strings.LastIndex()返回子串最后一次出现的位置, 不存在返回-1
	s3 := "abbabbabbab"
	fmt.Println(strings.Index(s3, "abb"))
	fmt.Println(strings.LastIndex(s3, "abb"))
 
	// 6. join()对string切片的元素添加分隔符的操作, 返回的是添加分隔符之后的字符串
	s4 := []string{"I", "am", "a", "student"}
	fmt.Println(s4)
	fmt.Println(strings.Join(s4, " "))
}

[]byte以及[]rune类型:

byte 是uint8类型的别名,uint8 类型,代表了 ASCII 码的一个字符。Go语言字符串的底层是byte[]字节数组,在默认的utf-8编码中,英文字符占8位,一个字节,而中文是3个字节。

Go语言支持 Unicode(UTF-8),因此字符同样称为 Unicode 代码点或者 runes,并在内存中使用 int32 来表示。
rune在Go内部是int32类型的别名,占用4个字节;

在文档中,一般使用格式 U+hhhh 来表示,其中 h 表示一个 16 进制数。
在书写 Unicode 字符时,需要在 16 进制数之前加上前缀\u或者\U。
如果需要使用到 4 字节,则使用\u前缀,如果需要使用到 8 个字节,则使用\U前缀。

Unicode函数:
判断是否为字母:unicode.IsLetter(ch)
判断是否为数字:unicode.IsDigit(ch)
判断是否为空白符号:unicode.IsSpace(ch)

注意:

  1. 对于英文字符串,不管是用rune类型还是byte类型,不管是字符串的长度还是取值,都是相同的。
  2. 对于中文字符来说,rune类型的操作就比byte类型的操作更加友好很多,我们可以通过[:]操作直接取出中文的对应数量,而byte取出来却是乱码。
package main

import (
	"fmt"
)

func main() {
	testStr := "this is a test!"
	testStr1 := "这是一个测试"
	fmt.Println("The length of str is: ",len(testStr))
	fmt.Println("字符串长度为: ",len(testStr1))
}

The length of str is:  15
字符串长度为:  18

数据类型,点击查阅。

4、派生类型:

包括: 指针类型(Pointer)、数组类型、结构化类型(struct)、Channel 类型
函数类型 、切片类型 、接口类型(interface)、Map 类型

切片类型:

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用,Go中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。实质上,切片就是变长数组。

结构:

type SliceHeader struct {
 
    Data uintptr //第一个是字符串指向的底层字节数组,
 
    Len int //第二个是字符串的字节的长度
 
    Cap int  //切片指向的内存空间的最大容量(对应元素的个数不是字节数)
 
}

声明方式:

var name [] T

T : 表切片元素类型,可以是整型、浮点型、布尔型、切片、map 、函数等。

切片的元素使用“ [] ”进行访问,在方括号中提供切片的索引即可访问元素,索引的范围从 0开始,且不超过切片的最大容量。

代码如下:

a := make([]int , 3,3) //创建一个长度为3,容量为3的整型切片。
 
a[0] = 1 //为切片元素赋值。

其他方式:

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 = make([]int, 3) // 有3个元素的切片, len和cap都为3
 
    h = make([]int, 2, 3) // 有2个元素的切片, len为2, cap为3
 
    i = make([]int, 0, 3) // 有0个元素的切片, len为0, cap为3
 
)

函数操作:

内置函数len()返回切片长度
内置函数cap()返回切片底层数组容量
内置函数append()对切片追加元素
内置函数copy()用于复制一个切片

package main

import (
    "fmt"
)

func main() {
    //创建切片
    var number []int
    printSlice(number)

    //允许追加空切片
    number = append(number,0)
    printSlice(number)

    //向切片添加一个元素
    number = append(number,1)
    printSlice(number)

    //同时添加多个元素
    number = append(number,2,3,4)
    printSlice(number)

    //创建的新的切片是之前的切片容量的两倍
    number1 := make([]int,len(number),(cap(number))*2)

    //拷贝number的内容到number1中
    copy(number1,number)
    printSlice(number1)
}

func printSlice(x []int) {
    fmt.Printf("len=%d cap=%d slice=%v\n", len(x), cap(x), x)
}

指针类型:

一个指针变量指向了一个值的内存地址。

类似于变量和常量,在使用指针前你需要声明指针。指针声明格式如下:

var var_name *var-type

var-type 为指针类型,var_name 为指针变量名,* 号用于指定变量是作为一个指针。以下是有效的指针声明:

var ip *int        /* 指向整型*/
var fp *float32    /* 指向浮点型 */

当一个指针被定义后没有分配到任何变量时,它的值为 nil;nil 指针也称为空指针;nil在概念上和其它语言的null、None、nil、NULL一样,都指代零值或空值。

if(ptr == nil)    /* ptr 是空指针 */

注意:
Go语言同样支持多级指针 **var-type;

结构体指针访问结构体字段仍然用“ . ”点操作符,Go语言没有“->”操作符

Go语言支持垃圾回收,所以禁止指针运算,因为会给垃圾回收带来很多不便。

函数中允许返回局部变量的地址,因为Go编译器使用“栈逃逸”机制将这种局部变量的空间分配在堆上,如下:

func sum(a , b int) *int{
	sum := a + b
	return &sum  //允许,sum会被分配在heap上
}

其他:
Go 指针数组 你可以定义一个指针数组来存储地址
Go 指向指针的指针 Go 支持指向指针的指针
Go 向函数传递指针参数 通过引用或地址传参,在函数调用时可以改变其值
https://www.runoob.com/go/go-pointers.html

数组类型:

数组是具有相同唯一类型的一组已编号且长度固定的数据项序列,这种类型可以是任意的原始类型例如整型、字符串或者自定义类型。

数组元素可以通过索引(位置)来读取(或者修改),索引从 0 开始,第一个元素索引为 0,第二个索引为 1,以此类推。

声明方式:

var arr [2]int  //声明数组,元素默认值都为0

arr := [3]int{1,2,3} //指定长度和初始化字面量

arr := [...]int{1,2,3} //不指定长度,由后面的初始化列表数量来确定长度

arr := [3]int{1:1,2:3} //指定总长度,并通过索引值进行初始化,没有初始化元素时使用默认值

arr := [...]int{1:1,2:3} //不指定长度,通过索引值初始化,由最后的索引值确定长度

注意:

  1. 数组创建完长度就固定了,不可以再追加元素
  2. 数组是值类型的,数组赋值或作为函数参数都是值拷贝
  3. 数组长度是数组的组成部分,[10]int和[20]int表示不同的类型
  4. 可以根据数组创建切片

结构化类型(struct)

struct结构中的类型可以是任意类型;struct的存储空间是连续的,其中字段按照声明时的顺序存放(注意字段之间有对齐要求)

struct类型字面量:

struct {
	FileName FieldType
	FileName FieldType
	FileName FieldType
}

自定义struct类型:

type TypeName struct{
	FileName FieldType
	FileName FieldType
	FileName FieldType
}

struct初始化:

type Person struct{
	Name string 
	Age int
}

type Student struct{
	*Person
	Number int 
}

p := &Person{
	Name : "xxx",
	Age : 12,
}

s := Student{
	Person : p,
	Number : 110,
}
struct{}{}  //匿名struct初始化

点击查看struct文章!

map类型

Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

map的类型格式是:map[K]T,其中K可以是任意可以进行比较的类型,T是值类型

创建方式:

使用字面量创建,如下:

ma := map[string]int{"a":1,"b":2}

使用内置的make函数创建,如下:

make(map[K]T)  //map的容量使用默认值
make(map[K]T,len) //map的容量使用给定的len值

ma := make(map[string]int,10)

操作方式:

访问格式:mapName[key]
删除格式:使用内置函数delete(mapName,key)
用range可以遍历map,但不保证顺序

mp := make(map[int]string)
mp[1] = "xxx"
mp[2] = "yyy"
delete (mp,1)

for k,v := range mp{
	...
}

注意:

  1. Go内置的map不是并发安全的,并发安全的map可以使用标准包sync中的map
  2. 不要直接修改map value内某个元素的值,如果想修改map的某个键值,则必须整体赋值,想map中的value是结构体,如想修改结构体里的某值,则必须修改完后再将整个结构体赋值给map

Channel 类型

接口类型:

接口是一个编程规约,也是一组方法签名的集合。

接口内部存放的具体类型变量被称为接口指向的“实例”。

空接口:

由于空接口的方法集为空,所以任何类型都被认为实现了空接口,任意类型的实例都可以赋值或者传递给空接口,包括非命名类型的实例。

注意: 非命名类型由于不能定义自己的方法,所以方法集为空,因此其类型变量除了传递给空接口,不能传递给任何其他接口。

接口声明:

特点:

  1. 接口的命名一般以“er”结尾
  2. 接口定义的内部方法声明不需要func引导
  3. 在接口定义中,只有方法声明没有方法实现

接口字面量类型声明:

interface {
	MethodSignature1
	MethodSignature2
}

使用type关键字声明:

type InterfaceName interface{
	MethodSignature1
	MethodSignature2
}

可以内嵌匿名接口字段,就是一个接口定义里面包括其他接口,如下:

type  Reader interface{
	Read(p []byte)(n int,err error)
}

type ReadFinallyer interface{
	Reader 
}

接口初始化:

没有初始化的接口变量,其默认值是nil

实例赋值接口
如果具体类型实例的方法集是某个接口的方法集的超集,则称该具体类型实现了接口,可以将该具体类型的实例直接赋值给接口类型的变量。

接口变量赋值给接口变量
已经初始化的接口类型变量a直接赋值给另一种接口变量b,要求b的方法集是a的方法集的子集,此时接口变量b绑定的具体实例是接口变量a绑定的具体实例的副本。

type Printer interface{
	Print()
}

type s struct{}

func (s S) Print(){
	....
}

func main(){
	var i Printer
	i = S{}  //i = new(S)
	i.Print()
}

接口的动态类型和静态类型
动态类型:接口绑定的具体实例的类型称为接口的动态类型,接口可以绑定不同类型的实例,所以接口的动态类型是随着其绑定的不同类型实例而发生变化的

静态类型
在这里插入图片描述
类型断言:

类型查询:

空接口

Go语言的空接口有点像C语言中的void *,只不过void * 是指针,而Go语言的空接口内部封装了指针而已。

空接口组成(两个字段):实例类型、指向绑定实例的指针

用途:Go语言没有泛型,如果一个函数需要接收任意类型的参数,则参数类型可以使用空接口类型,这是弥补没有泛型的一种手段。

空接口是反射的基础,后续知识再补充。

注意:当空接口的两个字段都为nil时,空接口才为nil

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值