变量
变量的本质是计算机分配的一小块内存,专门用于存放指定数据,在程序运行过程中该数值可以发生改变;当程序运行结束,存放该数据的内存就会释放,该变量就会随着内存的释放而消失。
变量声明
使用var关键字
var 变量名 变量类型 = [变量值]
//定义多个变量,
var vname1,vname2,vname3 type
//定义变量,并初始化
var vname1 int = 1
//同时初始化多个变量
var vname1,vname2,vname3 = v1,v2,v3
使用 :=
//定义变量,并初始化
vname1 := 1
//同时初始化多个变量
vname1,vname2,vname3 := v1,v2,v3
// 示例: var a, b, c = 3, 4, "foo"
如果一个变量定义了,但是没有赋值,那么这个变量的值就是这个类型的 "零值"
分组声明
在Go语言中,同时声明多个常量、变量,或者导入多个包时,可采用分组的方式进行声明
// 同时导入多个包
import (
"fmt"
"errors"
)
// 声明多个常量
const (
i = 100,
pi = 3.14
prex = "Go_"
)
// 声明多个变量
var (
a int,
b string = "hello",
)
匿名变量: _(下划线)
任何赋予它的值都会被丢弃,Go语言对于已声明但未使用的变量会在编译阶段报错,可以通过其来丢弃。
_, b := 1,2
变量分类
变量分为局部变量和全局变量:
- 局部变量: 是定义在大括号
{}
内部的变量,大括号的内部也是局部变量的作用域,定义了就必须使用。 - 全局变量: 是定义在函数和大括号
{}
外部的变量,可以只定义不使用。
package main
import "fmt"
// 全局变量:在当前go文件中生效...
// 定义在go文件非函数内,在package和import下面
// 全局变量的定义必须使用 var 关键字, 如果直接使用 := 则无法创建该变量
// 全局变量和局部变量是可以重名的,优先级。到底用谁
var c int
func main() {
// 局部变量:只在一定的范围内生效...
// 在函数体内声明变量
var a int = 3
var b int = 4
// 如果全局变量有,那么直接使用全局变量来接收。
c = a + b
fmt.Printf("a=%d,b=%d,c=%d\n", a, b, c)
fmt.Printf("c内存地址:%p\n", &c)
b = 1
// 但是如果在局部有和全局同名的变量,优先使用局部变量
c := a + b
fmt.Printf("a=%d,b=%d,c=%d\n", a, b, c)
fmt.Printf("c内存地址:%p\n", &c)
b = 5
// 就近原则
//Go语言程序中全局变量与局部变量名称可以相同,但是函数体内的局部变量会被优先考虑。
c = a + b
fmt.Printf("a=%d,b=%d,c=%d\n", a, b, c)
fmt.Printf("c内存地址:%p\n", &c)
// Printf 格式化输出 (参数一:需要打印的内容,%是占位符,通过后续参数给他一一赋值)
fmt.Printf("a=%d,b=%d,c=%d\n", a, b, c)
}
变量作用域
全局变量:
- 如果以大写字母开头,所有地方都可以访问,跨package访问时需要带上package名称;
- 如果以小写字母开头,则本package内都可以访问。
函数内部的局部变量:
- 仅本函数内可以访问。
- {}可以固定一个作用域。
- 内部声明的变量可以跟外部声明的变量有冲突,以内部的为准--就近原则。
var (
A=3 //所有地方都可以访问
b=4 //本package内可以访问
)
func foo(){
b:=5 //本函数内可以访问
{
b:=6 //本作用域内可以访问
}
}
变量的交换
package main
import "fmt"
func main() {
/* 在编程中遇到的第一个问题:变量交换
var a int = 100
var b int = 200
var t int
t = a
a = b
b = t
*/
// 在Go语言中,程序变量交换,也有语法糖
var a int = 100
var b int = 200
// fmt.Println 可以传递多个参数,用逗号隔开,直接打印
fmt.Println("a=", a, ",b=", b)
// 把a,b赋值给b,a 语法糖, 底层本质还是用到了临时变量。简化我们的开发
b, a = a, b
fmt.Println("交换后 a=", a, ",b=", b)
// 复杂的问题都给我们简单化了,我们开发很轻松,编译器帮我们在底层处理。
}
理解变量(内存地址)
未使用变量的限制
go语言在编译阶段会对声明但未使用变量报错,比如以下代码就会报错: 声明了i变量但并未使用。
package main
func main(){
//编译阶段报错
var i int
}
常量
程序编译阶段就确定下来的值,而程序在运行时则无法改变该值。
常量定义后未被使用,不会在编译时报错。
常量中的数据类型只可以是布尔型、数字型(整型、浮点型和复数型)和字符串。
语法:
const 常量名 [常量类型] = 常量值 // 常量类型可以省略
const PI float32 = 3.1415
const AGE = 100
const Prex = "Go_"
const(
PI=3.14
E=2.71
)
const(
a=100
b //100,跟上一行的值相同
c //100,跟上一行的值相同
)
iota 常量
iota是go语言的常量计数器,只能在常量的表达式中使用。
- 默认开始值是0,每调用一次加1。
- 每个const分组的第一个常量被默认设置为它的0值,第二及后续的常量被默认设置为它前面那个常量的值,如果前面那个常量的值是iota,则它也被设置为iota。
const (
n1 = iota //0
n2 //1
n3 //2
n4 //3
)
const (
n1 = iota //0
n2 //1
_ //丢弃该值,常用在错误处理中
n4 //3
)
const (
n1 = iota //0
n2 = 100 //100
n3 = iota //2
n4 //3
)
const n5 = iota //0
const (
_ = iota
KB = 1 << (10 * iota) // <<移位操作,速度比乘除法快
MB = 1 << (10 * iota) // 1<<3 相当于1*2*2*2 0001 -> 1000
GB = 1 << (10 * iota)
TB = 1 << (10 * iota)
PB = 1 << (10 * iota)
)
const (
a, b = iota + 1, iota + 2 //1,2
c, d //2,3
e, f //3,4
)
每遇到一个const关键字,iota就会重置
复数型
默认类型是complex128
(64位实数+64位虚数)。如果需要小一些的,也有complex64
(32位实数+32位虚数)。复数的形式为RE+IMi
,其中RE是实数部分,IM是虚数部分,而最后的i是虚数单位。
声明使用:
var c complex64 = 5 + 5i
输入输出
输入
package main
import "fmt"
func main() {
fmt.Println("输入您的名字:")
var name string
fmt.Scan(&name) // 这里记住,要在变量的前面加个&, 后面讲指针会提到
fmt.Println("你输入的名字是", name)
}
(1)fmt.Scan
语法:
func Scan(a ...interface{}) (n int, err error)
- Scan 从标准输入扫描文本,读取由空白符分隔的值保存到传递给本函数的参数中,换行符视为空白符。
- 本函数返回成功扫描的数据个数和遇到的任何错误。如果读取的数据个数比提供的参数少,会返回一个错误报告原因。
package main
import "fmt"
func main() {
var (
name string
age int
isMarried bool
)
fmt.Scan(&name, &age, &isMarried) // 输入类型不一致,按默认值
fmt.Printf("扫描结果 name:%s age:%d married:%t\t", name, age, isMarried)
}
将上述代码在终端运行,在终端依次输入 yuan 、26、false使用空格分隔。
go run main.go yuan 26 false 扫描结果 name:yuan age:26 married:false
(2)fmt.Scanf
语法
func Scanf(format string, a ...interface{})(n int, err error)
- Scanf从标准输入扫描文本,根据format参数指定的格式去读取由空白符分隔的值保存到传递给本函数的参数中。
- 本函数返回成功扫描的数据个数和遇到的任何错误。
// 案例1
var (
name string
age int
isMarried bool
)
fmt.Scanf("1:%s 2:%d 3:%t", &name,&age,&isMarried)
fmt.Printf("扫描结果 姓名:%s 年龄:%d 婚否:%t", name,age,isMarried)
// 案例2
var a, b int
fmt.Scanf("%d+%d", &a, &b)
fmt.Println(a + b)
(3)fmt.Scanln
语法
func Scanln(a ...interface{}) (n int, err error)
- Scanln类似于Scan,它遇到换行立即停止扫描。
- 本函数返回成功扫描的数据个数和遇到的任何错误。
Scanln和Scan的区别就是Scanln遇到换行立即结束输入,而Scan则会将换行符作为一个空白符继续下一个输入
输出
fmt.Print有几个变种:
Print: 输出到控制台,不接受任何格式化操作
Println: 输出到控制台并换行
Printf : 只可以打印出格式化的字符串,只可以直接输出字符串类型的变量(不可以输出别的类型)
Sprintf:格式化并返回一个字符串而不带任何输出
(1)Print 和Println
Print和Println()函数可以打印出字符串或变量的值。
name := "yuan"
age := 24
fmt.Print(name, age)
fmt.Println("hello world")
fmt.Println(name)
fmt.Println(age)
fmt.Println(name, age)
fmt.Println("姓名:", name, "年龄:", age)
(2)格式化输出(Printf)
Printf 根据格式说明符格式化并写入标准输出。Printf 只打印字符串
在 Printf中,有一些常用的格式化占位符,我们在这里列出一下 :
- %v 以默认的方式打印变量的值
- %T 打印变量的类型
- %s 正常输出字符串
- %t 打印true或false
- %p 带0x的指针
- %d 整型
- %#p 不带0x的指针
- %o 不带零的八进制
- %#o 带零的八进制
- %x 小写的十六进制
- %X 大写的十六进制
- %#x 带0x的十六进制
- %U 打印Unicode字符
- %#U 打印带字符的Unicode
- %b 打印整型的二进制
- %q 字符串带双引号,字符串中的引号带转义符
- %f (=%.6f) 6位小数点
- %e (=%.6e) 6位小数点(科学计数法)
- %g 用最少的数字来表示
- %.3g 最多3位数字来表示
- %.3f 最多3位小数来表示
比如上面打印一个人的基本信息格式:
name := "yuan"
age := 24
isMarried := false
salary := 3000.549
fmt.Printf("姓名:%s 年龄:%d 婚否:%t 薪资:%.2f\n", name, age, isMarried, salary)
fmt.Printf("姓名:%v 年龄:%v 婚否:%v 薪资:%v\n", name, age, isMarried, salary)
fmt.Printf("姓名:%#v 年龄:%#v 婚否:%#v 薪资:%#v\n", name, age, isMarried, salary)
// 整形和浮点型
fmt.Printf("%b\n", 12) // 二进制表示:1100
fmt.Printf("%d\n", 12) // 十进制表示:12
fmt.Printf("%o\n", 12) // 八进制表示:14
fmt.Printf("%x\n", 12) // 十六进制表示:c
fmt.Printf("%X\n", 12) // 十六进制表示:c
fmt.Printf("%f\n", 3.1415) // 有小数点而无指数:3.141500
fmt.Printf("%.3f\n", 3.1415) // 3.142
fmt.Printf("%e\n", 1000.25) // 科学计数法: 1.000250e+03,默认精度为6
// 设置宽度
fmt.Printf("学号:%s 姓名:%-20s 平均成绩:%-4d\n", "1001", "alvin", 100)
fmt.Printf("学号:%s 姓名:%-20s 平均成绩:%-4d\n", "1002", "zuibangdeyuanlaoshi", 98)
fmt.Printf("学号:%s 姓名:%-20s 平均成绩:%-4d\n", "1003", "x", 78)
(3)Sprintf
Printf和Sprintf都是替换字符串,Printf是直接输出到终端,Sprintf是不直接输出到终端,而是返回该字符串
name := "yuan"
age := 24
isMarried := false
salary := 3000.549
info := fmt.Sprintf("姓名:%s 年龄:%d 婚否:%t 薪资:%.2f\n", name, age, isMarried, salary)
fmt.Println(info)