本文参考了go语言官方文档,对比了C声明语法的不足,解释了go语言声明语法的特点及其规则。
1、C语言的声明语法go语言的变量定义主要对照C语言,在C语言的基础上进行改进的。C语言中的声明语法包括变量声明和函数声明两种,其规则分别如下:
变量声明C语言中,变量的声明主要包含两个部分,放在左边的变量类型和放置在右边的变量名称,如基本数据类型、数组、指针等三种主要类型的定义如下:
int a; int b[]; int *c;函数声明
函数的声明包含返回变量类型、函数名称、输入参数三个部分,其声明格式为
返回变量类型 函数名称(输入参数类型 输入参数名称){},如:
int main(int argc,char *argv[])2、C语言的声明语法评价
C语言的声明方法是比较巧妙的,将类型放在左边直接代表声明变量,不需要增加额外的声明语法。然而,在Go语言官方文档中详细总结了很多C语言声明的问题,并提出了go语言在声明语法中改进的思路。其中,我认为C语言的声明语法其实问题并不多,其两点问题如下:
- 1、变量名称和类型没有完全区分,如声明数组int a[]、指针数组 int *b[]等,这会导致声明语法不易阅读。并且导致理解容易混乱,同样是int类型,却可以定义整型、数组、指针等等。当前热门的java等都处理了这个问题,其变量的类型和名称是完全独立的,如数组定义写为int[] a = new int[];
- 2、由于类型和声明语法的重用,代码中类型的解析比较困难。也会导致C、java语言中变量强制转换都会为类型加上括号等情况。
Go语言在设计声明语法时主要考虑其易读性,符合阅读习惯,同样是第1节中的三种声明情况,在阅读可以表示为:
- a: int
- b: array[3] of int
- c: pointer to int
其特点是类型在变量名之后,中间通过符号:表明在定义一个变量。Go语言对其精炼修改,并将var作为语法声明关键字,得到其声明语法规则。其基本数据类型、数组、指针等三种主要类型的定义如下:
var a int = 5
var b [3]int = []int{1,2,3}
var c *int = &a
也可以使用符号:替代var关键字,并默认进行类型推导:
a := 5
b := [3]int{1,2,3}
c := &a
可以看到Go语言的语法声明其实是比较易读的,特别是设计到复杂的一些类型,比如数组指针*[3]int,可以读作point to array[3] of int。
函数声明Go语言的函数声明同样遵循变量声明的准则并利用func作为函数声明关键字,如:
func main(argc int, argv []string) int4、golang函数声明规则的一个优势
- 由于C中函数没有其类型关键字,因次函数类型和它的名称是完全耦合的,这有时是不灵活的。go语言中明确函数类型关键字为func,函数本身只是该类型的一个值,这使得函数的值传递、闭包等实现十分方便,案例如下:
//函数的值传递
hypot := func(x, y float64) float64 {
return math.Sqrt(x*x + y*y)
}
fmt.Println(hypot(5, 12))
//函数值作为另一个函数的输入参数
func compute(fn func(float64, float64) float64) float64 {
return fn(3, 4)
}
compute(hypot)
//闭包的实现
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos := adder()
for i := 0; i < 10; i++ {
fmt.Println(pos(i))
}
}
****输出****
0
1
3
6
10
15
21
28
36
45
闭包概述
闭包的实现形式通常为:
- 用户创建了一个函数func1,func1在函数体内又声明了另一个函数func2,并将func2作为值传递出去。
- 用户声明一个函数类型的变量fn来接收返回参数化func2之后,可以将fn当作普通函数来运行。
需要注意的是在func1中可以定义局部变量,这些局部变量对func2是可见的,在func1函数执行完成后那些被func2引用过的局部变量会被保存起来,并仅对fn可见,这就是闭包的关键点。
如果从内存模型来解释上述案例的闭包,就是在调用adder()时局部变量sum保存在方法栈的局部变量表中,当方法执行完毕出栈时由于返回的fn函数还引用着sum变量,此时go会将sum保存到堆中进行存储,且只有fn函数能够对这两个变量的引用,其他地方是无法引用这两个变量的,这其实和Java的虚拟机模型是一致的,这就是闭包的核心所在,这种结构在go中拥有极大的威力。
在go中函数可以作为基本数据类型进行值传递并作为函数返回参数
闭包的作用go是没有类的,因此也没有java中私有变量、公有变量这些概念。在go中,只要定义了一个非局部变量,那么他就是公有的,通过闭包可以实现私有变量这一操作,这是非常重要的。比如你写了一个工具包,内部需要有很多非局部变量,但是又不想这些变量对使用者可见,这时就可以使用闭包方便的解决。
5、Go语言变量声明测试代码 代码:
package main
import (
"fmt"
"unsafe"
)
// 变量定义测试
func main() {
// 1、直接通过var关键字命名,如果未指定初值,go会为其附默认初值
// 这和java是不同的,java的局部变量是必须附初值的,而静态变量和全局变量不需要,这和JVM处理有关系
fmt.Println("**************************************************************************************************************")
fmt.Println("1、直接通过var关键字命名,如果为指定初值,go会为其附默认初值")
fmt.Println("这和java是不同的,java的局部变量是必须附初值的,而静态变量和全局变量不需要,这和JVM处理有关系")
fmt.Println("")
var i int = 11
var ii int //会默认附初值
fmt.Println("i =", i, "ii =", ii)
fmt.Println("")
// 2、自动类型推导
fmt.Println("**************************************************************************************************************")
fmt.Println("2、自动类型推导,不指定类型")
fmt.Println("")
var j = 11.34
var k = "sddda"
fmt.Println("j =", j)
fmt.Println("k =", k)
fmt.Println("")
// 3、var省略 :=方式
fmt.Println("**************************************************************************************************************")
fmt.Println("3、省略var 通过:=方式定义并赋值")
fmt.Println("")
l := 3.4
fmt.Println("l =", l)
fmt.Println("")
// 4、多变量命名(4种方式)
fmt.Println("**************************************************************************************************************")
fmt.Println("4、多变量命名(4种方式)")
fmt.Println("①:var x1, x2, x3 int = 1, 2, 3")
fmt.Println("②:var n1, n2, n3 = 1, 2, 3 省略类型")
fmt.Println("③:m1, m2, m3 := 1, 2, 3 省略var")
fmt.Println("")
var x1, x2, x3 int = 1, 2, 3
fmt.Println("x1 x2 x3=", x1, x2, x3)
var n1, n2, n3 = 1, 2, 3
fmt.Println("n1 n2 n3=", n1, n2, n3)
m1, m2, m3 := 1, 2, 3
fmt.Println("m1 m2 m3=", m1, m2, m3)
var (
l1 = 1
l2 = 2
l3 = 3
)
fmt.Println("l1 l2 l3=", l1, l2, l3)
fmt.Println("")
// 5、查看数据类型及其占用的字节数
fmt.Println("**************************************************************************************************************")
fmt.Println("5、查看数据类型及其占用的字节数")
fmt.Println("通过Printf+\T方式输出类型,unsafe.Sizeof输入字节大小")
fmt.Println("")
q := -1.0
fmt.Printf("q为%v,的类型是%T,占用%d个字节n", q, q, unsafe.Sizeof(q))
fmt.Println("")
}
输出
1、直接通过var关键字命名,如果为指定初值,go会为其附默认初值
这和java是不同的,java的局部变量是必须附初值的,而静态变量和全局变量不需要,这和JVM处理有关系
i = 11 ii = 0
2、自动类型推导,不指定类型
j = 11.34 k = sddda
3、省略var 通过:=方式定义并赋值
l = 3.4
4、多变量命名(4种方式)
①:var x1, x2, x3 int = 1, 2, 3
②:var n1, n2, n3 = 1, 2, 3 省略类型
③:m1, m2, m3 := 1, 2, 3 省略var
x1 x2 x3= 1 2 3
n1 n2 n3= 1 2 3
m1 m2 m3= 1 2 3
l1 l2 l3= 1 2 3
5、查看数据类型及其占用的字节数
通过Printf+T方式输出类型,unsafe.Sizeof输入字节大小
q为-1,的类型是float64,占用8个字节



