8小时速成golang(四)语法新奇 4种变量定义方法 常量iota 函数 多返回值,值传递,引用传递

 

1、从一个main函数初见golang语法

  package main

  import "fmt"

  func main() {
          /* 简单的程序 万能的hello world */
          fmt.Println("Hello Go")
  }

终端运行

$ go run test1_hello.go 
Hello Go
$

go run 表示 直接编译go语言并执行应用程序,一步完成

 

你也可以先编译,然后再执行

 $go build test1_hello.go 
 $./test1_hello
 Hello Go
  • 第一行代码package main定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
  • 下一行import "fmt"告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
  • 下一行func main()是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。

 

注意:这里面go语言的语法,定义函数的时候,‘{’ 必须和函数名在同一行,不能另起一行。

 

  • 下一行 /.../ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 / 开头,并以 / 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
  • 下一行fmt.Println(...)可以将字符串输出到控制台,并在最后自动增加换行字符 \n。 使用 fmt.Print("hello, world\n") 可以得到相同的结果。 Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。

 

2、变量的声明

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

变量声明

第一种,指定变量类型,声明后若不赋值,使用默认值0

复制代码
var v_name v_type
v_name = value
package main
import "fmt"
func main() {
        var a int
        fmt.Printf(" = %d\n", a)
}
$go run test.go
a = 0
复制代码

 第二种,根据值自行判定变量类型。

var v_name = value

第三种,省略var, 注意 :=左侧的变量不应该是已经声明过的,否则会导致编译错误。

v_name := value
// 例如
var a int = 10
var b = 10
c : = 10

 

e.g.

复制代码
package main
import "fmt"
func main() {
        //第一种 使用默认值
        var a int
        fmt.Printf("a = %d\n", a)
       //第二种 省略后面的数据类型,自动匹配类型
        var c = 20
        fmt.Printf("c = %d\n", c)
       //第三种 省略var关键字
        d := 3.14
        fmt.Printf("d = %f\n", d)
       //第四种 既有数据类型 又有数值
        var b int = 10
        fmt.Printf("b = %d\n", b)
}
复制代码

 

多变量声明

复制代码
package main
import "fmt"

var x, y int
var ( //这种分解的写法,一般用于声明全局变量
        a int
        b bool
)


var c, d int = 1, 2
var e, f = 123, "liudanbing"


//这种不带声明格式的只能在函数体内声明
//g, h := 123, "需要在func函数体内实现"


func main() {
        g, h := 123, "需要在func函数体内实现"
        fmt.Println(x, y, a, b, c, d, e, f, g, h)


        //不能对g变量再次做初始化声明
        //g := 400


        _, value := 7, 5  //实际上7的赋值被废弃,变量 _  不具备读特性
        //fmt.Println(_) //_变量的是读不出来的
        fmt.Println(value) //5
}
复制代码

 

3、常量

常量是一个简单值的标识符,在程序运行时,不会被修改的量。
常量中的数据类型只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
常量的定义格式:
1
const identifier [type] = value

你可以省略类型说明符 [type],因为编译器可以根据变量的值来推断其类型。

  • 显式类型定义:
  • const b string = "abc"
  • 隐式类型定义:
    const b = "abc"

     

复制代码
package main


import "fmt"


func main() {
   const LENGTH int = 10
   const WIDTH int = 5   
   var area int
   const a, b, c = 1, false, "str" //多重赋值


   area = LENGTH * WIDTH
   fmt.Printf("面积为 : %d\n", area)
   println(a, b, c)   
}
复制代码

 

常量还可以用作枚举

const (
    Unknown = 0
    Female = 1
    Male = 2
)

常量可以用len(), cap(), unsafe.Sizeof()常量计算表达式的值。常量表达式中,函数必须是内置函数,否则编译不过:

复制代码
package main
import "unsafe"
const (
    a = "abc"
    b = len(a)
    c = unsafe.Sizeof(a)
)


func main(){
    println(a, b, c)
}
复制代码

输出结果为:abc, 3, 16

unsafe.Sizeof(a)输出的结果是16 。

字符串类型在 go 里是个结构, 包含指向底层数组的指针和长度,这两部分每部分都是 8 个字节,所以字符串类型大小为 16 个字节。

 

优雅的常量 iota

有些概念有名字,并且有时候我们关注这些名字,甚至(特别)是在我们代码中。

const (
    CCVisa            = "Visa"
    CCMasterCard      = "MasterCard"
    CCAmericanExpress = "American Express"
)

 

在其他时候,我们仅仅关注能把一个东西与其他的做区分。有些时候,有些时候一件事没有本质上的意义。比如,我们在一个数据库表中存储产品,我们可能不想以 string 存储他们的分类。我们不关注这个分类是怎样命名的,此外,该名字在市场上一直在变化。

我们仅仅关注它们是怎么彼此区分的。

const (
    CategoryBooks    = 0
    CategoryHealth   = 1
    CategoryClothing = 2
)

使用 0, 1, 和 2 代替,我们也可以选择 17, 43, 和 61。这些值是任意的。

在 Go,常量有许多微妙之处。当用好了,可以使得代码非常优雅且易维护的。

自增长

在 golang 中,一个方便的习惯就是使用  iota  标示符,它简化了常量用于增长数字的定义,给以上相同的值以准确的分类。

const (
    CategoryBooks = iota // 0
    CategoryHealth       // 1
    CategoryClothing     // 2
)

 

iota和表达式
iota可以做更多事情,而不仅仅是 increment。更精确地说,iota总是用于 increment,但是它可以用于表达式,在常量中的存储结果值

复制代码
type Allergen int

const (
    IgEggs Allergen = 1 << iota         // 1 << 0 which is 00000001
    IgChocolate                         // 1 << 1 which is 00000010
    IgNuts                              // 1 << 2 which is 00000100
    IgStrawberries                      // 1 << 3 which is 00001000
    IgShellfish                         // 1 << 4 which is 00010000
)
复制代码

这个工作是因为当你在一个const组中仅仅有一个标示符在一行的时候,它将使用增长的iota取得前面的表达式并且再运用它,。在 Go 语言的spec中, 这就是所谓的隐性重复最后一个非空的表达式列表.

如果你对鸡蛋,巧克力和海鲜过敏,把这些 bits 翻转到 “on” 的位置(从左到右映射 bits)。然后你将得到一个 bit 值00010011,它对应十进制的 19。

复制代码
fmt.Println(IgEggs | IgChocolate | IgShellfish)


// output:
// 19
type ByteSize float64


const (
    _           = iota                   // ignore first value by assigning to blank identifier
    KB ByteSize = 1 << (10 * iota)       // 1 << (10*1)
    MB                                   // 1 << (10*2)
    GB                                   // 1 << (10*3)
    TB                                   // 1 << (10*4)
    PB                                   // 1 << (10*5)
    EB                                   // 1 << (10*6)
    ZB                                   // 1 << (10*7)
    YB                                   // 1 << (10*8)
)
复制代码

当你在把两个常量定义在一行的时候会发生什么?

"<<", 右边的数指定移动的位数,KB ByteSize = 1 << (10 * iota),就是1左移10位,变成10000000000,1024

 

 

Banana 的值是什么? 2 还是 3? Durian 的值又是?

const (
    Apple, Banana = iota + 1, iota + 2
    Cherimoya, Durian
    Elderberry, Fig
)

结果,

在下一行增长,而不是立即取得它的引用。

// Apple: 1
// Banana: 2
// Cherimoya: 2
// Durian: 3
// Elderberry: 3
// Fig: 4

 

 

4、函数

函数返回多个值

Go 函数可以返回多个值,例如:
复制代码
package main
import "fmt"
func swap(x, y string) (string, string) {
   return y, x
}

func main() {
   a, b := swap("Mahesh", "Kumar")
   fmt.Println(a, b)
}
复制代码

以上实例执行结果为:

Kumar Mahesh

 

init函数与import

首先我们看一个例子:init函数:

init 函数可在package main中,可在其他package中,可在同一个package中出现多次

main函数

main 函数只能在package main中

执行顺序

golang里面有两个保留的函数:init函数(能够应用于所有的package)和 main函数(只能应用于package main)。

这两个函数在定义时  不能 有任何的 参数和返回值。

虽然一个package里面可以写任意多个init函数,但这无论是对于可读性还是以后的可维护性来说,

我们都强烈建议用户在一个package中每个文件只写一个init函数。

 

go程序会自动调用init()和main(),所以你不需要在任何地方调用这两个函数。

每个package中的init函数都是可选的,但package main就必须包含一个main函数。

程序的初始化和执行都起始于main包。

如果main包还导入了其它的包,那么就会在编译时将它们依次导入。有时一个包会被多个包同时导入,那么它只会被导入一次

(例如很多包可能都会用到fmt包,但它只会被导入一次,因为没有必要导入多次)。

当一个包被导入时,如果该包还导入了其它的包,那么会先将其它包导入进来,

然后再对这些包中的包级常量和变量进行初始化,接着执行init函数(如果有的话),依次类推。

等所有被导入的包都加载完毕了,就会开始对main包中的包级常量和变量进行初始化,然后执行main包中的init函数(如果存在的话),

最后执行main函数。下图详细地解释了整个执行过程:

----------------------------------------------------------------------------------------------------------------------------------------------------------

先输出其他包 - 包的常量变量初始化- init函数-      再main包- main包的常量变量初始化- main包的init函数 - main函数

 ----------------------------------------------------------------------------------------------------------------------------------------------------------

 

代码结构

Lib1.go

package InitLib1
import "fmt"
func init() {
    fmt.Println("lib1")
}

Lib2.go

package InitLib2
import "fmt"
func init() {
    fmt.Println("lib2")
}

main.go

复制代码
package main
import (
    "fmt"
    _ "GolangTraining/InitLib1"
    _ "GolangTraining/InitLib2"
)

func init() {
    fmt.Println("libmain init")
}

func main() {
    fmt.Println("libmian main")
}
复制代码

代码很简单,只是一些简单的输出

lib1
lib2
libmain init
libmian main

 

输出的顺序与我们上面图给出的顺序是一致的

那我们现在就改动一个地方,Lib1包导入Lib2,main包不管

复制代码
package InitLib1

import (
    "fmt"
    _ "GolangTraining/InitLib2"
)

func init() {
    fmt.Println("lib1")
}
复制代码

输出

lib2
lib1
libmain init
libmian main

main包以及Lib1包都导入了Lib2,但是只出现一次,并且最先输出,

说明如果一个包会被多个包同时导入,那么它只会被导入一次,而先输出lib2是因为main包中导入Lib1时,Lib1又导入了Lib2,

会首先初始化Lib2包的东西

函数参数

函数如果使用参数,该变量可称为函数的形参。

形参就像定义在函数体内的局部变量。

调用函数,可以通过两种方式来传递参数:

值传递

值传递是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

默认情况下,Go 语言使用的是值传递,即在调用过程中不会影响到实际参数。

 

以下定义了 swap() 函数:

/* 定义相互交换值的函数 */
func swap(x, y int) int {
   var temp int
   temp = x /* 保存 x 的值 */
   x = y    /* 将 y 值赋给 x */
   y = temp /* 将 temp 值赋给 y*/
   return temp;
}

接下来,让我们使用值传递来调用 swap() 函数:

复制代码
package main
import "fmt"
func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int = 200
   fmt.Printf("交换前 a 的值为 : %d\n", a )
   fmt.Printf("交换前 b 的值为 : %d\n", b )

   /* 通过调用函数来交换值 */
   swap(a, b)
   fmt.Printf("交换后 a 的值 : %d\n", a )
   fmt.Printf("交换后 b 的值 : %d\n", b )
}

/* 定义相互交换值的函数 */
func swap(x, y int) int {
   var temp int
   temp = x /* 保存 x 的值 */
   x = y    /* 将 y 值赋给 x */
   y = temp /* 将 temp 值赋给 y*/
   return temp;
}
复制代码

以下代码执行结果为:

交换前 a 的值为 : 100

交换前 b 的值为 : 200

交换后 a 的值 : 100

交换后 b 的值 : 200

 

引用传递(指针传递)


指针
Go 语言中指针是很容易学习的,Go 语言中使用指针可以更简单的执行一些任务。
接下来让我们来一步步学习 Go 语言指针。
我们都知道,变量是一种使用方便的占位符,用于引用计算机内存地址。
Go 语言的取地址符是 &,放到一个变量前使用就会返回相应变量的内存地址

 


以下实例演示了变量在内存中地址:

package main
import "fmt"
func main() {
   var a int = 10   
   fmt.Printf("变量的地址: %x\n", &a  )
}

以上运行结果为

变量的地址: c00000a0b8
Process finished with the exit code 0

现在我们已经了解了什么是内存地址和如何去访问它。接下来我们将具体介绍指针。

引用传递是指在调用函数时将实际参数的地址传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

引用传递指针参数传递到函数内,以下是交换函数 swap() 使用了引用传递:

/* 定义交换值函数*/
func swap(x *int, y *int) {
   var temp int
   temp = *x    /* 保持 x 地址上的值 */
   *x = *y      /* 将 y 值赋给 x */
   *y = temp    /* 将 temp 值赋给 y */
}

以下我们通过使用引用传递来调用 swap() 函数:

复制代码
package main
import "fmt"
func main() {
   /* 定义局部变量 */
   var a int = 100
   var b int= 200
   fmt.Printf("交换前,a 的值 : %d\n", a )
   fmt.Printf("交换前,b 的值 : %d\n", b )

   /* 调用 swap() 函数
   * &a 指向 a 指针,a 变量的地址
   * &b 指向 b 指针,b 变量的地址
   */
   swap(&a, &b)
   fmt.Printf("交换后,a 的值 : %d\n", a )
   fmt.Printf("交换后,b 的值 : %d\n", b )
}


func swap(x *int, y *int) {
   var temp int
   temp = *x    /* 保存 x 地址上的值 */
   *x = *y      /* 将 y 值赋给 x */
   *y = temp    /* 将 temp 值赋给 y */
}
复制代码

以上代码执行结果为:

交换前,a 的值 : 100

交换前,b 的值 : 200

交换后,a 的值 : 200

交换后,b 的值 : 100

 

posted @   陈晓猛  阅读(113)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2020-02-11 node解决通过npm无法安装forever的方法
2019-02-11 【find】Linux中find常见用法示例
点击右上角即可分享
微信分享提示

目录导航