Go语言基础:数据类型

embedded/2025/4/2 6:38:27/

一、基础数据类型:Go语言的积木块

1.1 数字类型全家福

package mainimport ("fmt"
)func main() {// 有符号整数类型var a int    = 42                        // int 类型,自动选择32或64位var b int8   = 127                       // int8 类型,8位有符号整数var c int16  = 32767                     // int16 类型,16位有符号整数var d int32  = 2147483647                // int32 类型,32位有符号整数var e int64  = 9223372036854775807       // int64 类型,64位有符号整数// 无符号整数类型var f uint   = 42                        // uint 类型,自动选择32或64位var g uint8  = 255                       // uint8 类型,8位无符号整数var h uint16 = 65535                     // uint16 类型,16位无符号整数var i uint32 = 4294967295                // uint32 类型,32位无符号整数var j uint64 = 18446744073709551615      // uint64 类型,64位无符号整数// 浮点数类型var k float32 = 3.14                     // float32 类型,32位浮点数var l float64 = 3.141592653589793        // float64 类型,64位浮点数// 复数类型var m complex64  = 1 + 2i                // complex64 类型,实部和虚部都是 float32var n complex128 = 1 + 2i                // complex128 类型,实部和虚部都是 float64// 打印每个变量的类型fmt.Printf("%T %T %T %T %T\n", a, b, c, d, e)fmt.Printf("%T %T %T %T %T\n", f, g, h, i, j)fmt.Printf("%T %T\n", k, l)fmt.Printf("%T %T\n", m, n)
}

1.2 字符与布尔类型

var ch byte = 'A'      		// ASCII字符
var unicodeCh rune = '中' // Unicode字符
var flag bool = true

1.3 字符串的特别之处

Go语言的字符串是不可变的,也就是说,一旦创建后就无法修改其中的内容。如果需要修改字符串的内容,通常的做法是将字符串转换成一个可变的字节切片([]byte)或字符切片([]rune),对切片进行修改后再转换回字符串。

s := "hello"
b := []byte(s)   // 将字符串转换为字节切片
b[0] = 'H'       // 修改字节切片中的内容
s2 := string(b)  // 将修改后的字节切片转换回字符串
fmt.Println(s2)  // 输出: "Hello"

二、复合数据类型:构建复杂结构

2.1 数组:刻板的容器

特点

  • 长度固定
  • 值类型(赋值会复制整个数组)
  • 类型包含长度信息:[3]int ≠ [5]int
var arr1 [3]int             	// [0 0 0]
arr2 := [...]int{1,2,3}     	// 编译器推导长度
arr3 := [2][3]int{{1,2}, {3}} // 多维数组

痛点时刻:当我们需要动态大小时怎么办?

2.2 切片:灵活的舞者

关键特性

  • 底层引用数组
  • 自动扩容机制
  • 长度 vs 容量
slice1 := make([]int, 3, 5) // 长度3,容量5
slice2 := arr2[1:3]         // 基于数组创建
slice3 := []int{1,2,3}      // 直接初始化// 动态操作
slice3 = append(slice3, 4)  // 自动扩容
copy(slice1, slice2)        // 复制元素

扩容条件

  • 当向切片中追加元素(使用append函数)时,如果切片的长度未超过容量,则直接在底层数组上进行追加。
  • 如果追加元素后长度超过容量,Go会创建一个新的底层数组,并将旧切片的数据复制到新数组中。

扩容策略(目的:平衡内存使用和性能,避免频繁的内存分配和数据拷贝)

  • 如果切片的当前容量小于1024,新的容量一般是原来的2倍。
  • 如果切片的当前容量大于等于1024,新的容量会增加为原来的1.25倍(具体倍数可能会根据实现细节有所不同)。

注意事项

  • 扩容会导致新的内存分配和数据拷贝,因此如果预先知道切片的最大容量,最好在创建切片时指定容量以减少扩容的开销。
  • 由于扩容会创建新的底层数组,因此在扩容后,旧的切片和新的切片将不再共享同一个底层数组。

2.3 映射:闪电查询专家

特点

  • 键的类型必须是可比较的,而值可以是任意类型。
  • Map是引用类型,赋值操作不会复制整个映射。
  • 内置函数 make 用于初始化 map,使用时需注意防止空指针异常。
  • map 本身并不是线程安全的。具体来说,如果多个 goroutine 同时对一个 map 进行读写操作而没有采取适当的同步措施,那么程序可能会出现竞态条件(race condition),这可能导致不可预测的行为或崩溃。
m1 := make(map[string]int)
m2 := map[string]int{"apple": 5,"pear":  3,
}// 安全操作
if count, exists := m2["banana"]; exists {fmt.Println(count)
}// 遍历(无序!)
for k, v := range m2 {fmt.Println(k, v)
}

2.4 结构体:自定义数据蓝图

type Person struct {Name stringAge  intContact struct {Phone stringEmail string}
}p := Person{Name: "Alice",Age:  30,
}
p.Contact.Phone = "123-4567"

三、变量 vs 常量:变与不变的哲学

3.1 变量声明三剑客

特点

  • 作用域:变量的作用域由声明的位置决定,在函数内声明的变量属于局部作用域,全局声明的变量则属于包级作用域。
  • 生命周期:局部变量随函数调用结束而销毁,而全局变量则在整个程序运行期间一直存在。
  • 可变性:变量在程序运行过程中其值是可以被修改的。
// 标准式
var x int = 10// 类型推导
var y = "hello"// 短声明(这种方式语法简洁,只能在函数内部使用,非常适合局部变量的声明,但不适用于全局变量。)
z := 3.14// 分组声明(不推荐)
var (a = 1b = true
)

3.2 常量的奥妙

常量的特点

  • 不可变性:常量一旦赋值便不能再被修改,适用于需要保持固定值的场景。
  • 作用域:与变量类似,常量也有作用域的概念,取决于它们的声明位置。
  • 编译期常量:常量的值在编译期间确定,因此在运行时具有更高的效率。
  • 无地址分配:常量没有在内存中占据一个特定的地址,因为它们的值是直接嵌入到使用它们的代码中的。
const PI = 3.1415
const (Red = iota   // 0Green        // 1Blue         // 2
)// 编译期计算
const MatrixSize = 10 * 10

关键差异

特性变量常量
可修改
内存地址
类型推导支持必须显式
作用域相同相同

3.3 生命周期探秘

var globalVar = "永生" // 包生命周期func demo() {localVar := "临时工" // 函数执行期间存在fmt.Println(localVar)
}

内存小剧场:当函数执行结束,"临时工"会被垃圾回收器请出场外~


四、互动与思考

在学习过程中,我鼓励大家不断思考并动手实践。以下是一些问题和讨论话题,希望你能参与进来:

  1. 数据类型选择:在你以往的开发经历中,有没有遇到过数据类型选择不当导致性能问题的情况?你认为如何在保证代码简洁的前提下选择合适的数据类型?
  2. 复合数据类型使用场景:你觉得在何种场景下数组更合适,何种场景下又应该优先选择切片或映射?分享你的思考和实际案例吧!
  3. 变量和常量的管理:在大型项目中,如何合理规划变量和常量的作用域和生命周期?这不仅关乎代码质量,也涉及到团队协作和维护成本。

欢迎大家在评论区留言讨论,我们一起分享心得,共同进步!

学习检查清单

✅ 能说出3种数字类型的区别
✅ 会正确使用切片扩容
✅ 能区分 var 与 := 的使用场景
✅ 知道常量为什么不能取地址


http://www.ppmy.cn/embedded/178197.html

相关文章

蓝桥刷题note12(lcm,gcd问题)

lcm(a,b):a,b的最小公倍数 gcd(a,b):a,b的最大公约数 性质: 1.a*blcm(a,b)*gcd(a,b) 2.gcd(a,b)gcd(a%b,b)gcd(b,a%b) 求gcd: nt gcd(int a, int b) {if (b 0) {return a;}return gcd(b, a % b); } 1.薅羊毛 双十一购物节来了!作为精打细…

RK3588,V4l2 读取Gmsl相机, Rga yuv422转换rgb (mmap)

RK3588, 使用V4l2 读取 gmsl 相机,获得yuv422格式图像, 使用 rga 转换 rgb 图像。减少cpu占用率. 内存管理方式采用 mmap… 查看相机信息 v4l2-ctl --all -d /dev/cam0 , 查看自己相机分辨率,输出格式等信息,对应修改后续代码测试… Driver Info:Driver name : rkcif…

HCIA—— 31 HTTP的报文、请求响应报文、方法、URI和URL

学习目标: HTTP的报文、请求响应报文、方法、URI和URL 学习内容: HTTP报文——请求报文和响应报文;HTTP报文结构HTTP的---请求报文首部和响应报文首部方法URI和URL 目录 1.HTTP报文 1)HTTP的报文——请求报文和响应报文 HTTP协议的请求和响…

Fegin 400错误分析

问题描述 使用Fegin进行远程接口调用出现400的错误,但是使用postman或curl命令进行接口访问却没有任何问题。 问题分析 根据Fegin调用失败,而postman直接访问会出现400错误。而400错误一般都是客户端错误,因此对方服务器接口应该是没有任何问题,可通过开启日志看看Feign…

J2EE框架技术 第四章 J2EE的IOC

序:本章将叙述在项目完成完成基本的增删改查后,融入了项目的重要思想之一,控制翻转。其主要目的是为了降低程序的耦合性,使项目使用起来更加灵活。 第一节:IOC的概念 一、什么是IOC? 概念: Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思…

LangChain4j 入门(二)

LangChain 整合 SpringBoot 下述代码均使用 阿里云百炼平台 提供的模型。 创建项目&#xff0c;引入依赖 通过 IDEA 创建 SpringBoot 项目&#xff0c;并引入 Spring Web 依赖&#xff0c;SpringBoot 推荐使用 3.x 版本。 引入 LangChain4j 和 WebFlux 依赖 <!--阿里云 D…

鸿蒙学习手册(HarmonyOSNext_API16)_数据持久化②:键值型数据库

概述 键值型数据库就像一个大抽屉柜&#xff0c;每个抽屉都有一个唯一的标签&#xff08;键&#xff09;&#xff0c;里面可以放任何东西&#xff08;值&#xff09;。当你需要存或取东西时&#xff0c;直接看标签拿对应的抽屉就行&#xff0c;不用管其他抽屉里有什么。这种简…

C++ STL常用算法之常用排序算法

常用排序算法 学习目标&#xff1a; 掌握常用的排序算法。 算法简介&#xff1a; sort //对容器内元素进行排序 random_shuffle //洗牌&#xff0c;指定范围内的元素随机调整次序 merge //容器元素合并&#xff0c;并存储到另一容器中 reverse //反转指定范围的元素 so…