• 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
    ", twoPi) // 2*Pi = 6.283185307179586
    }

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

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

  • 相关阅读:
    ceph 手工部署
    zstack 搭建部署
    ceph crush
    mini2440动态加载hello.ko模块
    j-flash配置用于烧录mini 2440 nor flash
    (转载)PPP协议规范
    at91sam9263: 定时器
    cyg_io_read返回值是0,因为读到的字节长度在参数中
    read函数
    Linux编译错误:‘cout’在此作用域中尚未声明
  • 原文地址:https://www.cnblogs.com/menkeyi/p/6087920.html
Copyright © 2020-2023  润新知