Go语言的数据类型:
变量:
var语句用于声明一个变量列表,var语句可以出现在包或函数级别
变量的声明语法一般是:
var 变量名字 类型 = 表达式
通常情况下:"类型"或“=表达式”两个部分可以省略其中的一个
例子:
// 声明并初始化一个整数类型变量 xvar x int = 10
和函数的参数列表一样,类型在最后
在Go中,变量是程序中存储数据的基本单元,每个变量都有一个类型和一个值,并且可以被赋值,传递和修改。
以下是一些基本的变量定义和使用方式:
package mainimport "fmt"func main() { // 声明并初始化一个整数类型变量 x var x int = 10 // 声明并初始化一个字符串类型变量 s var s string = "hello" // 声明一个布尔类型变量 b,不需要显式初始化,默认为 false var b bool // 打印变量的值 fmt.Println(x) fmt.Println(s) fmt.Println(b) // 修改变量的值 x = 20 s = "world" b = true // 再次打印变量的值 fmt.Println(x) fmt.Println(s) fmt.Println(b) // 短变量声明语法 y := 30 z, w := "foo", true // 打印新的变量 fmt.Println(y) fmt.Println(z) fmt.Println(w) // 类型推导 m := 40 n := "bar" // 打印新的变量 fmt.Println(m) fmt.Println(n)}
在该示例当中,我们首先使用var关键字声明和初始化了三个变量:x,s和b
其中,x是一个整数类型的变量,初始化为10;s是一个字符串类型变量,初始化为"hello";b是一个布尔类型变量,没有显示初始化,因此默认值为false。
通过fmt.println函数打印了这三个变量的值,并修改了它们的值。
示例接下来,演示了Go中的短变量声明语法和类型推导方式。
使用短变量声明语法可以更简洁地定义和初始化变量,而类型推导则可以让编译器自动推断变量类型,避免冗余长的类型声明。
需要注意的是,在使用变量时,我们需要特别注意变量的作用域和生命周期,并尽可能地遵循最佳实践,以确保代码的正确性和可读性。
变量的初始化
变量声明可以包含初始值,每个变量对应一个
如果初始化值已经存在,则类型可以省略类型,变量会从初始值中获得类型
var i, j int = 1, 2func Func() { var c, python, java = true, false, "no!" //根据 变量值推定 变量类型 fmt.Println(i, j, c, python, java)}
短变量声明
在函数中,简洁赋值语句:=可以在类型明确的地方代替var声明
函数外的每个语句都必须以关键字开始(var,func等等),因此:=结果不能在函数外使用
import "fmt"func Func() { var i, j int = 1, 2 k := 3 c, python, java := true, false, "no!" fmt.Println(i, j, k, c, python, java)}
零值
没有明确初始值的变量声明会被赋予它们的零值
零值是:
①、数值类型是0
②、布尔类型是false
③、字符串为""(空字符串)
func Func() { var i int var f float64 var b bool var s string fmt.Printf("%v %v %v %q\n", i, f, b, s)}
常量
在Go中,常量是一种固定不变的值,其值在编译时就已经确定,不能被修改。
常量通常用于存储程序中不可变的值,例如数学常数,密码等。
以下是一些基本的常量定义和使用方式:
package mainimport "fmt"func main() { // 声明一个整数类型常量 const x int = 10 // 声明一个字符串类型常量 const s string = "hello" // 打印常量的值 fmt.Println(x) fmt.Println(s) // 尝试修改常量的值,会导致编译错误 // x = 20 // s = "world"}
在该示例中,我们使用const关键字定义了两个常量:x和s
其中,x是一个整数类型常量,初始化为10;s是一个字符串常量,初始化为“hello”.
我们通过fmt.println函数打印了这两个常量的值,并尝试修改它们的值,结果会导致编译错误。需要注意的是,在使用常量时,我们需要注意常量的作用域和生命周期,并尽可能低遵守最佳实践,以确保代码的正确性和可读性。
数值常量
数值常量是高精度的值,一个未指定类型的常量由上下文来决定其类型。
在Go中,数值常量是一种固定不变的的数值,其值在编译时就已经确定,不能被修改。数值常量可以使用各种进制和精度表示
以下是一些基本的数值常量定义和使用方式:
package mainimport "fmt"func main() { // 十进制表示整数常量 const x int = 10 // 八进制表示整数常量 const y int = 012 // 十六进制表示整数常量 const z int = 0x1a // 浮点数常量 const a float64 = 3.14 // 复数常量 const b complex128 = 1 + 2i // 打印常量的值 fmt.Println(x) fmt.Println(y) fmt.Println(z) fmt.Println(a) fmt.Println(b) // 尝试修改常量的值,会导致编译错误 // x = 20 // y = 0123 // z = 0x1b // a = 3.15 // b = 2 + 1i}
在该示例中,使用不同的进制和精度表示了各种类型的数值常量,包括整数常量,浮点数常量和复数常量,通过fmt.println函数打印了这些常量的值,并尝试修改他们的值,结果会导致编译错误。需要注意的是,在使用常量时,我们需要注意常量的作用域和生命周期,并尽可能低遵守最佳实践,以确保代码的正确性和可读性。
Go数据基本类型
①、基本类型
数字number,字符串string和布尔型boolean等等。
②、聚合类型
数组array和结构体struct
③、引用类型
指针pointer,切片slice,字典map,函数func和通道channel。
④、接口类型:接口interface
其中,基本类型又分为:
①、整型:
int8,uint8,byte,int16,uint16,int32,uint32,int64,uint64,int,uint,uintptr。
②、浮点型
float32,float64
③、复数类型:
complex64,complex128
④、布尔类型
bool
⑤、字符串
string
⑥、字符型
rune
本例中展示了几种类型的变量
同导入语句一样,变量声明页可以分组成一个语法块
int,uint和uintpr在32位系统上通常是为32位宽,在64位系统上则为64位宽。当需要一个整数值时应该使用int类型,除非有特殊理由使用固定大小或无符号的整数类型
package basicimport ("fmt""math/cmplx")var ( ToBe bool = false MaxInt uint64 = 1<<64 - 1 z complex128 = cmplx.Sqrt(-5 + 12i))func BasicTypeDemo() { fmt.Printf("Type: %T Value: %v\n", ToBe, ToBe) fmt.Printf("Type: %T Value: %v\n", MaxInt, MaxInt) fmt.Printf("Type: %T Value: %v\n", z, z)}
类型转换
表达式T(v)将值v转换为类型T
一些关于数值的转换:
var i int = 42var f float64 = float64(i)var u uint = uint(f)
或者,更加简单的形式
i := 42f := float64(i)u := uint(f)
与C不同的是,Go在不同类型的项之间赋值时需要显示转换。
类型推导
在声明一个变量而不指定其类型时,变量的类型由右值推导得出。
当右值声明了类型时,新变量的类型与其相同
var i intj := i // j 也是一个 int
不过当右边包含未指明类型的数值常量时,新变量的类型就可能是int,float64或complex128了,这取决于常量的精度
i := 42 // intf := 3.142 // float64g := 0.867 + 0.5i // complex128
整型
Go语言同时提供了有符号和无符号类型的整数运算
有符号整型类型:
int8,长度:1字节, 取值范围:(-128 ~ 127)int16,长度:2字节,取值范围:(-32768 ~ 32767)int32,长度:4字节,取值范围:(-2,147,483,648 ~ 2,147,483,647)int64.长度:8字节,取值范围:(-9,223,372,036,854,775,808 ~9,223,372,036,854,775,807)
无符号整型类型:
uint8,长度:1字节, 取值范围:(0 ~ 255)uint16,长度:2字节,取值范围:(0 ~ 65535)uint32,长度:4字节,取值范围:(0 ~ 4,294,967,295)uint64.长度:8字节,取值范围:(0 ~ 18,446,744,073,709,551,615)
byte是unit8类型等价类型,byte类型一般用于强调数值是一个原始的数据而不是一个小的整数。
uinptr是一种无符号的整数类型,没有指定具体的bit大小但是足以容纳指针。
uinptr类型只有在底层编程才需要,特别是在Go语言和C语言函数库或操作系统接口相互交互的地方。
此外在这里还需要了解下进制的转换方便以后学习和使用:
十进制整数: 使用0-9的数字表示且不以0开头。// 100 123455八进制整数: 以0开头,0-7的数字表示。// 0100 0600十六进制整数: 以0X或者是0x开头,0-9|A-F|a-f组成 //0xff 0xFF12
整型运算
在整型运算,算术运算,逻辑运算和比较运算,运算符优先级从上到下递减顺序排序
* / % << >> & &^+ - | ^== != < <= > >=&&||
在同一个优先级,使用左优先结合规则,但是使用括号可以明确优先顺序。
Go语言支持所有常规的整数四则运算,+,-,*,/和%(取余运算只用于整数)
由于强制类型的关系,在Go语言中,不同类型的整型值不能直接进行算术运算,比如下面的这样的计算就会报编译错误
var intValue1 int8intValue2 := 8 // intValue2 将会被自动推导为 int 类型intValue3 := intValue1 + intValue2
编译报错信息如下:
invalid operation: intValue1 + intValue2 (mismatched types int8 and int)
类型转化之后就好了
intValue3 := intValue1 + **int8**(intValue2)
在Go语言中,也支持自增/自减运算符,即++/--,但是只能作为语句,不能作为表达式,且只能用于后缀,不能放到变量前面
intValue1++ // 有效,intValue1 的值变成 9intValue1 = intValue1++ // 无效,编译报错--intValue1 // 无效,编译报错
还支持+=,-=,*=,/=,%=这种快捷写法
intValue1 += intValue1 // 18intValue1 -= intValue1 // 0intValue1 *= intValue1 // 81intValue1 /= intValue1 // 1intValue1 %= intValue1 // 0
bit位操作运算符
位运算的几个例子:
浮点型:
浮点型。float32精确度到小数点后7位,float64精确到小数点后15位
由于精确度的缘故,在使用==或者!=来比较浮点数时应当非常小心。
浮点型(IEEE-754 标准):float32:(+- 1e-45 -> +- 3.4 * 1e38)32位浮点类型float64:(+- 5 1e-324 -> 107 1e308)64位浮点类型
浮点型中指数部分由E或e以及带正负号的10进制整数表示,例3.9E-2表示浮点数0.039,3.9E+1表示浮点数为39.有时候浮点数类型值也可以被简化,比如39.0可以被简化为39,0.039可以简化为.039.在go中浮点数的相关部分只能由10进制表示法表示。
复数
复数类型:complex64: 由两个float32类型的值分别表示复数的实数部分和虚数部分complex128: 由两个float64类型的值表示复数的实数部分和虚数部分
复数类型的值一般由浮点数表示的实数部分,加号+,浮点数表示的虚数部分以及小写字母i组成。例如:
var x complex128 = complex(1,2) //1+2i
对于一个复数c=complex(x,y)可以通过Go语言中内置函数real(z)获得该复数的实部,也就是x,通过imag(c)获得该复数的虚部,也就是y。
布尔型
在Go语言中,布尔值的类型为 bool,值是 true 或 false。
在Go语言中,使用 bool 关键字声明布尔数据类型,并且只能采用 true 或 false . 布尔数据类型的 默认值为 false 。
布尔可以做3种逻辑运算,&&(逻辑且),||(逻辑或),!(逻辑非),布尔类型的值不支持其他类 型的转换.
Go 语言支持以下逻辑运算符:
布尔值可以和&&和||操作符结合,并且可能会有短路行为:如果运算符左边值已经确定整个表达式的值,那么运算符右边的值将不在被求值,因此下面的表达式总是安全的
s != "" && s[0] == 'x'
如果不做左边的s!=""判断而是直接执行s[0]操作,如果应用于空字符串将会导致panic异常。
rune类型
字符是 UTF-8 编码的 Unicode 字符,Unicode 为每一个字符而非字形定义唯一的码值(即一个整 数)
例如 字符a 在 unicode 字符表是第 97 个字符,所以其对应的数值就是 97, 也就是说对于Go语言处理字符时,97 和 a 都是指的是字符a,而 Go 语言将使用数值指代字符时,将这 样的数值称呼为 rune 类型。
rune类型是 Unicode 字符类型,和 int32 类型等价,通常用于表示一个 Unicode 码点。
rune 和 int32 可以互换使用。一个Unicode代码点通常由”U+”和一个以十六进制表示法表示的整数表 示,例如英文字母’A’的Unicode代码点为”U+0041”。
在 Go 中,rune 类型是用于表示 Unicode 码点的类型。Unicode 是一种标准,用于为世界上各种语言 和符号分配唯一的数字编码,以便它们可以在计算机中存储和处理。
在 Go 中,rune 类型实际上是一个 int32 类型的别名。它可以用于表示任何 Unicode 码点,并提供了 一些有用的函数和方法,用于处理字符串和 Unicode 编码。
以下是一些基本的 rune 类型使用方式:
func RuneDemo() { // 使用单引号表示一个 rune 类型值 var r rune = '你' // 打印这个 rune 类型值 fmt.Println(r) // 将 rune 类型转换为字符串类型 s := string(r) fmt.Println(s) // 遍历一个字符串并打印每个字符的 Unicode 码点 for i, c := range "hello 世界" { fmt.Printf("字符 %d: %U\n", i, c) }}
在该示例中:
①、我们首先使用单引号表示了一个 rune 类型值,即 Unicode 码点 "你"。我们通过 fmt.Println 函数打印了这个 rune 类型值,并将其转换为字符串类型。
②、接下来,我们遍历了一个字符串,并通过 %U 格式化符号打印了每个字符的 Unicode 码点。输出的结果如下:
20320你字符 0: U+0068字符 1: U+0065字符 2: U+006C字符 3: U+006C字符 4: U+006F字符 5: U+0020字符 6: U+4E16字符 9: U+754C
字符串
在Go语言中,组成字符串的最小单位是字符,存储的最小单位是字节,字符串本身不支持修改。
字节是数据存储的最小单元,每个字节的数据都可以用整数表示,例如一个字节储存的字符a,实际存 储的是97而非字符的字形,将这个实际存储的内容用数字表示的类型,称之为byte。
字符串是不可变的字节序列,它可以包含任意数据,包括0值字节,但是主要还是为了人可读的文本。内置的 len()函数返回字符串的字节数
字符串的表示法有两种,即:原生表示法和解释型表示法。
原生表示法,需用用反引号”`”把字符序列包 起来,
如果用解释型表示法,则需要用双引号”””包裹字符序列。
var str1 string = "呵呵"var str2 string = `呵呵`
这两种表示的区别是,前者表示的是所见即所得的(除了回车符)。后者所表示的值中转义符会起作用。字符串值是不可变的,如果我们创建了一个此类型的值,就不可能再对它本身做任何修改。
var str string // 声明一个字符串变量str = "呵呵 呵呵" // 字符串赋值ch := str[0] // 取字符串的第一个字符
指针类型
和C/C++中的指针不同,Go语言中的指针不能进行偏移和运算,是安全指针。
要搞明白Go语言中的指针需要先知道3个概念:指针地址、指针类型和指针取值。
Go语言中的指针
Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。
但是,如果传递数据使用指针,而无须拷贝数据。
Go语言中的指针操作非常简单,只需要记住两个符号:&(取地址)和*(根据地址取值)。
指针地址和指针类型
每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。
Go语言中使用&字符放在变量前面对变量进行“取地址”操作。
Go语言中的值类型(int、float、bool、string、array、struct)都有对应的指针类型,如:*int 、 *int64 、 *string 等。
取变量指针的语法如下:
ptr := &v // v的类型为T
其中:
v:代表被取地址的变量,类型为Tptr:用于接收地址的变量,ptr的类型就为*T,称做T的指针类型。*代表指针。
举个例子:
func main() { a := 10 b := &a fmt.Printf("a:%d ptr:%p\n", a, &a) // a:10 ptr:0xc00001a078 fmt.Printf("b:%p type:%T\n", b, b) // b:0xc00001a078 type:*int fmt.Println(&b) // 0xc00000e018}
看一下b:=&a的图:
指针取值:
在对普通变量使用&操作符取地址后会获得这个变量的指针,反过来,怎么对指针取值呢?可以对指针使用*操作,也就是指针取值,代码如下。
func main() { //指针取值 a := 10 b := &a // 取变量a的地址,将指针保存到b中 fmt.Printf("type of b:%T\n", b) c := *b // 指针取值(根据指针去内存取值) fmt.Printf("type of c:%T\n", c) fmt.Printf("value of c:%v\n", c)}
输出如下:
type of b: *inttype of c: intvalue of c:10
总结:取地址操作符&和取值操作符 * 是一对互补操作符,&取出地址, * 根据地址取出地址指向的 值。
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下:
1.对变量进行取地址(&)操作,可以获得这个变量的指针变量。2.指针变量的值是指针地址。3.对指针变量进行取值(*)操作,可以获得指针变量指向的原变量的值
指针传值示例:
func modify1(x int) { x = 100}func modify2(x *int) { *x = 100}func main() { a := 10 modify1(a) fmt.Println(a) // 10 modify2(&a) fmt.Println(a) // 100}
空指针
当一个指针被定义后没有分配到任何变量时,它的值为 nil
package mainimport "fmt"func main() { var p *string fmt.Println(p) fmt.Printf("p的值是%v\n", p) if p != nil { fmt.Println("非空")} else { fmt.Println("空值") }}