go语言基础之数据类型,变量,常量
Go 语言数据类型
在 Go 编程语言中,数据类型用于声明函数和变量。
数据类型的出现是为了把数据分成所需内存大小不同的数据,编程的时候需要用大数据的时候才需要申请大内存,就可以充分利用内存。
Go 语言按类别有以下几种数据类型:
序号 | 类型和描述 |
---|---|
1 | 布尔型 布尔型的值只可以是常量 true 或者 false。一个简单的例子:var b bool = true。 |
2 | 数字类型 整型 int 和浮点型 float32、float64,Go 语言支持整型和浮点型数字,并且支持复数,其中位的运算采用补码。 |
3 | 字符串类型: 字符串就是一串固定长度的字符连接起来的字符序列。Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。 |
4 | 派生类型: 包括:(a) 指针类型(Pointer)(b) 数组类型(c) 结构化类型(struct)(d) Channel 类型(e) 函数类型(f) 切片类型(g) 接口类型(interface)(h) Map 类型 |
布尔类型
package main
import "fmt"
var doing bool
func main() {
fmt.Println("布尔值默认是:", doing)
}
//输出结果为
布尔值默认是: false
package main
import "fmt"
var doing bool = true
func main() {
fmt.Println("当前布尔值是:", doing)
}
//输出结果:
当前布尔值是: true
数字类型
Go 也有基于架构的类型,例如:int、uint 和 uintptr。
序号 | 类型和描述 |
---|---|
1 | uint8 无符号 8 位整型 (0 到 255) |
2 | uint16 无符号 16 位整型 (0 到 65535) |
3 | uint32 无符号 32 位整型 (0 到 4294967295) |
4 | uint64 无符号 64 位整型 (0 到 18446744073709551615) |
5 | int8 有符号 8 位整型 (-128 到 127) |
6 | int16 有符号 16 位整型 (-32768 到 32767) |
7 | int32 有符号 32 位整型 (-2147483648 到 2147483647) |
8 | int64 有符号 64 位整型 (-9223372036854775808 到 9223372036854775807) |
浮点型
序号 | 类型和描述 |
---|---|
1 | float32 IEEE-754 32位浮点型数 |
2 | float64 IEEE-754 64位浮点型数 |
3 | complex64 32 位实数和虚数 |
4 | complex128 64 位实数和虚数 |
其他数字类型
以下列出了其他更多的数字类型:
序号 | 类型和描述 |
---|---|
1 | byte 类似 uint8 |
2 | rune 类似 int32 |
3 | uint 32 或 64 位 |
4 | int 与 uint 一样大小 |
5 | uintptr 无符号整型,用于存放一个指针 |
格式化和获取数据类型
我们可以使用fmt.Printf 来进行格式化。 %s 替换strin类型,%d 替换int类型,%f替换小数类型,%T获取值的数据类型
// %s 替换string 类型 ,%d 替换int 类型 ,%f 替换小数类型, %T ,变量的值类型
package main
import "fmt"
var a = "zhangsan"
var b = 5
// 注意,c的类型是 float64
var c = 10.0
func main() {
fmt.Printf("变量a 的值是 %s,变量b 的值是 %d, 变量c的值是%f,类型是: %T", a, b, c, c)
}
// 输出结果:
变量a 的值是 zhangsan,变量b 的值是 5, 变量c的值是10.000000,类型是: float64
数据类型转换
整型和浮点类型的数据不能够直接做运算,需要先将数据 转换为同一个类型
package main
import "fmt"
// 有两个变量,值分别为为 10 和 100.0
var a = 10
var b = 100.0
func main() {
// 使用格式化字符串和%T ,获取数据类型
// 此时,a是int 类型,b则是float64类型
fmt.Printf("a的数值类型是:%T,b的数值类型是:%T", a, b)
// 声明并定义变量result
// 使用float64(),将int 类型的变量a 转换我为float64类型
result := float64(a) * b
fmt.Println("\n结果是:", result)
}
//输出结果:
a的数值类型是:int,b的数值类型是:float64
结果是: 1000
Go 语言变量
变量来源于数学,是计算机语言中能储存计算结果或能表示值抽象概念。
变量可以通过变量名访问。
Go 语言变量名由字母、数字、下划线组成,其中首个字符不能为数字。
声明变量的一般形式是使用 var 关键字:
var identifier type
可以一次声明多个变量:
var identifier1, identifier2 type
package main
import "fmt"
func main() {
var a string = "Runoob"
fmt.Println(a)
var b, c int = 1, 2
fmt.Println(b, c)
}
以上实例输出结果为:
Runoob
1 2
变量声明
第一种,指定变量类型,如果没有初始化,则变量默认为零值。
var v_name v_type
v_name = value
零值就是变量没有做初始化时系统默认设置的值。
package main
import "fmt"
func main() {
// 声明一个变量并初始化
var a = "abc"
fmt.Println(a)
// 没有初始化就为零值
var b int
fmt.Println(b)
// bool 零值为 false
var c bool
fmt.Println(c)
}
// 输出结果为:
abc
0
false
-
数值类型(包括complex64/128)为 0
-
布尔类型为 false
-
字符串为 ""(空字符串)
-
以下几种类型为 nil:
var a *int var a []int var a map[string] int var a chan int var a func(string) int var a error // error 是接口
package main
import "fmt"
// 定义两个变量,name 为字符串类型, age 为整型
var name string = "zhangsan"
var age int = 10
func main() {
// 字符串 使用 %s, 数字 使用%d
fmt.Printf("名字是:%s,年龄是:%d", name, age)
}
输出结果是:
0 0 false ""
第二种,根据值自行判定变量类型。
var v_name = value
package main
import "fmt"
func main() {
var d = true
fmt.Println(d)
}
//输出结果为:
true
第三种,如果变量已经使用 var 声明过了,再使用 := 声明变量,就产生编译错误,
注意 := 这种方式只能在函数体 内部 使用
格式:
v_name := value
例如:
var intVal int
intVal :=1 // 这时候会产生编译错误,因为 intVal 已经声明,不需要重新声明
直接使用下面的语句即可:
intVal := 1 // 此时不会产生编译错误,因为有声明新的变量,因为 := 是一个声明语句
intVal := 1 相等于:
var intVal int
intVal =1
可以将 var f string = "abc" 简写为 f := "abc":
package main
import "fmt"
func main() {
f := "abc" // var f string = "abc"
fmt.Println(f)
}
//输出结果:
abc
多变量声明
//类型相同多个变量, 非全局变量
var vname1, vname2, vname3 type
vname1, vname2, vname3 = v1, v2, v3
var vname1, vname2, vname3 = v1, v2, v3 // 和 python 很像,不需要显示声明类型,自动推断
vname1, vname2, vname3 := v1, v2, v3 // 出现在 := 左侧的变量不应该是已经被声明过的,否则会导致编译错误
// 这种因式分解关键字的写法一般用于声明全局变量
var (
vname1 v_type1
vname2 v_type2
)
package main
var x, y int
var ( // 这种因式分解关键字的写法一般用于声明全局变量
a int
b bool
)
var c, d int = 1, 2
var e, f = 123, "hello"
//这种不带声明格式的只能在函数体中出现
//g, h := 123, "hello"
func main() {
g, h := 123, "hello"
println(x, y, a, b, c, d, e, f, g, h)
}
// 输出结果:
0 0 0 false 1 2 123 hello 123 hello
值类型和引用类型
值类型变量的值存储在堆中,值类型变量,修改一个变量,不会影响另外一个,因为它们使用的是不同的内存地址
引用类型,修改一个值,另一个也发生改变。因为它们的指针指向的是同一个内存地址
所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:
当使用等号 =
将一个变量的值赋值给另一个变量时,如:j = i
,实际上是在内存中将 i 的值进行了拷贝:
你可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。
值类型变量的值存储在堆中。
内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局,并且位置分配也可能不同。
更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。
一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。
这个内存地址称之为指针,这个指针实际上也被存在另外的某一个值中。
同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。
当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。
如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。
package main
import "fmt"
//变量在内存中的存储方式
//定义的全局变量,属于文件main.go,直接放入在栈中,压栈时先进入,则弹栈时后出,遵循先进后出的原则
var num int = 10
var sum int = 20
func main() {
//局部变量,属于函数体 main, 变量产生在 堆中.遵循先进先出。其生命随着函数的结束而结束
//main 通过指针指向 堆中的位置
var test1 = "hello"
var test2 = "world"
fmt.Println("num=", num)
// 调用内存地址使用&符号
fmt.Println("num的内存地址是:", &num)
fmt.Println("sum的内存地址是:", &sum)
// 调用函数中变量
fmt.Println(test1)
fmt.Println(test2)
}
//输出结果:
num= 10
num的内存地址是: 0xa3a258
sum的内存地址是: 0xa3a260
hello
world
// 在声明了变量后, 如果改变它的值,不会修改它的内存地址
package main
import (
"fmt"
)
var a int
func main() {
fmt.Println(&a)
a = 10
fmt.Println(&a)
a = 1010100000
fmt.Println(&a)
}
// 输出结果:
0x816168
0x816168
0x816168
// 值变量类型示例
package main
import "fmt"
func main() {
//值类型变量的值存储在堆中,值类型变量,修改一个变量,不会影响另外一个,因为它们使用的是不同的内存地址
var a = 10
var b = a
fmt.Println("a=", a, "\nb=", b)
fmt.Println("a=", &a, "\nb=", &b)
}
//输出结果:
a= 10
b= 10
a= 0xc0000aa058
b= 0xc0000aa070
// 演示引用类型
package main
import "fmt"
//类
type Car struct {
number int
}
//用用类型的变量
func main() {
//创建对象c1
c1 := new(Car)
c1.number = 10
// 创建对象c2
// 引用类型,修改一个值,另一个也发生改变。因为它们的指针指向的是同一个内存地址
c2 := new(Car)
c2 = c1
c2.number = 20
fmt.Println("c1.number=", c1.number, &c1.number)
fmt.Println("c2.number=", c2.number, &c2.number)
}
//输出结果:
c1.number= 20 0xc000014098
c2.number= 20 0xc000014098
// a=3,b=4. 交换a和b的值
// 方法一:
package main
import "fmt"
var a int = 3
var b int = 4
func main() {
fmt.Println(a, b)
var c = a
a = b
b = c
fmt.Println(a, b)
}
// a=3,b=4. 交换a和b的值
// 方法二
package main
import "fmt"
var a int = 3
var b int = 4
func main() {
fmt.Println(a, b)
a, b = b, a
fmt.Println(a, b)
}
变量的作用域
- 在函数内部声明的变量叫做局部变量,生命周期仅限与函数内部
- 在函数外部生命的变量叫做全局变量,生命周期作用于整个包,如果是大写的,则作用于整个程序
- 语句块中的变量,生命周期仅限于语句块,语句块外部不能被识别
package main
import "fmt"
//定义全局变量
var a string = "abc"
func main() {
n()
m()
if 1 == 1 {
// 定义语句块内的变量
var b string = "hello"
fmt.Println("语句块输出:", b)
}
//fmt.Println(b),则报错。说明语句块中定义的变量,出了语句块就不能被识别
fmt.Println("主函数输出:", a)
}
func n() {
fmt.Println("n函数输出", a)
}
func m() {
// 定义函数体内的局部变量
a := "nihao"
fmt.Println("m函数输出:", a)
}
//输出结果:
n函数输出 abc
m函数输出: nihao
语句块输出: hello
主函数输出: abc
变量的作用优先级
package main
import "fmt"
//定义了a 为全局变量,作用在整个文件中,最先被加载
var a int = 10
func main() {
//在函数体内定义,则 a 为 局部变量,只能作用于数体内部,只用使用到次 变量才会被加载.
var a int = 20
//变量调用,就近原则。
fmt.Println("a的值是:", a)
}
//输出结果:
a的值是: 20
简短形式,使用 := 赋值操作符
我们知道可以在变量的初始化时省略变量的类型而由系统自动推断,声明语句写上 var 关键字其实是显得有些多余了,因此我们可以将它们简写为 a := 50 或 b := false。
a 和 b 的类型(int 和 bool)将由编译器自动推断。
这是使用变量的首选形式,但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值。使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。
package main
/*
:=
定义即声明,无需写var
系统自动定义数据类型,无需定义数据类型
格式只能作用在函数体内
*/
import "fmt"
func main() {
a := "hello"
fmt.Println(a)
//查看数值类型
fmt.Printf("数据类型是%T", a)
}
//输出结果
hello
数据类型是string
注意事项
如果在相同的代码块中,我们不可以再次对于相同名称的变量使用初始化声明,例如:a := 20 就是不被允许的,编译器会提示错误 no new variables on left side of :=,但是 a = 20 是可以的,因为这是给相同的变量赋予一个新的值。
如果你在定义变量 a 之前使用它,则会得到编译错误 undefined: a。
如果你声明了一个局部变量却没有在相同的代码块中使用它,同样会得到编译错误,例如下面这个例子当中的变量 a:
package main
import "fmt"
func main() {
var a string = "abc"
fmt.Println("hello, world")
}
尝试编译这段代码将得到错误 a declared but not used。
此外,单纯地给 a 赋值也是不够的,这个值必须被使用,所以使用
fmt.Println("hello, world", a)
会移除错误。
但是全局变量是允许声明但不使用的。 同一类型的多个变量可以声明在同一行,如:
var a, b, c int
多变量可以在同一行进行赋值,如:
var a, b int
var c string
a, b, c = 5, 7, "abc"
上面这行假设了变量 a,b 和 c 都已经被声明,否则的话应该这样使用:
a, b, c := 5, 7, "abc"
右边的这些值以相同的顺序赋值给左边的变量,所以 a 的值是 5, b 的值是 7,c 的值是 "abc"。
这被称为 并行 或 同时 赋值。
如果你想要交换两个变量的值,则可以简单地使用 a, b = b, a,两个变量的类型必须是相同。
空白标识符
空白标识符 _ 也被用于抛弃值,如值 5 在:_, b = 5, 7 中被抛弃。
_ 实际上是一个只写变量,你不能得到它的值。这样做是因为 Go 语言中你必须使用所有被声明的变量,但有时你并不需要使用从一个函数得到的所有返回值。
并行赋值也被用于当一个函数返回多个返回值时,比如这里的 val 和错误 err 是通过调用 Func1 函数同时得到:val, err = Func1(var1)。
package main
import "fmt"
/* 空白标识符 _ 被抛弃的值*/
func main() {
//定义多个变量,接收test()函数的返回值
_, str1, str2 := test()
fmt.Println("str1=", str1, "\nstr2=", str2)
}
//test的小括号里定义 传入的参数,小括号后面定义返回的参数
func test() (int, int, string) {
//定义多个变量
a, b, c := 10, 20, "hello"
//返回的许哟啊和 定义的返回参数的类型,数量 一致
return a, b, c
}
//返回值是:
str1= 20
str2= hello
Go 语言常量
常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量使用const 修饰,代表永远只读,不能修改
const 只能修改boolean , number(int相关类型,浮点类型,complex) 和string
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:const identifier [type] = value
const b string = "hello world"
const b = "hello world"
const Pi = 3.1415926
const a = 9 / 3
const c = getValue() //值被保护,不会被修改
你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。
- 显式类型定义:
const b string = "abc"
- 隐式类型定义:
const b = "abc"
多个相同类型的声明可以简写为
const c_name1, c_name2 = value1, value2
const (
a = 9 / 3
b = "hello world"
Pi = 3.1415926
)
package main
import "fmt"
//计算r=5 的圆面积和周长
func main() {
const Pi float32 = 3.14
var r = 5
//周长
c := 2 * Pi * float32(r)
//面积
s := Pi * float32(r) * float32(r)
fmt.Println("周长是:", c, "\n面积是:", s)
}
iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),const 中每新增一行常量声明将使 iota 计数一次(iota 可理解为 const 语句块中的行索引)。
第一个 iota 等于 0,每当 iota 在新的一行被使用时,它的值都会自动加 1;所以 a=0, b=1, c=2 可以简写为如下形式:
package main
import "fmt"
func main() {
const (
a = iota //0
b //1
c //2
)
fmt.Println(a, b, c)
}
// 输出结果
0 1 2
package main
import "fmt"
func main() {
const (
// 做位运算
a = 1 << iota //1
b //2
c //4
d //8
)
fmt.Println(a, b, c, d)
}
// 输出结果
1 2 4 8
package main
import "fmt"
func main() {
const (
i = 1 << iota // 1 << 0
j = 2 << iota // 2 << 0
k // 2 << 1
m
n
)
fmt.Println(i, j, k, m, n)
}
//输出结果:
1 4 8 16 32
附录:
获取系统的环境变量
我们可以用os.Getenv 来获取系统的环境变量
package main
import (
"fmt"
"os"
)
func main() {
//获取系统参数. go 的工作目录
var home = os.Getenv("GOROOT")
fmt.Println("go的工作目录是:", home)
// 获取go的项目目录
var path = os.Getenv("GOPATH")
fmt.Println("go的项目目录是:", path)
}
//输出结果
go的工作目录是: C:\Program Files\Go
go的项目目录是: F:\goproject