数据类型默认值
-
布尔类型(bool) :
false
-
整型(int、int8、int16、int32、int64、uint、uint8、uint16、uint32、uint64) :
0
-
浮点型(float32、float64) :
0.0
-
复数类型(complex64、complex128) :
(0+0i)
,其中i
是虚数单位 -
字符串(string) :空字符串
""
-
字节切片([]byte) :
nil
,但如果是作为结构体字段或数组元素,则会被初始化为长度为0的切片(即[]byte{}
,但注意这仅适用于全局变量或结构体字段的自动初始化,局部变量仍会是nil
) -
其他切片(如
[]int
) :nil
,但同样,如果作为结构体字段或数组元素,则会被初始化为长度为0的切片(如[]int{}
) -
映射(map) :
nil
-
通道(chan) :
nil
-
接口(interface{}) :
nil
-
结构体(struct):结构体中每个字段都会被初始化为该字段类型的零值。如果结构体包含其他结构体或指针等,这些也会被递归地初始化为它们的零值。
-
指针(*T) :
nil
,其中T
是任意类型 -
函数(func) :函数类型没有零值的概念,因为函数不是变量,而是可以被调用的代码块。但是,函数类型的变量(即函数指针)的零值是
nil。
基本数据类型相互转换
基本数据类型转string
fmt.Sprintf("%参数", 表达式) 会返回转换后的字符串
package main
import (
"fmt"
_"strconv"
)
func main() {
i := 123
s := fmt.Sprintf("%d", i) // 将int转换为string
fmt.Println(s, "is a string")
fmt.Printf("s的数据类型为%T s=%q\n",s,s)
f := 3.14
sf := fmt.Sprintf("%.2f", f) // 将float64转换为带两位小数的string
fmt.Println(sf, "is a string")
fmt.Printf("sf的数据类型为%T sf=%q\n",sf,sf)
c := 'A'
sc := fmt.Sprintf("%c", c) // 将rune(在Go中char实际上是rune类型)转换为string
fmt.Println(sc, "is a string")
fmt.Printf("sc的数据类型为%T sc=%q\n",sc,sc)
// 注意:bool类型通常不直接转换为string,但可以通过fmt.Sprintf实现
b := true
sb := fmt.Sprintf("%t", b) // 将bool转换为string
fmt.Println(sb, "is a string")
fmt.Printf("sb的数据类型为%T sb=%q\n",sb,sb)
}
使用strconv
包
对于整数和浮点数,strconv
包提供了Itoa
(int to ASCII)和FormatFloat
等函数,但请注意Itoa
只适用于int
类型。对于其他整数类型(如int64
),你可能需要使用FormatInt
。
package main
import (
"fmt"
"strconv"
)
func main() {
i := 123
s := strconv.Itoa(i) // 将int转换为string
fmt.Println(s, "is a string")
i64 := int64(1234567890)
s64 := strconv.FormatInt(i64, 10) // 将int64转换为string,10表示十进制
fmt.Println(s64, "is a string")
// 注意:对于float64,strconv.FormatFloat是更通用的选择
f := 3.14
sf := strconv.FormatFloat(f, 'f', -1, 64) // 'f'表示固定点数表示,-1表示精度(自动),64表示float64
fmt.Println(sf, "is a string")
}
string
转基本数据类型
使用strconv
包
strconv
包提供了ParseInt
、ParseUint
、ParseFloat
和ParseBool
等函数,用于将string
转换为相应的基本数据类型。
package main
import (
"fmt"
"strconv"
)
func main() {
s := "123"
i, err := strconv.Atoi(s) // Atoi是ParseInt的简便封装,只处理int类型
if err != nil {
fmt.Println("Error converting string to int:", err)
}
fmt.Println(i, "is an int")
s64 := "1234567890"
i64, err := strconv.ParseInt(s64, 10, 64) // 第二个参数是基数(10表示十进制),第三个参数是bitSize
if err != nil {
fmt.Println("Error converting string to int64:", err)
}
fmt.Println(i64, "is an int64")
sf := "3.14"
f, err := strconv.ParseFloat(sf, 64) // 第二个参数是bitSize(32或64)
if err != nil {
fmt.Println("Error converting string to float64:", err)
}
fmt.Println(f, "is a float64")
sb := "true"
b, err := strconv.ParseBool(sb)
if err != nil {
fmt.Println("Error converting string to bool:", err)
}
fmt.Println(b, "is a bool")
}
//在将 String 类型转成 基本数据类型时,要确保 String 类型能够转成有效的数据,比如 我们可以把 "123" , 转成一个整数,但是不能把 "hello" 转成一个整数,如果这样做,Golang 直接将其转成 0 , 其它类型也是一样的道理. float => 0, bool => false
对于'%'的解释:
在Go语言的fmt.Sprintf
函数中,%
后跟的字母称为格式说明符(format specifier),它们决定了如何将后续的值(参数)格式化为字符串。每个格式说明符都对应一种不同的数据类型或值的表示方式。在你给出的例子中,%d
、%.2f
、%c
、%t
分别代表了不同的数据类型或值的格式化方式:
-
%d
:用于整数(int
、int8
、int16
、int32
、int64
等)的十进制表示。它会把整数转换成其十进制形式的字符串。 -
%.2f
:用于浮点数(float32
、float64
)的格式化表示,其中.2
指定了小数点后的位数,即保留两位小数。它会把浮点数转换成具有两位小数的字符串。 -
%c
:用于字符(在Go中实际上是rune
类型,用于表示Unicode字符)的格式化。它会把rune
类型的值转换成对应的单个字符的字符串。 -
%t
:用于布尔值(bool
)的格式化。它会把布尔值转换成字符串"true"或"false"。
这些格式说明符允许开发者在将值转换为字符串时,指定值的表示形式,比如整数是否以十六进制显示、浮点数保留多少位小数、字符是否应该直接显示等。这种灵活性是fmt
包提供的重要特性之一,使得在输出格式化字符串时非常方便。
在fmt.Printf
和fmt.Sprintf
等函数中,这些格式说明符都是通用的,可以用于任何需要格式化输出的场景。不过,需要注意的是,格式说明符必须与它后面的参数类型相匹配,否则可能会导致运行时错误或不符合预期的输出。例如,你尝试用%d
来格式化一个浮点数,结果可能不是你想要的。
指针变量
想象一下,每个变量在计算机的内存中都有一个独特的"家"(地址)。指针就像是那个"家"的门牌号,但它不是直接存储数据,而是存储了数据所在的"家"的地址。通过指针,我们可以直接找到并操作那个"家"里的数据,而不需要复制数据本身。
指针变量的声明和初始化
在Go语言中,我们可以通过在变量类型前加上*
符号来声明一个指针变量。例如,var ptr *int
声明了一个名为ptr
的指针变量,它可以指向一个int
类型的变量。
初始化指针变量通常有两种方式:
-
使用
&
操作符 :&
操作符可以获取一个变量的地址,并将这个地址赋值给指针变量。例如,var a int = 10; ptr = &a;
这样,ptr
就指向了a
的地址。 -
使用
new
函数 :new
函数是Go语言内建的一个函数,用于为指定类型分配内存,并返回指向这块内存的指针。例如,ptr = new(int)
会为int
类型分配内存,并将返回的指针赋值给ptr。
指针的使用
指针的使用主要涉及两个操作符:&
(取地址)和*
(解引用,即根据地址取值)。
-
取地址 :如上所述,使用
&
操作符可以获取变量的地址。 -
解引用 :使用
*
操作符可以获取指针所指向的变量的值。例如,如果ptr
指向了a
的地址,那么*ptr
就等价于a
的值。
例子:
package main
import(
"fmt"
)
func main(){
var a int =123
var str string = "forword"
fmt.Printf("a: %p, str:%p",&a,&str)
}
a: 0xc00000a0e8, str:0xc000026070
内存地址是0x十六进制前缀的一组数据
var ptr *int =&bfmt.Printf("ptr地址=%v\n",&ptr)
fmt.Printf("b的地址=%v\n",ptr)
fmt.Printf("ptr值=%v\n",*ptr)
b的地址为 0xc00000a108
ptr地址=0xc000058030
ptr=0xc00000a108
ptr值=1234
指针的重要性
指针在Go语言中非常重要,原因有以下几点:
-
提高效率:通过指针,我们可以直接操作内存中的数据,避免了数据复制的开销,这在处理大型数据结构时尤为重要。
-
实现引用传递:在Go语言中,函数的参数传递默认是值传递。但如果我们传递的是指针,那么在函数内部对指针所指向的变量的修改会影响到原始变量。
-
实现动态数据结构:如切片(slice)、映射(map)和通道(channel)等Go语言中的高级数据结构,在底层都是通过指针来实现的。
注意事项
虽然指针很强大,但也需要谨慎使用,以避免出现以下问题:
-
空指针解引用 :如果指针没有被正确初始化(即它指向的是
nil
),那么尝试解引用它将导致运行时错误。 -
悬挂指针:如果指针指向的内存被释放或重新分配,但指针本身没有被更新,那么这个指针就变成了悬挂指针,解引用它也可能导致错误。
-
内存泄漏:如果不再需要某个指针指向的内存,但没有显式地释放它(Go语言有垃圾回收机制,但在某些情况下仍需注意),就可能导致内存泄漏。