Golang——2、基本数据类型和运算符

article/2025/8/3 16:36:34

基本数据类型和运算符

  • 1、基本数据类型
    • 1.1、整形
    • 1.2、浮点型
    • 1.3、布尔值
    • 1.4、字符串
    • 1.5、byte和rune类型
    • 1.6、修改字符串
  • 2、基本数据类型之间的转换
    • 2.1、数值类型之间的相互转换
    • 2.2、其他类型转换成string类型
    • 2.3、string类型转换成数值类型
  • 3、Golang中的运算符
    • 3.1、算数运算符
    • 3.2、关系运算符
    • 3.3、逻辑运算符
    • 3.4、赋值运算符
    • 3.5、位运算符

1、基本数据类型

Go语言中数据类型分为:基本数据类型和复合数据类型
基本数据类型有:整型、 浮点型、 布尔型、 字符串
复合数据类型有:数组、切片、结构体、函数、map、通道(channel)、接口等。

1.1、整形

整型分为以下两个大类:
有符号整形按长度分为:int8、int16、int32、int64。
对应的无符号整型:uint8、uint16、uint32、uint64。

在这里插入图片描述
在这里插入图片描述

package main
import ("fmt""unsafe"
)
func main() {// 使用unsafe.Sizeof查看存储空间var num int = 20fmt.Printf("num: %v, num类型为: %T, 所占字节数: %d\n", num, num, unsafe.Sizeof(num))var num1 int8 = 111var num2 int16 = 300var num3 int32 = 1000var num4 int64 = 55434fmt.Printf("num1: %v, num类型为: %T, 所占字节数: %d\n", num1, num1, unsafe.Sizeof(num1))fmt.Printf("num2: %v, num类型为: %T, 所占字节数: %d\n", num2, num2, unsafe.Sizeof(num2))fmt.Printf("num3: %v, num类型为: %T, 所占字节数: %d\n", num3, num3, unsafe.Sizeof(num3))fmt.Printf("num4: %v, num类型为: %T, 所占字节数: %d\n", num4, num4, unsafe.Sizeof(num4))
}

在这里插入图片描述

两种不同的类型相加,涉及到类型转换问题:

var a1 int32 = 10
var a2 int64 = 20
fmt.Println(int64(a1) + a2) // 将a1转换成int64
fmt.Println(a1 + int32(a2)) // 将a2转换成int32

但是要注意高位向地位转换的情况,可能会发生截断。

数字字面量语法:Go1.13版本之后引入了数字字面量语法,这样便于开发者以二进制、八进制或十六进制浮点数的格式定义数字,例如:v := 0b00101101, 代表二进制的101101,相当于十进制的45。 v := 0o377, 代表八进制的377,相当于十进制的255。v := 0x1p-2,代表十六进制的1除以 2²,也就是0.25。而且还允许我们用_来分隔数字,比如说:v := 123_456 等于123456。

var a = 11
fmt.Printf("a=%v\n", a)   // 原样输出
fmt.Printf("a=%d\n", a)   // 十进制输出
fmt.Printf("a=%b\n", a)   // 二进制输出
fmt.Printf("a=%o\n", a)   // 八进制输出
fmt.Printf("a=0x%x\n", a) // 十六进制输出

在这里插入图片描述


1.2、浮点型

Go语言支持两种浮点型数:float32和float64。这两种浮点型数据格式遵循IEEE 754标准:float32的浮点数的最大范围约为3.4e38,可以使用常量定义:math.MaxFloat32。float64的浮点数的最大范围约为1.8e308,可以使用一个常量定义:math.MaxFloat64。

打印浮点数可以使用%f,默认保留六位小数,使用%v就是原样输出。

package mainimport ("fmt""unsafe"
)func main() {var f1 float32 = 3.1415fmt.Printf("f1: %v--%f--%.2f, 类型为: %T, 所占字节数: %d\n", f1, f1, f1, f1, unsafe.Sizeof(f1))var f2 float64 = 3.1513213fmt.Printf("f2: %v--%f--%.2f, 类型为: %T, 所占字节数: %d\n", f2, f2, f2, f2, unsafe.Sizeof(f2))
}

在这里插入图片描述

Golang中浮点数默认是float64:

f3 := 2.12345
fmt.Printf("f3: %v--%f--%.2f, 类型为: %T, 所占字节数: %d\n", f3, f3, f3, f3, unsafe.Sizeof(f3))

在这里插入图片描述

Golang中浮点数精度丢失问题:

d := 1129.6
fmt.Println(d * 100)m1 := 8.2
m2 := 3.3
fmt.Println(m1 - m2)

在这里插入图片描述
这里我们期望输出的分别是112960和4.9,但是由于精度损失,所以不符合我们预期。
怎么解决呢?可以使用第三方包来解决:https://github.com/shopspring/decimal

这个后面再细说。

Golang科学计数法表示浮点数类型:

f4 := 3.14e2
f5 := 3.14e-2
fmt.Printf("f4: %v--%f\n", f4, f4)
fmt.Printf("f5: %v--%f\n", f5, f5)

在这里插入图片描述


1.3、布尔值

Go语言中以bool类型进行声明布尔型数据,布尔型数据只有true(真)和false(假)两个值。
注意:
1、布尔类型变量的默认值为false。
2、Go语言中不允许将整型强制转换为布尔型。
3、布尔型无法参与数值运算,也无法与其他类型进行转换。

var flag1 bool
flag2 := true
fmt.Printf("flag1: %v\n", flag1)
fmt.Printf("flag2: %v\n", flag2)

在这里插入图片描述
另外,整形和浮点型数据不初始化默认为0,string类型不初始化默认为空串。

注意点2、3,bool不行与其他类型转换,也不能用整数转bool,这点与C/C++以示区分。

if flag2 {fmt.Println("true")
} else {fmt.Println("false")
}

所以上面if条件判断里面就不能写一个整数。


1.4、字符串

Go语言中的字符串以原生数据类型出现,使用字符串就像使用其他原生数据类型(int、 bool、float32、 float64等)一样。Go语言里的字符串的内部实现使用UTF-8编码。

package mainimport "fmt"func main() {var s1 string = "hello world"s2 := "hello world"fmt.Println(s1, s2)
}

在这里插入图片描述

Golang中定义一个多行字符串时,需要使用到反引号字符`:

s3 := `hello 鹏哥,
hello 杭哥,
hello 蛋哥。`
fmt.Println(s3)

在这里插入图片描述

在这里插入图片描述

// 1.len计算字符串长度
var str1 = "hello golang"
fmt.Println(len(str1))// 2.fmt.Sprintf拼接字符串
var str2 = "hello C++"
str3 := str1 + " " + str2
str4 := fmt.Sprintf("%v %v", str1, str2)
fmt.Println(str3)
fmt.Println(str4)// 3.strings.Split分割字符串
str5 := "123-456-789"
arr := strings.Split(str5, "-") // 返回值是一个切片
fmt.Println(arr)// 4.strings.contains判断是否包含
str6 := "this is string"
var flag = strings.Contains(str6, "this")
fmt.Println(flag)// 5.前缀/后缀判断 strings.HasPrefix/strings.HasSuffix
var flag1 = strings.HasPrefix(str6, "is")
var flag2 = strings.HasSuffix(str6, "string")
fmt.Printf("flag1: %v, flag2: %v\n", flag1, flag2)// 6.获取字串的下标 strings.Index() / strings.LastIndex()
pos1 := strings.Index(str6, "is")
pos2 := strings.LastIndex(str6, "ing")
fmt.Printf("pos1: %v, pos2: %v\n", pos1, pos2) // 找不到返回-1// 7.strings.Join操作,将切片转换成字符串
str7 := strings.Join(arr, "-")
fmt.Println(str7)

在这里插入图片描述


1.5、byte和rune类型

组成每个字符串的元素叫做“字符”, 可以通过遍历字符串元素获得字符。 字符用单引号(’)包裹起来。

var a = 'a'
fmt.Printf("值: %v, 字符: %c, 类型: %T\n", a, a, a)
var str = "this is string"
fmt.Printf("值: %v, 字符: %c, 类型: %T\n", str[2], str[2], str[2])

在这里插入图片描述
golang中定义字符属于int类型,可以看到上面a的类型是int32,字符串中字符类型是uint8。

如果要计算字符串占用字节数呢?可以使用unsafe.Sizeof吗?

var str = "this is string"
fmt.Printf("str所占字节数为: %d\n", unsafe.Sizeof(str))

在这里插入图片描述
实际上str字符串所占字节数应该是14个字节,但是计算出来的确实16。所以不能使用unsafe.Sizeof来计算,需要使用len函数来计算。

var str1 = "hello world"
var str2 = "你好golang"
fmt.Printf("str1所占字节数为: %d\n", len(str1))
fmt.Printf("str2所占字节数为: %d\n", len(str2))

在这里插入图片描述
str2所占字节数为12,这是因为golang中汉字采用的是utf8编码,所以一个汉字占3个字节,英文字母占一个字节,所以算下来总共12字节。

var ch = '你'
fmt.Printf("值: %v, 字符: %c, 类型: %T\n", ch, ch, ch)

在这里插入图片描述

Go 语言的字符有以下两种:
1、uint8类型,或者叫byte型,代表了ASCII码的一个字符。
2、rune类型,代表一个UTF-8字符。
当需要处理中文、日文或者其他复合字符时,则需要用到rune类型。rune类型实际是一个int32。
Go使用了特殊的rune类型来处理Unicode,让基于Unicode的文本处理更为方便,也可以使用byte型进行默认字符串处理,性能和扩展性都有照顾。

下面看遍历字符串:

var str = "hello 你好"
for i := 0; i < len(str); i++ {fmt.Printf("%v(%c) ", str[i], str[i])
}

在这里插入图片描述
这样遍历对于纯英文数字的字符串是没有问题的,但是对于有中文汉字的字符串就不行了。因为这样遍历每次是取一个字节出来,所以对于有汉字的字符串不能这样遍历。

而应该采下面rune的遍历方式:

var str = "hello 你好"
for _, ch := range str { // runefmt.Printf("%v(%c) ", ch, ch)
}

在这里插入图片描述


1.6、修改字符串

要修改字符串,需要先将其转换成[]rune或[]byte,完成后再转换为string。无论哪种转换,都会重新分配内存,并复制字节数组。

var str1 = "hello golang"
byteStr := []byte(str1)
byteStr[0] = 'H'
fmt.Println(string(byteStr))var str2 = "你好 golang"
runeStr := []rune(str2)
runeStr[0] = '哈'
fmt.Println(string(runeStr))

在这里插入图片描述


2、基本数据类型之间的转换

2.1、数值类型之间的相互转换

注意:golang中只有强制类型转换,没有隐式类型转换。

// 1.整形和整形之间的转换
var a int8 = 20
var b int16 = 111
fmt.Println(int16(a) + b)// 2.浮点型和浮点型之间的转换
var f1 float32 = 3.1415
var f2 float64 = 3.2131
fmt.Println(float64(f1) + f2)// 3.整形和浮点型之间的转换
var x int = 30
var y float64 = 3.154
fmt.Println(float64(x) + y)

一般是从低位向高位进行转换,高位向低位转换可能会溢出。


2.2、其他类型转换成string类型

1、sprintf把其他类型转换成string类型。

package mainimport "fmt"func main() {var i int = 20var f float64 = 3.1415var b bool = truevar ch = 'a'str := fmt.Sprintf("%d %f %t %c", i, f, b, ch)fmt.Printf("值: %v, 类型: %T\n", str, str)
}

在这里插入图片描述
注意:Sprintf使用中需要注意转换的格式int为%d、float为%f、bool 为%t、byte为%c。

2、使用strconv包里面的几种转换方法进行转换。

func FormatInt(i int64, base int) string
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
func FormatBool(b bool) string
func FormatUint(i uint64, base int) string

1、FormatInt的第一个参数就是要转换的数值,第二个参数表示进制数。
2、FormatFloat第一个参数表示要转换的数值,第二参数是格式化类型,第三个参数表示保留的小数位数,第四个参数表示格式化的类型(32/64)。
格式化类型:
‘f’(-ddd.dddd)、
‘b’(-ddddp±ddd,指数为二进制)、
‘e’(-d.dddde±dd,十进制指数)、
‘E’(-d.ddddE±dd,十进制指数)、
‘g’(指数很大时用’e’格式,否则’f’格式)、
‘G’(指数很大时用’E’格式,否则’f’格式)。
3、FormatBool直接传对应的bool类型即可。
4、FormatUint用于将字符转换成字符串,第一个参数表示字符需要强转成Uint64,第二个参数表示进制数。

var a int = 10
str1 := strconv.FormatInt(int64(a), 10)
fmt.Printf("值: %v, 类型: %T\n", str1, str1)var f float32 = 3.14159
str2 := strconv.FormatFloat(float64(f), 'f', 6, 64)
fmt.Printf("值: %v, 类型: %T\n", str2, str2)str3 := strconv.FormatBool(true)
fmt.Printf("值: %v, 类型: %T\n", str3, str3)x := 'x'
str4 := strconv.FormatUint(uint64(x), 10)
fmt.Printf("值: %v, 类型: %T\n", str4, str4)

在这里插入图片描述


2.3、string类型转换成数值类型

func ParseInt(s string, base int, bitSize int) (i int64, err error)
func ParseFloat(s string, bitSize int) (float64, error)
func ParseBool(str string) (bool, error)

以上四个函数用户将字符串类型string转换成对应的类型。
1、ParseInt第一个参数字符串,第二参数表示进制数,第三个参数表示转换后的位数——32/64。
2、ParseFloat第一个参数字符串,第二个参数表示转换的位数。
可以注意到它们的返回值都是两个,第一个返回值表示结果,第二个表示出错,而当我们调用函数接收返回值时可以把error省略掉,直接用前面说的_来表示即可。

str1 := "123456"
num1, _ := strconv.ParseInt(str1, 10, 64)
fmt.Printf("值: %v, 类型: %T\n", num1, num1)str2 := "1233.1415926"
num2, _ := strconv.ParseFloat(str2, 64)
fmt.Printf("值: %v, 类型: %T\n", num2, num2)str3 := "true"
flag, _ := strconv.ParseBool(str3)
fmt.Printf("值: %v, 类型: %T\n", flag, flag)

在这里插入图片描述
上面的string类型转bool类型其实没什么意义。

注意:在go语言中数值类型没法直接转换成bool类型bool类型也没法直接转换成数值类型。


3、Golang中的运算符

Golang运算符包括:算数运算符、关系运算符、逻辑运算符、赋值运算符、位运算符。

3.1、算数运算符

在这里插入图片描述
这边最主要是%运算:求余=被除数 -(被除数/除数)*除数

fmt.Println(10%3, -10%3)

在这里插入图片描述

注意:在golang中,++和--只能独立使用,且不存在前置++/--,错误写法如下
在这里插入图片描述
正确写法:

var x int = 1
x++
fmt.Println(x)

3.2、关系运算符

在这里插入图片描述
例如:

var a = 10
var b = 9
fmt.Println(a > b)  // true
fmt.Println(a >= b) // true
fmt.Println(a == b) // false
fmt.Println(a < b)  // false
fmt.Println(a <= b) // false
fmt.Println(a != b) // true

3.3、逻辑运算符

在这里插入图片描述
这里演示一下使用:

var a = 10
var b = 5
if a >= 10 && b < 10 {fmt.Println("hhahaha")
}

在这里插入图片描述

最主要在于逻辑运算符的短路问题:
先来看正常的情况,所有条件满足的情况下执行if语句内的代码

package mainimport "fmt"func test() bool {fmt.Println("test...")return true
}func main() {var x int = 10if x > 5 && test() {fmt.Println("ok...")}
}

在这里插入图片描述

对于&&运算符,只要第一个条件不满足,就不会再去判断后面的条件了,因此在下面没有输出任何信息:

package mainimport "fmt"func test() bool {fmt.Println("test...")return true
}func main() {var x int = 10if x > 10 && test() {fmt.Println("ok...")}
}

在这里插入图片描述

对于||运算符,只要第一个条件满足,就无需再判断后面的条件了,所以下面的代码只输出ok:

package mainimport "fmt"func test() bool {fmt.Println("test...")return true
}func main() {var x int = 10if x > 5 || test() {fmt.Println("ok...")}
}

在这里插入图片描述


3.4、赋值运算符

在这里插入图片描述


3.5、位运算符

在这里插入图片描述

package mainimport "fmt"func main() {var x int = 10      // 1010var y int = 2       // 0010fmt.Println(x << y) // 40fmt.Println(x >> y) // 2fmt.Println(x & y)  // 0010->2fmt.Println(x | y)  // 1010->10fmt.Println(x ^ y)  // 1000->8
}

在这里插入图片描述


http://www.hkcw.cn/article/UriMYPonYZ.shtml

相关文章

服务器如何配置防火墙管理端口访问?

配置服务器防火墙来管理端口访问&#xff0c;是保障云服务器安全的核心步骤。下面我将根据你使用的不同操作系统&#xff08;Linux: Ubuntu/Debian/CentOS&#xff1b;Windows Server&#xff09;介绍常用防火墙配置方法。 ✅ 一、Linux 防火墙配置&#xff08;UFW / firewalld…

4.2.2 Spark SQL 默认数据源

在本实战概述中&#xff0c;我们探讨了如何在 Spark SQL 中使用 Parquet 格式作为默认数据源。首先&#xff0c;我们了解了 Parquet 文件的存储特性&#xff0c;包括其二进制存储方式和内嵌的 Schema 信息。接着&#xff0c;通过一系列命令&#xff0c;我们演示了如何在 HDFS 上…

4.0/Q2,GBD数据库最新文章解读

文章题目&#xff1a;Global burden of Type 2 Diabetes Mellitus attributable to dietary risks in elderly adults: insights from the Global Burden of Disease study 2021 DOI&#xff1a;10.3389/fnut.2025.1557923 中文标题&#xff1a;老年人饮食风险导致的 2 型糖尿病…

mobile app 工具简要对比

支持mobile app UI自动化测试的工具比较多&#xff0c;其中使用时间很长&#xff0c;应用很广泛的有appium&#xff0c;前面博客也详细介绍过appium webdriverio工具的特点&#xff0c;此篇博客将介绍之前项目实际使用或者调研过的mobile app ui工具&#xff0c;最后再对多个工…

【Doris基础】Apache Doris业务场景全解析:从实时数仓到OLAP分析的完美选择

目录 1 Doris核心能力概述 2 实时数据分析场景 2.1 实时数据仓库 2.2 实时监控与告警 3 交互式OLAP分析场景 3.1 自助式BI分析 3.2 用户行为分析 4 大数据分析场景 4.1 日志分析系统 4.2 时序数据处理 5 Doris技术架构适配性分析 5.1 适合Doris的场景特征 5.2 不适合Doris的场景…

投稿 IEEE Transactions on Knowledge and Data Engineering 注意事项

投稿 IEEE Transactions on Knowledge and Data Engineering 注意事项 要IEEE overleaf 模板私信,我直接给我自己论文,便于编辑 已经投稿完成了,有一些小坑 准备工作 注册IEEE账户:若没有IEEE账户,需前往IEEE官网注册。注册成功后,可用于登录投稿系统。现在新的系统,…

Python----目标检测(《Fast R-CNN》和Fast R-CNN)

一、《Fast R-CNN》 1.1、基本信息 作者&#xff1a;Ross Girshick 机构&#xff1a;Microsoft Research 发表时间&#xff1a;2015年 论文链接&#xff1a;arXiv:1504.08083 代码开源&#xff1a;GitHub仓库&#xff08;MIT License&#xff09; 1.2、主要内容 Fast R…

十一、【核心功能篇】测试用例管理:设计用例新增编辑界面

【核心功能篇】测试用例管理&#xff1a;设计用例新增&编辑界面 前言准备工作第一步&#xff1a;创建测试用例相关的 API 服务 (src/api/testcase.ts)第二步&#xff1a;创建测试用例编辑页面组件 (src/views/testcase/TestCaseEditView.vue)第三步&#xff1a;配置测试用例…

YC-8002型综合变配电监控自动化系统

一 .系统概述 YC-8002型综合变配电监控自动化系统是西安亚川电力科技有限公司为适应广大客户要求&#xff0c;总结多项低 压配电网络自动化工程实例的经验&#xff0c;基于先进的电子技术、计算机和网络通讯等技术自主研发的--套结合本公司网络配电产品的应用于低压配电领域的…

DeviceNET转EtherCAT网关:医院药房自动化的智能升级神经中枢

在现代医院药房自动化系统中&#xff0c;高效、精准、可靠的设备通信是保障患者用药安全与效率的核心。当面临既有支持DeviceNET协议的传感器、执行器&#xff08;如药盒状态传感器、机械臂限位开关&#xff09;需接入先进EtherCAT高速实时网络时&#xff0c;JH-DVN-ECT疆鸿智能…

股指期货出现大幅贴水时,为什么不适合套期保值?

先简单说下股指期货贴水。股指期货有个理论价格&#xff0c;正常情况下&#xff0c;期货价格和理论价格应该是差不多的。但要是期货价格比理论价格低了不少&#xff0c;这就叫贴水。就好比一件衣服&#xff0c;本来标价100元&#xff0c;现在市场上只卖80元&#xff0c;这就是贴…

脱发因素机器学习数据分析

脱发因素机器学习数据分析 一、背景描述 随着年龄增长&#xff0c;脱发成为影响外貌与健康的重要问题。 本数据集包含遗传、荷尔蒙变化、医疗状况、药物治疗、营养缺乏、心理压力等12个可能导致脱发的因素&#xff0c; 旨在通过数据分析挖掘各因素与脱发的潜在关联&#xf…

Transformer架构技术学习笔记:从理论到实战的完整解析

引言&#xff1a;重新定义序列建模的里程碑 2017年&#xff0c;Vaswani等人在论文《Attention Is All You Need》中提出的Transformer架构&#xff0c;彻底改变了自然语言处理领域的游戏规则。与传统RNN/LSTM相比&#xff0c;Transformer具有三大革命性特征&#xff1a; 全注意…

从0开始学习R语言--Day12--泊松分布

今天我们来看一个很经典的回归模型&#xff1a;泊松分布。 泊松分布 我们一般会把泊松分布用于预测问题&#xff0c;比如想知道成年人每天接到的骚扰电话次数&#xff0c;医院每天的急诊病人等。但在一些方面&#xff0c;跟我们想的会有出入。例如你不能将其应用在预测下周你的…

机器学习无监督学习sklearn实战一:K-Means 算法聚类对葡萄酒数据集进行聚类分析和可视化( 主成分分析PCA特征降维)

本项目代码在个人github链接&#xff1a;https://github.com/KLWU07/Machine-learning-Project-practice/tree/main/1-Wine%20cluster%20analysis 如果对于聚类算法理论不理解可参考这篇之前文章机器学习中无监督学习方法的聚类&#xff1a;划分式聚类、层次聚类、密度聚类&…

接口的概念及特性

目录 1、接口的概念2、语法规则2.1 接口的定义2.2、接口的使用 3、特性4、实现多个接口5、接口中的继承6、接口使用实例 1、接口的概念 在现实生活中&#xff0c;接口的例子有很多&#xff0c;比如&#xff1a;笔记本的 USB 接口&#xff0c;电源插座等。在电脑的 USB 口上&am…

品牌控价维护渠道生态与品牌价值的关键

在品牌发展的征程中&#xff0c;随着经销商队伍不断壮大&#xff0c;价格管控已成为关乎品牌存亡的核心命题。价格体系一旦失控&#xff0c;渠道乱象便如潮水般涌来。低价倾销不仅挤压正规经销商的生存空间&#xff0c;削弱其市场竞争力&#xff0c;更会引发消费者对产品价值的…

2011肠衣问题

1 D类竞赛题目---具体题目 D题 天然肠衣搭配问题 天然肠衣&#xff08;以下简称肠衣&#xff09;制作加工是我国的一个传统产业&#xff0c;出口量占世界首位。肠衣经过清洗整理后被分割成长度不等的小段&#xff08;原料&#xff09;&#xff0c;进入组装工序。 传统的生产…

Express教程【001】:Express创建基本的Web服务器

文章目录 1、初识express1.1 什么是Express1.2 主要特点1.3 Express的基本使用1.3.1 安装1.3.2 创建基本的Web服务器 1、初识express 目标&#xff1a; 能够使用express.static()快速托管静态资源能够使用express路由精简项目结构能够使用常见的express中间件能够使用express创…

CentOS 7 环境中部署 LNMP(Linux + Nginx + MySQL 5.7 + PHP)

在 CentOS 7 环境中部署 LNMP&#xff08;Linux Nginx MySQL 5.7 PHP&#xff09; 环境的详细步骤如下。此方案确保各组件版本兼容&#xff0c;并提供完整的配置验证流程。 1. 更新系统 sudo yum update -y 2. 安装 MySQL 5.7 2.1 添加 MySQL 官方 YUM 仓库 由于MySQL并不…