目录
【整型int】
整型溢出
整型字面值
【浮点型float】
浮点型字面值
复数类型
【字符串string】
Go 字符串类型的内部表示
Go 字符串类型的常见操作
关于整型、浮点型、字符串 的一些其它示例:
字符串和数值类型的转换
【创建自定义数据类型】
【输出方法Print】
【运行测试】
Go语言中的基本数据类型包括下面这些:
基本数据类型
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // alias for uint8
rune // alias for int32,represents a Unicode code point
float32 float64
complex64 complex128
分为平台无关整型和平台相关整型这两种,平台无关整型在任何 CPU 架构或任何操作系统下面,长度都是固定不变的。
byte 等同于 uint8;rune 等同于 int32
平台相关整型的长度会根据运行平台的改变而改变。Go语言原生提供了三个平台相关整型,它们是 int、uint 与 uintptr。
在编写有移植性要求的代码时,千万不要强依赖这些类型的长度,可以通过 unsafe 包提供的 SizeOf 函数来获取。
var ao, bo = int(5), uint(6)
var p uintptr = 0x12345678
fmt.Println(unsafe.Sizeof(ao)) // 8
fmt.Println(unsafe.Sizeof(bo)) // 8
fmt.Println(unsafe.Sizeof(p)) // 8
如果整型的结果超出了它的边界值,就会发生整型溢出问题。出现溢出情况后,对应的整型变量的值依然会落到它的取值范围内,只是结果值与预期不符。这个问题最容易发生在循环语句的结束条件判断中,因为这也是经常使用整型变量的地方。
var num int8 = 127
num += 1
fmt.Println(num) //期望结果128,实际-128
var num2 uint8 = 1
num2 -= 2
fmt.Println(num2) //期望结果-1,实际255
a := 53 // 十进制
b := 0700 // 八进制,以"0"为前缀
c1 := 0xaabbcc // 十六进制,以"0x"为前缀
c2 := 0Xddeeff // 十六进制,以"0X"为前缀
//Go 1.13 版本中,Go 又增加了对二进制字面值的支持和两种八进制字面值的形式
d1 := 0b10000001 // 二进制,以"0b"为前缀
d2 := 0B10000001 // 二进制,以"0B"为前缀
e1 := 0o700 // 八进制,以"0o"为前缀
e2 := 0O700 // 八进制,以"0O"为前缀
//Go 1.13 版本还支持在字面值中增加数字分隔符“_”,分隔符可以用来将数字分组以提高可读性。
a := 5_3_7 // 十进制: 537
b := 0b_1000_0111 // 二进制位表示为10000111
c1 := 0_700 // 八进制: 0700
c2 := 0o_700 // 八进制: 0700
d1 := 0x_5c_6d // 十六进制:0x5c6d
//通过标准库 fmt 包的格式化输出函数,将一个整型变量输出为不同进制的形式
var a int8 = 59
fmt.Printf("%b\n", a) //输出二进制:111011
fmt.Printf("%d\n", a) //输出十进制:59
fmt.Printf("%o\n", a) //输出八进制:73
fmt.Printf("%O\n", a) //输出八进制(带0o前缀):0o73
fmt.Printf("%x\n", a) //输出十六进制(小写):3b
fmt.Printf("%X\n", a) //输出十六进制(大写):3B
Go 语言提供了 float32 与 float64 两种浮点类型,默认值都是0.0,但是它们占用的内容空间大小不一样,并且可表示的浮点数的范围和精度也不一样。一般比较常用的float64是双精度,float32由于精度不足可能会导致输出结果出现异常,比如下面的代码:
var x1 float32 = 16777216.0
var x2 float32 = 16777217.0
fmt.Println("x1==x2? ", x1 == x2) // true
//用十进制表示的浮点值形式
float1 := 3.1415
float2 := .15 // 整数部分如果为0,整数部分可以省略不写
float3 := 81.80
float4 := 82. // 小数部分如果为0,小数点后的0可以省略不写
//十进制的科学计数法形式(e/E 代表的幂运算的底数为 10)
float5 := 6674.28e-2 // 6674.28 * 10^(-2) = 66.742800
float6 := .12345E+5 // 0.12345 * 10^5 = 12345.000000
//十六进制科学计数法形式( p/P 代表的幂运算的底数为 2)
float7 := 0x2.p10 // 2.0 * 2^10 = 2048.000000
float8 := 0x1.Fp+0 // 1.9375 * 2^0 = 1.937500
//通过 %f 可以输出浮点数最直观的原值形式
var f9 float64 = 123.45678
fmt.Printf("%f\n", f9) // 123.456780
//将浮点数输出为科学计数法形式
fmt.Printf("%e\n", f9) // 1.234568e+02 //十进制的科学计数法
fmt.Printf("%x\n", f9) // 0x1.edd3be22e5de1p+06 //十六进制的科学计数法
Go 语言中没有提供 float 类型。这不像整型那样既提供了 int16、int32 等类型又有 int 类型。也就是说:Go 提供的浮点类型都是平台无关的。
复数类型分别是 complex64 和 complex128,complex64 的实部与虚部都是 float32 类型,而 complex128 的实部与虚部都是 float64 类型。如果一个复数没有显示赋予类型,那么它的默认类型为 complex128。下面是表示复数字面值的方法:
//第一种,可以通过复数字面值直接初始化一个复数类型变量
var c = 5 + 6i
var d = 0o123 + .12345E+5i // 83+12345i
//第二种,通过complex 函数创建一个 complex128 类型值
var c = complex(5, 6) // 5 + 6i
var d = complex(0o123, .12345E+5) // 83+12345i
//第三种,通过 real 和 imag 函数来获取一个复数的实部与虚部,返回值为一个浮点类型
var c = complex(5, 6) // 5 + 6i
r := real(c) // 5.000000
i := imag(c) // 6.000000
Go语言中的字符串类型的值在它的生命周期内是不可改变的,最大好处就是不用再担心字符串的并发安全问题,这样字符串可以被多个 Goroutine 共享,不用担心并发安全问题。另外也由于字符串的不可变性,针对同一个字符串值无论在程序的几个位置被使用,Go 编译器只需要为它分配一块存储就好了,大大提高了存储利用率。
var s string = "hello"
s[0] = 'k' // 错误:字符串的内容是不可改变的
s = "gopher" //可以重新赋值
Go 采用的是 Unicode 字符集,每个字符都是一个 Unicode 字符。打印字符串中的每个字节,以及整个字符串的长度:
var s = "中国人"
fmt.Printf("the length of s = %d\n", len(s)) // 9
for i := 0; i < len(s); i++ {
fmt.Printf("0x%x ", s[i]) // 0xe4 0xb8 0xad 0xe5 0x9b 0xbd 0xe4 0xba 0xba
}
fmt.Printf("\n")
通过字符序列的视角来看字符串
var s = "中国人"
fmt.Println("the character count in s is", utf8.RuneCountInString(s)) // 3
for _, c := range s {
fmt.Printf("0x%x ", c) // 0x4e2d 0x56fd 0x4eba
}
fmt.Printf("\n")
string 类型其实是一个“描述符”,它本身并不真正存储字符串数据,而仅是由一个指向底层存储的指针和字符串的长度字段组成的。获取字符串长度的时间复杂度是O(1)
// $GOROOT/src/reflect/value.go
// StringHeader是一个string的运行时表示
type StringHeader struct {
Data uintptr
Len int
}
因此,直接将 string 类型通过函数 / 方法参数传入也不会带来太多的开销。因为传入的仅仅是一个“描述符”,而不是真正的字符串数据。
1、在字符串的实现中,真正存储数据的是底层的数组,因此一般可以使用字符串的下标获取某个字节(而不是字符)
var s = "中国人"
fmt.Printf("0x%x\n", s[0]) // 0xe4:字符“中” utf-8编码的第一个字节
2、使用 for 与 for range 迭代字符串,这两种形式的迭代对字符串进行操作得到的结果是不同的。
//字节视角的迭代,使用 for
var s = "中国人"
for i := 0; i < len(s); i++ {
fmt.Printf("index: %d, value: 0x%x\n", i, s[i])
}
//字符串中 Unicode 字符的码点值,使用 for ... range
var s = "中国人"
for i, v := range s {
fmt.Printf("index: %d, value: 0x%x\n", i, v)
}
常⽤字符串函数
1. strings 包:https://golang.org/pkg/strings
2. strconv 包:https://golang.org/pkg/strconv
//go01/data_type.go
package main
import "fmt"
func main() {
//1.布尔类型
var b1 bool
b1 = true
fmt.Printf("b1: %T,%t\n", b1, b1)
b2 := false
fmt.Printf("b2: %T,%t\n", b2, b2)
//bool类型占用存储空间是1个字节
fmt.Println("b2 的占用空间 =", unsafe.Sizeof(b2)) //1
//2.整数
var i1 int8
i1 = 100
fmt.Println(i1)
var i2 uint8
i2 = 200
fmt.Println(i2)
var i3 int
i3 = 1000
fmt.Println(i3)
//语法角度:int,int64不认为是同一种类型
//var i4 int64
//i4 = i3 //cannot use i3 (type int) as type int64 in assignment
var i5 uint8
i5 = 100
var i6 byte
i6 = i5
fmt.Println(i5, i6)
var i7 = 100
fmt.Printf("%T,%d\n", i7, i7)
//浮点
var f1 float32
f1 = 3.14
var f2 float64
f2 = 4.67
fmt.Printf("f1: %T,%.2f\n", f1, f1)
fmt.Printf("f2: %T,%.3f\n", f2, f2)
fmt.Println(f1)
var f3 = 2.55
fmt.Printf("f3: %T\n", f3)
//字符串
//1.定义字符串
var s1 string
s1 = "你好"
fmt.Printf("s1: %T,%s\n", s1, s1)
s2 := `Hello World`
fmt.Printf("s2: %T,%s\n", s2, s2)
//2.区别:'A',"A"
v1 := 'A'
v2 := "A"
fmt.Printf("v1: %T,%d\n", v1, v1) //int32
fmt.Printf("v2: %T,%s\n", v2, v2)
v3 := '中'
fmt.Printf("v3: %T,%d,%c,%q\n", v3, v3, v3, v3)
//3.转义字符
fmt.Println("\"HelloWorld\"")
fmt.Println("Hello\nWor\tld")
fmt.Println(`He"lloWor"ld`)
fmt.Println("Hello`Wor`ld")
//字节长度:和编码无关,用 len(str)获取
fmt.Println(len("ab")) //2
fmt.Println(len("你好")) //6
//字符数量:和编码有关,用编码库来计算
fmt.Println(utf8.RuneCountInString("ab")) //2
fmt.Println(utf8.RuneCountInString("你好")) //2
//字符串拼接,用 + 号
sk1 := "你好"
sk2 := "再见"
fmt.Println(sk1 + sk2) //你好再见
//指针对应的是变量在内存中的存储位置,也就是说 指针的值就是变量的内存地址
dat := 10
datP := &dat //data的指针
// datP = datP + 1 //报错:指针不支持运算
datV := *datP //dataP指针对应的值
fmt.Println(datP, datV) //0xc00001e1a0 10
fmt.Printf("%T %T \n", datP, datV) //*int int
//判断数据类型
data := "个人信息"
fmt.Println(getVarType(data)) //string
}
func getVarType(args interface{}) string {
switch args.(type) {
case nil:
return "nil"
case int:
return "int"
case int64:
return "int64"
case float64:
return "float64"
case bool:
return "bool"
case string:
return "string"
case func(int) float64:
return "func(int)"
default:
return "unknown"
}
}
/*字符串分割成数组,以及数组拼接成字符串*/
func stringFn(s string) {
parts := strings.Split(s, ",")
for _, part := range parts {
fmt.Println(part)
}
fmt.Println(strings.Join(parts, "-"))
}
var str string = "true"
var b bool
b, _ = strconv.ParseBool(str)
fmt.Printf("b type %T b=%v\n", b, b) //b type bool b=true
var str2 string = "1234590"
var n1 int64
var n2 int
n1, _ = strconv.ParseInt(str2, 10, 64)
n2 = int(n1)
fmt.Printf("n1 type %T n1=%v\n", n1, n1) //n1 type int64 n1=1234590
fmt.Printf("n2 type %T n2=%v\n", n2, n2) //n2 type int n2=1234590
var str3 string = "123.456"
var f1 float64
f1, _ = strconv.ParseFloat(str3, 64)
fmt.Printf("f1 type %T f1=%v\n", f1, f1) //f1 type float64 f1=123.456
var str4 string = "hello"
var n3 int64 = 11
n3, _ = strconv.ParseInt(str4, 10, 64)
fmt.Printf("n3 type %T n3=%v\n", n3, n3) //n3 type int64 n3=0
//MyInt的数值性质与 int32 完全相同
type MyInt int32
//但仍然是完全不同的两种类型,无法直接让它们相互赋值或放在同一个运算中直接计算
var m int = 5
var n int32 = 6
var a MyInt = m // 错误:在赋值中不能将m(int类型)作为MyInt类型使用
var a MyInt = n // 错误:在赋值中不能将n(int32类型)作为MyInt类型使用
//需要显示的转换类型才可以
var a MyInt = MyInt(m) // ok
var a MyInt = MyInt(n) // ok
//也可以通过类型别名(Type Alias)来自定义数值类型,此时定义的新类型与原类型没有区别
type YourInt = int32
var n int32 = 6
var a YourInt = n // ok
fmt.Print:输出到控制台(仅只是输出)
fmt.Println:输出到控制台并换行
fmt.Printf:仅输出格式化的字符串和字符串变量(整型和整型变量不可以)
fmt.Sprintf:格式化并返回一个字符串,不输出。
//go01/print.go
package main
import (
"fmt"
)
func main() {
fmt.Print("输出到控制台不换行--- ")
fmt.Println("输出到控制台并换行")
fmt.Printf("name=%s,age=%d\n", "Tom", 30)
fmt.Printf("name=%s,age=%d,height=%v\n", "Tom", 30, fmt.Sprintf("%.2f", 180.567))
}
编写测试程序
1.源码⽂文件以 _test 结尾:xxx_test.go
2.测试⽅方法名以 Test 开头:func TestXXX(t *testing.T) {...}
运行方法: go test -v run_test.go
//go01/run_test.go
package main_test
import "testing"
const (
Monday = 1 + iota
Tuesday
Wednesday
)
func TestPoint(t *testing.T) {
t.Log(Monday, Tuesday, Wednesday)
a := 1
aPtr := &a
t.Log(a, aPtr)
t.Logf("%T %T", a, aPtr)
}
源代码:go-demo-2023: Go语言基本用法和实用笔记 - Gitee.com