变量是一个抽象概念,它有内存地址,数据类型和值组成:
- 内存地址:给出了变量在内存中存储的其实地址,一般占用4或8字节(32位、64位机器)。
- 数据类型:定义了变量占用内存的大小。
- 值:表示内存地址中存放的内容。
基本类型
go语言内置了如下这些基本数据类型
整型
- 无符号:
uint, uint8, uint16, uint32, uint64, uintptr ,byte
,值范围为0 ~ 2^n-1
,uintptr初始值为nil,其他初始值为0,uint和uintptr长度跟随硬件平台而变,uintptr能够容纳当前硬件平台的一个指针,byte与uint8等价,表示一个字节。 - 有符号:
int , int8, int16, int32, int64,rune
,值范围为-2^(n-1) ~ 2^(n-1)-1
,初始值为0,其中int类型的长度跟随硬件平台而变,rune用来表示一个unicode字符,与int32等价,可以互换使用。
整型中byte和rune也叫字符型,byte是uint8的别称,用来表示一个ASCII字符。
rune是int32的别称,用来表示一个Unicode字符,Unicode字符长度不一,一般用十六进制表示var ch1 byte = 'A' //字符 var ch2 byte = 65 //十进制 var ch3 byte = '\x41' //十六进制,以 ‘ \x ’开头 var ch4 byte = '\101' //八进制,以 ‘ \ ’开头 //Unicode字符A的定义方式 var ch5 rune = '\u0041' //用小u表示4字节字符 var ch6 int64 = '\U00000041' //大U表示8字节 fmt.Printf("ASCII 字符: %c,%c,%c,%c \n", ch1, ch2, ch3, ch4) fmt.Printf("Unicode 字符: %c,%c, 对应整数: %v,%d, 对应十六进制表示:%U", ch5, ch6, ch5, ch5, ch5) // 输出 // ASCII 字符: A,A,A,A // Unicode 字符: A,A, 对应整数: 65,65, 对应十六进制表示:U+0041%
Unicode,ASCII都是字符集,Unicode是ASCII的超集。
UTF-8是可变长编码规则,可编码长度从1~4字节不等,优势是不存在字节序问题,所以很适合在网络上传输。
浮点型
- float32, float64:浮点型都是有符号的,应该优先使用float64,因为float32能精确表示的浮点数并不多,误差容易扩散。浮点型定义时可以只写整数或小数部分,但是小数点不能省。可以使用科学计数法来书写,e或E指定指数部分。
var f1 float64 = 1.
var f2 = .000002
f3, f4 := 1.2e12, 3.5E-12
复数类型
- complex64, complex128:复数
布尔型
- bool: 取值为true和false,不能参与运算,逻辑运算会产生bool值,go语言中只有相同数据类型才能进行比较,如果是类对象,需要实现相同的接口才能比较。但是可以转换成相同类型后进行比较。
go语言逻辑运算也会截断,如(a>b && a<c)
,如果a>b为false,则直接返回false而不会比较a和c。
变量定义方式
-
1、标准格式
go语言变量类型是后置的
var 变量名 类型
var a int
-
2、缺省类型方式
go语言根据初始化值自动推导类型,这种方式必须显式初始化。
var b = 1
- 3、批量定义
var(
c int = 10
d float32
e []byte
)
-
4、简短格式
这种方式只能用在函数内部,必须显式初始化,在局部变量定义中比较常用。
f := 1.0
f1,f2,f3 := 1, 1.0, "aaa"
需要注意,同一作用域内不能重复定义变量
var g int = 10 g := 12 //这一行将会报错,g重复定义
不同作用域可以重复定义,go会使用相同作用域内的变量,不会覆盖
var h int = 10 { h := 12.0 fmt.Printf("h = %f", h) } fmt.Printf("h = %d", h) //输出: h = 12.000000, h = 100
在简短定义多个变量时,只要左边有一个新定义的变量,其他变量就可以重复赋值(注意不是重复定义,因为赋值类型必须和之前定义的一致)
//这个例子中,i重复复制1.0,但是类型任然是int,但是会把2.0强转为int。 i := 1 i, i1 := 2.0, "xxx" fmt.Printf("type of i is : %T, i = %d, i1 = %s", i, i, i1) //输出:type of i is : int, i = 2, i1 = xxx
匿名变量
go语言中定义的变量如果没有使用就会报错,从根源上杜绝了变量的泛滥。
然而有些函数的多个返回值有时候我们并不全都需要,但是全都定义又必须要使用,这时候就需要用到匿名变量。
匿名变量用一个下划线"_"表示,不分配内存,可以重复定义,任何类型变量都可以赋值给它,但是不能使用它,也不能将它赋值给其他变量和参与运算(即只能作为左值)。
//正常情况下定义两个返回值
conn, err := net.Dial("tcp", "127.0.0.1")
fmt.Printf("conn = %v, err = %v \n", conn, err)
//不关系err这个返回值时
conn1, _ := net.Dial("tcp", "127.0.0.1")
//当所有返回值都不关心时,直接不定义返回值就可以,不能所有返回值都用匿名变量
// _, _ := net.Dial("tcp", "127.0.0.1")
类型转换
- go语言没有隐式类型转换,所有需要转换的类型都必须显式申明:
var a TypeA
var b TypeB = TypeB(a) //将a的值强制转换为TypeB类型并赋值给b,a不变
c := 1.0
var b int = int(c)
- 只有底层类型相同(如int16和int32互转)的变量才能相互转化,不同底层类型(如bool和int)互转将会编译错误。
- 短类型转为长类型没有风险,长类型转为短类型有精度丢失风险。
作用域
作用域表示可访问某变量的范围,go语言有两种作用域
- 1、局部作用域
定义在局部作用域中的变量称为局部变量,局部变量出了作用域就会被销毁,像函数参数、返回值、函数内部定义的变量都是局部变量,函数调用一旦完成就这些变量都会被销毁。
在函数内部也可以用大括号{ }
来定义一个更小的局部作用域。
func foo(a, b int) (c, d int) {
//通过{}定义局部作用域,i出了{}就没了,所以只能在{}内使用
{
i := 100
fmt.Printf("自定义局部作用域: i = %d \n", i)
}
c, d = b, a
fmt.Printf("函数参数和返回值都是局部变量: a = %d,b = %d,c = %d, d = %d \n", a, b, c, d)
return
}
- 2、全局作用域
函数外部定义的变量就是全局变量,这些变量的生命周期与进程相同,在进程结束时才会销毁。
- 全局变量必须使用
var
关键字进行定义- 全局变量可以被本文件中的所有函数访问,如果需要在其他源文件中访问变量,需要将变量首字母大写(即通过变量名大小写控制访问权限),并在需要访问的文件中通过
import
导入当前源文件。
var AAA int = 10 //外部文件可以访问
var bbb = 123 //外部文件无法访问
func main() {
...
}
常量
- 常量是与变量相对于的一个概念,他们都是对应一块内存,只是变量可以被修改,常量一旦定义就不能被修改。
- 常量在编译时就创建,只能定义bool,字符串和数字型(包括整型,字符,浮点型,复数)
- 常量的算数、逻辑、比较运算的结果也是常量
-
len、cap、real、image、complex、unsafe.Sizeof
的调用返回是常量。
定义方式和变量类似
//定义变量
var a int = 1
//定义常量
const b int = 2
//同时声明多个常量
const(
c = 1.0
d = "str"
)
//同时申明多个常量时,未赋值的常量会复用前一个常量的值
const(
e = 1
f
g = 2
h
)
fmt.Println(e,f,g,h) //输出:1 1 2 2
//使用iota常量生成器
const(
i = iota
j
k
l
)
fmt.Println(i,j,k,l) //输出:0 1 2 3
总结
- go语言行尾不用加分号,go语言没有分号。
- go语言变量类型是后置的,所有变量定义即自动初始化为初始值,不存在c语言的声明和定义的区别。
- 简短定义变量只能使用在函数内部。
- 同一作用域不能重复定义变量,不同作用域可以重复定义,简短定义左边至少有一个新变量。
- 首字母大写的变量或函数可以被import该文件的外部文件访问。
完整示例代码
/*
* 这个文件主要介绍变量的相关内容
*/
package main
import (
"fmt"
"net"
)
func foo(a, b int) (c, d int) {
//通过{}定义局部作用域,i出了{}就没了,所以只能在{}内使用
{
i := 100
fmt.Printf("自定义局部作用域: i = %d \n", i)
}
c, d = b, a
fmt.Printf("函数参数和返回值都是局部变量: a = %d,b = %d,c = %d, d = %d \n", a, b, c, d)
return
}
var AAA int = 10 //外部文件可以访问
var bbb = 123 //外部文件无法访问
func main() {
//变量标准定义方式,自动初始化
var a int
fmt.Printf("变量标准定义模式,自动初始化为: a = %d \n", a)
//缺省类型的定义方式,自动推到类型,明确初始化
var b = 1
fmt.Printf("缺省类型的定义方式,自动推到类型: type fo b is %T \n", b)
//批量定义变量
var (
c int = 12
d float32
e []byte
)
fmt.Printf("批量定义变量: c = %d,d = %f,e = %s \n", c, d, e)
//简短格式,缺省var和类型,可同时定义多个变量
f := 1.2
f1, f2, f3 := 1, 1.2, "xxxx"
fmt.Printf("简短格式定义变量,type of f is : %T, f = %f ,f1 = %d,f2 = %f,f3 = %s\n", f, f, f1, f2, f3)
//同一作用域不能重复定义变量
var g int = 10
//g := 12 //这一行将会报错,重复定义
fmt.Printf("相同作用域内重复定义会出错: g = %d\n", g)
//不同作用域可以重复申明,go会使用相同作用域内的变量
var h = 100
{
h := 12.0 //较小作用域内定义
fmt.Printf("较小作用域内重复定义: h = %f, ", h)
}
fmt.Printf("较大作用域内定义的变量没有被覆盖: h = %d \n", h)
//例外情况,简短定义只要左边有一个新变量,其他变量可以重复之前定义过的变量
i := 1
i, i1 := 2.0, "xxx"
fmt.Printf("简短定义只要左边有一个新变量,其他变量可以重复之前定义过的变量: type of i is : %T,i = %d, i1 = %s \n", i, i, i1)
//变量交换
j, k := 1, 2
fmt.Printf("变量交换: j=%d,k=%d , ", j, k)
j, k = k, j
fmt.Printf(" 交换后: j=%d,k=%d \n", j, k)
// 匿名变量
//正常情况下定义两个返回值
conn, err := net.Dial("tcp", "127.0.0.1")
fmt.Printf("conn = %v, err = %v , ", conn, err)
//不关系err这个返回值时
conn1, _ := net.Dial("tcp", "127.0.0.1")
fmt.Printf("conn1 = %v \n", conn1)
//当所有返回值都不关心时,直接不定义返回值就可以,不能所有返回值都用匿名变量
// _, _ := net.Dial("tcp", "127.0.0.1")
foo(1, 2)
fmt.Printf("全局变量:AAA = %d,bbb = %d \n", AAA, bbb)
//byte和rune使用
var ch1 byte = 'A' //字符
var ch2 byte = 65 //十进制
var ch3 byte = '\x41' //十六进制,以 ‘ \x ’开头
var ch4 byte = '\101' //八进制,以 ‘ \ ’开头
//Unicode字符A的定义方式
var ch5 rune = '\u0041' //用小u表示4字节字符
var ch6 int64 = '\U00000041' //大U表示8字节
fmt.Printf("ASCII 字符: %c,%c,%c,%c \n", ch1, ch2, ch3, ch4)
fmt.Printf("Unicode 字符: %c,%c, 对应整数: %v,%d, 对应十六进制表示:%U", ch5, ch6, ch5, ch5, ch5)
}
输出
变量标准定义模式,自动初始化为: a = 0
缺省类型的定义方式,自动推到类型: type fo b is int
批量定义变量: c = 12,d = 0.000000,e =
简短格式定义变量,type of f is : float64, f = 1.200000 ,f1 = 1,f2 = 1.200000,f3 = xxxx
相同作用域内重复定义会出错: g = 10
较小作用域内重复定义: h = 12.000000, 较大作用域内定义的变量没有被覆盖: h = 100
简短定义只要左边有一个新变量,其他变量可以重复之前定义过的变量: type of i is : int,i = 2, i1 = xxx
变量交换: j=1,k=2 , 交换后: j=2,k=1
conn = <nil>, err = dial tcp: address 127.0.0.1: missing port in address , conn1 = <nil>
自定义局部作用域: i = 100
函数参数和返回值都是局部变量: a = 1,b = 2,c = 2, d = 1
全局变量:AAA = 10,bbb = 123
ASCII 字符: A,A,A,A
Unicode 字符: A,A, 对应整数: 65,65, 对应十六进制表示:U+0041%