02 . Go语言的变量,常量,作用域

时间:2022-07-25
本文章向大家介绍02 . Go语言的变量,常量,作用域,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

第一个Go程序

Go命令
go help

// go build
//    go build main
//    这个时候是编译可执行文件。 
//    如果没有 -o 指定输出, 那么就在本目录生成名字是main的可执行文件。
//       go build xxx
//          这个时候是编译xxx模块
//          会编译然后删除生成文件,也就是仅仅告诉你是否可以编译,有什么问题。
//          这一种方式,虽然可以理解为参数是包名,其实本质上参数是目录(文件夹)的名字,
//          也就是说,会查找 $GOROOT/src/xxx 或者 $GOPATH/src/xxx ,找到这个文件夹就编译,否则出错。
//          多个项目的时候,切换项目请及时切换$GOPATH

// clean
//     清理go build 产生的各种文件

// doc
//     展示对于包或者命令的文档,前提得有文档


// env
//     打印Go需要的环境变量的值

// bug
//     打开默认的浏览器让你汇报一个bug


// fix
//     针对你提供的代码,自动将旧的API替换成新的API
//     默认从stdin读入代码,从stdout输出代码
//     如果你提供一个文件,那就重写这个文件api
//     如果你提供一个文件夹,他就递归的重写里面每一个.go文件


// fmt
//     格式化,规范你的go代码


// generate
// go generate的逻辑是:
//    执行geanerate命令,自动生成代码文件
//    然后当你编译的时候就可以使用这些文件,假如你修改了某个结构体,却没有重新generate,
//    那么之前依赖这个结构体生成的代码就会出问题


// get
//    下载并安装你指定的包,会递归的下载安装指定的包


// install 
//    编译执行文件,放到$GOPATH/bin, 编译模块,放到$GOPATH/pkg

// run
//   编译并运行,运行结束会删除编译生成的可执行文件


// test
//   带有test.go的文件在build被忽略,会自动读取源码目录*_test.go文件,生成并运行测试用的可执行文件
//   性能测试系统可以给出代码的性能数据,帮助测试者分析性能问题


// tool
//   二阶命令


// version 
//   打印版本号

// vet
//   代码静态检查
//   可以检测出类似print参数格式错误,无法到达代码等
Hello World
package main
// Go源文件以package声明开头,说明源文件所属的包

import "fmt"
// 使用import 导入依赖的包,其此为包级别的变量,常量,类型和函数的声明和赋值

func main() {
	// 函数内可以定义局部的变量,常量
	fmt.Println("Hello World")
}

// 运行
youmen@youmendeMacBook-Pro src % go build hello.go 
youmen@youmendeMacBook-Pro src % ./hello 
Hello World
youmen@youmendeMacBook-Pro src % go run hello.go 
Hello World

// go build 编译自身包和依赖包

// 使用go build编译时,参数不为空时
// 如果fileName为同一main包下的源文件名(可能一个或者多个),
// 编译器将生成一个与第一个filename同名的可执行文件(如执行 go build test1.go  test2.go 会生成一个test1文件); 
// 如果filename为非main包下源文件名,编译器将只对该包进行语法检查,不会生成可执行文件

// 使用go build编译参数为空时
// 如果当前目录下存在 main 包,则会生成一个与当前目录名同名的可执行文件(如在 hello 目录中执行go build命令时,会生成 hello文件);如果不存在 main 包,则只对当前目录下的程序源码进行语法检查,不会生成可执行文件。


// go install 编译并安装自身包和依赖包
// go fmt 格式化输出代码

// 参数
// -I 针对包的目录搜索
// -d 打印声明信息
// -e 不限制错误打印的个数
// -f 打印栈结构
// -h 发生错误时进入恐慌(panic)状态
// -o 指定输出文件名 
// -S 打印产生的汇编代码
// -V 打印编译器版本
// -u 禁止使用 unsafe 包中的代码
// -w 打印归类后的语法解析树
// -x 打印编译信息

youmen@youmendeMacBook-Pro day01 % go build -x hello.go
WORK=/var/folders/2k/bdg95_md7qxbnwbh1b86qcmm0000gn/T/go-build023067131
mkdir -p $WORK/b001/
cat >$WORK/b001/importcfg.link << 'EOF' # internal
packagefile command-line-arguments=/Users/youmen/Library/Caches/go-build/f9/f949be0d2820ac87750eb4d2c59a7e18e1ffab7c94db4ed324d0fa8df5d8896a-d
packagefile fmt=/usr/local/go/pkg/darwin_amd64/fmt.a
packagefile runtime=/usr/local/go/pkg/darwin_amd64/runtime.a
packagefile errors=/usr/local/go/pkg/darwin_amd64/errors.a
packagefile internal/fmtsort=/usr/local/go/pkg/darwin_amd64/internal/fmtsort.a
packagefile io=/usr/local/go/pkg/darwin_amd64/io.a
packagefile math=/usr/local/go/pkg/darwin_amd64/math.a
packagefile os=/usr/local/go/pkg/darwin_amd64/os.a
packagefile reflect=/usr/local/go/pkg/darwin_amd64/reflect.a
packagefile strconv=/usr/local/go/pkg/darwin_amd64/strconv.a
packagefile sync=/usr/local/go/pkg/darwin_amd64/sync.a
packagefile unicode/utf8=/usr/local/go/pkg/darwin_amd64/unicode/utf8.a
packagefile internal/bytealg=/usr/local/go/pkg/darwin_amd64/internal/bytealg.a
packagefile internal/cpu=/usr/local/go/pkg/darwin_amd64/internal/cpu.a
packagefile runtime/internal/atomic=/usr/local/go/pkg/darwin_amd64/runtime/internal/atomic.a
packagefile runtime/internal/math=/usr/local/go/pkg/darwin_amd64/runtime/internal/math.a
packagefile runtime/internal/sys=/usr/local/go/pkg/darwin_amd64/runtime/internal/sys.a
packagefile internal/reflectlite=/usr/local/go/pkg/darwin_amd64/internal/reflectlite.a
packagefile sort=/usr/local/go/pkg/darwin_amd64/sort.a
packagefile sync/atomic=/usr/local/go/pkg/darwin_amd64/sync/atomic.a
packagefile math/bits=/usr/local/go/pkg/darwin_amd64/math/bits.a
packagefile internal/oserror=/usr/local/go/pkg/darwin_amd64/internal/oserror.a
packagefile internal/poll=/usr/local/go/pkg/darwin_amd64/internal/poll.a
packagefile internal/syscall/execenv=/usr/local/go/pkg/darwin_amd64/internal/syscall/execenv.a
packagefile internal/syscall/unix=/usr/local/go/pkg/darwin_amd64/internal/syscall/unix.a
packagefile internal/testlog=/usr/local/go/pkg/darwin_amd64/internal/testlog.a
packagefile syscall=/usr/local/go/pkg/darwin_amd64/syscall.a
packagefile time=/usr/local/go/pkg/darwin_amd64/time.a
packagefile unicode=/usr/local/go/pkg/darwin_amd64/unicode.a
packagefile internal/race=/usr/local/go/pkg/darwin_amd64/internal/race.a
EOF
mkdir -p $WORK/b001/exe/
cd .
/usr/local/go/pkg/tool/darwin_amd64/link -o $WORK/b001/exe/a.out -importcfg $WORK/b001/importcfg.link -buildmode=exe -buildid=rVXQxABwQkdy_FI00RsL/yf_AjuJJYda-p8wMA7El/EVj7Mr4eTh1CznhPCir7/rVXQxABwQkdy_FI00RsL -extld=clang /Users/youmen/Library/Caches/go-build/f9/f949be0d2820ac87750eb4d2c59a7e18e1ffab7c94db4ed324d0fa8df5d8896a-d
/usr/local/go/pkg/tool/darwin_amd64/buildid -w $WORK/b001/exe/a.out # internal
mv $WORK/b001/exe/a.out hello
rm -r $WORK/b001/
注释
// 定义main包
// Go语言以"包"为管理单位,每个Go源文件必须先声明他所属的包,所以我们会看到每个Go源文件的开头都是一个package的声明,格式如下:
// package name
// 其中, package 是声明包的关键字,name为包的名字

// Go语言的包与文件夹是一一对应的,它具有以下几点特性:
// 一个目录下的同级文件属于同一个包
// 包名可以与其目录名不同
// main包是Go语言程序的入口名, 一个Go语言程序必须有一个且仅有一个main包,否则无法编译出可执行文件

package main  // 定义包main

import "fmt"  // 导入标准包fmt
// 在包声明之后,是import语句,用于导入程序中所依赖的包,导入的包使用双引号""包围,格式:  
// import "name"
// 其中import为导入包的关键字, name为导入包的名字
// 导入的fmt包,这行代码会告诉Go编译器,我们需要用到fmt包中的函数或者变量等,fmt包是Go语言标准库提供的,用于格式化输入输出的内容
// 需要注意的是, 导入的包不能含有代码中没有使用的包,否则编译报错
// 可以使用()将包的名字保卫起来,并且每个包名占用一行



func main() {
  // 程序入口,声明main函数,即程序启动运行第一个函数,main函数只能声明在main包中,不能声明在其他包中,
  // 并且,一个main包中必须有且仅一个main函数
  // Go语言的main()函数不能带参数,也不能定义返回值,命令行传入的参数在os.Arga变量里保存,如果需要支持命令行开关可使用flag包.
  
	/*
		我是一个注释
		这里调用fmt包下的Println的函数打印内容到控制台
	*/
	fmt.Println("hello world")  
  // 打印helloworld到控制台,
  // Println是fmt中的一个函数,他用来格式化数据,比如字符串,整数,小数等,
  // Println函数打印完会自动换行,ln是line的缩写
  // .是Go语言运算符的一种,这里表示调用fmt包中的Println函数
  // 另外,代码fmt.Println("Hello World!")的结尾,不需要使用;来作为结束符,Go 编译器会自动帮我们添加,当然,在这里加上;也是可以的。
}
Go安装目录清单
#  /bin:包含可执行文件,如:编译器,Go 工具
# /doc:包含示例程序,代码工具,本地文档等
# /lib:包含文档模版
# /misc:包含与支持 Go 编辑器有关的配置文件以及 cgo 的示例
# /os_arch:包含标准库的包的对象文件(.a)
# /src:包含源代码构建脚本和标准库的包的完整源代码(Go 是一门开源语言)
# /src/cmd:包含 Go 和 C 的编译器和命令行脚本
Go运行时(runtime)

尽管 Go 编译器产生的是本地可执行代码,这些代码仍旧运行在 Go 的 runtime(这部分的代码可以在 runtime 包中找到)当中。这个 runtime 类似 Java 和 .NET 语言所用到的虚拟机,它负责管理包括内存分配、垃圾回收、栈处理、goroutine、channel、切片(slice)、map 和反射(reflection)等等。 runtime 主要由 C 语言编写(Go 1.5 开始自举),并且是每个 Go 包的最顶级包。你可以在目录 $GOROOT/src/runtime 中找到相关内容。

Go环境变量

Go 开发环境依赖于一些操作系统环境变量,你最好在安装 Go 之前就已经设置好他们。如果你使用的是 Windows 的话,你完全不用进行手动设置,Go 将被默认安装在目录 c:/go 下。这里列举几个最为重要的环境变量:

// $GOROOT 表示 Go 在你的电脑上的安装位置,它的值一般都是 $HOME/go,当然,你也可以安装在别的地方。
// $GOARCH 表示目标机器的处理器架构,它的值可以是 386、amd64 或 arm。
// $GOOS 表示目标机器的操作系统,它的值可以是 darwin、freebsd、linux 或 windows。
// $GOBIN 表示编译器和链接器的安装位置,默认是 $GOROOT/bin,如果你使用的是 Go 1.0.3 及以后的版本,
// 一般情况下你可以将它的值设置为空,Go 将会使用前面提到的默认值。
// 目标及其是指你打算运行你的Go应用程序的机器

Go 编译器支持交叉编译,也就是说你可以在一台机器上构建运行在具有不同操作系统和处理器架构上运行的应用程序,也就是说编写源代码的机器可以和目标机器有完全不同的特性(操作系统与处理器架构)。 为了区分本地机器和目标机器,你可以使用 GOHOSTOS 和 GOHOSTARCH 设置本地机器的操作系统名称和编译体系结构,这两个变量只有在进行交叉编译的时候才会用到,如果你不进行显示设置,他们的值会和本地机器(GOOS 和 GOARCH)一样。

  • GOPATH 默认采用和 GOROOT 一样的值,但从 Go 1.1 版本开始,你必须修改为其它路径。它可以包含多个 Go 语言源码文件、包文件和可执行文件的路径,而这些路径下又必须分别包含三个规定的目录:src、pkg 和 bin,这三个目录分别用于存放源码文件、包文件和可执行文件。
  • $GOARM 专门针对基于 arm 架构的处理器,它的值可以是 5 或 6,默认为 6。
  • $GOMAXPROCS 用于设置应用程序可使用的处理器个数与核数
生成代码文档

go doc 工具会从 Go 程序和包文件中提取顶级声明的首行注释以及每个对象的相关注释,并生成相关文档。 它也可以作为一个提供在线文档浏览的 web 服务器,http://golang.org 就是通过这种形式实现的。

一般用法

# go doc package 获取包的文档注释,例如:go doc fmt 会显示使用 godoc 生成的 fmt 包的文档注释。

# go doc package/subpackage 获取子包的文档注释,例如:go doc container/list。

# go doc package function 获取某个函数在某个包中的文档注释,例如:go doc fmt Printf 会显示有关 fmt.Printf() 的使用说明。

// Go语言 1.13 版本移除了 godoc 工具,可以通过go get 命令来获取 godoc 工具
// go get golang.org/x/tools/cmd/godoc
# 这个工具只能获取在 Go 安装目录下 ../go/src 中的注释内容。此外,它还可以作为一个本地文档浏览 web 服务器。
# 在命令行输入 godoc -http=:6060,然后使用浏览器打开 http://localhost:6060 后,
# 你就可以看到本地文档浏览服务器提供的页面。

变量

变量概念

变量是几乎所有编程语言中最基本的组成元素,从根本上来说,变量相当是对一块数据存储空间的命名,程序可以通过定义一个变量来申请一块数据存储空间,之后通过引用变量名来使用这块存储空间

  • 变量是计算机语言中存储数据的抽象概念,变量的功能就是存储数据,变量通过变量名访问
  • 变量的本质是计算机分配的一小块内存,专门用于存放指定数据,在程序中该数值可以发生改变
  • 变量的存储往往具有瞬时性,或者说是临时存储,当程序运行结束,存放该数据的内存就会释放,而该变量就会消失.
  • Go语言的变量由字母,数字下划线组成,首个字符不能为数字;
  • Go语法规定,定义的局部变量若没有被调用则编译错误
声明变量

1.未初始化的标准格式

# var 变量名 变量类型
func variableZeroValue() {
	var a int
	var s string
	fmt.Printf("%d %qn",a,s)
}

2.未初始化的批量格式

var (
	aa = 3
  ss = "kkk"
)
// 不用每行都用var声明
// 未初始化变量的默认值
// 整形和浮点型变量默认值: 0
// 字符串默认值为空字符串
// 布尔型默认为false
// 函数,指针变量值为nil

3.初始化变量的标准格式

// var 变量名  类型  = 表达式
var k = "zhou"

4.初始化变量的编译器自动推断类型格式

// var 变量名 = 表达式
// 简短模式有以下限制:
// 定义变量,同时显式初始化
// 不能提供数据类型
// 只能在函数内部
func var_demo1() {
	a := 3+4
	fmt.Println(a)
}

5.初始化变量的简短声明格式(短变量声明格式)

func varialbeShorter() {
	a,b,c,s := 3,4,true,"def"

	// := 能写的短一点,但是函数外不能这样定义
	fmt.Println(a,b,c,s)
}
# 变量名 := 表达式
# 使用 := 赋值操作符, :=可以高效的创建一个新的变量,称之为初始化声明
# 声明语句省略了var关键字
# 变量类型将由编译器自动推断
# 这是声明变量的首选形式但是它只能被用在函数体内,而不可以用于全局变量的声明与赋值
# 该变量名必须是没有定义过的变量,若定义过,将发生编译错误
# 在多个短变量声明和赋值中,至少有一个新声明的变量出现在左侧中,
# 那么即便有其它变量名可能是重复声明的,编译器也不会报错。
变量多重赋值

多个变量同时赋值

# Go语法中,变量初始化和变量赋值是两个不同的概念。Go语言的变量赋值与其他语言一样,
# 但是Go提供了其他程序员期待已久的多重赋值功能,可以 实现变量交换。
# 多重赋值让Go语言比其他语言减少了代码量。
匿名变量
# Go语言的函数可以返回多个值,而事实上我们并不是对所有的返回值都用 得上。那么就可以使用匿名变量,用“_”下划线替换即可。
# 匿名变量不占用命名空间,不会分配内存。
变量逃逸分析

什么是栈

栈是一种拥有特殊规则的线性表数据结构

概念

栈只允许从特性表的同一端放入和取出数据,按照后进先出(LIFO,Last InFirst Out)的顺序

往栈中放入元素的过程叫做入栈,入栈会增加栈的元素数量,最后放入的元素总是位于栈的顶部,最先放入的元素总是位于栈的底部. 从栈中取出元素时,只能从栈顶部取出,取出元素后,栈的元素数量会变少,最先放入的元素总是最后被取出,最后放入的元素总是被先取出,不允许从栈底获取数据,也不允许对栈成员(除了栈顶部的成员)进行任何查看和修改操作. 栈的原理类似于将书籍一本一本的堆起来,书按顺序一本一本从顶部放入,取书时只能从顶部一本一本取出

变量和栈什么关系

栈可用于内存分配,栈的分配和回收速度非常快,下面的代码展示了栈在内存分配上的使用:

func calc(a, b int) int {
    var c int
    c = a * b
    var x int
    x = c * 10
    return x
}

// 第 1 行,传入 a、b 两个整型参数。
// 第 2 行,声明整型变量 c,运行时,c 会分配一段内存用以存储 c 的数值。
// 第 3 行,将 a 和 b 相乘后赋值给 c。
// 第 5 行,声明整型变量 x,x 也会被分配一段内存。
// 第 6 行,让 c 乘以 10 后赋值给变量 x。
// 第 8 行,返回 x 的值。

// 上面代码在没有任何优化的情况下,会进行变量c和x的分配过程,go语言默认情况下会将c和x分配在栈上,这两个变量在calc()函数退出就不再使用,函数结束时,保存c和x的栈内存再出栈释放内存,整个分配内存的过程通过栈的分配和回收都会非常迅速.

什么是堆

堆在内存分配中类似于往一个房间里摆放各种家具,家具的尺寸有大有小,分配内存时,需要找一块足够装下家具的空间再摆放家具。经过反复摆放和腾空家具后,房间里的空间会变得乱七八糟,此时再往这个空间里摆放家具会发现虽然有足够的空间,但各个空间分布在不同的区域,没有一段连续的空间来摆放家具。此时,内存分配器就需要对这些空间进行调整优化,如下图所示。

堆分配内存和栈分配内存相比,堆适合不可预知大小的内存分配,但是付出代价是分配速度较慢,而且会形成内存碎片.

栈和堆的区别

栈(heap): 堆是用于存放进程执行中被动态分配的内存段, 他的大小并不固定,可动态扩张或缩减,当进程调用malloc等函数分配内存时,新分配的内存就被动态加入到堆上了(堆被扩张), 当利用free等函数释放内存时,被释放的内存从堆中被删除(堆被缩减).

栈(stack): 栈又被称堆栈,用来存放程序暂时创建的局部变量,也就是我们函数大括号{}中定义的局部变量.

在程序编译阶段,编译器会根据实际情况自动选择在栈或堆上分配局部变量的存储空间,不论使用var还是new关键字声明变量都不会影响编译器的选择.

变量逃逸(Escape Analysis) 自动决定变量分配方式,提高运行效率

堆和栈各有优缺点,该怎么在编程中处理这个问题呢?在 C/C++ 语言中,需要开发者自己学习如何进行内存分配,选用怎样的内存分配方式来适应不同的算法需求。比如,函数局部变量尽量使用栈,全局变量、结构体成员使用堆分配等。程序员不得不花费很长的时间在不同的项目中学习、记忆这些概念并加以实践和使用。 Go语言将这个过程整合到了编译器中,命名为“变量逃逸分析”。通过编译器分析代码的特征和代码的生命周期,决定应该使用堆还是栈来进行内存分配。 在实际的开发中,并不需要刻意的实现变量的逃逸行为,因为逃逸的变量需要额外分配内存,同时对性能的优化可能会产生细微的影响。 虽然Go语言能够帮助我们完成对内存的分配和释放,但是为了能够开发出高性能的应用我们任然需要了解变量的声明周期。例如,如果将局部变量赋值给全局变量,将会阻止 GC 对这个局部变量的回收,导致不必要的内存占用,从而影响程序的性能。

变量生命周期

变量的生命周期指的是在程序运行期间变量有效存在的时间间隔。 变量的生命周期与变量的作用域有着不可分割的联系:

// 全局变量:它的生命周期和整个程序的运行周期是一致的;
// 局部变量:它的生命周期则是动态的,从创建这个变量的声明语句开始,到这个变量不再被引用为止;

// 形式参数和函数返回值:它们都属于局部变量,在函数被调用的时候创建,函数调用结束后被销毁。
// 在定义函数时函数名后面括号中的变量叫做形式参数(简称形参),形参只有在函数调用时才会生效,
// 函数调用结束后会被销毁,在函数未被调用时,函数的形参并不占用实际的存储单元,也没有实际值,形参一般作为函数的局部变量使用

常量

声明方式

1、相对于变量,常量是恒定不变的值,例如圆周率。

常量是一个简单值的标识符,在程序运行时,不会被修改。

2、常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符 串型。

3、常量的定义格式:

# const标识符[类型]=值
# 可以省略类型说明符[type],因为编译器可以根据变量的值来自动推断其 类型。
# 显式类型定义:const B string = "Steven"
# 隐式类型定义:const C = "Steven"

4、多个相同类型的声明可以简写为:

# const WIDTH , HEIGHT = value1, value2

5、常量定义后未被使用,不会在编译时出错。

Example

package main

import "fmt"

func consts_demo() {
	const (
		C5 int = iota
		C6
		C7
	)

	const (
		C1 string = "qinhua"
		C2 string = "beida"
	)

	const C3,C4 = "wuda","huake"
	const NAME string = "zhou"
	const AGE int = 18
	fmt.Println(NAME,AGE,C1,C2,C3,C4,C5,C6,C7)
}

func main() {
	consts_demo()
}

// 输出信息
// zhou 18 qinhua beida wuda huake 0 1 2
枚举(常量组)

例如以下格式

package main

import "fmt"

const(
	Unknown = 0
	Famale = 1
	Male = 2
	// 数字0,1,2分别代表未知性别,女性和男性
)

const (
	// 常量组中如果不指定类型和初始值,则与上一行非空常量值相同
	a = 10
	b
	c
)

func main() {
	fmt.Println(Unknown,Famale,Male,a,b,c)
}

// 输出结果
0 1 2 10 10 10
iota

1 . iota,特殊常量值,是一个系统定义的可以被编译器修改的常量值。iota只能用 在常量赋值中。 2 . 在每一个const关键字出现时,被重置为0,然后每出现一个常量,iota所代表的数值会自动增加1, iota可以理解为常量的计数器,不论该常量的值是什么,只要有一个常量,那么iota就加1. 3 . iota可以被用作枚举值

Example1

package main

import "fmt"

const (
	a = iota
	b = iota
	c = iota
	// 第一个iota等于0,每当iota在新的一行被使用时,他的值都会自动加1
)

const (
	// 我们可以简写为以下格式
	d = iota
	e
	f
)

func main() {
	fmt.Println(a, b, c)
	fmt.Println(d,e,f)
}

Example2

package main

import "fmt"

const (
	i = 1<<iota
	j = 3<<iota
	k
	l
)

const (
	ai ='—'
	bi
	ci = iota
	di
)


func main() {
	fmt.Println("i=",i)
	fmt.Println("j=",j)
	fmt.Println("k=",k)
	fmt.Println("l=",l)
	fmt.Println(ai,bi,ci,di)
}

作用域

作用域

作用域是指变量可以使用范围,Go语言使用大括号显示的标识作用域范围,大括号内包含一连串的语句,叫做语句块,语句块可以嵌套,语句块内定义的变量不能在语句块外使用;

package main

import "fmt"

// 作用域内定义变量只能被声明一次必须使用,否则编译错误,
// 在不同作用域可定义相同的变量,此时局部变量将覆盖全局
func main() {
	// 作用域: 定义标识符可以使用的范围
	// 在Go中用{}来定义作用域的范围
	// 使用原则: 子语句块可以使用父语句块中的标识符,父不能用子的
	outer := 1
	{
		innner :=2
		fmt.Println(outer)
		fmt.Println(innner)
		outer := 21
		{
			inner2 :=3
			fmt.Println(outer,innner,inner2)
		}
	}
	fmt.Println("hello")
}

// 输出
// 1
// 2
// 21 2 3
// hello

打印

fmt.Print(name) // 只会打印变量不会换行
fmt.Println(name) // 打印变量加换行
fmt.Printf("%T,%T",name,name) //占位符

Go编译成不同平台二进制包

CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go