Go语言基础教程:变量、基本数据类型、输出、注释、运算符、if-else条件判断、函数

道阻且长,行则将至,行而不辍,未来可期🌟。人生是一条且漫长且充满荆棘的道路,一路上充斥着各种欲望与诱惑,不断学习,不断修炼,不悔昨日,不畏将来!
GO语言也被称为21世纪的C语言,在开发与性能效率上都占据优势(Python+C)🚀。让我们一起来了解这门语言的魅力吧,希望这篇能够带给你们或多或少的帮助!!

在这里插入图片描述

一、变量的使用

变量指定了某存储单元(Memory Location)的名称,该存储单元会存储特定类型的值,变量的关键在于变,这也意味着它代表并不是一个固定不变的数据。在 Go 中,有多种语法用于声明变量。


1.1 定义变量


在所有语言内,遍历必须是先声明,后使用的。而部分语言在声明变量的时候需要指定变量类型,这是区分静态与动态语言的关键。

1、变量声明方式:指定变量类型

变量定义语法:

var 变量名 变量类型 = 变量值

变量声明方式:

package main

import "fmt"

func main() {
	var age int // 声明并未赋值,int类型变量值默认为0
	
	// 注意:Go语言中变量定义了必须使用,否则报错
	
	fmt.Println(age)

	age = 18 // 赋值
	fmt.Println(age)

	var width int = 180 // 变量声明并赋值
	fmt.Println(width)

	// var width int 已经声明的变量不能重复声明,否则报错
}

以上代码输出结果:

0
18
180

2、变量声明方式:类型推断 (Type Inference)


如果变量有初始值,那么 Go 能够自动推断具有初始值的变量的类型。因此,如果变量有初始值,就可以在变量声明中省略 type。

如果变量声明的语法是 var name = initialvalue,Go 能够根据初始值自动推断变量的类型。

在下面的例子中,我们省略了变量 age 的 int 类型,Go 依然推断出了它是 int 类型。

package main

import "fmt"

func main() {
	var age = 18
	fmt.Println(age)
}

3、声明多个变量


Go 能够通过一条语句声明多个变量。

声明多个变量的语法是 var name1, name2 = initialvalue1, initialvalue2

package main

import "fmt"

func main() {
	var age1 ,age2 = 18,28

	fmt.Println(age1,age2)

	// 声明多个变量但是不赋值
	var a1,b1 int // 这就表示a/b变量都是int类型

	fmt.Println(a1,b1)

	// 声明多个变量,且类型不同
	var (
		a2 int
		b2 string
	)
	fmt.Println(a2,b2)
	
	// 声明多个变量,且赋值
	var (
		name = "jack"
		height = 180
	)
	fmt.Println(name,height)
}

至于string类型,我们将会在下面说明

上面程序运行结果如下:

18 28
0 0
0 
jack 180

4、简短声明


Go 也支持一种声明变量的简洁形式,称为简短声明(Short Hand Declaration),该声明使用了 := 操作符。

声明变量的简短语法是 name := initialvalue,省略var关键字,类型由Go自行推断出来

package main

import "fmt"

func main() {
	age := 10
	
	fmt.Println(age)
	
	// 多个变量使用简短声明
	a,b := 1,2
	fmt.Println(a,b)
}

简短声明要求 := 操作符左边的所有变量都有初始值。下面程序将会抛出错误 cannot assign 1 values to 2 variables,这是因为 age 没有被赋值。

package main

import "fmt"

func main() {  
    name, age := "naveen" // error

    fmt.Println("my name is", name, "age is", age)
}

简短声明的语法要求 := 操作符的左边至少有一个变量是尚未声明的。考虑下面的程序:

package main

import "fmt"

func main() {
    a, b := 20, 30 // 声明变量a和b
    fmt.Println("a is", a, "b is", b)
    
    b, c := 40, 50 // b已经声明,但c尚未声明
    fmt.Println("b is", b, "c is", c)
    
    b, c = 80, 90 // 给已经声明的变量b和c赋新值,注意:没有冒号!!!
    fmt.Println("changed b is", b, "c is", c)
}

在上面程序中,由于 b 已经被声明,而 c 尚未声明,因此运行成功并且输出:

a is 20 b is 30
b is 40 c is 50
changed b is 80 c is 90

但是如果我们运行下面的程序:

package main

import "fmt"

func main() {  
    a, b := 20, 30 // 声明a和b

    a, b := 40, 50 // 错误,:=左边没有尚未声明的变量
}

上面运行后会抛出 no new variables on left side of := 的错误,这是因为 a 和 b 的变量已经声明过了,:= 的左边并没有尚未声明的变量。


由于 Go 是强类型(Strongly Typed)语言,因此不允许某一类型的变量赋值为其它类型的值。下面的程序会抛出错误 cannot use "naveen" (type string) as type int in assignment,这是因为 age 本来声明为 int 类型,而我们却尝试给它赋字符串类型的值。

package main

func main() {  
    age := 29      // age是int类型
    age = "naveen" // 错误,尝试赋值一个字符串给int类型变量
}

1.2 常量

常量:顾名思义就是经常使用的变量。

在Go语言内,定义常量一经赋值就是不可改变的,而在其它语言内,如Python:通常是以大写字母来表示常量,但是由于Python比较自由,所以只是在约定方面建议不更改,但实际是可以改变的。

Go语言常量定义:

package main

func main() {
	// 定义常量(在函数内定义只能在当前函数作用域使用)
	const ip = "127.0.0.1"
	
	// ip = "0.0.0." 不可以改变常量的值,会产生报错
}

1.3 变量的赋值与内存相关

关于这部分内容,深入理解对我们理解程序的底层会更有帮助。

这一部分内容我们可以了解到,变量在内存中的结构是怎样的。因为我们的数据都是先存储到内存里面的,最后才到硬盘里面。程序的运行也是在内存中的,程序中产生的数据都是在内存中有存储的,并且每个都会分配一个内存地址,实际内存地址可能是这种格式:0x7ffcad3b8f3c,如果我们想拿到这个数据的话不可能把这一堆内存地址记录下来吧,那么变量则是与这个内存地址做绑定的。

在这里插入图片描述
那么我们调用这个a变量等同于通过0x00001这个内存地址获取它对应的数据。

变量名的本质是什么?

这是很多学习编程的人容易忽略的问题,他们只关注了变量名代指内存中某一块数据,却忽略了变量名是否也在内存中存储?那么它的地址又是什么?

实际上变量名就是为了让我们编程时更加方便,对人友好,可计算机可不认识什么变量 a,它只知道地址和指令。所以实际在编译后,变量名都被取而代之为其对应的内存地址

你可以认为编译器自动维护了一个映射表,变量名 -> 内存地址,将变量名转换为内存地址,然后再根据这个地址去读写数据。

也就是有这样一个映射表存在,将变量名自动转化为地址:

a:0x000001
b:0x000002
c:0x000003

当我们了解以上内容的时候,对于变量的理解已经到位了。那么下面我们来看看Go语言中变量的赋值,以及内存的一些变化。

package main

import "fmt"

func main() {
	a := 10
	fmt.Println(a) // a这个变量关联了一个内存地址,调用这个a变量等于通过这个地址去内存中取值
	fmt.Println(&a) // 在变量前面增加&符号,查看其关联的内存地址
}

打印结果:

10
0xc00001c068

如果我们修改数据的话,内存地址是不变的,根据这个地址去覆盖其原有的值

package main

import "fmt"

func main() {
	name := "Jack"
	fmt.Println(name, &name)

	name = "Tom" // 修改name变量的值(根据内存地址,去到所在内存位置覆盖掉其原有的值)
	fmt.Println(name, &name)
}

输出结果:

Jack 0xc00008e1e0
Tom 0xc00008e1e0

当一个变量指向另外一个变量时,它们的内存地址并不相同,只是将数据复制到了一块新的内存空间进行存储

package main

import "fmt"

func main() {
	name := "Jack"
	fmt.Println(name, &name)

	nickname := name
	fmt.Println(nickname, &nickname)
}

输出结果:

Jack 0xc000010200
Jack 0xc000010220 // 不完全相同

这与Python是有差异的,在Python中,如果一个变量指向另外一个变量,等同于复用了它的内存地址。

name = "jack"
nick_name = name

print(id(name))
print(id(nick_name))

输出结果:

140417703147248
140417703147248

关于Go语言的变量赋值,注意事项:

使用:int、string、bool这三种数据类型时,会将数据拷贝一份到到新的内存地址存储。也就是我们上面第三个实例的情况:name = “jack”;nickname = name

后续还会学习到其它数据类型,我们到时再来分析它们之间的关系;这些数据类型又分为两类:

  • 值类型:上面三种数据类型属于值类型,每个数据都是独立的,不会被复用
  • 引用类型:后续会了解到

二、变量基本类型


下面是 Go 支持的基本类型:

数字类型:

  • int8, int16, int32, int64, int
  • uint8, uint16, uint32, uint64, uint
  • float32, float64
  • complex64, complex128
  • byte
  • rune

string(字符串类型):Go 的字符串是由单个字节连接起来的。Go 语言的字符串的字节使用 UTF-8 编码标识 Unicode 文本。

bool(布尔类型):布尔型的值只可以是常量 true 或者 false。

派生类型:我们留到后续讲解


2.1 有符号整型

有符号数可以用来区分正负。而无符号数只有正数,没有负数。


int8:表示 8 位有符号整型
大小:8 位
范围:-128~127

iint16:表示 16 位有符号整型
大小:16 位
范围:-32768~32767

iint32:表示 32 位有符号整型
大小:32 位
范围:-2147483648~2147483647

iint64:表示 64 位有符号整型
大小:64 位
范围:-9223372036854775808~9223372036854775807

int:根据不同的底层平台(Underlying Platform),表示 32 或 64 位整型。除非对整型的大小有特定的需求,否则你通常应该使用 int 表示整型。

大小:在 32 位系统下是 32 位,而在 64 位系统下是 64 位。

范围:在 32 位系统下是 -2147483648~2147483647,而在 64 位系统是 -9223372036854775808~9223372036854775807

package main

import "fmt"

func main() {  
    var a int = 89
    b := 95
    fmt.Println("value of a is", a, "and b is", b)
    
	fmt.Printf("%T",b)
}

上面程序会输出如下结果:

value of a is 89 and b is 95
int

在上述程序中,a 是 int 类型,而 b 的类型通过赋值(95)推断得出。上面我们提到,int 类型的大小在 32 位系统下是 32 位,而在 64 位系统下是 64 位。接下来我们会证实这种说法。

在 Printf 方法中,使用 %T 格式说明符(Format Specifier),可以打印出变量的类型。Go 的 unsafe 包提供了一个 Sizeof 函数,该函数接收变量并返回它的字节大小。unsafe 包应该小心使用,因为使用 unsafe 包可能会带来可移植性问题。不过出于本教程的目的,我们是可以使用的。

下面程序会输出变量 a 和 b 的类型和大小。格式说明符 %T 用于打印类型,而 %d 用于打印字节大小。

package main

import (
	"fmt"
	"unsafe"
)

func main() 
	var a int = 89
	b := 95
	fmt.Println("value of a is", a, "and b is", b)
	fmt.Printf("type of a is %T, size of a is %d", a, unsafe.Sizeof(a)) // a 的类型和大小
	fmt.Printf("\ntype of b is %T, size of b is %d", b, unsafe.Sizeof(b)) // b 的类型和大小
}

以上程序会输出以下结果:

value of a is 89 and b is 95
type of a is int, size of a is 8
type of b is int, size of b is 8

从上面的输出,我们可以推断出 a 和 b 为 int 类型,且大小都是 32 位(4 字节)。如果你在 64 位系统上运行上面的代码,会有不同的输出。在 64 位系统下,a 和 b 会占用 64 位(8 字节)的大小。

不同整型之间是不能直接使用“算术运算符”的,例如:

package main

func main() {
	var i1 int8 = 10
	var i2 int16 = 10

	v3 := int16(16) + i2 // 只有两个数据类型相同才能进行相加
}

上面的操作也就是整数类型间的转换,例如:将int8转换成int16,但是需要注意如下问题:

  • 低位可以往位置转换(int8 -> int16)
  • 高位往低位转可能会出现问题(int16 -> int8)

在Golang里面高位往地位转的话,当数据值大于低位的范围以后,则会从低位的起始开始往前递增

package main

import "fmt"

func main() {
	// int8范围:-128 - 127
	var i2 int16 = 128
	
	fmt.Println(int8(i2))
	// 打印:-128,128大于int8的范围后,则回到最起始
	
	var i3 int16 = 129
	
	fmt.Println(int8(i2))
	// 打印:-127,129大于int8的范围后,则回到最起始+1
	
	// 如果是130,转换为int8以后则是-126
}

2.2 无符号整型


uint8:表示 8 位无符号整型
大小:8 位
范围:0~255

uint16:表示 16 位无符号整型
大小:16 位
范围:0~65535

uint32:表示 32 位无符号整型
大小:32 位
范围:0~4294967295

uint64:表示 64 位无符号整型
大小:64 位
范围:0~18446744073709551615

uint:根据不同的底层平台,表示 32 或 64 位无符号整型。

大小:在 32 位系统下是 32 位,而在 64 位系统下是 64 位。

范围:在 32 位系统下是 0~4294967295,而在 64 位系统是 0~18446744073709551615。


2.3 string类型


在 Golang 中,字符串是字节的集合。如果你现在还不理解这个定义,也没有关系。我们可以暂且认为一个字符串就是由很多字符组成的。我们后面会在一个教程中深入学习字符串。 下面编写一个使用字符串的程序。

package main

import (  
    "fmt"
)

func main() {  
    first := "Naveen"
    last := "Ramanathan"
    name := first +" "+ last
    fmt.Println("My name is",name)
}

上面程序中,first 赋值为字符串 “Naveen”,last 赋值为字符串 “Ramanathan”。+ 操作符可以用于拼接字符串。我们拼接了 first、空格和 last,并将其赋值给 name。

上述程序将打印输出:

My name is Naveen Ramanathan

还有许多应用于字符串上面的操作,我们将会在一个单独的教程里看见它们。


2.4 bool类型


bool 类型表示一个布尔值,值为 true 或者 false。表示正确、错误的

package main

import "fmt"

func main() {
	a := 10
	b := 20
	
	fmt.Println(a > b)
	fmt.Println(a < b)
}

在上面的程序中,a 赋值为 10,b 赋值为 20。

我们通过关系运算符进行比较,a 大于 b结果是不正确的,所以为falsea 小于 b结果是正确的,所以为true

false
true

或者我们直接定义变量的值为:true或者false,通常这类情况都是在某个结构外面定义一个标识,这种情况在实际项目中使用较多,提高代码的可读性(搭配后面学习到的条件判断使用)。

package main

import "fmt"

func main() {
	auth := false
}

三、输出


输出指的是将我们需要展示出来的内容打印到控制台。而输出也经常作用于:

  • 程序运行的结果是否符合我们的预期
  • 清除程序运行的进度
  • 代码调试(也可以用于Debug代替)

在之前的程序里面,我们已经实际接触到了Go语言内输出相关的代码

import fmt

func main(){
	fmt.Println("Hello Golang")
}

通过上面代码里面的fmt包可以实现输出功能,将Hello Golang打印在控制台上

在这里插入图片描述


3.1 常用打印功能

这里我们主要使用比较常用的打印功能,主要是换行与不换行的区别。

package main

import "fmt"

func main() {
	fmt.Println("能够换行的输出1")
	fmt.Println("能够换行的输出2") // 下一次打印的内容将会在这一行的下面
	
	fmt.Print("不能够换行1")
	fmt.Print("不能够换行2") // 下一次打印的内容将会和这一个行内容并排
	
	fmt.Println("Hello", "Golang") // 在同一个输出内,我们可以通过 , 来进行多个内容拼接。这个行为可以搭配后续学习到的变量一起使用
	
	fmt.Print("Hello\nWorld") // 我们也可以通过转义符内的:\n换行符来实现指定位置换行的一个效果
}

上面效果打印:

在这里插入图片描述


3.2 格式化输出

格式化输出的目的主要是在一堆文本中引用占位符,从而省去通过,分隔的操作。代码中常用的占位符如下:

  • 字符串占位符:%s
  • 整数数字占位符:%d
  • 百分号占位符:%%(比较特殊,因为在格式化输出方法内,不能直接使用%作为字符串展示,所以我们要想打印百分号,需得使用%%实现)
  • 布尔占位符:%t
  • 字符串占位符:%s

由于使用了占位符,那么我们需要使用,在后面按照顺序将内容填充到使用占位符的位置

Go语言通过:fmt.Printf方法来实现格式化输出

实例:

package main

import "fmt"

func main() {
	// 而在下面实例中,我们需要注意的是,%%是不需要数据填充的,我们仅作为能够打印出%的用途
	fmt.Printf("数字占位符%%d:%d \n", 123)
	fmt.Printf("浮点类型占位符%%f:%f \n", 3.1415926)
	fmt.Printf("布尔类型占位符%%t:%t \n", true)
	fmt.Printf("字符串占位符%%s:%s \n", "Hello Golang")

	fmt.Printf("我的名字是:%s;今年:%d岁;薪资:%0.1fw,相比去年上涨了80%%", "Jack", 20, 3.5)
	// 上面在完整内容的结尾处,我们通过逗号,按顺序对占位符进行对应数据类型填充值
	
	// 我们也可以观察到,浮点类型我们使用的%0.1f,这表示只保留小数点后1位,默认是6位
	// 对%s也可以是:%0.1s,这表示只保留填充值的第一个字符,Jack -> J
}

上面实例结果:

在这里插入图片描述


3.3 内置输出方法与fmt的区别

Go语言内存在:printprintln等内置打印方法。但是它们在我们实际代码中并不常见,这是因为它的作用是标准错误输出。

package main

func main() {
	println("output1")
	print("output2")
}

关于内置输出与fmt包的常用打印区别:

  • 内置的Println()/Print():都是标准错误输出,而fmt.Println()函数是标准输出
  • 内置的Println()/Print():输出结果可能与预期结果顺序不一致,而fmt.Println()函数输出结果与预期结果完全一致。(这个特性是由标准错误输出和标准输出决定)
  • 内置Println()/Print():不能接受数组和结构体类型的数据
  • 内置Println()/Print():对于组合类型的数据输出的结果是参数值的地址,而fmt.Println()函数输出的是字面量

重点说一下标准错误输出和标准输出的区别;

  • 标准错误输出:在Linux中是stderr,在Java中是System.err,在Golang中是Print()/Println()
  • 标准输出:在Linux中是stdout,在Java中是System.out,在Golang中是fmt.Println()

其实从字面意思上就能看出,一个是专为输出错误用的,一个是通常输出用的,都是输出流。使用内置函数 println 和 print 的好处显而易见:不用引用任何包,并且代码编译更简单;但是也有一定的风险,因为 Go 语言官方文档中声明的这几个函数一般用于内部测试,不保证随着 Go 语言的版本升级会始终提供。一旦不提供,将意味着大量的代码修改工作,因此建议大家使用 fmt 包中的 Println 或 Print 函数。


四、注释


注释的作用也表示:注解说明,通过//符号后面加上注解说明的内容来实现,在上面的部分实例中我们已经使用过,通常它的主要作用就是对代码进行解释用途,或者将某些暂不使用的代码进行注释,因为编译器是不会将注释过的内容进行编译的

注释也分为单行注释与多行注释

  1. 单行注释:主要多用于对某段代码进行解释
  2. 多行注释:对功能函数、文件、模块等大范围的内容进行一个作用说明

多行注释语法:

/*
	该文件主要对用户数据进行处理
*/

五、运算符


运算符用于在程序运行时执行数学或逻辑运算。

这里介绍几个我们在Go语言内最常用的几个内置运算符:

  • 算术运算符
  • 关系运算符
  • 逻辑运算符
  • 赋值运算符

算术运算符

下表列出了所有Go语言的算术运算符。假定 A 值为 10,B 值为 20。
在这里插入图片描述
关系运算符

下表列出了所有Go语言的关系运算符。假定 A 值为 10,B 值为 20。
在这里插入图片描述

逻辑运算符

下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。
运算符关系运算符与逻辑运算符在条件判断中最为常用


赋值运算符

下表列出了所有Go语言的赋值运算符。
在这里插入图片描述


六、条件语句


6.1 基本使用

if 是条件语句。if 语句的语法是

if condition {  
	// 触发条件执行的代码块
}

条件语句的作用是,当满足什么样的条件,执行什么样的代码。这就和人思考一样,满足了有钱的条件下,才能去买房、买车、旅游…

如果 conditiontrue,则执行 {} 之间的代码。

不同于其他语言,例如 C 语言,Go 语言里的 { } 是必要的,即使在 { } 之间只有一条语句。

if 语句还有可选的 else ifelse 部分。

if condition {  
	// 代码块
} else if condition {
	// Code
} else {
	// 当上面的if else if 不成立的时候才执行的Code
}

if-else 语句之间可以有任意数量的 else if。条件判断顺序是从上到下。如果 ifelse if 条件判断的结果为真,则执行相应的代码块。 如果没有条件为真,则 else 代码块被执行。

让我们编写一个简单的程序来检测一个数字是奇数还是偶数。

package main

import (  
    "fmt"
)

func main() {  
    num := 10
    if num % 2 == 0 { //checks if number is even
        fmt.Println("the number is even") 
    }  else {
        fmt.Println("the number is odd")
    }
}

if num%2 == 0 语句检测 num 取 2 的余数是否为零。 如果是为零则打印输出 “the number is even”,如果不为零则打印输出 “the number is odd”。在上面的这个程序中,打印输出的是 the number is even

if 还有另外一种形式,它包含一个 statement 可选语句部分,该组件在条件判断之前运行。它的语法是

if statement; condition {  
	// Code
}

让我们重写程序,使用上面的语法来查找数字是偶数还是奇数。

package main

import (  
    "fmt"
)

func main() {  
	// 使用if的同时声明了num变量
	
    if num := 10; num % 2 == 0 { // checks if number is even
        fmt.Println(num,"is even") 
    }  else {
        fmt.Println(num,"is odd")
    }
}

在上面的程序中,numif 语句中进行初始化,num 只能从 ifelse 中访问。也就是说 num 的范围仅限于 if else 代码块。如果我们试图从其他外部的 if 或者 else 访问 num,编译器会不通过。

让我们再写一个使用 else if 的程序。

package main

import (  
    "fmt"
)

func main() {  
    num := 99
    if num <= 50 {
        fmt.Println("number is less than or equal to 50")
    } else if num >= 51 && num <= 100 {
        fmt.Println("number is between 51 and 100")
    } else {
        fmt.Println("number is greater than 100")
    }

}

在上面的程序中,如果 else if num >= 51 && num <= 100 为真,程序将输出 number is between 51 and 100


需要注意的地方:

else 语句应该在 if 语句的大括号 } 之后的同一行中。如果不是,编译器会不通过。

让我们通过以下程序来理解它。

package main

import (  
    "fmt"
)

func main() {  
    num := 10
    if num % 2 == 0 { //checks if number is even
        fmt.Println("the number is even") 
    }  
    else {
        fmt.Println("the number is odd")
    }
}

在上面的程序中,else 语句不是从 if 语句结束后的 } 同一行开始。而是从下一行开始。这是不允许的。如果运行这个程序,编译器会输出错误,

main.go:12:5: syntax error: unexpected else, expecting }

出错的原因是 Go 语言的分号是自动插入。

在 Go 语言规则中,它指定在 } 之后插入一个分号,如果这是该行的最终标记。因此,在if语句后面的 } 会自动插入一个分号。

实际上我们的程序变成了

if num%2 == 0 {  
      fmt.Println("the number is even") 
};  //semicolon inserted by Go
else {  
      fmt.Println("the number is odd")
}

分号插入之后。从上面代码片段可以看出第三行插入了分号。

由于 if{…} else {…} 是一个单独的语句,它的中间不应该出现分号。因此,需要将 else 语句放置在 } 之后处于同一行中。

我已经重写了程序,将 else 语句移动到 if 语句结束后 } 的后面,以防止分号的自动插入。

package main

import (  
    "fmt"
)

func main() {  
    num := 10
    if num % 2 == 0 { //checks if number is even
        fmt.Println("the number is even") 
    } else {
        fmt.Println("the number is odd")
    }
}

此时的if编写才符合Go语言的规范。


6.2 条件嵌套

任何条件语句之内都可以继续写条件语句。无限套娃!当然这样的写法也只是为了满足我们的功能需求,以及增加代码的可读性。

package main

import "fmt"

func main() {
	money := 1500000

	if money > 0 {
		if money > 7000000 {
			fmt.Println("700w买房、买车")
		} else if money > 5000000 {
			fmt.Println("500w买房")
		} else if money > 2000000 {
			fmt.Println("200w买车")
		} else {
			fmt.Println("小于200w,距离梦想太遥远,继续赚钱")
		}
	} else {
		fmt.Println("口袋空空如也,赶紧去打工!")
	}
}

上面的条件说明了,余额大于0才可以考虑什么样的事情。当然我们也可以拆分成if else if...的形式,不过对于代码的可读性就没有那么好了。


七、函数(Function)


函数是什么?

函数是一块执行特定任务的代码。一个函数是在输入源基础上,通过执行一系列的算法,生成预期的输出。


7.1 函数的声明

在 Go 语言中,函数声明通用语法如下:

func functionname(parametername type) returntype {  
    // 函数体(具体实现的功能)
}

函数的声明以关键词 func 开始,后面紧跟自定义的函数名 functionname (函数名)。函数的参数列表定义在 () 之间,返回值的类型则定义在之后的 returntype (返回值类型)处。声明一个参数的语法采用 参数名 参数类型 的方式,任意多个参数采用类似 (parameter1 type, parameter2 type) 即(参数1 参数1的类型,参数2 参数2的类型)的形式指定。之后包含在 {} 之间的代码,就是函数体。

函数中的参数列表和返回值并非是必须的,所以下面这个函数的声明也是有效的

func functionname() {  
    // 译注: 表示这个函数不需要输入参数,且没有返回值
}

实例

我们以写一个计算商品价格的函数为例,输入参数是单件商品的价格和商品的个数,两者的乘积为商品总价,作为函数的输出值。

// 这两个参数我们可以称之为:位置参数;因为我们调用传递是根据位置来进行的
func calculateBill(price int, no int) int {  
    var totalPrice = price * no // 商品总价 = 商品单价 * 数量
    return totalPrice // 返回总价
}


// 如:calculateBill(10,20) 10这个数据根据位置传递给了price参数,20传递给了no参数

上述函数有两个整型的输入 priceno,返回值 totalPricepriceno 的乘积,也是整数类型。

如果有连续若干个参数,它们的类型一致,那么我们无须一一罗列,只需在最后一个参数后添加该类型。 例如,price int,no int 可以简写为 price, no int,所以示例函数也可写成

func calculateBill(price, no int) int {  
    var totalPrice = price * no
    return totalPrice
}

现在我们已经定义了一个函数,我们要在代码中尝试着调用它。调用函数的语法为 functionname(parameters)。调用示例函数的方法如下:

package main

import (  
    "fmt"
)

func calculateBill(price, no int) int {  
    var totalPrice = price * no
    return totalPrice
}
func main() {  
    price, no := 90, 6 // 定义 price 和 no,默认类型为 int
    totalPrice := calculateBill(price, no)
    fmt.Println("Total price is", totalPrice) // 打印到控制台上
}

该程序在控制台上打印的结果为

Total price is 540

7.2 多返回值


Go 语言支持一个函数可以有多个返回值。我们来写个以矩形的长和宽为输入参数,计算并返回矩形面积和周长的函数 rectProps。矩形的面积是长度和宽度的乘积, 周长是长度和宽度之和的两倍。即:

  • 面积 = 长 * 宽
  • 周长 = 2 * ( 长 + 宽 )
package main

import (  
    "fmt"
)

func rectProps(length, width float64)(float64, float64) {  
    var area = length * width
    var perimeter = (length + width) * 2
    return area, perimeter
}

func main() {  
    area, perimeter := rectProps(10.8, 5.6)
    fmt.Printf("Area %f Perimeter %f", area, perimeter) 
}

如果一个函数有多个返回值,那么这些返回值必须用 () 括起来。func rectProps(length, width float64)(float64, float64) 示例函数有两个 float64 类型的输入参数 lengthwidth,并返回两个 float64 类型的值。该程序在控制台上打印结果为:

Area 60.480000 Perimeter 32.800000

7.3 命名返回值


从函数中可以返回一个命名值。一旦命名了返回值,可以认为这些值在函数第一行就被声明为变量了。

上面的 rectProps 函数也可用这个方式写成:

func rectProps(length, width float64) (area, perimeter float64) {  
    area = length * width
    perimeter = (length + width) * 2
    return // 不需要明确指定返回值,默认返回 area, perimeter 的值
}

请注意:函数中的 return 语句没有显式返回任何值。由于 area 和 perimeter 在函数声明中指定为返回值, 因此当遇到 return 语句时, 它们将自动从函数返回。


7.4 空白符


_ 在 Go 中被用作空白符,可以用作表示任何类型的任何值。

我们继续以 rectProps 函数为例,该函数计算的是面积和周长。假使我们只需要计算面积,而并不关心周长的计算结果,该怎么调用这个函数呢?这时,空白符 _ 就上场了。

下面的程序我们只用到了函数 rectProps 的一个返回值 area

package main

import (  
    "fmt"
)

func rectProps(length, width float64) (float64, float64) {  
    var area = length * width
    var perimeter = (length + width) * 2
    return area, perimeter
}
func main() {  
    area, _ := rectProps(10.8, 5.6) // 该函数返回了两个值,但我们只需要第一个值
    fmt.Printf("Area %f ", area)
}

在程序的 area, _ := rectProps(10.8, 5.6) 这一行,我们看到空白符 _ 指的是:用来跳过不要的计算结果。


7.5 不定长参数


在此之前,我们如果需要向函数传递参数,只能一个值对应一个形参来接收,那如果我们的值非常多,那就需要对应更多的形参来接收,那么使用不定长形参可以接收任意值。

package main

import "fmt"

// 接收任意长度的int类型值
func testIndefiniteLength(numArr ...int) { 
	// 接收到的是一个数字类型的数组,关于数组我们留到后续讲解
	// numArr[0] 可以取出数组里面的第一个结果
	fmt.Println(numArr)
}


// 我们需要注意的是,不定长参数只能放到位置参数后面,否则程序产生错误
func test(name string, numArr ...int) { 
	fmt.Println(name)
	fmt.Println(numArr)
}

// func test(numArr ...int, name string)
// 这样的程序会产生错误,因为numArr是不定长,程序无法区分哪条数据是要给name参数的

func main() {
	testIndefiniteLength(1, 2, 3, 4, 5)
	test("jack", 1, 2, 3, 4, 5)
	// 第一个结果根据位置传递给了name参数,后面多余的结果都传递给了numArr
}

并且一个函数只能存在一个不定长参数,那么如果我们有更多数据类型的参数需要传递呢?这个我们后续可以通过数组来实现。


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值