Go
Linux下安装Go开发包
-
首先找到Go官网https://golang.google.cn/dl/
对于386的是32位OS的,所以针对自己的OS选择合适的安装包
-
选择安装目录通过cd命令切换到自己安装的目录
[root@bogon ~]# cd /usr/local/ [root@bogon local]# ll total 0 drwxr-xr-x. 2 root root 6 Apr 11 2018 bin drwxr-xr-x. 2 root root 6 Apr 11 2018 etc drwxr-xr-x. 2 root root 6 Apr 11 2018 games drwxr-xr-x. 2 root root 6 Apr 11 2018 include drwxr-xr-x. 2 root root 6 Apr 11 2018 lib drwxr-xr-x. 2 root root 6 Apr 11 2018 lib64 drwxr-xr-x. 2 root root 6 Apr 11 2018 libexec drwxr-xr-x. 2 root root 6 Apr 11 2018 sbin drwxr-xr-x. 5 root root 49 Nov 8 21:09 share drwxr-xr-x. 2 root root 6 Apr 11 2018 src
-
使用wget下载开发包
[root@bogon local]# wget https://dl.google.com/go/go1.20.2.linux-amd64.tar.gz --2023-03-23 10:06:46-- https://dl.google.com/go/go1.20.2.linux-amd64.tar.gz Resolving dl.google.com (dl.google.com)... 58.254.149.161 Connecting to dl.google.com (dl.google.com)|58.254.149.161|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 100107955 (95M) [application/x-gzip] Saving to: ‘go1.20.2.linux-amd64.tar.gz’ 100%[===============================================================>] 100,107,955 1.40MB/s in 54s 2023-03-23 10:07:41 (1.77 MB/s) - ‘go1.20.2.linux-amd64.tar.gz’ saved [100107955/100107955]
-
通过tar命令对下载的包进行解压
[root@bogon local]# tar -xzf go1.20.2.linux-amd64.tar.gz [root@bogon local]# ll total 97764 drwxr-xr-x. 2 root root 6 Apr 11 2018 bin drwxr-xr-x. 2 root root 6 Apr 11 2018 etc drwxr-xr-x. 2 root root 6 Apr 11 2018 games drwxr-xr-x. 10 root root 222 Mar 4 02:22 go -rw-r--r--. 1 root root 100107955 Mar 8 00:48 go1.20.2.linux-amd64.tar.gz drwxr-xr-x. 2 root root 6 Apr 11 2018 include drwxr-xr-x. 2 root root 6 Apr 11 2018 lib drwxr-xr-x. 2 root root 6 Apr 11 2018 lib64 drwxr-xr-x. 2 root root 6 Apr 11 2018 libexec drwxr-xr-x. 2 root root 6 Apr 11 2018 sbin drwxr-xr-x. 5 root root 49 Nov 8 21:09 share
-
使用
cd
命令进入该目录,然后执行bin/go version
命令就可以查看当前Go语言的版本了。[root@bogon local]# cd go/ [root@bogon go]# ll total 52 drwxr-xr-x. 2 root root 4096 Mar 4 02:21 api drwxr-xr-x. 2 root root 29 Mar 4 02:22 bin -rw-r--r--. 1 root root 52 Mar 4 02:19 codereview.cfg -rw-r--r--. 1 root root 1339 Mar 4 02:19 CONTRIBUTING.md drwxr-xr-x. 2 root root 104 Mar 4 02:21 doc drwxr-xr-x. 3 root root 18 Mar 4 02:21 lib -rw-r--r--. 1 root root 1479 Mar 4 02:19 LICENSE drwxr-xr-x. 11 root root 152 Mar 4 02:21 misc -rw-r--r--. 1 root root 1303 Mar 4 02:19 PATENTS drwxr-xr-x. 4 root root 33 Mar 4 02:22 pkg -rw-r--r--. 1 root root 1455 Mar 4 02:19 README.md -rw-r--r--. 1 root root 419 Mar 4 02:19 SECURITY.md drwxr-xr-x. 49 root root 4096 Mar 4 02:21 src drwxr-xr-x. 26 root root 12288 Mar 4 02:21 test -rw-r--r--. 1 root root 8 Mar 4 02:19 VERSION [root@bogon go]# bin/go version go version go1.20.2 linux/amd64
-
配置环境变量
#在此需要配置两个环境变量GOROOT和PATH GOROOT 的值应该为Go语言的当前安装目录:export GOROOT=/usr/local/go PATH 为了方便使用Go语言命令和 Go 程序的可执行文件,需要追加其值:export PATH=$PATH:$GOROOT/bin:$GOBIN #将变量添加到profile文件中 [root@bogon go]# vim /etc/profile 将下面两行代码,加入到文件末尾 export GOROOT=/usr/local/go export PATH=$PATH:$GOROOT/bin 之后 :wq 保存并退出 #在使用source命令是配置文件生效 [root@bogon go]# source /etc/profile ##最后使用 go env命令若出现如下内容则安装成功 [root@bogon go]# go env GO111MODULE="" GOARCH="amd64" GOBIN="" GOCACHE="/root/.cache/go-build" GOENV="/root/.config/go/env" GOEXE="" GOEXPERIMENT="" GOFLAGS="" GOHOSTARCH="amd64" GOHOSTOS="linux" GOINSECURE="" GOMODCACHE="/root/go/pkg/mod" GONOPROXY="" GONOSUMDB="" GOOS="linux" GOPATH="/root/go" GOPRIVATE="" GOPROXY="https://proxy.golang.org,direct" GOROOT="/usr/local/go" GOSUMDB="sum.golang.org" GOTMPDIR="" GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64" GOVCS="" GOVERSION="go1.20.2" GCCGO="gccgo" GOAMD64="v1" AR="ar" CC="gcc" CXX="g++" CGO_ENABLED="1" GOMOD="/dev/null" GOWORK="" CGO_CFLAGS="-O2 -g" CGO_CPPFLAGS="" CGO_CXXFLAGS="-O2 -g" CGO_FFLAGS="-O2 -g" CGO_LDFLAGS="-O2 -g" PKG_CONFIG="pkg-config" GOGCCFLAGS="-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build1836596645=/tmp/go-build -gno-record-gcc-switches"
Go语言的并发是基于 goroutine 的,goroutine 类似于线程,但并非线程。可以将 goroutine 理解为一种虚拟线程。Go语言运行时会参与调度 goroutine,并将 goroutine 合理地分配到每个 CPU 中,最大限度地使用 CPU 性能。
多个 goroutine 中,Go语言使用通道(channel)进行通信,通道是一种内置的数据结构,可以让用户在不同的 goroutine 之间同步发送具有类型的消息。这让编程模型更倾向于在 goroutine 之间发送消息,而不是让多个 goroutine 争夺同一个数据的使用权。
Hello world
package main \\ 声明文件属于main包
import "fmt" \\ 导入fmt其是go中的一个的一个标准输入输出的库
func main() {
fmt.Println("Hello world")
}
通过go build先编译后go run和直接go run的区别
- 如果我们先编译生成了可执行的exe文件,那么我们可以将该可执行文件拷贝到其他没有go开发环境的机器上,仍然可以运行
- 直接使用go run运行,该机器上必须有go的开发环境
- 在编译时,编译器会将程序运行以来的库文件包含在可执行文件中,所以可执行文件会变大很多
Go开发注意事项
- Go的源文件是一“go”为扩展名的
- Go应用程序的执行入口是main()函数
- Go语言严格区分大小写
- Go方法是由一条条语句后塍,每个语句后不需要添加; 系统后自己加
- 在Go中定义的变量和import的包如果没有使用,代码不能通过编辑
转义字符
\\ 一个 \
\t 制表符
\n 换行
\r 直接替换掉前面相应位数的字符
Go的注释
同java
命名规范
命名规则涉及变量、常量、全局函数、结构、接口、方法等的命名。
Go语言从语法层面进行了以下限定:
-
任何需要对外暴露的名字必须以大写字母开头,不需要对外暴露的则应该以小写字母开头。
-
当命名(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如: GetuserName ,那么使用这种形式的标识符的对象就
可以被 外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面句对象语言中的 public );
-
命名如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的 (像面向对象语言中的 private )
Go的关键字
1.Go的变量使用
-
指定变量的数据类型,但不赋值,使用默认值
package main import "fmt" func main() { // 只声明变量,不赋值 var i int fmt.Println("i=", i) }
-
根据值自行判断数据的类型(类型推导)
package main import "fmt" func main() { //类型推导 var d = true fmt.Println(d) }
-
使用 :=的方式,但此时的变量不能是声明过的,否则会报错
package main import "fmt" func main() { name := "tom" // 相当于 var name string = "tom" fmt.Println(name) }
-
一次声明多个变量
package main import "fmt" func main(){ //多个变量为同种数据类型 var n1,n2,n3 int fmt.Println(" n1=", n1, "\n", "n2=", n2, "\n", "n3=", n3) //多个变量为不同种数据类型 var name,age,local = "tom",21,false fmt.Println("name=",name,"age=",age,"local=",local) }
-
声明全局变量
package main import "fmt" var ( name2 = "21" age2 = 12 ) func main(){ }
读取变量的大小,可以使用unsafe包中地Sizeof方法
package main
import (
"fmt"
"unsafe"
)
// 查看当前变量占用的字节数 unsafe.Sizeof(变量名)
func main() {
c := "a"
fmt.Printf("c的类型是 %T", unsafe.Sizeof(c))
}
全局变量和局部变量区别
- 定义位置不同,局部变量在函数内,全局变量在包下,函数外
- 存活时间不同,局部变量随函数的执行结束而销毁,全局变量随main函数存活
- 作用域不同。
在Go中字符串有所不同,其是由字节组成的
匿名变量
func main() {
s := user.Hello()
fmt.Printf("s: %v", s)
_, age := getPerson()
fmt.Printf("age: %v\n", age)
}
常量
const 常量名 数据类型 = value
一次性定义多个常量
const (
a1 = 100
a2
a3
)
iota
iota在关键字const出现时被重置为0,const中每增加一行常量声明将使iota加一
举例:
1.跳过某些值
const (
a1 = iota
a2 //1
a3 //2
_
a4 //3
)
2.iota声明中间插队
const(
a1 = iota //0
a2 //1
a3 = iota //2
_
a4 //3
)
-
iota 定义在一行
const( a1,a2 = iota,iota+1 //0,1 a3 = iota //1 a4,a5 = iota+1 //3 )
-
定义数量级
const( _ = iota KB = 1 << (10 * iota) GB = 1 << (10 * iota) TB = 1 << (10 * iota) PB = 1 << (10 * iota) )
2.Go的数据类型
主要的数据类型分为2种,基本数据类型和派生/复杂数据类型
基本数据类型分为:整型、字符型、布尔型、字符串
派生/复杂数据类型:指针、数组、结构体、管道、函数、切片、接口、map类型
2 基本数据类型
2.1 使用细节
2.1.1整型
有符号:
无符号:
b := -1
//printf 是做格式化输出的 %T 相当于 Type
fmt.Printf("b的类型是%T 占用的字节大小是 %d \n", b, unsafe.Sizeof(b))
c := 111
// 查看当前变量占用的字节数 unsafe.Sizeof(变量名)
fmt.Printf("c的类型是 %T 占用的字节大小是 %d", c, unsafe.Sizeof(c))
使用细节
- Golang各整数类型分: 有符号和无符号 ,int、uint(无符号)的大小和系统无关
- Go的整型默认声明为int
- Go中整型变量在使用是,遵守保小不保大的原则。即:保证程序正常运行下,尽量使用占用空间小的数据类型
2.1.2浮点型
- Go中浮点类型有固定的范围和字节长度,不受具体的OS的影响
- Go中的浮点类型默认是64位
注意:通长情况下使用的64位的,因为其更加精确
var num4 = 1.2
//打印num4的数据类型
fmt.Printf("num4的数据类型是 %T ", num4)
//科学计数法
num5 := 5.123e2 // -> 5.123 * 10的2次方 e 大小写均可
fmt.Println("num5:", num5)
2.1.3字符类型
字符串:就是一串固定长度的字符连接起来的字符序列
Go中没有专门的字符类型,如果要存储单个的字符(字母),一般使用byte来保存。
所以对比传统的字符串不同,在Go中字符串是由byte组成的。
注意:
- 对于保存的字符在ASCII表中的字符直接可以使用byte保存
- 若不在ASCII中,我们就要考虑int类型保存
- 需要输出字符的本身内容,使用格式化输出
package main
import (
"fmt"
)
func main() {
//c := "a"
var c1 byte = 'a'
fmt.Println("c1=", c1)
//使用格式化输出,输出对应的字符
fmt.Printf("c1=%c", c1)
//在使用单个汉字时,由于byte的范围(0-255)有限,所以其数据类型int
e := '别'
fmt.Printf("e的数据类型是%T,e对应的码值是%d", e, e)
}
字符类型的使用细节:
- 对于字符常量的使用用单引号引起该字符
- Go中允许使用转义字符来讲特殊字符转为常量
- Go语言使用的是UTF-8编码
- 在Go中字符的本质就是一个整数,在直接输出是,是输出其对应的URF-8编码的编码值
- 也可以直接给某一个变量赋一个数字,然后按照格式化输出会输出改数字对应的unicode字符
- 字符类型是可以进行运算的,相当于一个整数,其有对应的Unicode
e := '别'
fmt.Printf("e的数据类型是%T,e对应的码值是%d \n", e, e)
f := 97
fmt.Printf("f=%c", f)
c3 := 10 + 'a'
fmt.Println(c3)
2.1.4布尔类型 bool
基本介绍
布尔类型也叫bool类型,bool类型数据只允许取值 true和false,其只占用一个字节
package main
import (
"fmt"
"unsafe"
)
func main() {
b := false
fmt.Printf("b的数据类型%T,占用的字节大小是%d", b, unsafe.Sizeof(b))
}
2.1.5字符串类型: string
基本介绍
字符串就是一串固定长度的字符连接起来的字符序列。Go的字符串是由单个字节连接起来的,Go语言的字符串的字节长度使用utf-8编码表示Unicode文本。
golang的字符串是由单个字节组成
var name = "中华人民共和国"
fmt.Printf("name的字节大小是%d", unsafe.Sizeof(name))
注意事项和使用细节
- 字符串一旦赋值并不可改变
- Go语言的字符串的字节使用的是UTF-8编码表示Unicode文本,这样Go中统一使用的是UTF-8编码
- 字符串有两种表现形式:
- 双引号:会识别转义字符
- 反引号:以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、输出源代码等效果。
- 字符串的拼接方式 使用“ + ”
注意当字符串过长时,在使用字符串拼接时,“+” 需要在上方
2.2 基本数据类型默认表
数据类型 | 默认值 |
---|---|
整型 | 0 |
浮点型 | 0 |
字符串 | “” |
布尔类型 | false |
eg:
位运算符
&: 同1为1,有0为0
|:同0为0,有1为1
^:相同为0,不同为1
<< : 左移运算符 对于左移运算符,左移n位,就是给原数扩大2的n次方倍。其功能把"<<“左边的运算数的各二进位全部左移若干位,由”<<"右边的数指定移动的位数,高位丢弃,低位补0。
“>>” : 右移运算符">>“是双目运算符。右移n位就是除以2的n次方。 其功能是把”>>“左边的运算数的各二进位全部右移若干位,”>>"右边的数指定移动的位数
A = 0011 1100
B = 0000 1101
A & B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
a = 60;b = 13
a = 0011 1100
b = 0000 1101
a & b = 0000 1100 12
a | b = 0011 1101 61
a ^ b = 0011 0010 49
其他运算符
运算符优先级
2.3 基本数据类型转换
在Go中不同于java/c不同类型的变量之间不能自动完成类型转换。(没有隐式转换)
2.3.1 基本语法:
表达式T(v) 将值v转换为类型T
T:就是数据类型
v:就是需要转换的变量
package main
import "fmt"
func main() {
var i int64 = 100
//将i转换为float
var j float32 = float32(i)
fmt.Printf("i=%v j=%v \n", i, j)
fmt.Printf("i的数据类型是 %T,j的数据类型是 %T", i, j)
var x int32= int32(j)
fmt.Printf("x的数据类型是%T", x)
}
2.3.2使用细节
-
Go中无论大转小还是小转大,都是可以转换的(但是在大转小时需要注意数据溢出)
-
被转换的事变量存储的数据,变量本身的数据类型没有发现变化
var x int32= int32(j) //即只是把j的值转换为了32位的大小,j本身的数据类型并没有发生任何变化
练习题
func main(){
var n1 int32 = 12
var n2 int64
var n3 int8
n2 = n1 + 20 //编译不通过,n1是int32 而n2是64,所以在运算时需要类型转换
n3 = n1 + 20
}
func main() {
var n1 int32 = 12
var n3 int8
var n4 int8
n4 = int8(n1) +127 //编译通过,但是结果会溢出,因为int8的范围是[-128-127]
n3 = int8(n1) + 128 //编译不通过,因为128不是int8
}
在import中如果对于一个包我们并没有使用,但是又不想删掉写好的代码,那么可以使用“_” 来表示忽略
2.4 基本数据类型和string的转换
2.4.1 基本类型转String类型
-
fmt.Sprintf(“%参数”,表达式)
func Sprintf(format string, a ...interface{}) string //a ...interface 表示空接口,可以接收任何类型的数据
Sprintf根据format参数生成格式化的字符串并返回该字符串。
package main import ( "fmt" ) func main() { var num1 = 123 var f float64 = 3.1 var b bool = true var c byte = 'c' //var str1 string var str1 = fmt.Sprintf("%d", num1) fmt.Println(str1) fmt.Printf("str1=%v,str的数据类型%T \n", str1, str1) var str2 = fmt.Sprintf("%f", f) fmt.Printf("str2=%v,str2的数据类型%T \n", str2, str2) var str3 = fmt.Sprintf("%t", b) fmt.Printf("str3=%v,str3的数据类型%T \n", str3, str3) var str4 = fmt.Sprintf("%c", c) fmt.Printf("str3=%v,str4的数据类型%T \n", str4, str4) }
-
使用strconv包的函数
方法:
-
func FormatBool(b bool) string
str0 = strconv.FormatBool(b) fmt.Printf("str0=%q,str的数据类型%T \n", str0, str0)
-
func FormatInt(i int64, base int) string
func FormatInt(i int64, base int) string eg: var str string str = strconv.FormatInt(int64(num1), 10) fmt.Printf("str=%q,str的数据类型%T \n", str, str)
返回i的base进制的字符串表示。base 必须在2到36之间,结果中会使用小写字母’a’到’z’表示大于10的数字。
-
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
func FormatFloat(f float64, fmt byte, prec, bitSize int) string
函数将浮点数表示为字符串并返回。
bitSize表示f的来源类型(32:float32、64:float64),会据此进行舍入。
fmt表示格式:‘f’(-ddd.dddd)、‘b’(-ddddp±ddd,指数为二进制)、‘e’(-d.dddde±dd,十进制指数)、‘E’(-d.ddddE±dd,十进制指数)、‘g’(指数很大时用’e’格式,否则’f’格式)、‘G’(指数很大时用’E’格式,否则’f’格式)。
prec控制精度(排除指数部分):对’f’、‘e’、‘E’,它表示小数点后的数字个数;对’g’、‘G’,它控制总的数字个数。如果prec 为-1,则代表使用最少数量的、但又必需的数字来表示f。
var str5 = strconv.FormatFloat(f, 'f', 10, 64) fmt.Printf("str5=%q,str的数据类型%T \n", str5, str5)
Itoa函数:将int转为string
var num int = 123 str = strconv.Itoa(num) fmt.Printf("str=%q,str的数据类型%T \n", str, str)
2.4.2 String转基本类型
-
使用strconv包中的函数
- func ParseInt
func ParseInt(s string, base int, bitSize int) (i int64, err error) //eg: str7 := "1234567" var n1 int64 n1, _ = strconv.ParseInt(str7, 10, 64) fmt.Printf("n1=%v,n1的数据类型是%T", n1, n1)
返回字符串表示的整数值,接受正负号。
base指定进制(2到36),如果base为0,则会从字符串前置判断,"0x"是16进制,"0"是8进制,否则是10进制;
bitSize指定结果必须能无溢出赋值的整数类型,0、8、16、32、64 分别代表 int、int8、int16、int32、int64;返回的err是*NumErr类型的,如果语法有误,err.Error = ErrSyntax;如果结果超出类型范围err.Error = ErrRange。
- func ParseUint
func ParseUint(s string, base int, bitSize int) (n uint64, err error)
ParseUint类似ParseInt但不接受正负号,用于无符号整型。
- func ParseFloat
func ParseFloat(s string, bitSize int) (f float64, err error) //eg var str8 string = "78.322" var f1 float64 f1, _ = strconv.ParseFloat(str8, 10) fmt.Printf("f1=%v,f1的数据类型是%T \n", f1, f1)
解析一个表示浮点数的字符串并返回其值。
如果s合乎语法规则,函数会返回最为接近s表示值的一个浮点数(使用IEEE754规范舍入)。bitSize指定了期望的接收类型,32是float32(返回值可以不改变精确值的赋值给float32),64是float64;返回值err是*NumErr类型的,语法有误的,err.Error=ErrSyntax;结果超出表示范围的,返回值f为±Inf,err.Error= ErrRange。
- func ParseBool
func ParseBool(str string) (value bool, err error) // 在使用时,该函数有两个返回值,所以在接收时需要两个变量去接收,若我们只需要前一个返回值,对于后一个返回值,可以使用“_”来忽略掉。 var b1 bool b1, _ = strconv.ParseBool(str0) fmt.Printf("b=%v,b1的数据类型是%T", b1, b1)
返回字符串表示的bool值。它接受1、0、t、f、T、F、true、false、True、False、TRUE、FALSE;否则返回错误。
注意:
在将string转为其他数据类型时,如出现无效的数据,Go会直接将其置为当前数据类型的默认值
var str9 string = "hello" var n0 int64 n0, _ = strconv.ParseInt(str9, 10, 64) fmt.Printf("n0=%v,n0的数据类型是%T \n", n0, n0) //若是bool时会是默认值false
运行结果:
-
2.5复杂数据类型
2.5.1指针
2.5.2基本介绍
- 基本数据类型,变量存的就是值,也叫值类型
- 获取变量的地址,用&,比如 var num int,获取num的地址:&num
- 指针类型,变量存的就是一个地址,这个地址指向的空间存的才是值;比如 var ptr *int = &num
- 获取指针类型所指向的值,使用: * ,比如 var ptr int*,使用 *ptr获取p指向的值
代码:
var i int = 10
fmt.Printf("i: %v\n", i)
fmt.Printf("i的地址是 %v \n", &i)
var ptr *int = &i
fmt.Printf("ptr 的地址是: %v\n", &ptr)
//对于ptr本身是指向i变量的地址
fmt.Printf("ptr: %v\n", ptr)
//实际上ptr的值是i的值,
fmt.Printf("ptr 的值是: %v\n", *ptr)
2.5.3 细节说明
2.6 值类型和引用类型
2.6.1 值类型
包括:基本数据类型、数组和结构体
值类型通常存储在栈内存中
2.6.2 引用类型
包括:指针、切片(slice)、map、管道(chan)、interface
引用类型数据通常存储在堆中
变量存储的都是一个地址,这个地址的空间对应的才是真正的数据,如果该地址没有被任何变量引用则会被认为垃圾回收。
2.7 数组
定义好之后,数组长度不可变
语法:
var 数组名 = [长度]数据类型
var a1 = [2]int
var a2 = [3]string
var arr = [3]int {1,2,3}
数组的遍历
普通for循环
var arr = [3]int{1,2,3}
for i := 0; i < len(arr); i++ {
fmt.Println(arr[i])
}
for range循环
for i, v := range arr{
fmt.Printf("i is %v ,v is %v", i,v)
}
切片
3 if语法和循环
3.1 单分支语句
语法
if 条件 {
执行语句
}
3.2 双分支语句
if 条件 {
//条件为真执行
}else{
//条件为假执行
}
3.3 嵌套语句
if 条件1 {
//条件1为真执行
if 条件2 {
//条件2为真执行
}
}
4 函数
4.1函数定义
语法
func 函数名 (形参列表) (返回值列表) { //返回值有多个要写括号,返回值可以是值类型或引用数据类型
函数体
}
案例:
func OperatorNum(n1 float64, n2 float64, operator string) float64 {
num1 := n1
num2 := n2
var operaor string = operator
var res float64
switch operaor {
case "+":
res = num1 + num2
case "-":
res = num1 - num2
case "*":
res = num1 * num2
case "/":
res = num1 / num2
default:
fmt.Print("input is error")
}
return res
}
4.1.1包
包的作用:
- 区分相同名字的函数,变量等标识符
- 进行项目管理
- 控制函数、变量等访问范围,即作用域
使用细节:
- 包名过长给包名起别名
import (
"fmt"
别名 "包名"
)
-
同一个包中不允许有同名的函数(也不能有相同的全局变量名)
-
包中函数访问方式
包名.函数名
-
如果要编译成一个可执行文件,就需要将这个包声明成main
D:\Go> go build -o bin\xx.exe go_pro\包名\main
//针对函数中的返回值,如果有返回值,不需要,可以使用_ 忽略
func main() {
// name := example.InputFunc()
// fmt.Printf("name: %v\n", name)
_, sub := GetSum(1, 2)
fmt.Printf("sum: %v\n", sub)
}
猴子吃桃子问题
有一堆桃子,猴子第一天吃了其中的一半,并再多吃了一个!以后每天猴子都吃其中的一半,然后再多吃一个。当到第十天时,想再吃时 (还没吃) ,发现只有1个桃子了。问题: 最初共多少个桃子?
// 10: => 1
// 9: => (第10天的桃子+1) * 2
// 8: => (第9天的桃子 + 1) * 2
// 第n天的桃子数: => (第n+ 1天的桃子 + 1) *2
func eatPaeach(n int) int {
if n == 10 {
return 1
} else {
return (eatPaeach(n+1) + 1) * 2
}
}
func main() {
fmt.Printf("res: %v\n", eatPaeach(9))
}
4.2 函数使用细节
-
返回值有多个。当有多个时要写括号,返回值可以是值类型或引用数据类型
-
函数名首字母大写,任何地方可以访问,函数名首字母小写,只有本包可以使用
-
函数内定义的变量是局部变量,只能在函数内使用
-
基本数据类型和数组默认都是值传递的,即进行值拷贝。在函数内修改,不会影响到原来的值
func test(n1 int) { n1 += 10 fmt.Printf("test()n1: %v\n", n1) fmt.Println(&n1) } func main() { num := 10 test(num) fmt.Printf("main() num is %v", num) fmt.Println(&num) }
-
如果要在函数内修改当前形参接受的实参的数据(实参是基本数据类型或者数组),那么可以传入实参的地址,函数内以指针的方式操作变量。
func test(n1 *int) { *n1 += 10 fmt.Printf("test()n1: %v\n", *n1) // fmt.Println(&n1) } func main() { num := 10 test(&num) fmt.Printf("main() num is %v", num)
-
Go中不支持函数的重载
-
函数也是一种特殊的数据类型,可以将函数赋值给变量,通过变量,可以完成对函数的调用。(闭包)
func test01(n1 int) int { return n1 } func main() { num := 1 a := test01 fmt.Printf("a type is %T\t test type is %T", a, test01) //通过变量访问函数 res := a(num) fmt.Printf("res: %T\n", res) }
输出:
-
函数既然是数据类型,那么函数就可以当做形参使用,并且调用
func test01(n1 int) int { return n1 } func myfunc(funcvar func(int) int,num int ) int{ reutrn funcvar(num1) } func main() { num := 1 a := test01 //fmt.Printf("a type is %T\t test type is %T", a, test01) //通过变量访问函数 //res := a(num) //fmt.Printf("res: %T\n", res) res2 := myfunc(test01, num) fmt.Printf("res: %T %v\n", res2, res2) }
-
为了简化数据类型定义,Go支持自定义数据类型
type 变量名 数据类型
4.3 init函数
初始化函数,是在main函数执行之前执行
细节
- 如果一个文件中同时包含全局变量定义。init函数和main函数,则执行顺序是:变量定义=> init函数 => main函数
- 作用:完成一些初始化的工作
4.3.1 go文件执行流程:
- 首先执行,被引入包的函数,开始变量定义执行init函数
- 在执行引入其他包的函数中的变量执行,之后init初始化,最后执行main
4.4 匿名函数
当函数只使用一次时,可以考虑使用匿名函数
方式一:
res := func (n1 int,n2 int) int {
return n1+n2
}(10,20)
方式二:
res := function(n1 int,n2 int) int){
return n1 + n2
}
sum := res(10,20)
全局匿名函数
将一个匿名函数交给一个全局变量
var (
Func = function(n1 int,n2 int) int){
return n1 + n2
}
)
5 闭包
其是一个函数和与其相关的引用环境组合成的一个整体
内层函数+外层变量
示例:
func makeSuffix(suffix string) func(string) string {
return func(name string) string {
if !strings.HasSuffix(name, suffix) {
return name + suffix
}
return name
}
}
func main() {
f := makeSuffix(".jpg")
fmt.Println("文件处理后= ", f("winter"))
}
defer关键字
当执行到defer是,暂时不执行,会将defer后面的语句压入到独立的栈中,当函数执行结束之后,再从defer中按照先入后出的方式出栈
注意:
当defer语句放入到栈中,会将相关的值拷贝同时入栈
系统函数:
-
len() 统计字符串长度,按照字节统计;其中不可以包含中文
-
[]rune(str) 字符串遍历,将字符串返回为一个切片,其中可以包含中文
-
字符串转为整型
n, err := strconv.Atoi(“66”) -
整数转为字符串:
str = strconv.Itoa(“12345”)
-
查找子串是否在指定的字符串中:
strings.Contains(“javagopython”,"go)