命名规则
Go语言中的函数名、变量名、常量名、类型名、语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母、数字或下划线。大写字母和小写字母是不同的:heapSort和Heapsort是两个不同的名字。 此外首字母是否大小写还影响到该函数、变量、常量的访问权限,命名以小写开头只能被同包访问。
根据Go的SDK包的命名风格习惯可以看出文件名以下划线 _ 分割多个单词;函数,变量常量等使用驼峰命名规则。
声明
声明语句定义了程序的各种实体对象以及部分或全部的属性。Go语言主要有四种类型的声明语句:var、const、type和func,分别对应变量、常量、类型和函数实体对象的声明。下面列举变量、常量和函数的声明,类型的声明后面再详细说明。
package main
import (
"fmt"
"strconv"
)
const name = "dskxvos"
func main() {
var age = 32
fmt.Println(welcome(name, age))
}
func welcome(name string, age int) (welcomeStatement string) {
return "欢迎访问我的博客,我是" + name + ",今年" + strconv.Itoa(age) + "岁"
}
以上代码 我们定义了一个名为name的常量,一个名为age的变量,除了运行的main函数之外,我们还定义了一个 名为welcome的函数,他接受string,int两个类型的参数,返回一个string的结果。
变量 & 常量
变量的常用声明方式有以下几种
//var 变量名 数据类型 = expression
//数据类型 和 expression 可以省略一个 因为省略数据类型时 会根据 expression推导其类型
var a float64 = 3.1415926 * 5
var b = 3.1415926 * 5
//简短变量声明 变量名 := expression 一般运用在局部变量的声明和初始化
c := a * a
//多变量声明只应该用在代码可读性高的地方 如for循环中
i, j := 12, 15
//返回值声明
//返回值声明时 如果变量已经存在,则会重新给该变量复制,
//但是返回值声明要求至少要声明一个新的变量,否则应该直接使用赋值 而不是声明 以下被注释的函数则为错误示范
f, err := os.Open("aaa")
//f 和err 变量都已经存在了,没有声明新的变量,不能使用 := 声明
//f, err := os.Open("aaa")
//虽然err变量已经存在了,但是定义了新的变量file 所以这段下面这段代码是可以通过编译的。
file, err := os.Open("aaaaaaa")
以上就是变量的基本定义方式,在第一个代码示例中,我们通过 const 声明了一个常量 ,常量的声明和变量类似 但是常量必须以 const 声明 并且不能使用** := **简短声明即可
指针
一个指针的值是另一个变量的地址。一个指针对应变量在内存中的存储位置。并不是每一个值都会有一个内存地址,但是对于每一个变量必然有对应的内存地址。通过指针,我们可以直接读或更新对应变量的值,而不需要知道该变量的名字(如果变量有名字的话),关于指针的概念就不在此过多赘述了。在Go中,我们通过 [&变量名] 得到某个变量的指针,通过 [*指针] 得到其内存中的值,代码示例如下
package main
import (
"fmt"
)
func main() {
var x = 1
var p *int = &x // &变量名 获取x的指针,其指针类型为 类型为 *int
fmt.Println(p)
fmt.Println(*p) //*指针类型 得到其内存中的值
*p = 2 //重写内存中的值
fmt.Println(x)
var a, b int
fmt.Println(&a == &a, &a == &b, a, &a == nil)
fmt.Println(f() == f())
}
func f() *int {
num := 1
return &num
}
关于以上代码的执行结果
// fmt.Println(p) 此时输出的是变量 x 的指针,即x的内存地址
0xc00001a098
//fmt.Println(*p) 通过*指针类型,得到其内存中的值,即声明x时赋值的1
1
// *p = 2 修改指针p内存中的值为2
// fmt.Println(x) 此时,x的指针p指向的内存中的值被修改为2
2
// var a, b int 声明 a b两个变量
// fmt.Println(&a == &a, &a == &b, a, &a == nil)
//1. a的指针和a的指针肯定是相同的
//2. a的值和b的值虽然是一样的,但是他们在内存中指向的是两个内存地址 所以他们的指针是不相同的
//3. 打印a的值,默认值为0
//4. a的指针不为空
true false 0 false
//通过f()函数返回两个 *int类型的指针,因为每次执行都会重新声明一个num的变量,所以他们的指针也是不相同的
false
new函数
另一个创建变量的方法是调用内建的new函数。表达式new(T)将创建一个T类型的匿名变量,初始化为T类型的零值,然后返回变量地址,返回的指针类型为*T。
// 通过new()返回 *int的指针
n := new(int)
//此时打印的是指针 即内存地址
fmt.Println(n)
//此时打印指针中内存的值为0
fmt.Println(*n)
//在新建一个 零值的匿名变量,返回其指针
m := new(int)
//此时打印 false ,虽然n和m内存中的值都为0,但是n和m存储的数据在内存中是不同的位置,即内存地址不同(指针)所以为false
fmt.Println(m == p)
new函数使用通常相对比较少,因为对于结构体来说,直接用字面量语法创建新变量的方法会更灵活,后面会详细说明。
变量的声明周期
变量的生命周期指的是在程序运行期间变量有效存在的时间段。对于在包一级声明的变量来说,它们的生命周期和整个程序的运行周期是一致的。而相比之下,局部变量的生命周期则是动态的:每次从创建一个新变量的声明语句开始,直到该变量不再被引用为止,然后变量的存储空间可能被回收。函数的参数变量和返回值变量都是局部变量。它们在函数每次被调用的时候创建。