查看源文件

Go变量、数据类型与运算

变量声明

声明变量,单独赋值

package main
import "fmt"
 
func main(){
	var i int
	i = 861
	fmt.Println("i=", i)
}

声明变量但不赋值,使用默认值

package main
import "fmt"
 
func main(){
	var i int
	fmt.Println("i=",i)
}
// i=0 是默认值

声明变量同时赋值

package main
import "fmt"
 
func main(){
	var number int = 6
	fmt.Println("number=",number)
}

类型推导

有时候我们会将变量的类型省略,这个时候编译器会根据等号右边的值来推导变量的类型完成初始化。

package main
import "fmt"
 
func main() {
	var number = 6
	fmt.Printf("number type is %T\n", number)
	fmt.Println("number=", number)
}

短变量声明

在函数内部,可以使用更简略的 ==:=== 方式声明并初始化变量。这属于语法糖的形式,go会自动识别变量类型。

注意

只能在函数内使用,在包级别不能使用短变量声明

package main
import "fmt"
 
func main() {
	n := 6
	m := 8
	fmt.Println(m, n)
}

一次声明多个变量

package main
import "fmt"
 
func main() {
	var (
		m = 5
		n = 6
		name = "string"
	)
	fmt.Println("m=", m, "n=", n, "name=", name)
}

匿名变量

在使用多重赋值时,如果想要忽略某个值,可以使用匿名变量(anonymous variable)。匿名变量用一个下划线 _ 表示。

package main
import "fmt"
 
func foo()(int, string){
	return 68, "test"
}
 
func main() {
	x, _ := foo() // 只想给x赋返回值68,另一个值忽略,不用起名字
	_, y := foo() // 只想给y赋返回值test,另一个值忽略
	fmt.Println("x=", x, "y=", y)
}

常量声明

相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。如写web程序时返回的状态码定义成常量。常量的声明和变量声明非常类似,只是把 var 换成了 ==const==,常量在定义的时候必须赋值。

常量的定义格式:

const identifier [type] = value

示例:

package main
import "fmt"
 
const status = 200
 
func main() {
	fmt.Printf("Type of status is %T\n", status)
	fmt.Println("status=", status)
}

iota计数

iota 在 const关键字出现时将被重置为 0( const 内部的第一行),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。

iota 可以被用作枚举值:

package main
import "fmt"
 
const (
	a = iota
	b
	c
)
 
func main() {
	fmt.Println("a=", a, "b=", b, "c=", c)
}

数据类型

整型

  • int

    • 64位系统占8字节
    • -2^63 ~ 2
  • int8

    • 1字节
    • -128 - 127
  • int16

    • 2字节
    • -2^15 ~ 2
  • int32

    • 4字节
    • -2^31 ~ 2
  • int64

    • 8字节
    • -2^63 ~ 2
  • uint

    • 64位系统占8字节
    • 0~2
  • uint8

    • 1字节
    • 0~255
  • uint16

    • 2字节
    • 0~2
  • uint32

    • 4字节
    • 0~2
  • uint64

    • 8字节
    • 0~2
  • rune:等价int32,表示一个Unicode码

  • byte:等价uint8,存储字符用byte

字符型

Go语言的字符有以下两种:

  1. 一种是 uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符。用 1 个字节的传统 ASCII 编码的字符来说,完全没有问题,例如 var ch byte = 'A',字符使用单引号括起来。

  2. 另一种是 rune 类型,代表一个 UTF-8 字符,当需要处理中文、日文或者其他复合字符时,则需要用到 rune 类型。rune 类型等价于 int32 类型。

编码

Go 语言的编码都统一成了 utf-8。非常的方便,很统一,再也没有编码乱码的困扰了

布尔型

一个布尔类型的值只有两种:true 或 false。if 和 for 语句的条件部分都是布尔类型的值,并且==和<等比较操作也会产生布尔型的值。

字符串

Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。

package main
 
import (
	"fmt"
)
 
// golang中string类型使用
func main() {
	//string的基本使用
	var str1 string = "hello world!"
	fmt.Println(str1)
	//在Go中字符串是不可变的,字符串一旦赋值了,字符串就不能修改了:
 
	//字符串的两种表示形式:
	//(1) 双引号, 会识别转义字符
	str2 := "abc\nabc"
	fmt.Println(str2)
 
	//(2) 反引号``,以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、输出源代码等效果,比如/n就保持/n,/t就保持/t
	str3 := `
    package main
    import (
        "fmt"
        "unsafe"
    )`
	fmt.Println(str3)
 
	//字符串拼接方式
	var str4 = "welcome " + "to"
	str4 += " China!"
	fmt.Println(str4)
	//当一个拼接的操作很长时,可以分行写
	// 但是注意,需要将+保留在上一行
	str5 := "hello " + "world" + "hello " +
		"world" + "hello " + "world" + "world" +
		"hello "
	fmt.Println(str5)
}

字符串处理

strings有很多方法:

package main
 
import (
	"fmt"
	"strings"
)
 
func main() {
	s := "gocodelearn"
 
	// 转大小写
	fmt.Println(strings.ToUpper(s))
	fmt.Println(strings.ToLower(s))
 
	// 是否包含某个元素
	fmt.Println(strings.Contains(s, "code"))
	// 是否包含任意一个元素
	fmt.Println(strings.ContainsAny(s, "xyzcode"))
 
	// 前缀和后缀
	fmt.Println(strings.HasPrefix(s, "go"))
	fmt.Println(strings.HasSuffix(s, "learn"))
 
	// 切割字符串,返回一个字符串切片
	fmt.Println(strings.Split(s, "code"))
  fmt.Println(strings.Split(s, " "))
 
	// 替换字符串
	fmt.Println(strings.ReplaceAll(s, "code", "programming"))
 
	// 字符串连接
	fmt.Println(strings.Join([]string{"go", "code", "learn"}, "-"))
 
	//统计某个子串在字符串中出现的次数
	fmt.Println(strings.Count(s, "o"))
 
	//字符串修建,去掉字符串两端的空白字符
	fmt.Println(strings.TrimSpace("  zxcv.  "))
}

查看类型和字节数

%T 查看:

package main
import (
	"fmt"
	"unsafe"
)
 
func main(){
	var i int = 10
	fmt.Printf("Type of i: %T \n", i)
	fmt.Printf("Byte size of i: %d \n", unsafe.Sizeof(i))
}

reflect查看:

package main
 
import (
	"fmt"
	"reflect"
)
 
func main() {
	var name string = "HX"
	fmt.Printf("name's type is: %s", reflect.TypeOf(name))
}

各种类型的默认值

默认零值

  • int / float32 / float64 ⇒ 0
  • bool ⇒ false
  • string ⇒ ""
package main
import (
	"fmt"
)
 
func main(){
	var a int //0
	var b float32 //0
	var c float64 //0
	var isGood bool // false
	var name string //""
 
	fmt.Printf("a=%v, b=%v, c=%v, isGood=%v, name=%v", a, b, c, isGood, name)
}

数据类型转换

  1. 基本类型转string

    package main
     
    import (
    	"fmt"
    )
     
    func main() {
    	var a int = 1
    	var b float64 = 8.6
    	var c byte = 'a'
    	var d bool = true
    	var str string
     
    	//使用Sprintf方法转成字符串
    	//参数的类型要和原值的类型保持一致,比如%d和a int
    	// 整型转字符串
    	str = fmt.Sprintf("%d", a)
    	fmt.Printf("Type of str is %T, str=%q \n", str, str)
    	// 浮点数转字符串
    	str = fmt.Sprintf("%f", b)
    	fmt.Printf("Type of str is %T, str=%q \n", str, str)
    	// 字符转字符串
    	str = fmt.Sprintf("%c", c)
    	fmt.Printf("Type of str is %T, str=%q \n", str, str)
    	// 布尔转字符串
    	str = fmt.Sprintf("%t", d)
    	fmt.Printf("Type of str is %T, str=%q \n", str, str)
    }
  2. 字符串转基本数据类型

    使用 strconv 包。在标准官方库文档有:https://studygolang.com/static/pkgdoc/pkg/strconv.htm

    package main
     
    import (
    	"fmt"
    	"strconv"
    )
     
    func main() {
     
    	// 字符串转布尔
    	// 1. strconv.ParseBool(str) 函数会返回两个值 (value bool, err error)
    	// 2. 只想获取到 value bool, 不想获取 err 所以用_忽略
    	var d bool
    	var str1 string = "true"
    	d, _ = strconv.ParseBool(str1)
    	fmt.Printf("Type of d: %T, b=%v \n", d, d)
     
    	// 字符串转整型
    	// 参数str1表示数字的字符串形式
    	// 参数10表示数字的字符串进制
    	// 参数64表示返回结果的bit大小,如int8、int16、int32
    	var str2 string = "12"
    	var a int64
    	a, _ = strconv.ParseInt(str2, 10, 64)
    	// a, _ = strconv.Atoi(str2) 这个更好,比较简洁
    	fmt.Printf("Type of a: %T, a=%v \n", a, a)
     
    	// 整型互转
    	var a1 int
    	a1 = int(a)
    	fmt.Printf("Type of a1: %T, a1=%v \n", a1, a1)
     
    // 整型转字符串
    i := 7277
    sn := strconv.Itoa(i)
    fmt.Printf("Type of sn", reflect.TypeOf(sn))
     
    	// 字符串转浮点数
    	var b float64
    	var str3 string = "8.5"
    	b, _ = strconv.ParseFloat(str3, 64)
    	fmt.Printf("Type of b: %T, a1=%v \n", b, b)
     
    }
     
    //注意:
    //在将 String 类型转成 基本数据类型时,要确保 String 类型能够
    //转成有效的数据,比如 我们可以把 "123" , 转成一个整数,但是
    //不能把 "hello" 转成一个整数,如果这样做,Golang 直接将其转成
    //0 , 其它类型也是一样的道理. float => 0, bool => false

指针变量

  • Go语言中的函数传参都是值拷贝,当我们想要修改某个变量的时候,我们可以创建一个指向该变量地址的指针变量。传递数据使用指针,而无须拷贝数据。类型指针不能进行偏移和运算。Go语言中的指针操作非常简单,只需要记住两个符号:==&(取地址)和 *(根据地址取值)==。

  • 一个指针变量可以指向任何一个值的内存地址,它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 和 8 个字节,占用字节的大小与所指向的值的大小无关。

  • 当一个指针被定义后没有分配到任何变量时,它的默认值为 nil。指针变量通常缩写为 ptr。

  • 每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用在变量名前面添加&操作符(前缀)来获取变量的内存地址(取地址操作),格式如下:

var i int = 10
var ptr  *int = &i
// 变量 i 的地址使用变量 ptr 进行接收,ptr 的类型为 *int,称做 int 的指针类型,*代表指针。

打印变量地址:

package main
 
import "fmt"
 
func main(){
	var i int = 3
	var name string = "bob"
	fmt.Printf("num:%p, name:%p", &i, &name)
}
// 使用 fmt.Printf 的动词%p打印 num和 str 变量的内存地址,指针的值是带有0x十六进制前缀的一组数据。

指针变量使用流程

  1. 定义指针变量。
  2. 为指针变量赋值。
  3. 访问指针变量中指向地址的值。
  4. 在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。
package main
 
import "fmt"
 
func main() {
	var i int = 10
	fmt.Println("var i address:", &i)
 
	// ip的类型是*int,赋值为&i
	var ip *int = &i
	fmt.Printf("ip=%v\n", ip)
	fmt.Printf("ip address is:%v\n", &ip)
	fmt.Printf("ip value is: %v", *ip)
}

运算符

算术运算符

  1. 加 +
  2. 减 -
  3. 乘 *
  4. 除 /
  5. 取余 %
  6. 自增自减 ++ --

注意

  1. 自增自减只能当做一个单独的语句,在单独的一行里面使用
  2. Golang 的 ++ 和 -- 只能写在变量的后面,不能写在变量的前面,即:只有 a++ a--,没有 ++a 和 --a

关系运算符

  1. ==
  2. !=
  3. >
  4. <
  5. >=
  6. <=

逻辑运算符

  1. and:&&
  2. or:||
  3. not:!

赋值运算符

  1. +=
  2. -=
  3. *=
  4. /=
  5. <<= 按位左移后赋值
  6. >>= 按位右移后赋值
  7. &= 按位与后赋值
  8. |= 按位或后赋值
  9. ^= 按位异或后赋值

位运算符

  1. & 与
  2. | 或
  3. ^ 异或
  4. << 左移
  5. >> 右移

指针运算符

  1. & 返回变量的地址 &a
  2. * 取地址对应的值 *ptr

获取时间

package main
 
import (
	"fmt"
	"time"
)
 
func main() {
	now := time.Now()
	fmt.Println(now) // 默认格式时间
 
	// 获取常见部分
	fmt.Println("当前时间:", now)
	fmt.Println("时间戳(秒):", now.Unix())
	fmt.Println("时间戳(毫秒):", now.UnixMilli())
	fmt.Println("年:", now.Year())
	fmt.Println("月:", now.Month())
	fmt.Println("日:", now.Day())
	fmt.Println("时:", now.Hour())
	fmt.Println("分:", now.Minute())
	fmt.Println("秒:", now.Second())
 
	// 格式化时间
	// Go 没用 YYYY-MM-DD HH:mm:ss 这种占位符风格,而是用了一个固定参考时间来表示格式。
	// 这是Go语言的诞生时间
	fmt.Println(now.Format("2006-01-02 15:04:05"))
 
	// 获取北京时间
	loc, _ := time.LoadLocation("Asia/Shanghai")
	fmt.Println(time.Now().In(loc).Format("2006-01-02 15:04:05"))
}

Go 时间格式化

Go 使用固定参考时间 ==2006-01-02 15:04:05== 来定义格式,而非常见的 YYYY-MM-DD 风格。这个参考时间是 Go 语言的诞生时间。


相关笔记: go-基础-01-环境配置-基础.md | go-基础-04-分支-循环.md | go-基础-05-函数-包.md | go-基础-02-数组-切片-map.md