4.4 变量

 

 

 

 

声明变量的一般形式是使用 var 关键字:var identifier type

需要注意的是,Go 和许多编程语言不同,它在声明变量时将变量的类型放在变量的名称之后。Go 为什么要选择这么做呢?

首先,它是为了避免像 C 语言中那样含糊不清的声明形式,例如:int* a, b;。在这个例子中,只有 a 是指针而 b 不是。如果你想要这两个变量都是指针,则需要将它们分开书写(你可以在 Go 语言的声明语法 页面找到有关于这个话题的更多讨论)。

而在 Go 中,则可以很轻松地将它们都声明为指针类型:

package main

import (
    "fmt"
)

/*
这种因式分解关键字的写法一般用于声明全局变量。等同于var aa int  、var bb bool
*/
var (
    aa  int
    bb  bool
    str string
)

func main() {
    var a, b *int

    //指针为nil 类型
    fmt.Println("指针a,b:", a, b)
}

      当一个变量被声明之后,系统自动赋予它该类型的零值:int 为 0,float 为 0.0,bool 为 false,string 为空字符串,指针为 nil。记住,所有的内存在 Go 中都是经过初始化的。

     变量的命名规则遵循骆驼命名法,即首个单词小写,每个新单词的首字母大写,例如:numShips 和 startDate

    但如果你的全局变量希望能够被外部包所使用,则需要将首个单词的首字母也大写。

    一个变量(常量、类型或函数)在程序中都有一定的作用范围,称之为作用域。如果一个变量在函数体外声明,则被认为是全局变量,可以在整个包甚至外部包(被导出后)使用,不管你声明在哪个源文件里或在哪个源文件里调用该变量。

   在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。而在if和for这些结构中声明的变量的作用域只在相应的代码块内。一般情况下,局部变量的作用域可以通过代码块(用大括号括起来的部分)判断。

   尽管变量的标识符必须是唯一的,但你可以在某个代码块的内层代码块中使用相同名称的变量,则此时外部的同名变量将会暂时隐藏(结束内部代码块的执行后隐藏的外部同名变量又会出现,而内部同名变量则被释放),你任何的操作都只会影响内部代码块的局部变量。

 

 

 值类型和引用类型

程序中所用到的内存在计算机中使用一堆箱子来表示(这也是人们在讲解它的时候的画法),这些箱子被称为 “ 字 ”。根据不同的处理器以及操作系统类型,所有的字都具有 32 位(4 字节)或 64 位(8 字节)的相同长度;所有的字都使用相关的内存地址来进行表示(以十六进制数表示)。

所有像 int、float、bool 和 string 这些基本类型都属于值类型,使用这些类型的变量直接指向存在内存中的值:

另外,像数组和结构这些复合类型也是值类型。

当使用等号 = 将一个变量的值赋值给另一个变量时,如:j = i,实际上是在内存中将 i 的值进行了拷贝:

你可以通过 &i 来获取变量 i 的内存地址,例如:0xf840000040(每次的地址都可能不一样)。值类型的变量的值存储在栈中。

内存地址会根据机器的不同而有所不同,甚至相同的程序在不同的机器上执行后也会有不同的内存地址。因为每台机器可能有不同的存储器布局,并且位置分配也可能不同。

更复杂的数据通常会需要使用多个字,这些数据一般使用引用类型保存。

一个引用类型的变量 r1 存储的是 r1 的值所在的内存地址(数字),或内存地址中第一个字所在的位置。

这个内存地址被称之为指针(你可以从上图中很清晰地看到),这个指针实际上也被存在另外的某一个字中。

同一个引用类型的指针指向的多个字可以是在连续的内存地址中(内存布局是连续的),这也是计算效率最高的一种存储形式;也可以将这些字分散存放在内存中,每个字都指示了下一个字所在的内存地址。

当使用赋值语句 r2 = r1 时,只有引用(地址)被复制。

如果 r1 的值被改变了,那么这个值的所有引用都会指向被修改后的内容,在这个例子中,r2 也会受到影响。

在 Go 语言中,指针属于引用类型,其它的引用类型还包括 slices,maps)和 channel。被引用的变量会存储在堆中,以便进行垃圾回收,且比栈拥有更大的内存空间。

 

package main

import (
    "fmt"
)

/*
这种因式分解关键字的写法一般用于声明全局变量。等同于var aa int  、var bb bool
*/
var (
    aa  int
    bb  bool
    str string
)

func main() {
    var a, b *int
    var str string = "aaa"
    cc := str
    str = "bbb"
    //指针为nil 类型
    fmt.Println("指针a,b:", a, b)

    //打印内存地址,&cc, &str  地址不一样说明cc := str这样赋值居然是深copy
    fmt.Println("内存地址:", &cc, &str)

    //str = "bbb" 打印出的结果是  aaa,bbb还是 说明是深copy
    fmt.Println("内存地址:", cc, str)
}

解释结果

 

 

 init 函数

      变量除了可以在全局声明中初始化,也可以在 init 函数中初始化。这是一类非常特殊的函数,它不能够被人为调用,而是在每个包完成初始化后自动执行,并且执行优先级比 main 函数高。

每一个源文件都可以包含且只包含一个 init 函数。初始化总是以单线程执行,并且按照包的依赖关系顺序执行。

一个可能的用途是在开始执行程序之前对数据进行检验或修复,以保证程序状态的正确性。

    这里来个小实验,坑了不少时间

   

注意目录结构,go 的单位是包,一般文件中的package定义 写的包名就是父目录的名字 这里就是test。

package test

import "math"
//只有开头为大写的变量或函数才能被其它文件来引用这个是规则
var Pi float64 = 100

func init() {
    Pi = 4 * math.Atan(1) // init() function computes Pi
}
init.go

test.go:

package main

import (
    "fmt"
  
    "./test"
)

var twoPi = 2 * test.Pi

func main() {
    fmt.Printf("2*Pi = %g\n", twoPi) // 2*Pi = 6.283185307179586
}

      然后执行既可,直接导入当前包"./test"

      init这个函数是自动执行的,如果写成别的并不会自动执行

 

posted @ 2016-11-22 09:36  MKY-门可意  阅读(285)  评论(0编辑  收藏  举报