【go学习合集】基础数据类型

学习目标:

熟悉Go基础数据类型及代码如何定义

学习内容

汇总

数据类型分类简介
有符号整型int平台相关(32位系统上是32位,64位系统上是64位)
int88位整型,范围 -128 到 127
int1616位整型,范围 -32768 到 32767
int3232位整型,范围 -2147483648 到 2147483647
int64范围 -9223372036854775808 到 9223372036854775807
无符号整型uint平台相关(32位系统上是32位,64位系统上是64位)
uint88位无符号整型,范围 0 到 255
uint1616位无符号整型,范围 0 到 65535
uint3232位无符号整型,范围 0 到 4294967295
uint6464位无符号整型,范围 0 到 18446744073709551615
别名整型byte等同于uint8
rune等同于 int32,表示一个 Unicode 码点
浮点型float3232位浮点数
float6464位浮点数
复数型complex64实部和虚部都是 float32
complex128实部和虚部都是 float64
布尔型bool布尔类型,值为 true 或 false
字符串string字符串类型,表示 UTF-8 编码的文本

代码定义方式

go中针对任何类型的变量都有四种赋值方式,var声明类型、var声明类型并赋值、使用:=+类型来显示定义,以及直接使用:=来自动隐式赋值。如果只声明不赋值,则会根据其数据类型设置默认值,比如string是“”,int是0。

package main

func main() {
	// 第一种方式
	var a int8 = 127
	// 第二种方式
	var b int8
	b = 127
	// 第三种方式
	c := int8(127)
	// 不显示定义类型,则默认是int
	d := 2143242
}

有符号整型

原码、反码、补码

为了更好的解释数据范围的来源,需要先了解一下操作系统中原码、反码、补码的概念。
在这里插入图片描述
因此,为了保证0表示的唯一,以及运算的方便,负数往往采用补码表示。

举例说明
+3:原码表示:0 0000011, 反码表示: 0 0000011, 补码表示: 0 0000011
-3: 原码表示:1 0000011, 反码表示: 1 1111100(正数原码取反得到), 补码表示: 1 1111101
因此 3+(-3) = 0000 0000(补码相加,各个位置相加即可,符号位进位需要舍弃)

数据范围

以int8举例,其在操作系统占8位,以二进制表示。 第一位为符号位,0代表正数,1表示负数。
因此其正数范围为 0 0000000 ~ 0 1111111 = 0~ 127。
因此其负数范围为 1 0000000 ~ 1 1111111 = -128 ~ - 1 (补码)
因此int8的数据范围为-128~127,同理可以推出其他几种数据范围

问题探讨

声明时如果超出数据范围
package main

func main() {
	var a int8 = 127
	
	var b int8 = 128 // 如果定义128,代码会报错,不给定义
}
赋值时超出数据范围
package main

import "fmt"

func main() {
	a := 1234323432
	fmt.Printf("%32b\n", a)
	var b int8
	b = int8(a)
	fmt.Println(b)
}

推理:

 a的二进制原码 = 10010011001001001000111111010008位为11101000,此为补码,-1得到反码 11100111
 然后再取反得到正数原码00011000 = 2*2*2 + 2*2*2*2 = 8+16 = 24
 所以为b的值为
-24

不同类型的数值进行相加
package main

func main() {
	var a = int8(32)
	var b = int16(64)
	c := a + b  //不同类型不给运算,此处报错
}

无符号整型

与有符号整型概念基本一致,无法表示负数

别名整型

在Go语言中,byte 和 rune 是 uint8 和 int32 类型的别名,它们主要用于处理字符和字符串。这些别名的主要用途包括:

byte

byte 是 uint8 类型的别名,表示一个8位无符号整数。它主要用于处理原始字节数据。使用 byte 可以使代码更加明确,表明变量或数据是用来表示字节而不是一般的整数。

用途:
处理原始二进制数据:例如读取文件或网络数据时,通常会以字节为单位进行处理。
字符串和字节数组的转换:在Go中,字符串是不可变的字节序列,[]byte 类型可以用来表示和操作可变的字节数组。

package main

import (
	"encoding/json"
	"fmt"
)

func main() {
	s := "abcdefg"
	bytes, _ := json.Marshal(s)
	fmt.Println(bytes)
}

------------------------运行结果-------------------
[34 97 98 99 100 101 102 103 34]

rune

rune 是 int32 类型的别名,表示一个32位有符号整数。它用来表示Unicode代码点。由于Unicode字符可能超出8位(即一个字节)的范围,所以需要用32位(即四个字节)来表示。

用途:
处理Unicode字符:当需要处理多语言或特殊字符时,使用 rune 可以确保每个字符都能被正确表示。
字符串的遍历:遍历字符串中的每个字符时,使用 rune 可以方便地处理各种Unicode字符,而不仅仅是ASCII字符。

package main

import (
    "fmt"
)

func main() {
    // 使用 rune
    var r rune = '界'
    fmt.Printf("rune: %c, %U, %d\n", r, r, r)

    // 遍历字符串中的每个字符
    str := "Hello, 世界"
    for i, c := range str {
        fmt.Printf("index: %d, char: %c, rune: %U\n", i, c, c)
    }
}
---------------- 运行结果---------------------
byte: a, 97
rune:, U+754C, 30028
index: 0, char: H, rune: U+0048
index: 1, char: e, rune: U+0065
index: 2, char: l, rune: U+006C
index: 3, char: l, rune: U+006C
index: 4, char: o, rune: U+006F
index: 5, char: ,, rune: U+002C
index: 6, char:  , rune: U+0020
index: 7, char:, rune: U+4E16
index: 10, char:, rune: U+754C

Unicode和UTF-8的区别

前面多次提到了这两个概念,那么他们之间有什么区别和联系呢?
Unicode是字符集而UTF-8是一种编解码规则。字符集为每一个字符分配一个独一无二的ID,而编码规则就是将字符集转换为字符。

总结

byte 用于表示和处理原始的字节数据,是 uint8 类型的别名。
rune 用于表示和处理Unicode字符,是 int32 类型的别名。
使用 byte 和 rune 可以使代码在处理字符和字节数据时更加清晰和安全。

字符串

结构体定义

Go语言中的字符串本质上是一个只读的字节数组(byte array),底层由以下结构体表示:

type StringHeader struct {
    Data uintptr
    Len  int
}

这个结构体定义了字符串的指针和长度。

Data :字段是一个指向存储字符串实际内容的指针,类型为 uintptr,指向字节数组的起始位置。
Len :字段表示字符串的长度,即字符的个数(而不是字节的个数)。

不可变性

字符串在Go语言中是不可变的,一旦创建后就不能被修改。这意味着,对字符串进行操作(如连接、切片等)都会返回一个新的字符串,而不会修改原始字符串的内容。

字符串的底层实现原理

  1. UTF-8 编码:
    Go语言中的字符串使用UTF-8编码存储字符数据。UTF-8 是一种可变长度的编码方式,每个字符可以占用1到4个字节,最大支持Unicode字符集。

  2. 内存分配:
    当创建一个新的字符串时,Go语言会在内存中分配足够的空间来存储字符串的内容,并且在需要时动态扩展或缩小内存空间。字符串的内存管理由Go的运行时系统来处理。

  3. 字符串切片:
    字符串可以像切片一样进行切片操作,但切片操作返回的仍然是一个新的字符串,共享相同的底层字节数组。

  4. 字节切片转换:
    可以通过类型转换将字符串转换为字节数组([]byte)进行直接的字节级操作。但是需要注意,如果修改了字节数组的内容,对原始字符串可能会造成意想不到的影响,因为字符串是不可变的。

package main

import (
	"fmt"
	"reflect"
	"unsafe"
)

func main() {
	str := "Hello, 世界"

	// 获取字符串的底层数据结构
	var sh reflect.StringHeader
	sh = *(*reflect.StringHeader)(unsafe.Pointer(&str))

	fmt.Printf("String data pointer: %v\n", sh.Data)
	fmt.Printf("String length: %d\n", sh.Len)

	// 尝试修改字符串(不可行,会导致编译错误)
	// str[0] = 'h'  // cannot assign to str[0]

	// 字符串切片操作
	substr := str[7:]
	fmt.Println("Substring:", substr) // 输出:世界

	// 修改原始字符串不会影响切片
	// str = "Goodbye"  // 这里会创建一个新的字符串,而不是修改原始字符串

	// 字节切片转换
	bytes := []byte(str)
	fmt.Println("Bytes:", bytes)
}
---------------运行结果------------------
String data pointer: 4372623558
String length: 13
Substring: 世界
Bytes: [72 101 108 108 111 44 32 228 184 150 231 149 140]

  • 15
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值