首页 > 编程语言 >学习go语言编程之数据类型

学习go语言编程之数据类型

时间:2023-08-12 22:34:23浏览次数:57  
标签:编程 int fmt 元素 数据类型 切片 Println 数组 go

数据类型概述

Golang语言内置了如下基础数据类型:

  • 布尔类型:bool
  • 整型:int8,unit8,int16,uint16,int32,uint32,int64,uint64,int,uint,uintptr
  • 浮点类型:float32,float64
  • 复数类型:complex64,complex128
  • 字符串:string
  • 字符类型:rune
  • 错误类型:error

同时,Golang还支持如下复合类型:

  • 指针:pointer
  • 数组:array
  • 切片:slice
  • 字典:map
  • 通道:chan
  • 结构体:struct
  • 接口:inerface

布尔类型

布尔类型的关键字为bool,可赋值为预定义的truefalse

var v1 bool
v1 = true
v2 := (1 == 2) // v2会被推导为bool类型

注意:bool类型不能接受其他类型的赋值,不支持自动或强制类型转换。

var b bool
b = 1 // 编译报错
b = bool(1) // 编译报错

整型

Golang支持多个整型,如下表:

类型 长度(字节) 值范围
int8 1 -128 ~ 127
uint8(即byte) 1 0 ~ 255
int16 2 -32768 ~ 32767
uint16 2 0 ~ 65535
int32 4 -2147483648 ~ 2147483647
uint32 4 0 ~ 4294967295
int64 8 -9223372036854775808 ~ 9223372036854775807
uint64 8 0 ~ 18446744073709551615
int 平台相关 平台相关
uint 平台相关 平台相关
uintptr 同指针 在32位平台下为4字节,64位平台下为8字节

类型表示

在Golang中,intint32被认为是两种不同的类型,编译器也不会自动做类型转换。

var value2 int32
value1 := 64 // 编译器自动推到为int类型
value2 = value1 // 编译报错
value2 = (int32)value1 // 明确使用强制类型转换,编译通过

数值运算

Golang支持的整数运算有:+-*/%

比较运算

Golang支持的比较运算有:><==>=<=!=
注意:两个不同类型的整型数不能直接比较:

var i int32 = 1
var j int64 = 2
if i == j { // 编译报错
    // TODO
}

但是不同类型的整型变量都可以与字面常量进行比较:

var i int32 = 1
var j int64 = 2
if i == 1 || j == 2 { // 编译通过
    // TODO
}

位运算

Golang支持的位运算如下表:

运算 含义 示例
<< 左移 124 << 2 // 结果为496
>> 右移 124 >> 2 // 结果为31
^ 异或 124 ^ 2 // 结果为126
& 124 & 2 // 结果为0
| 124 | 2 // 结果为126
^x 取反 ^2 // 结果为-3

浮点型

浮点型用于表示包含小数点的数据,Golang中的浮点型采用IEEE-754标准的表达方式。

浮点数表示

Golang定义了两个浮点类型:float32,float64。

var f1 float32
f1 = 12
f2 := 12.0 // 会被自动设定为float64,如果不加小数点会被推导为int整型

浮点数比较

浮点数因为涉及精度问题,不能直接使用==来判断是否相等。
使用math包中的Fdim函数进行比较:

import "math"
math.Fdim(f1, f2) < p // f1和f2为两个浮点数,p为自定义的精度,如:0.00001

复数类型

复数有2个实数构成,一个表示实部,一个表示虚部。

复数表示

var v1 complex64
v1 = 3.2 + 12i        // 实部为3.2,虚部为12i
v2 := 3.2 + 12i       // v2为complex128类型
v3 = complex(3.2, 12) // v3结果同v2

实部与虚部

对于一个复数z = complex(x, y),可以通过内置函数real(z)获得实部,通过imag(z)获得虚部。

字符串

声明和初始化字符串:

var str string          // 声明一个字符串变量
str = "Hello, World!"   // 为字符串变量赋值
ch := str[0]            // 取字符串得第一个字符

注意: 虽然可以通过下标访问字符串中的字符,但是字符串的内容不能在初始化之后修改。

s := "string"
s[0] = 'S'             // 编译报错

字符串操作

Golang支持的字符串操作如下表:

运算 含义 示例
x + y 字符串连接 "Hello" + " World!" // 结果为:Hello World!
len(s) 字符串长度 len("Hello") // 结果为5
s[i] 取字符串中指定下标的字符,从0开始 "Hello"[0] // 结果为'H'

字符串遍历

Golang支持两种字符串遍历方式。
方式1:以字节数组遍历

str := "Hello, 世界"
n := len(str)
for i := 0; i < n; i++ {
    ch := str[i]
    fmt.Println(i, ch)
}

输出:

0 72
1 101
2 108
3 108
4 111
5 44
6 32
7 228
8 184
9 150
10 231
11 149
12 140

从输出结果看一共是13个字节,但是从直观上看应该只有9个字符,这是因为每个中文字符在UTF-8编码中占3个字节。

方式2:以Unicode字符遍历

str := "Hello, 世界"
for i, ch := range str {
    fmt.Println(i, ch)
}

输出:

0 72
1 101
2 108
3 108
4 111
5 44
6 32
7 19990
10 30028

以Unicode字符方式遍历时,每个字符的类型是rune,而不是byte,所以一共是9个字符。

字符类型

在Golang中支持两个字符类型,一个是byte(实际上是uint8的别名),代表UTF-8字符串的单个字节的值;另一个是rune,代表单个Unicode字符。

数组

数组是指一系列同一类型数据的集合。
数组的声明方法如下:

[32]byte  // 长度为32的数组,每个元素为一个字节
[2*N] struct {x, y int32} // 复杂类型数组
[1000] *float64  // 指针数组
[3][5]int  // 二维数组
[2][2][2]float64 // 等同于[2]([2][2]float64)

数组长度在定义后就不可更改,使用内置函数len()获取数组长度。

arrLen := len(arr)

元素访问

可以使用下标来访问数组中的元素,从0开始。

arr := [3]int{1, 2, 3}
for i := 0; i < len(arr); i++ {
    fmt.Println(arr[i])
}

使用关键字range遍历容器中的元素:

arr := [3]int{1, 2, 3}
for i, v := range arr {
    fmt.Println(i, v)
}

值类型

Golang中的数组是一个值类型!所有的值类型变量在赋值和作为参数传递时都将产生一次复制动作。
如果将数组作为函数的参数类型,则在函数调用时该参数将发生数据复制。

// 在函数中修改数组元素值,不会影响到原始数组
func modifyArr(arr [3]int)  {
	arr[0] = 10 // 修改数组元素
	fmt.Println("In modifyArr(),arr: ", arr)
}

func main() {
    arr := [3]int{1, 2, 3}
	modifyArr(arr)
	fmt.Println("In main(),arr: ", arr)
}

输出:

In modifyArr(),arr:  [10 2 3]
In main(),arr:  [1 2 3]

显然,在函数中修改数组元素的值,并未影响到原始数组。

数组切片

数组的长度在定义后就不可更改,数组是值类型,每次传递都将产生一个副本。
数组切片可以随时动态扩充存放空间,可以被随意传递而不会导致所管理的元素被重复复制。

创建数组切片

创建数组切片的方法主要有两种:基于数组和直接创建。

  • 基于数组

数组切片可以基于一个已经存在的数组创建,数组切片可以只使用数组的一部分元素或者整个数组来创建,甚至可以创建一个比所基于的数组还要大的数组切片。

// 先定义一个数组
var arr [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

// 基于数组创建一个切片
var arrSlice []int = arr[:5]

fmt.Println(arr)
fmt.Println(arrSlice)

输出:

[1 2 3 4 5 6 7 8 9 10]
[1 2 3 4 5]

Golang支持使用arr[start:end]这种方式来基于数组生成一个数组切片,而且这种用法还非常灵活。
如下语法都是正确的:

var arrSlice1 []int = arr[:]  // 基于数组所有元素创建数组切片
var arrSlice2 []int = arr[:5] // 基于数组的前5个元素创建数组切片
var arrSlice3 []int = arr[5:] // 基于从第5个元素开始的所有元素创建数组切片
  • 直接创建

使用内置函数make()可以灵活地创建数组切片。

// 创建一个初始元素个数为5的数组切片,元素初始值都为0
arrSlice1 := make([]int, 5)
fmt.Println(arrSlice1, len(arrSlice1), cap(arrSlice1))

// 创建一个初始元素个数为5的数组切片,元素初始值都为0,并预留10个元素的存储空间
arrSlice2 := make([]int, 5, 10)
fmt.Println(arrSlice2, len(arrSlice2), cap(arrSlice2))

// 直接创建并初始化包含5个元素的数组切片
arrSlice3 := []int{1, 2, 3, 4, 5}
fmt.Println(arrSlice3, len(arrSlice3), cap(arrSlice3))

以上示例中,有意思的是arrSlice1arrSlice2的长度一样,但是存储空间却不同:arrSlice1的长度和存储空间都是5,而arrSlice2得长度为5(表示当前存储5个元素),存储空间却是10(表示最大可以存储10个元素)。

  • 基于数组切片

类似于数组切片可以基于一个数组创建,数组切片也可以基于另一个数组切片创建。

oldSlice := []int{1, 2, 3, 4, 5}
newSlice := oldSlice[:3] // 基于oldSlice的前三个元素创建新的数组切片
fmt.Println(newSlice)    // [1 2 3]

有意思的是,如上示例中选择的oldSlice元素范围甚至可以超过所包含的元素个数,比如newSlice可以基于oldSlice的前6个元素创建,虽然oldSlice只包含5个元素。只要这个选择的范围不超过oldSlice存储能力(即cap()返回的值),那么这个创建程序就是合法的。newSlice中超出oldSlice元素的部分都会填上0。

// 创建一个初始容量为5,最大空间为10的数组切片
oldSlice := make([]int, 5, 10)
for i := 0; i < len(oldSlice); i++ {
    oldSlice[i] = i + 1
}
fmt.Println("oldSlice:", oldSlice)

// 选择的元素范围超过了oldSlice的元素个数5,但是小于其最大空间10
newSlice := oldSlice[:6]
// newSlice超出oldSlice元素的部分都会设置为0
fmt.Println("newSlice:", newSlice)

输出:

oldSlice: [1 2 3 4 5]
newSlice: [1 2 3 4 5 0]

元素遍历

操作数组元素的所有方法都适用于数组切片。

arrSlice := []int{1, 2, 3, 4, 5}
// 使用下表访问数组切片元素
for i := 0; i < len(arrSlice); i++ {
    fmt.Print(arrSlice[i], " ")
}
fmt.Println()
// 使用range关键字访问数组切片元素
for _, v := range arrSlice {
    fmt.Print(v, " ")
}
fmt.Println()

动态增减元素

使用内置函数append()给数组切片动态增加元素。

arrSlice := make([]int, 5)
fmt.Println(arrSlice)
arrSlice = append(arrSlice, 1) // 使用append()函数动态添加元素之后生成一个新的数组切片
fmt.Println(arrSlice)

输出:

[0 0 0 0 0]    # 在增加元素之前,数组切面元素为初始化的5个
[0 0 0 0 0 1]  # 动态增加一个元素

函数append()的第二个参数其实是一个不定参数,可以按需求添加若干个元素,甚至直接将一个数组切片追加到另一个数组切片的末尾。

arrSlice := make([]int, 5)
fmt.Println(arrSlice)
arrSlice2 := []int{1,2,3}
arrSlice = append(arrSlice, arrSlice2...) // 将arrSlice2追加到arrSlice的末尾,第二个参数后面的三个省略号非常重要
fmt.Println(arrSlice)

输出:

[0 0 0 0 0]
[0 0 0 0 0 1 2 3]

数组切片会自动处理存储空间不足的问题,如果追加的内容长度超过当前已分配的存储空间(即cap()函数值大小),数组切片会自动分配一块足够大的内存。

内容复制

使用内置函数copy()可以将内容从一个数组切片复制到另一个数组切片。
如果两个数组切片不一样大,按其中较小的那个数组切片的元素个数进行复制。

arrSlice1 := []int{1, 2, 3, 4, 5, 6}
arrSlice2 := []int{7, 8, 9}
fmt.Println("Before Copy,arrSlice1:", arrSlice1)
copy(arrSlice1, arrSlice2) // 复制arrSlice2中的全部元素到arrSlice1的前三个位置
fmt.Println("After Copy,arrSlice1:", arrSlice1)

输出:

Before Copy,arrSlice1: [1 2 3 4 5 6]
After Copy,arrSlice1: [7 8 9 4 5 6]  # 复制后arrSlice1的前三个元素被arrSlice2的三个元素替换
arrSlice1 := []int{1, 2, 3, 4, 5, 6}
arrSlice2 := []int{7, 8, 9}
fmt.Println("Before Copy,arrSlice2:", arrSlice2)
copy(arrSlice2, arrSlice1) // 复制arrSlice1的前3个元素到arrSlice2中替换替换掉原来的三个元素
fmt.Println("After Copy,arrSlice2:", arrSlice2)

输出:

Before Copy,arrSlice2: [7 8 9]
After Copy,arrSlice2: [1 2 3] # 只复制了arrSlice1的前三个元素到arrSlice2中并替换掉原来的三个元素

字典

字典map是一堆键值对的未排序集合。
假设存在一个自定义数据结构PersonInfo,定义如下:

// 自定义数据结构
type PersonInfo struct {
	ID string
	Name string
	Address string
}

变量声明

// 定义一个键类型为string,值类型为PersonInfo的字典变量
// myMap是声明的map变量名,string是键的类型,PersonInfo则是其中所存放的值类型
var myMap map[string] PersonInfo

创建

使用内置函数make()创建一个新的map。

myMap = make(map[string] PersonInfo)

也可以选择是否在创建时指定该map的初始存储能力:

// 创建一个初始大小为100的map
myMap = make(map[string] PersonInfo, 100)

还可以在创建map的时候进行初始化:

myMap := map[string] PersonInfo {
    "12345": {"12345", "zhangsan", "beijing"},
}

元素赋值

// 网map中添加元素
personDB["1235"] = PersonInfo{"12345", "zhangsan", "beijing"}

元素删除

使用内置函数delete()删除map元素:

delete(myMap, "12345")

元素查找

value, ok := myMap["12345"]
if ok {
    // 找到元素了
} else {
    // 元素未找到
}

标签:编程,int,fmt,元素,数据类型,切片,Println,数组,go
From: https://www.cnblogs.com/nuccch/p/17625691.html

相关文章

  • 学习go语言编程之流程控制
    Golang支持如下4种流程控制语句:条件语句:if,else和elseif选择语句:switch,case和select循环语句:for,range跳转语句:goto条件语句示例代码:a:=3ifa<5{fmt.Println(a,"litterthan5")}else{fmt.Println(a,"notlitterthan5")}关于条件语句,要注意以下......
  • Golang之旅——内存管理
    转载放在最前一文带你了解,虚拟内存、内存分页、分段、段页式内存管理[Golang三关-典藏版]一站式Golang内存洗髓经|Go技术论坛刘丹冰Aceld感谢以上文章作者,收获满满......
  • SV 第五章 面向对象编程基础
    SystemVerilog验证5面向对象编程基础5.1概述对于Verilog和C语言来说,由于他们不是面向对象变成语言,数据的存储往往是分布式的,例如把数据、地址、指令分别保存在不同的数组里面,不利于程序的解读。面向对象变成使得用户可以创建复杂的数据类型,将数据类型紧密地结合在一起,可以在......
  • 2023-08-12:用go语言写算法。实验室需要配制一种溶液,现在研究员面前有n种该物质的溶液,
    2023-08-12:用go语言写算法。实验室需要配制一种溶液,现在研究员面前有n种该物质的溶液,每一种有无限多瓶,第i种的溶液体积为v[i],里面含有w[i]单位的该物质,研究员每次可以选择一瓶溶液,将其倒入另外一瓶(假设瓶子的容量无限),即可以看作将两个瓶子内的溶液合并,此时合并的溶液体积和物质含量......
  • RTMP流媒体服务器LntonMedia(免费版)视频直播点播平台采用Golang指针问题导致平台重复推
    我们的团队在研发视频流媒体平台时,广泛应用了Go语言。之前我们也与大家交流过关于Go语言指针的问题和应用。如果你对视频流媒体平台编译中如何运用Go语言指针感兴趣,可以了解一下我们的讨论。在对LntonMedia的编译中,我们发现Golang指针问题会导致系统内的重复推流。Golang遍历切片代......
  • 2308-习题 分支循环,goto语句
    1.习题3.1.输入三个整数,从大到小输出这三个数 1#define_CRT_SECURE_NO_WARNINGS2#include<stdio.h>3intmain()4{56inta=0;7intb=0;8intc=0;9inttemp=0;10scanf("%d%d%d",&a,&b,&c);11......
  • 《Rust编程之道》学习笔记一
    《Rust编程之道》学习笔记一序Rust语言的主要特点系统级语言无GC基于LLVM内存安全强类型+静态类型混合编程范式零成本抽象线程安全程序员的快乐何谓快乐?真正的快乐不仅仅是写代码时的“酸爽”,更应该是代码部署到生产环境之后的“安稳”。程序的三大定律程序必须......
  • Django之Auth模块
    一、Auth模块的使用1、Auth模块是Django自带的用户认证模块:开发一个网站无可避免的需要设计实现网站的用户系统。需要实现包括用户注册、用户登录、用户认证、注销、修改密码等功能。Django内置了强大的用户认证系统–auth,它默认使用auth_user表来存储用户数据。2、创建后台......
  • 【Django】request请求设置
    1.GET请求#query请求defget(self,request):print(request.GET)res=[]#最终返回的结果集合search_field=request.GET.get('search_field','')page_index=request.GET.get('page_index',1)......
  • 《CUDA编程:基础与实践》读书笔记(5):统一内存编程
    统一内存(unifiedmemory)是一种逻辑上的概念,它既不是显存、也不是主机内存,而是CPU和GPU都可以访问并能保证一致性的虚拟存储器。使用统一内存对硬件有较高的要求:对于所有功能,GPU架构都必须不低于Kepler架构,主机应用程序必须为64位。对于一些较新的功能,至少需要Pascal架构的GPU......