• [Go]程序实体


      Go语言中的程序实体包括变量、常量、函数、结构体、接口

    1、常见声明变量的方式

     1 package main
     2 
     3 import (
     4     "flag"
     5     "fmt"
     6 )
     7 
     8 func main()  {
     9 /*    第一种方式
    10     var name string
    11     flag.StringVar(&name, "name", "everyone", "The greeting object")*/
    12 
    13 /*    第二种方式,利用go语言自身的类型推断省去了string
    14     var name  = *flag.String("name", "everyone", "The greeting object")*/
    15 
    16 /*    第三种方式,短变量声明
    17     name := *flag.String("name", "everyone", "The greeting object")*/
    18 
    19     flag.Parse()
    20     fmt.Printf("Hello, %v!
    ", name)
    21 }

    2、go语言类型推断的好处

      可以明显提升程序的灵活性,似的代码重构变得更加容易,同时又不会给代码维护带来额外的负担,更不会损失程序的运行效率

           可以先看一段代码

    package main
    
    import (
        "flag"
        "fmt"
    )
    
    func getTheFlag() *string {
        return flag.String("name","everyone", "The greeting object")
    }
    func main()  {
        var name  = getTheFlag()
        flag.Parse()
        fmt.Printf("Hello, %v!
    ", *name)
    }

      我们可以用getTheFlag函数包装那个对flag.String函数的调用,并把其结果直接作为getTheFlag函数的结果,结果的类型是*string。这样一来,var name = 右边的表达式可以变为针对getTHRFlag函数的调用表达式,这实际上是对声明并赋值name变量的那行代码的重构(不改变某个程序与外界的任何交互方式和规则,而只改变其内部实现”的代码修改方式)。因此可以随意改变getTheFlag函数内部实现及其返回结果类型,而不用修改main函数中的任何代码,这是程序灵活性的质变。可以不显式地指定变量name的类型,使得它可以被赋予任何类型的值。也就是说,变量name的类型可以在其初始化时由其他程序动态决定。当改变getTheFlag函数的结果类型以后,Go语言的编译器会在你再次构建该程序时自动地更新变量name的类型

      Go语言是静态类型的,所以一旦初始化变量时确定了它的类型,这种类型的确定是在编译期完成的,因此不会对程序运行效率产生任何影响

    3、变量重声明

      通过使用短变量声明,可以对同一代码块((花括号括起来的区域)中的变量进行重声明(对已经声明过得变量再次声明)。它允许我们在使用短变量声明时不用理会被赋值的多个变量中是否包含旧变量

      变量重声明的前提条件如下:

      1、由于变量的类型在其初始化时就已经确定了,所以对它再次声明时赋予的类型必须与其原本的类型相同,否则会产生编译错误

      2、变量重声明只可能发生在某一段代码块中,如果与当前变量重名的是外层代码块中的变量,那么就是另外一种含义了

      3、变量的重声明只有在使用短变量声明时才会发生,否则也无法通过编译

      4、被声明并赋值的变量必须是多个,并且其中至少有一个是新的变量

    var err error
    n, err := io.WriteString(os.Stdout, "Hello,everyone!
    ")

    4、不同代码块的重名变量

      先看如下代码 

     1 package main
     2 
     3 import "fmt"
     4 
     5 var block = "package"
     6 func main() {
     7     block := "function"
     8     {
     9         block := "inner"
    10         fmt.Printf("The block is %s.
    ",block)
    11     }
    12     fmt.Printf("The block is %s.
    ",block)
    13 }
    14 
    15 //执行结果
    16 //The block is inner.
    17 //The block is function.

      这个命令源码文件中国有四个代码块,它们是:全域代码块、mai包代表的代码块、main函数代表的代码块,以及在main函数中的一个花括号包起来的代码块。在后三个代码块中分别声明了一个名为block的变量,并分别赋值

      从执行的结果看,对于不同的代码块来说,即使这些代码块有着直接嵌套关系,变量重名照样可以通过编译。

      那当变量重名时引用变量的时候到底用哪一个呢?

        1)代码引用变量的时候总会最优先查找当前代码块中的那个变量,注意这里的当前代码块仅仅是引用变量的代码所在的那个代码块,并不包含任何子代码块

        2)如果当前代码块中没有声明以此为名的变量,那么程序会沿着代码块的嵌套关系,从直接包含当前代码块的那个代码块开始,一层一层地查找

        3)一般情况下,程序会一直查到当前代码包代表的那层代码块,如果仍然找不到,那么Go语言的编译器就会报错了

      有个特殊情况,如果代码包导入语句写成import . xxx的形式,那么就会让“xxx”包中公开的程序实体视为当前代码包中的程序实体,在这个特殊情况下,程序在查找当前源码文件后悔先去查用这种方式导入的那些代码包

    5、不同代码块中的重名变量与变量重声明变量的区别

      1)变量重声明的变量一定是在某一代码块内的(不包含任何其他子代码块)

      2)变量重声明是对同一个变量的多次声明,这里变量只有一个。而重名变量中能够涉及的变量肯定有多个

      3)不论对变量重声明多少次,其类型必须始终保持一致,具体遵从它第一次被声明时给定的类型。而重名变量之间不存在类似的限制,它们的类型可以是任意的

      4)如果重名变量所在代码块之间存在直接或间接的嵌套关系,那么它们之间一定存在“屏蔽”的现象,但这种现象一定不会在变量重声明中出现

    6、如何判断一个变量的类型

     1 package main
     2 
     3 import "fmt"
     4 
     5 var container  = []string{"zero", "one", "two"}
     6 
     7 func main() {
     8     container := map[int]string{0: "zero", 1: "one", 2: "two"}
     9     fmt.Printf("The element is %q.
    ", container[1])
    10 }

      对以上代码,怎样在打印其中元素之前正确判断变量container的类型呢?

      答案是使用类型表达式,类型表达式的格式是x.(T),其中的x代表要被判断类型的那个值,这个值当下的类型必须是接口类型,不过具体是哪个接口类型是无所谓的

    value ok := interface{}(container).([]string)

      这里interface{}(container)用来把container变量的值转换为空接口值,空接口interface{}代表了不包含任何方法定义的空的接口类型

      .([]string)用于判断前者的类型是否为切片类型,圆括号中[]string是一个类型字面量,类型字面量就是用来表示数据类型本身的若干字符(String 是表示字符串类型的字面量)

    7、类型转换规则中需要注意的地方

      类型转换表达式的语法形式是T(x),其中x是一个变量,也可以是一个代表值的字面量,还可以是一个表达式(如果是表达式,那么表达式的结果只能有一个值)

      在这个上下文中,x可以被叫做源值,它的类型就是源类型,T代表的类型是目标类型

      1)对于整数类型值,整数常量之间的类型转换,原则上只要源值在目标类型的可表示范围内就是合法的

        var srcINT = int16(-255)
        dstInt := int8(srcINT)

      如上面的代码,把值类型从int16转换到int8

      2)虽然直接把整数值转换为一个string类型的值时可行的,但是被转换的整数值应该可以代表一个有效的Unicode代码点,否则转换的结果将会是仅由高亮的问号(�,专用于替换未知的,不被认可的以及无法展示的字符)组成的字符串值

      3)关于string类型与各种切片类型之间的互转

        a、一个值在从string类型向[]byte类型转换时代表着以UTF-8编码的字符会被拆分成零散、独立的字节

        b、 一个值从string类型向[]rune类型转换时代表着字符串会被拆分成一个个Unicode字符

    8、别名类型和潜在类型  

    type MyString = string

      MySting是string类型的别名类型,其与源类型除名字外其余一律相同

      byte是uint8的别名类型,rune是int32的别名类型

    type MyString2 string //注意这里没有等号

      这是MyString2和string就是两个不同的类型了,这里的MyString2是一个新类型,不同于其他任何类型,这种方式也叫做对类型的再定义。string类型可以被称为MyString2的潜在类型。

      潜在类型的含义是某个类型在本质上是哪个类型或者哪个类型的集合

      潜在类型相同的不同类型的值之间是可以进行类型转换的,因此MyString2类型的值与string类型的值可以使用类型转换表达式进行互转

      但对于集合类的类型[]MyString2与[]string来说这样做却是不合法的,因为[]MyString2与[]string的潜在类型不同,分布是MyString2和string

      另外,即使两个类型的潜在类型相同,它们的值之间也不能进行判等或比较,它们的变量之间也不能赋值

  • 相关阅读:
    linux下使用OpenCV的一些问题
    Google Colab调用cv2.imshow奔溃
    [LeetCode] Word Search
    信号
    信号
    [Leetcode] Subsets
    [LeetCode] Combinations
    [LeetCode] Minimum Window Substring
    [LeetCode] Set Matrix Zeroes
    sigaction 函数
  • 原文地址:https://www.cnblogs.com/yuxiaoba/p/9497828.html
Copyright © 2020-2023  润新知