hello world
// 表示该文件属于哪个包
package main
// 导入包
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
基本语法
命名
小写字母开头,驼峰式命名,区分大小写(大写字母用于导出)
声明变量
语法
// var 变量名字 类型 = 表达式
s := "" // 简短声明:类型自动推导,要用:= 简短变量声明语句中必须至少要声明一个新的变量
var s string
var s = ""
var s string = ""
// new 返回类型为*T的变量地址,是内建函数,不是关键字
p := new(int) // p为*int类型,指向匿名的int变量
用哪种不用哪种,为什么呢?
第一种形式,是一条短变量声明,最简洁,但只能用在函数内部,而不能用于包变量。
第二种形式依赖于字符串的默认初始化零值机制,被初始化为""。
第三种形式用得很少,除非同时声明多个变量。
第四种形式显式地标明变量的类型,当变量类型与初值类型相同时,类型冗余,但如果两者类型不同,变量类型就必须了。
实践中一般使用前两种形式中的某个,初始值重要的话就显式地指定变量的类型,否则使用隐式初始化。
变量类型
新命名的底层相同的类型,是不兼容的
type 类型名字 底层类型
// 两个温度不兼容
type Celsius float64 // 摄氏温度
type Fahrenheit float64 // 华氏温度
类型转化
两个类型的底层基础类型相同时,才能用类型转换T(x)
循环(只有for)
Go语言只有for循环这一种循环语句。for循环有多种形式,其中一种如下所示:
// 注意:判断条件无括号
for initialization; condition; post {
// zero or more statements
}
// 两个值分别代表索引和值,_表示丢掉不需要的值
for _, arg := range os.Args[1:]{
}
选择
if
判断条件无括号
Switch
// 无需break
switch coinflip() {
case "heads":
heads++
case "tails":
tails++
default:
fmt.Println("landed on edge!")
}
声明函数
// fToC函数,接收float64参数,返回float64值
// 函数可以返回多个变量
func fToC(f float64) float64 {
return (f - 32) * 5 / 9
}
赋值
// 元组赋值:交换两个变量的值
x, y = y, x
// 有些表达式会产生多个值,可以用_来丢弃不需要的值
f, err = os.Open("foo.txt")
medals := []string{"gold", "silver", "bronze"}
// 隐式地对slice的每个元素进行赋值操作,类似这样写的行为:
medals[0] = "gold"
medals[1] = "silver"
medals[2] = "bronze"
包和文件
导出
如果包内一个名字是大写字母开头的,那么该名字是导出的
导入
import (
"fmt"
"os"
"strconv"
"gopl.io/ch2/tempconv"
)
// 导入后就可以用tempconv来调用一些包的内容
基本概念
包(packages)
Go语言提供了一些很好用的package,并且这些package是可以扩展的。Go语言社区已经创造并且分享了很多很多。所以Go语言编程大多数情况下就是用已有的package来写我们自己的代码。
方法和接口:
方法是和命名类型关联的一类函数。Go语言里比较特殊的是方法可以被关联到任意一种命名类型。
接口是一种抽象类型,这种类型可以让我们以同样的方式来处理不同的固有类型,不用关心它们的具体实现,而只需要关注它们提供的方法。
指针:
指针是可见的内存地址,&操作符可以返回一个变量的内存地址,并且*操作符可以获取指针指向的变量内容,但是在Go语言里没有指针运算,也就是不能像c语言里可以对指针进行加或减操作。
变量的生存周期
包一级变量
和整个程序的运行周期是一致的。
局部变量
局部变量的生命周期则是动态的:每次从创建一个新变量的声明语句开始,直到该变量不再被引用为止,然后变量的存储空间可能被回收。函数的参数变量和返回值变量都是局部变量。它们在函数每次被调用的时候创建。
局部变量可能在函数返回之后依然存在。称作局部变量从函数中逃逸了,逃逸的变量需要额外分配内存,同时对性能的优化可能会产生细微的影响。
编译器会自动选择在栈上还是在堆上分配局部变量的存储空间,这个选择并不是由用var还是new声明变量的方式决定的。