提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
文章是Head First Go 第三章函数的内容
1. 使用Printf和Sprintf格式化输出
Go中的浮点数具有很高的精度,当要显示的时候,可能比较麻烦,如下:
package main
import(
"fmt"
)
func main() {
fmt.Println("About one-third: ", 1.0/3.0); //About one-third: 0.3333333333333333
}
为了处理这类问题,fmt 包提供了 Printf 函数,可以以特定的方式进行格式化,同时Sprintf 也一样,只不过这个是返回一个字符串。
package main
import(
"fmt"
)
func main() {
fmt.Printf("About one-third: %.2f\n", 1.0/3.0); //About one-third: 0.33
s := fmt.Sprintf("About one-third: %.2f", 1.0/3.0)
fmt.Printf(s) //About one-third: 0.33
}
2. 格式化动词
首先对于Printf的输出函数,具有两个特性
- 格式化动词(%.2f)
- 值得宽度(0.2)
不同的动词输出的格式也不一样,看下面的列表:
动词 | 输出 |
---|---|
%f | 浮点数 |
%d | 十进制数 |
%s | 字符串 |
%t | 布尔值 |
%v | 任何值(根据值选择适当的类型) |
%#v | 任何值,按照Go程序代码中显示的格式进行格式化 |
%T | 所提供值得类型(int, string等) |
%% | 一个完完全全的百分号 |
package main
import(
"fmt"
)
func main() {
fmt.Printf("A float: %f\n", 3.1415) //A float: 3.141500
fmt.Printf("A integer: %d\n", 15) //A integer: 15
fmt.Printf("A string: %s\n", "hello") //A string: hello
fmt.Printf("A boolean: %t\n", false) //A boolean: false
fmt.Printf("Values: %v %v %v\n", 1.2, "\t", true) //Values: 1.2 true
fmt.Printf("Values: %#v %#v %#v\n", 1.2, "\t", true) //Values: 1.2 "\t" true
fmt.Printf("Types: %T %T %T\n", 1.2, "\t", true) //Types: float64 string bool
fmt.Printf("Percent sign: %%\n") //Percent sign: %
}
%#v可以显示一些值,比如 \t,如果不使用这种方式输出,可能会被隐藏起来
3. 格式化宽度
我们可以使用格式化宽度来限制输出的位数。
fmt.Printf("A float: %.2f\n", 3.1415) //A float: 3.141500
fmt.Printf("A integer: %4d\n", 15) //A integer: 15
fmt.Printf("A string: %15s\n", "hello") //A string: hello
来看这三个值:
- %.2f 意思是输出了两位小数
- %4d 意思是最小宽度是4,但是如果宽度不够就用空格填充
- %15s 意思是最小宽度是15,一样宽度不够就用空格填充
PS:也可以加上负号,不加符号表示空格左填充,加了负号表示空格右填充
格式化最小宽度:%5.2f
整个数的最小宽度是5,小数点后的宽度是2,f 是格式化的类型,输出的数据符合四舍五入
package main
import(
"fmt"
)
func main() {
fmt.Printf("%%7.3f: %7.3f\n", 12.3456); //%7.3f: 12.346
fmt.Printf("%%7.2f: %7.2f\n", 12.3456); //%7.2f: 12.35
fmt.Printf("%%7.1f: %7.1f\n", 12.3456); //%7.1f: 12.3
fmt.Printf("%%.1f: %.1f\n", 12.3456); //%.1f: 12.3
fmt.Printf("%%.2f: %.2f\n", 12.3456); //%.2f: 12.35
}
4. 声明函数
func sayHi(){
fmt.Print("Hi")
}
func 是关键字,sayHi 是函数名,里面是函数体。
函数的命名规则:
- 名称必须以字母开头,后跟任何数量的附加字母和数字(违反规则就编译报错)
- 名称以大写字母的函数时可导出的,并且可以在当前包之外使用。如果只需要在当前包中使用函数,则应该以小写字母开头
- 包含多个单词的名称应该使用驼峰命名法
正确的 | 错误的 |
---|---|
double | 2times(以数字开头了) |
addPart | addpart(不满足驼峰命名) |
Publish | posts.publish(小写字母开头不能在包外使用) |
5. 声明函数参数
如果希望对函数的调用包含参数,则需要声明一个或多个参数。参数是函数的局部变量,其值是在调用函数时候设置的
package main
import(
"fmt"
)
func repeatLine(line string, times int){
for i := 0; i < times; i++{
fmt.Println(line)
}
}
func main() {
repeatLine("测试", 5)
//测试
//测试
//测试
//测试
//测试
}
你可以在函数声明中的圆括号之间声明一个或者多个参数,使用逗号分隔开来,调用的时候传递参数即可。注意一下:参数是函数的局部变量,在调用函数的时候设置
6. 函数和变量的作用域
在函数外部声明的变量在该函数块的作用域内。这意味着我们可以在包级别上声明一个变量,并且可以在该包中的任何函数内访问它。其次函数之间的变量不能串着用,也不能在外部调用。
7. 函数的返回值
现在有一个场景,我们需要传递两个参数,返回面积,如果参数合法就返回结果;如果不合法就返回一个错误信息。
返回值的特点:
- 函数的返回值要和设定的匹配
- 可以有多个返回值
- return 语句要写到最后
1. 一个返回值
我们这样设置,如果结果是负数,证明参数不合法,就返回-1;否则我们返回结果面积
package main
import(
"fmt"
)
func area(length int, width int) (int){
if length * width < 0{
return -1
}
return length * width
}
func main() {
i := area(4, -5)
if i == -1{
fmt.Printf("输入值错误") //输入值错误
}else{
fmt.Printf("i: %v\n", i) //i: 20
}
}
2. 错误值
上面的函数中我们是用的 -1 代表错误,但是有很多情况下,每一个值都是有意义的,所以这时候我们需要设定一个特定的错误值来返回,这时候就要使用一些内置包了。下面是两种写法
func main() {
//写法1
err := errors.New("height can't be negative")
fmt.Println(err) //height can't be negative
//写法2
err = errors.New("height can't be negative")
s := err.Error()
fmt.Println(s) //height can't be negative
}
同时这里我们也可以使用格式化的函数使得值可以在错误信息里面打印出来
func main() {
//格式化错误数据
err2 := fmt.Errorf("a height of %0.2f is invalid", -2.333333333)
fmt.Println(err2) //a height of -2.33 is invalid
fmt.Println(err2) //a height of -2.33 is invalid
}
3. 多个返回值
现在我们有办法可以返回自定义的函数了,那么还有一点就是上面说的,不是每一个返回值都可以作为错误值判断的,比如上面例子我们知道了面积肯定是>0的,-1才可以作为错误判断,但是有些地方全部整数都有意义,那么这时候就很难判断了。而 java 中可以使用 throw 的方式抛出异常,在 Go 语言中提供了多个返回值,那么我们就可以使用一个返回值专门返回异常,还是上面的例子:
package main
import (
"fmt"
)
func area(length int, width int) (int, error){
if length * width < 0{
return -1, fmt.Errorf("area %d is error", length * width);
}
return length * width, nil;
}
func main() {
i, err := area(5, -5)
if(err != nil){
fmt.Println(err)
return
}
fmt.Println(i)
}
上面这个例子中:func area(length int, width int) (int, error){}
,里面的(int,error)就是返回值,第一个返回值是 int,第二个返回值是错误,如果参数不合法,我们就把 error 通过第二个参数传递过来就行。下面是上面的代码错误的情况:
如果这样写 | 错误原因 |
---|---|
return length * width, | 函数返回值数量必须和设定好的一样 |
i, | 返回多少个就用多少个参数接收 |
返回的 i 和 error 有一个没有使用 | Go语言要求使用声明的每个变量 |
当然了,如果你不需要第二个参数,可以使用 _
来接收:i, _ := area(5, -5)
8. 函数接收参数的富本
package main
import (
"fmt"
)
func area(str string){ //
str = "bbb"
}
func main() {
name := "aaa"
area(name)
fmt.Println(name) //aaa
}
其实和 java 一样,我们传递给函数的值是一个副本来的,不能修改原来的值,因此在函数内部所做的任何更改在函数外部都不可见。上面就是一个例子,那么如果我们需要改变原始的值,怎么做呢?其实如果你有 C语言的基础,你就知道使用指针就可以做到,传递一个指针意思就是把这个参数的地址传递过去,函数直接在这个原来的数的地址基础上修改。
9. 指针
我们使用&
符号获取变量的地址,它是Go的地址运算符。每个变量的地址不同
package main
import "fmt"
func main() {
var myInt int
fmt.Println(&myInt) //0xc000018088
var myFloat float64
fmt.Println(&myFloat) //0xc0000180c0
var myBool bool
fmt.Println(&myBool) //0xc0000180c8
}
地址是什么?你可以理解为是变量的房子,每个变量在计算机中存储的位置就是变量的地址,表示变量地址的值叫做指针,使用指针就能随时找到这个变量在计算机中的位置。
10. 指针类型
指针类型可以写为一个 * 号,后面跟着指针指向变量的类型。例如一个指向 int 类型的指针的类型是 *int
package main
import (
"fmt"
"reflect"
)
func main() {
var myInt int
fmt.Println(reflect.TypeOf(&myInt)) //*int
var myFloat float64
fmt.Println(reflect.TypeOf(&myFloat)) //*float64
var myBool bool
fmt.Println(reflect.TypeOf(&myBool)) //*bool
}
我们可以声明一个指针,然后给这个指针赋值
package main
import (
"fmt"
)
func main() {
var myInt int
var myPointInt *int
myPointInt = &myInt
myInt = 4 //对原始赋值
fmt.Printf("myPointInt: %v\n", *myPointInt) //myPointInt: 4
}
如果要立刻为指针赋值,那么可以使用短变量声明:
func main() {
var myBool bool
myBoolPoint := &myBool
fmt.Printf("myBoolPoint: %v\n", myBoolPoint) //myBoolPoint: 0xc0000aa058
}
11. 获取或更改指针的值
获取指针的值和更改指针的值都是使用 *
package main
import (
"fmt"
)
func main() {
myInt := 4
myIntPoint := &myInt
fmt.Printf("myIntPoint: %v\n", myIntPoint) //myIntPoint: 0xc000018088
getInt := *myIntPoint //*号取值
fmt.Printf("getInt: %v\n", getInt) //getInt: 4
*myIntPoint = 10 //使用*号修改值
getInt = *myIntPoint
fmt.Printf("getInt: %v\n", getInt) //getInt: 10
}
12. 函数指针
可以从函数返回指针,只需要声明函数的返回类型是指针类型。
package main
import (
"fmt"
)
func createPointer() *float64{
var myFloat = 98.5
return &myFloat
}
func main() {
f := createPointer()
fmt.Printf("f: %v\n", f) //f: 0xc000018088
}
我们也可以把参数传给函数进行修改,把指针作为参数传给函数,那么直接修改的就是原来值
package main
import (
"fmt"
)
func createPointer(aaa *int) {
*aaa = 2
}
func main() {
resource := 4
fmt.Printf("resource: %v\n", resource) //resource: 4
createPointer(&resource)
fmt.Printf("resource: %v\n", resource) //resource: 2
}
如有错误,欢迎指出!!!