0. Go的一些特性
- 拥有 GC (Garbege Collection)不需要考虑手动管理内存。
- 不需要分号做语句结尾。
- 语言级别的并发。
- 简洁、优雅,代码风格统一。
1.变量
- 定义的变量必须使用。确实不需要的需要显示忽略,使用
_
。 - 变量声明顺序与 C/C++ 相反,先名称后类型。
- 变量区分大小写。第一个字母的大小写决定其可见性是否跨包。
- 变量命名风格:驼峰命名风格。
-
声明变量
给一个程序实体命名,并且设定其部分或者全部属性。主要有4个:变量(var)、常量(const)、类型(type)和函数(func)。与定义的含义一样,二者不做严格区分。包级别的变量初始化在 main 函数开始之前进行。Go 中不存在未初始化的变量。
声明变量:
//1.使用 var 关键字 var a, b, c bool var s1, s2 string = "hello", "world" //2.自动类型推导 var a, b, c, i, s = true, false, 3, 2.333, "hello" //3.短变量(只能在函数内部定义) a, b, c := 1, true, "Hi"
指针变量(不能进行算术运算):保存变量的地址
x := 1 p := &x //p 是整型的指针,指向 x *p = 2 // x 此时为2
swap 函数:
package main import "fmt" func swap(a, b int){ a, b = b, a } func swapByPointer(a, b *int) { *a, *b = *b, *a } func main() { a, b := 1, 2 fmt.Printf("before swap, a:%d, b:%d\n", a, b) swap(a, b) fmt.Printf("after swap, a:%d, b:%d\n", a, b) //未交换 swapByPointer(&a, &b) fmt.Printf("after swapByPointer, a:%d, b:%d\n", a, b) //交换成功 }
注意,函数,if 语句的花括号
//错误的函数声明,花括号必须与 func 放在同一行 func test() { }
2.变量到底分配在了哪里:栈 or 堆?
变量内存空间到底是分配在栈上还是堆上,由编译器来自行决定。
- 如果函数外部没有引用,则优先放到栈中;
- 如果函数外部存在引用,则必定放到堆中;
C 语言的例子:返回局部变量的地址,p指向了一个非法的地址,因为 test 函数中 a 变量的生命周期已然结束。
#include <stdio.h>
int* test(void){
int a = 10;
return &a;
}
int main(void){
int *p = test();
printf("%d\n", *p); // 程序 core 掉
return 0;
}
Go 的例子:
package main
import "fmt"
var global *int
func f(){
var x int // x 使用堆空间内存,由编译器来做
x = 1
global = &x
}
func g(){
y := new(int) //y 使用栈内存,因为无外部引用,即变得不可达,可回收。
*y = 1
}
func main() {
f() //调用 f() 给global赋值
fmt.Println(*global) // x 从 f() 中逃逸, global 引用了 x
}
3.变量到底可以存活多久(变量的生命周期)?
通过变量是否可达来确定它的生命周期
- 包级别变量:整个程序的执行时间。
- 局部变量:动态生命周期。执行时创建,一直生存到不可访问,空间被回收,生命终结。
2.赋值、多重赋值和可赋值性
- 直接赋值或通过指针间接赋值
- 多个变量可以一起赋值
a, b, c := 1, 2.2, "hello"
- 赋值时类型必须精准匹配,nil 可以被赋给任何接口或者引用变量
var a int = 10
var b int64 = 20
//a = b, error 类型不匹配
//b = a, error 类型不匹配
3.条件
func bounded(v int) int{
//if 语句可以没有圆括号,但必需要有花括号
if v > 100{
return 100
}else if (v < 0) {
return 0
}else {
return v
}
}
4.循环
循环,只有 for 循环一种,无 while 和 do…while
//经典的for语句 init; condition; post
for i := 10; i < 10; i++{
//do something
}
//精简的for语句 condition, while(i < 10){ ... }
i := 6
for i < 10{
//do something
}
//forever for(;;)
for{
//do something
}
5.switch
switch中不需要写 break,默认就会 break
注意 switch 中的 fallthrough 会无条件的进入下一个 case 中
package main
import (
"fmt"
)
func main() {
a := 10
switch {
case a > 10:
fmt.Println("a > 1")
case a > 2:
fmt.Println("a > 2")
fallthrough //无条件进入下一个 case
case a > 30:
fmt.Println("a > 30")
default:
fmt.Println("a")
}
}
6.包和文件
-
包和文件以及包的导入
Go语言中的包的作用和其他语言中库和模块的作用类似(如JAVA中的包,C++中的 namespace)。
每个包给它的声明提供独立的命名空间。
Go语言中每个包通过导入路径的唯一字符串来标识。包名按照约定时导入路径的最后一段。
gopl.io/ch2/tempconv 的包名就是 tempconv
导入一个没有被引用的包,会触发错误。
导入包时,导入的是路径而不是包名。 -
包初始化
包级别变量 => 执行 init() 函数
import _ “fmt” 匿名导入 fmt 包,无法使用当前包的方式,会执行 init() 方法。
import abc “fmt” 给 fmt 包起一个别名叫做 abc。
import . “fmt” 将 fmt 包中的方法导入当前包作用域中,直接调用不再需要指明 fmt 。
《Go程序设计语言》