go语言编程系列(六)

简介: go语言编程系列(六)

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位操作运算符

ed8c245bbee7966764b6f7a6c2cc742f.png

位运算的几个例子:

5c0f844d20d4dc4eff226327170bafe1.png

浮点型:

浮点型。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 语言支持以下逻辑运算符:

6cb90315c9efeed4b6b40e73cc3cd84e.png

布尔值可以和&&和||操作符结合,并且可能会有短路行为:如果运算符左边值已经确定整个表达式的值,那么运算符右边的值将不在被求值,因此下面的表达式总是安全的


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的图:

7923e77a2dde374c1b34580b92330726.png

指针取值:

在对普通变量使用&操作符取地址后会获得这个变量的指针,反过来,怎么对指针取值呢?可以对指针使用*操作,也就是指针取值,代码如下。


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("空值") }}
相关文章
|
1天前
|
Go 定位技术 索引
Go 语言Map(集合) | 19
Go 语言Map(集合) | 19
|
1天前
|
Go
go语言注释,标识符 | 17
go语言注释,标识符 | 17
|
2天前
|
存储 缓存 Go
go语言编程系列(五)
go语言编程系列(五)
|
2天前
|
搜索推荐 Java 编译器
go语言编程系列(四)
go语言编程系列(四)
|
1天前
|
存储 缓存 安全
速成班!去繁存简,一文让你学会go语言基础!!
速成班!去繁存简,一文让你学会go语言基础!!
|
2天前
|
存储 JSON 安全
go语言编程系列(七)
go语言编程系列(七)
|
2天前
|
自然语言处理 Java 测试技术
go语言编程系列(二)
go语言编程系列(二)
|
2天前
|
编译器 Go
go语言编程系列(三)
go语言编程系列(三)
|
4月前
|
开发框架 安全 中间件
Go语言开发小技巧&易错点100例(十二)
Go语言开发小技巧&易错点100例(十二)
60 1
|
1月前
|
JSON 中间件 Go
go语言后端开发学习(四) —— 在go项目中使用Zap日志库
本文详细介绍了如何在Go项目中集成并配置Zap日志库。首先通过`go get -u go.uber.org/zap`命令安装Zap,接着展示了`Logger`与`Sugared Logger`两种日志记录器的基本用法。随后深入探讨了Zap的高级配置,包括如何将日志输出至文件、调整时间格式、记录调用者信息以及日志分割等。最后,文章演示了如何在gin框架中集成Zap,通过自定义中间件实现了日志记录和异常恢复功能。通过这些步骤,读者可以掌握Zap在实际项目中的应用与定制方法
go语言后端开发学习(四) —— 在go项目中使用Zap日志库