目录
1、变量
变量是几乎所有编程语言中最基本的组成元素。从根本上说,变量相当于是对一块数据存储空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后可以通过引用变量名来
使用这块存储空间。
Go语言中的变量使用方式与C语言接近,但具备更大的灵活性。
声明变量的一般形式是使用 var 关键字:
var identifier(变量) type(数据类型)
变量声明有如下三种:
第一种 全定义,指定变量类型,声明后若不赋值,使用默认值。
var v_name v_type
比如:var a int = 10 //内存申请一个a变量,指定内存大小为int尺寸,内容为空。
第二种 类型推导,根据值自行判定变量类型。
var v_name = value
比如:var b = 10 //内存申请一个a变量,并向该变量中存储10这个数字。内存尺寸自动推导
第三种 简略声明,省略var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致
编译错误。
使用操作符 := 可以高效地创建一个新的变量,称之为初始化声明。
:= 不能被用在函数体外,也就是说,当你打算声明一个全局变量时,你只能使用var。
v_name := value
比如:c := 10
Go变声明时候会给初始值(默认值)
- 数字类型 int float byte rune 为 0
- string 为空字符串,注意c语言中没有这个类型
- complex 为 (0+0i)
- bool类型为false
- error类型为
变量声明语句不需要使用分号作为结束符。与C语言相比,Go语言摒弃了语句必须以
分号作为语句结束标记的习惯。
var关键字的另一种用法是可以将若干个需要声明的变量放置在一起,免得程序员需
要重复
写var关键字,如下所示:
var ( v1 int v2 string )
//类型相同多个变量, 非全局变量
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关键字可以保留,但不再是必要的元
素,如下所示:
var v1 int = 10 // 正确的使用方式1
var v2 = 10 // 正确的使用方式2,编译器可以自动推导出v2的类型
v3 := 10 // 正确的使用方式3,编译器可以自动推导出v3的类型
以上三种用法的效果是完全一样的。与第一种用法相比,第三种用法需要输入的字符数大大减少,是懒程序员和聪明程序员的最佳选择。这里Go语言也引入了另一个C和C++中没有的符号(冒号和等号的组合:=),用于明确表达同时进行变量声明和初始化的工作。
指定类型已不再是必需的,Go编译器可以从初始化表达式的右值推导出该变量应该声
明为哪种类型,这让Go语言看起来有点像动态类型语言,尽管Go语言实际上是不折不扣的强类型语言。
当然,出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误,比如下
面这个写法:
var i int
i := 2
会导致类似如下的编译错误:
no new variables on left side of :=
变量赋值:
在Go语法中,变量初始化和变量赋值是两个不同的概念。下面为声明一个变量之后的
赋值过程:
var v10 int //声明
v10 = 123 //赋值使用 没有引用
Go语言的变量赋值与多数语言一致,但Go语言中提供了C/C程序员期盼多年的多
重赋值功能,
比如下面这个交换i和j变量的语句:
i, j = j, i
优点为减少内存
(语法支持)
在不支持多重赋值的语言中,交互两个变量的内容需要引入一个中间变量:
t = i;
i = j;
j = t;
(函数调用)
多重赋值的特性在Go语言库的实现中也被使用得相当充分,在介绍函数的多重返回值时,将对其进行更加深入的介绍。总而言之,多重赋值功能让Go语言与C/C++语言相比可以非常明显地减少代码行数。
匿名变量:
我们在使用传统的强类型语言编程时,经常会出现这种情况,即在调用函数时为了获
取一个值,却因为该函数返回多个值而不得不定义一堆没用的变量。在Go中这种情况可以通过结合使用多重返回和匿名变量
来避免这种丑陋的写法,让代码看起来更加优雅。
假设GetName()函数的定义如下,它返回3个值,分别为firstName、lastName和
nickName:
func GetName() (firstName, lastName, nickName string) {
return "May", "Chan", "Chibi Maruko"}
如:
若只想获得nickName,则函数调用语句可以用如下方式编写:
_,_ , nickName := GetName()
这种用法可以让代码非常清晰,基本上屏蔽掉了可能混淆代码阅读者视线的内容,从而大幅降低沟通的复杂度和代码维护的难度。
局部变量
在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。
以下实例中 main() 函数使用了局部变量 a, b, c:
package main
import "fmt"
func main() {
/ 声明局部变量 /
var a, b, c int
/ 初始化参数 /
a = 10
b = 20
c = a + b
fmt.Printf ("结果: a = %d, b = %d and c = %d\n", a, b, c)
}
以上实例执行输出结果为:
结果: a = 10, b = 20 and c = 30
全局变量
在函数体外声明的变量称之为全局变量,全局变量可以在整个包甚至外部包(被导出
后)使用。
全局变量可以在任何函数中使用,以下实例演示了如何使用全局变量:
package main
import "fmt"
/ 声明全局变量 /
var g int
func main() {
/ 声明局部变量 /
var a, b int
/ 初始化参数 /
a = 10
b = 20
g = a + b
fmt.Printf("结果: a = %d, b = %d and g = %d\n", a, b, g)
}
以上实例执行输出结果为:
结果: a = 10, b = 20 and g = 30
Go 语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑。实例如下:
package main
import "fmt"
/ 声明全局变量 /
var g int = 20
func main() {
/ 声明局部变量 /
var g int = 10
fmt.Printf ("结果: g = %d\n", g)
}
以上实例执行输出结果为:
结果: g = 10
2、常量
在Go语言中,常量是指编译期间就已知且不可改变的值。
常量可以是数值类型(包括整型、浮点型和复数类型)、布尔类型、字符串类型等
字面常量
所谓字面常量(literal),是指程序中硬编码的常量,如:-12
3.14159265358979323846 // 浮点类型的常量
3.2+12i // 复数类型的常量
true // 布尔类型的常量
"foo" // 字符串常量
在其他语言中,常量通常有特定的类型,比如-12在C语言中会认为是一个int类型的常量。
如果要指定一个值为-12的long类型常量,需要写成-12l,这有点违反人们的直观感觉。
Go语言的字面常量更接近我们自然语言中的常量概念,它是无类型的。只要这个常量在相应类型的值域范内,就可以作为该类型的常量,比如上面的常量-12,它可以赋值给int、uint、
int32、int64、float32、float64、complex64、complex128等类型的变量。
常量定义
通过const关键字,你可以给字面常量指定一个友好的名字:
const Pi float64 = 3.14159265358979323846
const zero = 0.0 // 无类型浮点常量
const (
size int64 = 1024
eof = -1 // 无类型整型常量
)
const u, v float32 = 0, 3 // u = 0.0, v = 3.0,常量的多重赋值
const a, b, c = 3, 4, "foo" // a = 3, b = 4, c = "foo", 无类型整型和字符串常量
Go的常量定义可以限定常量类型,但不是必需的。如果定义常量时没有指定类型,那么它与字面常量一样,
是无类型常量。常量定义的右值也可以是一个在编译期运算的常量表达式,比如
const mask = 1 << 3
由于常量的赋值是一个编译期行为,所以右值不能出现任何需要运行期才能得出结果的表达式,
比如试图以如下方式定义常量就会导致编译错误:
const Home = os.Getenv("HOME") // os.Getenv("HOME")动态获取当前用户的家目录
原因很简单,os.GetEnv()只有在运行期才能知道返回结果,
在编译期并不能确定,所以无法作为常量定义的右值.
预定义常量:
Go语言预定义了这些常量:true、false和iota。
iota比较特殊,可以被认为是一个可被编译器修改的常量,在每一个const关键字出现时被重置为0,
然后在下一个const出现之前,每出现一次iota,其所代表的数字会自动增1。
从以下的例子可以基本理解iota的用法.
package main
import "fmt"
func main() {
const (
a = iota //0 (iota在每个const开头被重设为0)
b //1
c //2
d = "ha" //独立值,iota += 1
e //"ha" iota += 1
f = 100 //iota +=1
g //100 iota +=1
h = iota //7,恢复计数
i //8
)
fmt.Println(a,b,c,d,e,f,g,h,i)
}
以上实例运行结果为:
0 1 2 ha ha 100 100 7 8
枚举
枚举指一系列相关的常量,比如下面关于一个星期中每天的定义。通过上一节的例子,我们看到可以用
在const后跟一对圆括号的方式定义一组常量,这种定义法在Go语言中通常用于定义
枚举值。
Go语言并不支持众多其他语言明确支持的enum关键字。下面是一个常规的枚举表示法,
其中定义了一系列整型常量.
const (
Sunday = iota
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
numberOfDays // 这个常量没有导出
)
同Go语言的其他符号(symbol)一样,以大写字母开头的常量在包外可见。 小写字母包外不可见
以上例子中numberOfDays为包内私有,其他符号则可被其他包访问。
枚举应用案例:
package main
import "fmt"
const (
Running int = iota
Pending
Stopped
)
func main() {
fmt.Println("State running: ", Running)
fmt.Println("State pending: ", Pending)
fmt.Println("State Stoped: ", Stopped)
}
说明iota可以初始化counst的初始值防止常量内存大小未知,会有如下编译错误哦。
没有导出的const常量不能外部使用
3、运算符
Go 语言内置的运算符有:
• 算术运算符
• 关系运算符
• 逻辑运算符
• 位运算符
• 赋值运算符
• 其他运算符
算术运算符:
测试代码:
package main
import "fmt"
func main() {
var a int = 21
var b int = 10
var c int
c = a + b
fmt.Printf("第一行 - c 的值为 %d\n", c )
c = a - b
fmt.Printf("第二行 - c 的值为 %d\n", c )
c = a * b
fmt.Printf("第三行 - c 的值为 %d\n", c )
c = a / b
fmt.Printf("第四行 - c 的值为 %d\n", c )
c = a % b
fmt.Printf("第五行 - c 的值为 %d\n", c )
a++
fmt.Printf("第六行 - c 的值为 %d\n", a )
a--
fmt.Printf("第七行 - c 的值为 %d\n", a )
}
以上实例运行结果:
第一行 - c 的值为 31
第二行 - c 的值为 11
第三行 - c 的值为 210
第四行 - c 的值为 2
第五行 - c 的值为 1
第六行 - c 的值为 22
第七行 - c 的值为 21
注意: go有i++,但没有++i,写++i会报错!--运算符同理。
i++ i---的功能单独语句使用,不允许在表达式中使用。 a=i++ 编译异常。
关系运算符:
测试用例:
package main
import "fmt"
func main() {
var a int = 21
var b int = 10
if a == b {
fmt.Printf("第一行 - a 等于 b\n" )
} else {
fmt.Printf("第一行 - a 不等于 b\n" )
}
if a < b {
fmt.Printf("第二行 - a 小于 b\n" )
} else {
fmt.Printf("第二行 - a 不小于 b\n" )
}
if a > b {
fmt.Printf("第三行 - a 大于 b\n" )
} else {
fmt.Printf("第三行 - a 不大于 b\n" )
}
/* Lets change value of a and b */
a = 5
b = 20
if a <= b {
fmt.Printf("第四行 - a 小于等于 b\n" )
}
if b >= a {
fmt.Printf("第五行 - b 大于等于 a\n" )
}
}
以上实例运行结果:
第一行 - a 不等于 b
第二行 - a 不小于 b
第三行 - a 大于 b
第四行 - a 小于等于 b
第五行 - b 大于等于 a
逻辑运算符:
测试用例:
package main
import "fmt"
func main() {
var a bool = true
var b bool = false
if a && b {
fmt.Printf("第一行 - 条件为 true\n" )
}
if a || b {
fmt.Printf("第二行 - 条件为 true\n" )
}
/* 修改 a 和 b 的值 */
a = false
b = true
if a && b {
fmt.Printf("第三行 - 条件为 true\n" )
} else {
fmt.Printf("第三行 - 条件为 false\n" )
}
if !(a && b) {
fmt.Printf("第四行 - 条件为 true\n" )
}
}
以上实例运行结果:
第二行 - 条件为 true
第三行 - 条件为 false
第四行 - 条件为 true
位运算符:
左移几个会向左边扔掉几个,右移几个会向右边扔掉几个
左移几位就是乘2的n次幂,右移几位就是除以2的n次幂
测试用例:
package main
import "fmt"
func main() {
var a uint = 60 /* 60 = 0011 1100 */
var b uint = 13 /* 13 = 0000 1101 */
var c uint = 0
c = a & b /* 12 = 0000 1100 */
fmt.Printf("第一行 - c 的值为 %d\n", c )
c = a | b /* 61 = 0011 1101 */
fmt.Printf("第二行 - c 的值为 %d\n", c )
c = a ^ b /* 49 = 0011 0001 */
fmt.Printf("第三行 - c 的值为 %d\n", c )
c = a << 2 /* 240 = 1111 0000 */
fmt.Printf("第四行 - c 的值为 %d\n", c )
c = a >> 2 /* 15 = 0000 1111 */
fmt.Printf("第五行 - c 的值为 %d\n", c )
}
以上实例运行结果:
第一行 - c 的值为 12
第二行 - c 的值为 61
第三行 - c 的值为 49
第四行 - c 的值为 240
第五行 - c 的值为 15
赋值运算符:
注意:=和==
测试用例:
package main
import "fmt"
func main() {
var a int = 21
var c int
c = a
fmt.Printf("第 1 行 - = 运算符实例,c 值为 = %d\n", c )
c += a
fmt.Printf("第 2 行 - += 运算符实例,c 值为 = %d\n", c )
c -= a
fmt.Printf("第 3 行 - -= 运算符实例,c 值为 = %d\n", c )
c *= a
fmt.Printf("第 4 行 - *= 运算符实例,c 值为 = %d\n", c )
c /= a
fmt.Printf("第 5 行 - /= 运算符实例,c 值为 = %d\n", c )
c = 200
c <<= 2
fmt.Printf("第 6 行 - <<= 运算符实例,c 值为 = %d\n", c )
c >>= 2
fmt.Printf("第 7 行 - >>= 运算符实例,c 值为 = %d\n", c )
c &= 2
fmt.Printf("第 8 行 - &= 运算符实例,c 值为 = %d\n", c )
c ^= 2
fmt.Printf("第 9 行 - ^= 运算符实例,c 值为 = %d\n", c )
c |= 2
fmt.Printf("第 10 行 - |= 运算符实例,c 值为 = %d\n", c )
}
以上实例运行结果:
第 1 行 - = 运算符实例,c 值为 = 21
第 2 行 - += 运算符实例,c 值为 = 42
第 3 行 - -= 运算符实例,c 值为 = 21
第 4 行 - *= 运算符实例,c 值为 = 441
第 5 行 - /= 运算符实例,c 值为 = 21
第 6 行 - <<= 运算符实例,c 值为 = 800
第 7 行 - >>= 运算符实例,c 值为 = 200
第 8 行 - &= 运算符实例,c 值为 = 0
第 9 行 - ^= 运算符实例,c 值为 = 2
第 10 行 - |= 运算符实例,c 值为 = 2
其他运算符:
测试用例
package main
import "fmt"
func main() {
var a int = 4
var b int32
var c float32
var ptr *int
/* 运算符实例 */
fmt.Printf("第 1 行 - a 变量类型为 = %T\n", a )
fmt.Printf("第 2 行 - b 变量类型为 = %T\n", b )
fmt.Printf("第 3 行 - c 变量类型为 = %T\n", c )
/* & 和 * 运算符实例 */
ptr = &a /* 'ptr' 包含了 'a' 变量的地址 */
fmt.Printf("a 的值为 %d\n", a) //通过变量名称访问内存
fmt.Printf("*ptr 为 %d\n", *ptr) //通过地址访问内存
}
以上实例运行结果:
第 1 行 - a 变量类型为 = int
第 2 行 - b 变量类型为 = int32
第 3 行 - c 变量类型为 = float32
a 的值为 4
*ptr 为 4
打印ab的地址