您现在的位置是:首页 >学无止境 >Golang 数据类型网站首页学无止境

Golang 数据类型

张胤尘 2025-12-14 00:01:03
简介Golang 数据类型

数据类型

golang 中,数据类型是用于定义变量可以存储的数据种类和范围的。由于 golang 是一种静态类型语言,这意味着在编译时必须明确指定变量的类型。golang 提供了丰富的内置数据类型,可以分为两大类:基本数据类型和复合数据类型

基本数据类型

基本数据类型划分为:数值类型、布尔类型、字符串类型。

数值类型

数值类型划分为:整数类型、浮点数类型、复数类型。

整数类型

整数类型划分为:有符号整数、无符号整数。

有符号整数
数据类型位数范围说明
int88-128 到 1278 位有符号整数,占用 1 个字节
int1616-32768 到 3276716 位有符号整数,占用 2 个字节
int3232-2147483648 到 214748364732 位有符号整数,占用 4 个字节
int6464-9223372036854775808 到 922337203685477580764 位有符号整数,占用 8 个字节
int平台相关32 位系统:-2147483648 到 2147483647;64 位系统:-2147483648 到 2147483647默认整数类型,大小取决于平台(通常是 4 个字节)
package main

import "fmt"

func main() {
	// 声明并初始化有符号整数
	var a int8 = -128
	var b int16 = 32767
	var c int32 = -2147483648
	var d int64 = 9223372036854775807
	var e int = -100 // 默认的 int 类型

	// 打印有符号整数
	fmt.Println("int8:", a)  // int8: -128
	fmt.Println("int16:", b) // int16: 32767
	fmt.Println("int32:", c) // int32: -2147483648
	fmt.Println("int64:", d) // int64: 9223372036854775807
	fmt.Println("int:", e)   // int: -100
}
无符号整数
数据类型位数范围说明
uint880 到 2558 位无符号整数,占用 1 个字节
uint16160 到 6553516 位无符号整数,占用 2 个字节
uint32320 到 429496729532 位无符号整数,占用 4 个字节
uint64640 到 1844674407370955161564 位无符号整数,占用 8 个字节
uint平台相关32 位系统:0 到 4294967295;64 位系统:0 到 18446744073709551615默认无符号整数类型,在 32 位系统上是 4 个字节,在 64 位系统上是 8 个字节
uintptr平台相关与平台指针大小相同用于存储指针的无符号整数,大小与平台的指针大小相同
package main

import "fmt"

func main() {
	// 声明并初始化无符号整数
	var a uint8 = 255
	var b uint16 = 65535
	var c uint32 = 4294967295
	var d uint64 = 18446744073709551615
	var e uint = 100 // 默认的 uint 类型

	// 打印无符号整数
	fmt.Println("uint8:", a)  // uint8: 255
	fmt.Println("uint16:", b) // uint16: 65535
	fmt.Println("uint32:", c) // uint32: 4294967295
	fmt.Println("uint64:", d) // uint64: 18446744073709551615
	fmt.Println("uint:", e)   // uint: 100
}
浮点数类型
数据类型位数精度范围(十进制数字)说明
float3232精度约为 6 到 7 位32 位浮点数,适用于需要较小存储空间和较低精度的场景
float6464精度约为 15 到 16 位64 位浮点数,适用于需要高精度计算的场景,如科学计算和金融计算
package main

import "fmt"

func main() {
	// 声明并初始化浮点数
	var a float32 = 3.1415926
	var b float64 = 2.718281828459045

	// 打印浮点数
	fmt.Println("float32:", a)	// float32: 3.1415925
	fmt.Println("float64:", b)	// float64: 2.718281828459045
}

需要注意的是,可能打印 a 会出现不是 3.1415926 的现象,由于 float32 在底层存储时,尾数位只有23位(不包含隐藏位,其余的1位符号位,8位指数位)所以可以保证小数点后的有效数字精度约为 6-7 位十进制数,故 float32 只能存储有限的位数,因此会进行截断或舍入,最终实际存储的值可能是 3.1415925

复数类型
数据类型组成部分精度说明
complex64由两个 float32 组成精度约为 6 到 7 位32 位复数类型,适用于需要较小存储空间和较低精度的场景
complex128由两个 float64 组成精度约为 15 到 16 位64 位复数类型,适用于需要高精度计算的场景
package main

import "fmt"

func main() {
	// 声明并初始化复数
	var a complex64 = complex(3, 4)      // 实部为 3,虚部为 4
	var b complex128 = complex(5.5, 6.6) // 实部为 5.5,虚部为 6.6

	// 打印复数
	fmt.Println("complex64:", a)	// complex64: (3+4i)
	fmt.Println("complex128:", b)	// complex128: (5.5+6.6i)
}

布尔类型(bool

布尔类型(bool)用于表示逻辑值,它只有两个可能的值:truefalse

  • true:表示逻辑上的“真”。
  • false:表示逻辑上的“假”。

布尔类型在 golang 中主要用于以下场景:

  • 条件判断:在 ifforswitch 等控制流语句中,布尔值用于决定程序的执行路径。
  • 逻辑运算:布尔值可以参与逻辑运算,如 &&(逻辑与)、||(逻辑或)、!(逻辑非)。
package main

import "fmt"

func main() {
	var isReady bool = true
	var flag bool = false

	fmt.Println("isReady:", isReady) // isReady: true
	fmt.Println("flag:", flag)       // flag: false

	if isReady {
		fmt.Println("Ready to go!") // Ready to go!
	} else {
		fmt.Println("Not ready yet.")
	}

	// 当isReady和flag都是true进入if代码段,否则执行else代码段
	if isReady && flag {
		fmt.Println("result is true")
	} else {
		fmt.Println("result is false") // result is false
	}
}

字符串类型(string

golang 语言中,字符串类型(string)用于表示文本数据。字符串在 golang 中是不可变的,这意味着一旦创建,字符串的内容不能被修改。

字符串是由字符组成的不可变序列,通常用双引号""或反引号```定义。双引号用于普通字符串,反引号用于原始字符串(Raw String)。

普通字符串可以包含 Unicode 字符,支持转义字符(如 等)。原始字符串不会处理转义字符,通常用于包含换行符或多行文本。

package main

import "fmt"

func main() {
    
    // 普通字符串
	var str1 string = "Hello, World!"
    var str2 string = "Go语言支持中文字符"
    var str3 string = "这是一个带有换行符的字符串
"
    
    fmt.Println(str1)
    fmt.Println(str2)
    fmt.Println(str3)
    
    // 原始字符串 
    var rawStr string = `这是一个原始字符串
    它包含换行符和
这样的转义字符`
    fmt.Println(rawStr)
}

字符串是不可变的,这意味着一旦创建,字符串的内容不能被修改。如果需要修改字符串,必须创建一个新的字符串。

package main

import "fmt"

func main() {
	var str string = "Hello"
	// str[0] = 'h' 	// 错误:字符串是不可变的
	str = "hello" // 正确:重新分配一个新的字符串

	fmt.Println(str) // hello
}

复合数据类型

数组(array

数组是一种固定大小的序列类型,用于存储相同类型的元素。数组的大小在声明时确定,且不可变。数组元素下标从0开始。

  • 固定大小:声明后大小不可变。
  • 同类型元素:数组中的所有元素必须是相同的类型。
  • 索引访问:可以通过索引访问和修改数组中的元素。
  • 内存连续:数组的元素在内存中是连续存储的。
package main

import "fmt"

func main() {
    // 声明一个长度为5的int数组
	var arr [5]int = [5]int{1, 2, 3, 4, 5}
	fmt.Println("数组:", arr)
	fmt.Println("数组长度:", len(arr)) // 获取数组长度5

	// 修改数组元素
	arr[0] = 10
	fmt.Println("修改后的数组:", arr)	// [10 2 3 4 5]
}

切片(slice

切片是基于数组的动态数据结构,可以动态增长和缩减。切片提供了更灵活的数组操作。

  • 动态大小:可以动态增长和缩减。
  • 底层基于数组:切片是对底层数组的引用。
  • 丰富的操作:支持appendcopylencap等操作。
  • 切片操作:可以通过切片操作符[:]创建新的切片。
package main

import "fmt"

func main() {
    // 声明一个切片
	slice := []int{1, 2, 3, 4, 5}
	fmt.Println("切片:", slice)
	fmt.Println("切片长度:", len(slice)) // 获取切片长度5

	// 向切片追加元素
	slice = append(slice, 6)
	fmt.Println("追加后的切片:", slice) // 追加后的切片: [1 2 3 4 5 6]

	// 切片操作
	slice = slice[1:3]              // 从下标1开始到下标3结束(不包含下标3)
	fmt.Println("切片操作后的切片:", slice) // 切片操作后的切片: [2 3]
}

映射(map

映射是一种键值对的集合,键是唯一的,值可以是任意类型。

  • 键的唯一性:映射中的键必须是唯一的。如果尝试向映射中添加一个已经存在的键,其对应的值会被覆盖。
  • 动态大小:映射的大小是动态的,可以根据需要动态添加、删除或修改键值对。
  • 键值对:键的类型必须是可比较的,例如基本类型、指针、接口、通道、结构体等。不可比较的类型(如切片、映射、函数)不能作为键;值可以是任意类型,包括基本类型(如 intstring)、结构体、切片、映射等;
package main

import "fmt"

func main() {
	// 创建一个映射,键是string类型,值是int类型
	m := make(map[string]int)
	m["Alice"] = 30
	m["Bob"] = 25

	// 获取值
	fmt.Println("Alice's age:", m["Alice"]) // Alice's age: 30

	// 检查键是否存在
	if age, ok := m["Charlie"]; ok {
		fmt.Println("Charlie's age:", age)
	} else {
		fmt.Println("Charlie is not in the map") // Charlie is not in the map
	}
}

结构体(struct

结构体是一种自定义的数据类型,用于组合不同类型的数据。

  • 自定义类型:可以包含不同类型的数据字段。
  • 方法绑定:可以为结构体定义方法。
  • 嵌入:可以嵌入其他结构体。
package main

import "fmt"

// 自定义结构体
type Person struct {
	Name string
	Age  int
}

// 结构体Person的方法,方法名称时SayHello
func (p Person) SayHello() string {
	return "Hello, my name is " + p.Name
}

func main() {
	// 声明一个结构体变量
	p := Person{Name: "Alice", Age: 30}

	fmt.Println("结构体:", p)             // 结构体: {Alice 30}
	fmt.Println("结构体的Name字段:", p.Name) // 结构体的Name字段: Alice

	// 修改结构体字段
	p.Age = 31
	fmt.Println("修改后的结构体:", p) // 修改后的结构体: {Alice 31}

	// 调用结构体方法
	fmt.Println(p.SayHello()) // Hello, my name is Alice
}

指针(pointer

指针存储变量的内存地址,通过指针可以间接访问和修改变量的值。

  • 存储内存地址:指针存储变量的内存地址。
  • 间接访问:可以通过指针间接访问和修改变量的值。
  • 指针类型:指针类型为*T,其中T是基础类型。
package main

import "fmt"

func main() {
	var a int = 10
	var p *int = &a // 声明一个指针p,指向变量a的地址

	fmt.Println("变量a的值:", a)      // 变量a的值: 10
	fmt.Println("指针p指向的地址:", p)   // 指针p指向的地址: 0xc0000120e0
	fmt.Println("指针p指向地址的值:", *p) // 指针p指向地址的值: 10

	// 通过指针修改变量a的值
	*p = 20
	fmt.Println("修改后的变量a的值:", a) // 修改后的变量a的值: 20
}

指针指向自定义结构体类型,代码如下所示:

package main

import "fmt"

// 自定义结构体
type Person struct {
	Name string
	Age  int
}

// 结构体Person的方法,方法名称时SayHello
func (p Person) SayHello() string {
	return "Hello, my name is " + p.Name
}

func main() {
	persion := Person{Name: "Bob", Age: 18}
	var pPtr *Person = &persion

	fmt.Println("指针pPtr指向地址的值:", *pPtr) // 指针pPtr指向地址的值: {Bob 18}
	pPtr.Age = 19
	fmt.Println("修改后的值:", persion) // 修改后的值: {Bob 19}
    
    fmt.Println(pPtr.SayHello()) // Hello, my name is Bob
}

函数(function

函数是一段可以重复使用的代码,用于执行特定的任务。

  • 参数和返回值:可以有参数和返回值。
  • 多返回值:支持多返回值。
  • 作为变量传递:函数可以作为变量传递。
package main

import "fmt"

func add(a, b int) int {
    return a + b // 返回两个整数的和
}

func main() {
    result := add(5, 3) // 调用函数
    fmt.Println("函数返回的结果:", result)	// 函数返回的结果: 8
}

接口(interface

接口定义了一组方法,类型通过实现这些方法来满足接口。接口是一种类型抽象。

  • 方法签名:接口定义了一组方法签名。
  • 动态绑定:类型通过实现接口中的方法来满足接口。
package main

import "fmt"

type Animal interface {
	Speak() string // 定义接口方法
}

type Dog struct {
	Name string
}

// Dog结构体实现了接口Animal的Speak方法
func (d Dog) Speak() string {
	return "Woof!"
}

func main() {
	var animal Animal = Dog{Name: "Buddy"} // 声明一个接口变量
	fmt.Println("动物说话:", animal.Speak())   // 动物说话: Woof!
}

通道(channel

通道用于在 golang 协程( goroutine )之间通信,可以发送和接收数据。

  • 协程间通信:用于协程间的通信。
  • 同步和异步操作:支持同步和异步操作。
  • 带缓冲通道:可以是带缓冲的通道。
发送和接收数据
  • 发送数据:ch <- value
  • 接收数据:value := <-ch
  • 安全接收:value, ok := <-ch,其中ok是一个布尔值,如果通道已经关闭且没有更多数据可接收,ok将会是false
关闭通道
  • 通道可以通过close(ch)函数来关闭。一旦通道被关闭,就不能再向其发送数据,但仍然可以从通道中接收已有的数据。
  • 注意:通常只有发送方应该关闭通道,因为关闭通道是一种通知接收方不再有新数据到来的信号。
package main

import (
	"fmt"
	"time"
)

func main() {
	ch := make(chan int) // 创建一个通道

	// 启动一个协程,向通道ch中发送数据
	go func() {
		ch <- 42
        close(ch) // 发送完数据后关闭通道
	}()

	val := <-ch // 从通道接收数据
	fmt.Println("从通道接收到的数据:", val) // 从通道接收到的数据: 42
    
     _, ok := <-ch
    if !ok {
        fmt.Println("通道已关闭")
    }
    
	time.Sleep(1 * time.Second)    // 等待协程完成
}

类型转换

类型转换是指将一个类型的值转换为另一个类型的值。在 golang 语言中,类型转换是显式的,需要明确指定目标类型。

  • 显式转换:类型转换必须显式进行,不能自动转换。
  • 目标类型必须明确:转换时需要明确指定目标类型。
  • 转换规则:只有类型兼容的值才能进行转换。

基本类型转换

package main

import "fmt"

func main() {
	var a int = 10
	var b float64 = float64(a) // 将int转换为float64
	fmt.Println("转换后的值:", b)   // 转换后的值: 10

	var c float64 = 3.14
	var d int = int(c)       // 将float64转换为int
	fmt.Println("转换后的值:", d) // 转换后的值: 3
}

将高精度类型转换为低精度类型时,可能会导致精度损失。例如上面给出的代码中所示,将float64转换为int时,小数部分会被截断。

另外针对基本类型转换时需要保证类型兼容的值,例如,不能将string直接转换为int

需要注意,当对一个类型使用 type 关键字定义别名之后,再进行类型转换时编译器会认为不是相同的类型,需要显示的类型转换

package main

import "fmt"

func main() {
    type MyInt int
    
    var a MyInt = 1
    
    // error: cannot use a (variable of type MyInt) as int value in variable
    // var b int = a
    
    var b int = int(a)
    
    fmt.Println(a, b) // 1 1
}

切片和数组转换

package main

import "fmt"

func main() {
	var arr [5]int = [5]int{1, 2, 3, 4, 5}
	var slice []int = arr[:] // 将数组转换为切片

	fmt.Println("切片:", slice) // 切片: [1 2 3 4 5]
}

接口类型转换

接口类型转换需要使用类型断言(Type Assertion)用于将接口类型转换为具体的类型。类型断言是 golang 语言中处理接口类型转换的一种方式,它允许在运行时检查接口变量是否实现了某个具体类型,并将其转换为该类型。

value, ok := interfaceValue.(T)
  • interfaceValue 是接口类型的变量。
  • T 是目标类型。
  • value 是转换后的值。
  • ok 是一个布尔值,表示转换是否成功。

单值形式

value := interfaceValue.(T)
  • 如果转换成功,value 是转换后的值。
  • 如果转换失败,程序会触发 panic

多值形式

value, ok := interfaceValue.(T)
  • 如果转换成功,value 是转换后的值,oktrue
  • 如果转换失败,value 是目标类型的零值,okfalse

注意事项

  • 类型兼容性:只有当接口变量实际存储的值是目标类型时,类型断言才会成功。
  • 避免 panic:在不确定接口变量具体类型时,建议使用双值形式进行类型断言,以避免程序 panic
  • 嵌入类型:如果接口变量存储的是嵌入类型,类型断言也可以成功。例如,Animal 接口存储的是 *Dog 类型时,animal.(*Dog) 也是有效的。

代码示例

package main

import "fmt"

type Animal interface {
    Speak() string
}

type Dog struct{}

func (d Dog) Speak() string {
    return "Woof!"
}

func main() {
    
    // 单值形式
    var animal1 Animal = Dog{}
    dog1 := animal1.(Dog) // 单值形式,如果失败会panic
    fmt.Println("动物说话:", dog1.Speak())
    
    // 多值形式
    var animal2 Animal = Dog{}
    if dog2, ok := animal2.(Dog); ok {
        fmt.Println("动物说话:", dog2.Speak())
    } else {
        fmt.Println("类型转换失败")
    }
}

类型推断

类型推断是指编译器根据上下文自动推断变量的类型。在 golang 语言中,类型推断主要通过短变量声明(:=)和函数返回值实现。

  • 短变量声明:使用:=声明变量时,编译器会根据赋值的值自动推断变量的类型。
  • 函数返回值:函数返回值的类型由函数签名决定,编译器会根据返回值的类型自动推断变量的类型。
  • 简洁性:减少代码冗余,提高代码可读性。

短变量声明

package main

import "fmt"

func main() {
	a := 10           // 编译器推断a的类型为int
	b := "Hello"      // 编译器推断b的类型为string
	fmt.Println(a, b) // 10 Hello
}

注意::=只能在函数内部使用,不能在函数外部使用。

函数返回值

package main

import "fmt"

// 函数的返回值是int类型
func add(a, b int) int {
	return a + b
}

func main() {
	result := add(5, 3)             // 编译器推断result的类型为int
	fmt.Println("函数返回的结果:", result) // 函数返回的结果: 8
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。