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^63-1
-
int8 - 1字节
- -128 - 127
int16- 2字节
- -2^15 ~ 2^15-1
int32- 4字节
- -2^31 ~ 2^31-1
int64- 8字节
-
-2^63 ~ 2^63-1
-
uint
- 64位系统占8字节
-
0~2^64-1
-
uint8 - 1字节
- 0~255
uint16- 2字节
- 0~2^16-1
uint32- 4字节
- 0~2^32-1
uint64- 8字节
- 0~2^64-1
- rune:等价int32,表示一个Unicode码
- byte:等价uint8,存储字符用byte
字符型
Go语言的字符有以下两种:
-
一种是 uint8 类型,或者叫 byte 型,代表了 ASCII 码的一个字符。用 1 个字节的传统 ASCII 编码的字符来说,完全没有问题,例如
var ch byte = 'A',字符使用单引号括起来。 -
另一种是 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=> 0bool=> falsestring=> ""
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)
}
数据类型转换
- 基本类型转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)
}
- 字符串转基本数据类型
使用 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十六进制前缀的一组数据。
指针变量使用流程
- 定义指针变量。
- 为指针变量赋值。
- 访问指针变量中指向地址的值。
- 在指针类型前面加上
*号(前缀)来获取指针所指向的内容。
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)
}
运算符
算术运算符
- 加
+ - 减
- - 乘
* - 除
/ - 取余
% - 自增自减
++--
注意
- 自增自减只能当做一个单独的语句,在单独的一行里面使用
- Golang 的
++和--只能写在变量的后面,不能写在变量的前面,即:只有a++a--,没有++a和--a
关系运算符
==!=- >
- \<
- >=
<=
逻辑运算符
- and:
&& - or:
|| - not:
!
赋值运算符
+=-=*=/=<<=按位左移后赋值>>=按位右移后赋值&=按位与后赋值|=按位或后赋值^=按位异或后赋值
位运算符
&与|或^异或<<左移>>右移
指针运算符
&返回变量的地址&a*取地址对应的值*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-环境配置-基础 | go-基础-04-分支-循环 | go-基础-05-函数-包 | go-基础-02-数组-切片-map