文档
Go语言官网:
https://golang.org (需要翻墙)
https://golang.google.cn/ (中国镜像站)
go中文社区:
编码
默认是utf-8
关于变量于内存
解疑
1.编程圈一直流行着值传递、引用传递、地址传递(指针传递)三种说法,给后世留下了颇为坑爹的概念,现在来缕缕。。
2.不同的语言,分为引用语义和值语义。(变量和变量绑定的数据不一定绑定在一起(python的变量名和变量绑定的数据就是分开的,go就是不分开)
3.不同的人,不是说的值传递、引用传递、地址传递定义也不完全一致。(广义的值传递还包括地址传递)
所以不同语言讨论这些值传递、引用传递、地址传递、变量、变量绑定的数据就没有很强的针对性。。所以得单独在某个语言内讨论。
缕清概念
a = 1 ; b = a
在这里就以最为经典的c++的值传递、引用传递、地址传递为标准,定义值传递、引用传递、地址传递
值传递: 重新开辟内存空间存储
引用传递:变量的别名(b就是a!)
地址传递:1的地址被a,b同时存储,b新开辟内存空间
形参是指向实参地址的一个指针,顾名思义,在函数中对形参指向的内容操作,实参本身会被修改。
python(a和b具有两块独立的内存地址,1单独存储在一块内存地址,python中地址传递表达的意思是数据的地址copy一份出来重新开辟一份空间)
值传递:
go(a和b没有两块独立的内存地址,变量名和数据存储在同一个内存地址,go中值传递表达的意思是数据copy一份出来重新开辟一份空间)
引用传递
Go 没有引用传递方式。为什么这么说,因为 Go 没有变量的引用这一概念。
c++
在 C++ 中,引用是变量的别名,实际上是同一个东西,在内存中也存在同一个地址。换句话说,不管在哪里对引用操作,都相当直接操作被引用的变量。
void rFoo(int & ref) { printf("%p\n", &ref);// 0x7ffee5aef768 } int main() { int a = 1; printf("%p\n", &a);// 0x7ffee7307768 int & b = a; printf("%p\n", &b);// 0x7ffee5aef768 rFoo(b); return 0; }
那么按照引用传递的定义,实参 b 传给形参 ref 之后,ref 将是 b 的别名(也即a、b、ref都是同一个变量),他们将拥有相同地址。
go变量与内存
源码剖析
func makeslice(et *_type, len, cap int) unsafe.Pointer { mem, overflow := math.MulUintptr(et.size, uintptr(cap)) if overflow || mem > maxAlloc || len < 0 || len > cap { // NOTE: Produce a 'len out of range' error instead of a // 'cap out of range' error when someone does make([]T, bignumber). // 'cap out of range' is true too, but since the cap is only being // supplied implicitly, saying len is clearer. // See golang.org/issue/4085. mem, overflow := math.MulUintptr(et.size, uintptr(len)) if overflow || mem > maxAlloc || len < 0 { panicmakeslicelen() } panicmakeslicecap() } return mallocgc(mem, et, true) }
// makemap implements Go map creation for make(map[k]v, hint). // If the compiler has determined that the map or the first bucket // can be created on the stack, h and/or bucket may be non-nil. // If h != nil, the map can be created directly in h. // If h.buckets != nil, bucket pointed to can be used as the first bucket. func makemap(t *maptype, hint int, h *hmap) *hmap { mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size) if overflow || mem > maxAlloc { hint = 0 } // initialize Hmap if h == nil { h = new(hmap) } h.hash0 = fastrand() // Find the size parameter B which will hold the requested # of elements. // For hint < 0 overLoadFactor returns false since hint < bucketCnt. B := uint8(0) for overLoadFactor(hint, B) { B++ } h.B = B // allocate initial hash table // if B == 0, the buckets field is allocated lazily later (in mapassign) // If hint is large zeroing this memory could take a while. if h.B != 0 { var nextOverflow *bmap h.buckets, nextOverflow = makeBucketArray(t, h.B, nil) if nextOverflow != nil { h.extra = new(mapextra) h.extra.nextOverflow = nextOverflow } } return h }
func makechan(t *chantype, size int) *hchan { elem := t.elem // compiler checks this but be safe. if elem.size >= 1<<16 { throw("makechan: invalid channel element type") } if hchanSize%maxAlign != 0 || elem.align > maxAlign { throw("makechan: bad alignment") } mem, overflow := math.MulUintptr(elem.size, uintptr(size)) if overflow || mem > maxAlloc-hchanSize || size < 0 { panic(plainError("makechan: size out of range")) } // Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers. // buf points into the same allocation, elemtype is persistent. // SudoG's are referenced from their owning thread so they can't be collected. // TODO(dvyukov,rlh): Rethink when collector can move allocated objects. var c *hchan switch { case mem == 0: // Queue or element size is zero. c = (*hchan)(mallocgc(hchanSize, nil, true)) // Race detector uses this location for synchronization. c.buf = c.raceaddr() case elem.kind&kindNoPointers != 0: // Elements do not contain pointers. // Allocate hchan and buf in one call. c = (*hchan)(mallocgc(hchanSize+mem, nil, true)) c.buf = add(unsafe.Pointer(c), hchanSize) default: // Elements contain pointers. c = new(hchan) c.buf = mallocgc(mem, elem, true) } c.elemsize = uint16(elem.size) c.elemtype = elem c.dataqsiz = uint(size) if debugChan { print("makechan: chan=", c, "; elemsize=", elem.size, "; elemalg=", elem.alg, "; dataqsiz=", size, "\n") } return c }
go传值原理
go只有值传递(copy一份)
广义的值传递包括传地址(slice map chan func传的是地址)
why go
简介:go语言的发明团队研究C,因此go遗传了很多C的风格,有人风趣的称go为 better c
1.go效率已经超过C++、JAVA,仅次于C,
2.原生支持并发(并发从此简单)
3.支持面向过程、面向对象(只支持封装,不支持继承和多态)即面向接口、函数式等编程范式
5.借鉴静态语言和动态语言的特性(如支持切片、range)
6.简洁、开源、易于使用 (因为以下核心特性:GC,goroutine,channel以及interface,偏向组合而不是继承、保留但大幅度简化指针,等)
7.值传递?引用传递?地址传递?python的不可变对象采用值传递,可变对象采用引用传递的说法是错误的,python只按地址传递,go均为值传递(copy一份)(slice、map和channel为引用类型的说法其实就是传地址),要改变原有数据可用指针
8.自举:go语言底层用go实现
9.跨平台:支持绝大部分主流计算架构和操作系统
10.代码风格强制统一(新人也能看懂老兵代码)
11.内存分配:垃圾回收;只需要new分配内存,不需要释放
12.没有继承,只有组合
总之,Go语言的设计理念很明确,就是将动态类型语言的编程容易度和静态类型语言的安全效率结合起来的系统语言
标准命令
build:用于编译给定的代码包或Go语言源码文件及其依赖包。 clean:用于清除执行其他go命令后遗留的目录和文件。 doc:用于执行godoc命令以打印指定代码包。 env:用于打印Go语言环境信息。 fix:用于执行go tool fix命令以修正给定代码包的源码文件中包含的过时语法和代码调用。 fmt:用于执行gofmt命令以格式化给定代码包中的源码文件。 get:用于下载和安装给定代码包及其依赖包(提前安装git或hg)。 list:用于显示给定代码包的信息。 run:用于编译并运行给定的命令源码文件。 install:编译包文件并编译整个程序。 test:用于测试给定的代码包。 tool:用于运行Go语言的特殊工具。 version:用于显示当前安装的Go语言的版本信息。
语言结构
包是Go语言里最基本的分发单位,也是工程管理中依赖关系的体现。
要生成Go可执行程序,必须建立一个名字为main的包,并且在该包中包含一个叫main()的函数(该函数是Go可执行程序的执行起点)。
Go语言的main()函数不能带参数,也不能定义返回值。
test.go
package main //每个 Go 应用程序都包含一个名为 main 的包 import "fmt" //fmt 包实现了格式化 IO(输入/输出)的函数 //main 函数是每一个可执行程序所必须包含的,且是在启动后第一个执行的函数(如果有init()函数则会先执行该函数) func main() { fmt.Println("Hello, World!") }
编译程序
go run test.go //这个命令,会将编译、链接和运行3个步骤合并为一步,运行完后在当前目录下也看不到任何中间文件和最终的可执行文件 go build test.go //windows下生成test.exe文件,双击即可运行,命令行执行test.exe执行test.go代码
注释
// 单行注释
/* */多行注释
变量
与python不一样的是,python变量名和数据分开存储,go的变量名和数据在一起(实际上不会占用内存)
reflect.TypeOf(v) //取得变量的类型 &v //取得变量的地址 _是特殊标识符,用来忽略结果
变量命名
变量名由字母、数字(不能为首字符)、下划线组成,不能使用关键字
以下关键字不能声明为变量名
break | default | func | interface | select |
case | defer | go | map | struct |
struct | chan | else | goto | package |
switch | const | if | range | type |
fallthrough | for | import | return | var |
continue |
以下标识符不能声明为变量名:
append | bool | byte | cap | close | complex | complex64 | complex128 | uint16 |
copy | false | float32 | float64 | imag | int | int8 | int16 | uint32 |
int32 | int64 | iota | len | make | new | nil | panic | uint64 |
println | real | recover | string | true | uint | uint8 | uintptr |
变量声明
基本格式
var name type //格式,声明后若不赋值,使用默认值
多变量声明
var v1 int var v2 string //可简化为 var ( v1 int v2 string )
类型相同多个变量
var a,b int
变量赋值
注意:被赋值的变量未被使用程序编译出错
一般赋值
方式一
var v1, v2 int = 1, 2 //显示赋值
方式二
var v1 = 10 //编译器可以自动推导出v2的类型
方式三
v1 = 10 //作为局部变量时,和全局变量同名会使用全局变量
方式四
v1 := 10 //初始化=声明—+赋值,只能作为局部变量,和全局变量同名不会使用全局变量 //编译器可以自动推导出v3的类型; //这种不带声明格式的只能在函数体中出现出现在:=左侧的变量不应该是已经被声明过的,否则会导致编译错误
package main import "fmt" var str string func main() { fmt.Println(&str, ) str = "hy" fmt.Println(&str, ) str := "d" fmt.Println(&str, ) } ################ 0x53c410 0x53c410 0xc0420301c0
多重赋值
i, j = j, i
变量交换
fmt.Println("Hello, World!") a, b := 10, 20 a, b = b, a fmt.Println(a, b)
匿名变量
匿名变量不占用命名空间,不会分配内存,所以匿名变量之间不存在重复声明。
_多用于占位,表示忽略值
_ , a := 1, 2 fmt.Println(a) // _不能使用,让代码更清晰
变量作用域
Go 语言中变量可以在三个地方声明: 函数内定义的变量称为局部变量 函数外定义的变量称为全局变量 函数定义中的变量称为形式参数
局部变量
在函数体内声明的变量称之为局部变量,它们的作用域只在函数体内,参数和返回值变量也是局部变量。
package main import "fmt" func main() { /* 声明局部变量 */ var a, b, c int /* 初始化参数 */ a = 10 b = 20 c = a + b fmt.Printf ("结果: a = %d, b = %d and c = %d\n", a, b, c) } ###################### 结果: a = 10, b = 20 and c = 30
// 形式参数 package main import "fmt" /* 声明全局变量 */ var a int = 20; func main() { /* main 函数中声明局部变量 */ var a int = 10 var b int = 20 var c int = 0 fmt.Printf("main()函数中 a = %d\n", a); c = sum( a, b); fmt.Printf("main()函数中 c = %d\n", c); } /* 函数定义-两数相加 */ func sum(a, b int) int { fmt.Printf("sum() 函数中 a = %d\n", a); fmt.Printf("sum() 函数中 b = %d\n", b); return a + b; } ############## main()函数中 a = 10 sum() 函数中 a = 10 sum() 函数中 b = 20 main()函数中 c = 30
全局变量
Go 语言程序中全局变量与局部变量名称可以相同,但是函数内的局部变量会被优先考虑
package main import "fmt" /* 声明全局变量 */ var g int = 20 func main() { /* 声明局部变量 */ var g int = 10 fmt.Printf ("结果: g = %d\n", g) } ################### 结果: g = 10
常量
相对于变量,常量是恒定不变的值,多用于定义程序运行期间不会改变的那些值。 常量的声明和变量声明非常类似,只是把var换成了const,常量在定义的时候必须赋值。
定义:程序在运行时,不会被修改的量(无法被重新赋值)。
内建常量: true false iota nil
格式:
const name [type] = value or exp //type为可选,type只可以是布尔型、数字型(整数型、浮点型和复数)和字符串型。
特殊常量iota
iota,特殊常量,可以认为是一个可以被编译器修改的常量。
在每一个const关键字出现时,被重置为0,然后再下一个const出现之前,每出现一次iota,其所代表的数字会自动增加1。
package main import "fmt" func main() { const ( a = iota //0 b //1 c //2 d = "ha" //独立值,iota += 1 e //"ha" iota += 1 f = 100 //iota +=1 g //100 iota +=1 h = iota //7,恢复计数 i //8 ) fmt.Println(a,b,c,d,e,f,g,h,i) } ############### 0 1 2 ha ha 100 100 7 8
枚举
作用: 易用性 - 便于用户使用(.就出来了/智能提示) 安全性 - 防止用户乱输入(限制输入,只能输入已定义枚举的常量)
iota可省略
const ( Sunday = iota Monday Tuesday Wednesday Thursday Friday Saturday numberOfDays // 这个常量没有导出 )
用户交互
fmt.Scan //输入,不换行 fmt.Scanln //输入,换行 fmt.Scanf //格式化输入,不换行 fmt.Print //输出,不换行 fmt.Println //输出,换行 fmt.Printf //格式化输出,不换行(打印多个变量更方便)
package main import "fmt" var firstname string func main() { lastname := "gini" fmt.Println("Please input your full name: ") fmt.Scanln(&firstname) fmt.Println(firstname, lastname) }
运算符
Go语言的大多数位运算符与C语言都比较类似,除了取反在C语言中是~x,而在Go语言中是^x 布尔类型
数据类型
全部类型介绍
int
, uint
和 uintptr
在 32 位系统上通常为 32 位宽,在 64 位系统上则为 64 位宽。 当你需要一个整数值时应使用 int
类型,除非你有特殊的理由使用固定大小或无符号的整数类型。
布尔类型 : bool
整型: int,int8,int16,int32,int64,uint,uint8,uint16,uint32,uint64,uintptr
浮点类型:float32,float64
复数类型: complex,complex64,complex128
字符串类型:string
字符类型:rune
错误类型: error
此外,Go语言也支持以下复合类型
指针:pointer
数组:array
切片:slice
字典:map
通道:chan
结构体:struct
接口:interface
基础数据类型
与其它语言不同,bool类型不能和其它类型转换
字符类型值是单引号,字符类型值是双引号
字符串类型
常用的字符串操作
+ 字符串连接 len(s) 字符串长度 s[i] 取字符 [m:n]切片
package main import "fmt" var a string func main() { a = "hello" mySlice := a[:2] fmt.Println(mySlice) fmt.Println("len(mySlice):", len(mySlice)) // 字符串遍历 for i, v := range a { fmt.Printf("%d: %c,", i, v) } fmt.Printf("\n\n") //字符串遍历 for i := 0; i < len(a); i++ { fmt.Printf("%d: %c,", i, a[i]) } } #################### he len(mySlice): 2 0: h,1: e,2: l,3: l,4: o, 0: h,1: e,2: l,3: l,4: o,
更多的字符串操作,请参考标准库strings包。
// Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Package strings implements simple functions to manipulate UTF-8 encoded strings. // // For information about UTF-8 strings in Go, see https://blog.golang.org/strings. package strings import ( "unicode" "unicode/utf8" ) // explode splits s into a slice of UTF-8 strings, // one string per Unicode character up to a maximum of n (n < 0 means no limit). // Invalid UTF-8 sequences become correct encodings of U+FFFD. func explode(s string, n int) []string { l := utf8.RuneCountInString(s) if n < 0 || n > l { n = l } a := make([]string, n) for i := 0; i < n-1; i++ { ch, size := utf8.DecodeRuneInString(s) a[i] = s[:size] s = s[size:] if ch == utf8.RuneError { a[i] = string(utf8.RuneError) } } if n > 0 { a[n-1] = s } return a } // primeRK is the prime base used in Rabin-Karp algorithm. const primeRK = 16777619 // hashStr returns the hash and the appropriate multiplicative // factor for use in Rabin-Karp algorithm. func hashStr(sep string) (uint32, uint32) { hash := uint32(0) for i := 0; i < len(sep); i++ { hash = hash*primeRK + uint32(sep[i]) } var pow, sq uint32 = 1, primeRK for i := len(sep); i > 0; i >>= 1 { if i&1 != 0 { pow *= sq } sq *= sq } return hash, pow } // hashStrRev returns the hash of the reverse of sep and the // appropriate multiplicative factor for use in Rabin-Karp algorithm. func hashStrRev(sep string) (uint32, uint32) { hash := uint32(0) for i := len(sep) - 1; i >= 0; i-- { hash = hash*primeRK + uint32(sep[i]) } var pow, sq uint32 = 1, primeRK for i := len(sep); i > 0; i >>= 1 { if i&1 != 0 { pow *= sq } sq *= sq } return hash, pow } // countGeneric implements Count. func countGeneric(s, substr string) int { // special case if len(substr) == 0 { return utf8.RuneCountInString(s) + 1 } n := 0 for { i := Index(s, substr) if i == -1 { return n } n++ s = s[i+len(substr):] } } // Contains reports whether substr is within s. func Contains(s, substr string) bool { return Index(s, substr) >= 0 } // ContainsAny reports whether any Unicode code points in chars are within s. func ContainsAny(s, chars string) bool { return IndexAny(s, chars) >= 0 } // ContainsRune reports whether the Unicode code point r is within s. func ContainsRune(s string, r rune) bool { return IndexRune(s, r) >= 0 } // LastIndex returns the index of the last instance of substr in s, or -1 if substr is not present in s. func LastIndex(s, substr string) int { n := len(substr) switch { case n == 0: return len(s) case n == 1: return LastIndexByte(s, substr[0]) case n == len(s): if substr == s { return 0 } return -1 case n > len(s): return -1 } // Rabin-Karp search from the end of the string hashss, pow := hashStrRev(substr) last := len(s) - n var h uint32 for i := len(s) - 1; i >= last; i-- { h = h*primeRK + uint32(s[i]) } if h == hashss && s[last:] == substr { return last } for i := last - 1; i >= 0; i-- { h *= primeRK h += uint32(s[i]) h -= pow * uint32(s[i+n]) if h == hashss && s[i:i+n] == substr { return i } } return -1 } // IndexRune returns the index of the first instance of the Unicode code point // r, or -1 if rune is not present in s. // If r is utf8.RuneError, it returns the first instance of any // invalid UTF-8 byte sequence. func IndexRune(s string, r rune) int { switch { case 0 <= r && r < utf8.RuneSelf: return IndexByte(s, byte(r)) case r == utf8.RuneError: for i, r := range s { if r == utf8.RuneError { return i } } return -1 case !utf8.ValidRune(r): return -1 default: return Index(s, string(r)) } } // IndexAny returns the index of the first instance of any Unicode code point // from chars in s, or -1 if no Unicode code point from chars is present in s. func IndexAny(s, chars string) int { if len(chars) > 0 { if len(s) > 8 { if as, isASCII := makeASCIISet(chars); isASCII { for i := 0; i < len(s); i++ { if as.contains(s[i]) { return i } } return -1 } } for i, c := range s { for _, m := range chars { if c == m { return i } } } } return -1 } // LastIndexAny returns the index of the last instance of any Unicode code // point from chars in s, or -1 if no Unicode code point from chars is // present in s. func LastIndexAny(s, chars string) int { if len(chars) > 0 { if len(s) > 8 { if as, isASCII := makeASCIISet(chars); isASCII { for i := len(s) - 1; i >= 0; i-- { if as.contains(s[i]) { return i } } return -1 } } for i := len(s); i > 0; { r, size := utf8.DecodeLastRuneInString(s[:i]) i -= size for _, c := range chars { if r == c { return i } } } } return -1 } // LastIndexByte returns the index of the last instance of c in s, or -1 if c is not present in s. func LastIndexByte(s string, c byte) int { for i := len(s) - 1; i >= 0; i-- { if s[i] == c { return i } } return -1 } // Generic split: splits after each instance of sep, // including sepSave bytes of sep in the subarrays. func genSplit(s, sep string, sepSave, n int) []string { if n == 0 { return nil } if sep == "" { return explode(s, n) } if n < 0 { n = Count(s, sep) + 1 } a := make([]string, n) n-- i := 0 for i < n { m := Index(s, sep) if m < 0 { break } a[i] = s[:m+sepSave] s = s[m+len(sep):] i++ } a[i] = s return a[:i+1] } // SplitN slices s into substrings separated by sep and returns a slice of // the substrings between those separators. // // The count determines the number of substrings to return: // n > 0: at most n substrings; the last substring will be the unsplit remainder. // n == 0: the result is nil (zero substrings) // n < 0: all substrings // // Edge cases for s and sep (for example, empty strings) are handled // as described in the documentation for Split. func SplitN(s, sep string, n int) []string { return genSplit(s, sep, 0, n) } // SplitAfterN slices s into substrings after each instance of sep and // returns a slice of those substrings. // // The count determines the number of substrings to return: // n > 0: at most n substrings; the last substring will be the unsplit remainder. // n == 0: the result is nil (zero substrings) // n < 0: all substrings // // Edge cases for s and sep (for example, empty strings) are handled // as described in the documentation for SplitAfter. func SplitAfterN(s, sep string, n int) []string { return genSplit(s, sep, len(sep), n) } // Split slices s into all substrings separated by sep and returns a slice of // the substrings between those separators. // // If s does not contain sep and sep is not empty, Split returns a // slice of length 1 whose only element is s. // // If sep is empty, Split splits after each UTF-8 sequence. If both s // and sep are empty, Split returns an empty slice. // // It is equivalent to SplitN with a count of -1. func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) } // SplitAfter slices s into all substrings after each instance of sep and // returns a slice of those substrings. // // If s does not contain sep and sep is not empty, SplitAfter returns // a slice of length 1 whose only element is s. // // If sep is empty, SplitAfter splits after each UTF-8 sequence. If // both s and sep are empty, SplitAfter returns an empty slice. // // It is equivalent to SplitAfterN with a count of -1. func SplitAfter(s, sep string) []string { return genSplit(s, sep, len(sep), -1) } var asciiSpace = [256]uint8{'\t': 1, '\n': 1, '\v': 1, '\f': 1, '\r': 1, ' ': 1} // Fields splits the string s around each instance of one or more consecutive white space // characters, as defined by unicode.IsSpace, returning an array of substrings of s or an // empty list if s contains only white space. func Fields(s string) []string { // First count the fields. // This is an exact count if s is ASCII, otherwise it is an approximation. n := 0 wasSpace := 1 // setBits is used to track which bits are set in the bytes of s. setBits := uint8(0) for i := 0; i < len(s); i++ { r := s[i] setBits |= r isSpace := int(asciiSpace[r]) n += wasSpace & ^isSpace wasSpace = isSpace } if setBits < utf8.RuneSelf { // ASCII fast path a := make([]string, n) na := 0 fieldStart := 0 i := 0 // Skip spaces in the front of the input. for i < len(s) && asciiSpace[s[i]] != 0 { i++ } fieldStart = i for i < len(s) { if asciiSpace[s[i]] == 0 { i++ continue } a[na] = s[fieldStart:i] na++ i++ // Skip spaces in between fields. for i < len(s) && asciiSpace[s[i]] != 0 { i++ } fieldStart = i } if fieldStart < len(s) { // Last field might end at EOF. a[na] = s[fieldStart:] } return a } // Some runes in the input string are not ASCII. // Same general approach as in the ASCII path but // uses DecodeRuneInString and unicode.IsSpace if // a non-ASCII rune needs to be decoded and checked // if it corresponds to a space. a := make([]string, 0, n) fieldStart := 0 i := 0 // Skip spaces in the front of the input. for i < len(s) { if c := s[i]; c < utf8.RuneSelf { if asciiSpace[c] == 0 { break } i++ } else { r, w := utf8.DecodeRuneInString(s[i:]) if !unicode.IsSpace(r) { break } i += w } } fieldStart = i for i < len(s) { if c := s[i]; c < utf8.RuneSelf { if asciiSpace[c] == 0 { i++ continue } a = append(a, s[fieldStart:i]) i++ } else { r, w := utf8.DecodeRuneInString(s[i:]) if !unicode.IsSpace(r) { i += w continue } a = append(a, s[fieldStart:i]) i += w } // Skip spaces in between fields. for i < len(s) { if c := s[i]; c < utf8.RuneSelf { if asciiSpace[c] == 0 { break } i++ } else { r, w := utf8.DecodeRuneInString(s[i:]) if !unicode.IsSpace(r) { break } i += w } } fieldStart = i } if fieldStart < len(s) { // Last field might end at EOF. a = append(a, s[fieldStart:]) } return a } // FieldsFunc splits the string s at each run of Unicode code points c satisfying f(c) // and returns an array of slices of s. If all code points in s satisfy f(c) or the // string is empty, an empty slice is returned. // FieldsFunc makes no guarantees about the order in which it calls f(c). // If f does not return consistent results for a given c, FieldsFunc may crash. func FieldsFunc(s string, f func(rune) bool) []string { // First count the fields. n := 0 inField := false for _, rune := range s { wasInField := inField inField = !f(rune) if inField && !wasInField { n++ } } // Now create them. a := make([]string, n) na := 0 fieldStart := -1 // Set to -1 when looking for start of field. for i, rune := range s { if f(rune) { if fieldStart >= 0 { a[na] = s[fieldStart:i] na++ fieldStart = -1 } } else if fieldStart == -1 { fieldStart = i } } if fieldStart >= 0 { // Last field might end at EOF. a[na] = s[fieldStart:] } return a } // Join concatenates the elements of a to create a single string. The separator string // sep is placed between elements in the resulting string. func Join(a []string, sep string) string { switch len(a) { case 0: return "" case 1: return a[0] case 2: // Special case for common small values. // Remove if golang.org/issue/6714 is fixed return a[0] + sep + a[1] case 3: // Special case for common small values. // Remove if golang.org/issue/6714 is fixed return a[0] + sep + a[1] + sep + a[2] } n := len(sep) * (len(a) - 1) for i := 0; i < len(a); i++ { n += len(a[i]) } b := make([]byte, n) bp := copy(b, a[0]) for _, s := range a[1:] { bp += copy(b[bp:], sep) bp += copy(b[bp:], s) } return string(b) } // HasPrefix tests whether the string s begins with prefix. func HasPrefix(s, prefix string) bool { return len(s) >= len(prefix) && s[0:len(prefix)] == prefix } // HasSuffix tests whether the string s ends with suffix. func HasSuffix(s, suffix string) bool { return len(s) >= len(suffix) && s[len(s)-len(suffix):] == suffix } // Map returns a copy of the string s with all its characters modified // according to the mapping function. If mapping returns a negative value, the character is // dropped from the string with no replacement. func Map(mapping func(rune) rune, s string) string { // In the worst case, the string can grow when mapped, making // things unpleasant. But it's so rare we barge in assuming it's // fine. It could also shrink but that falls out naturally. // The output buffer b is initialized on demand, the first // time a character differs. var b []byte // nbytes is the number of bytes encoded in b. var nbytes int for i, c := range s { r := mapping(c) if r == c { continue } b = make([]byte, len(s)+utf8.UTFMax) nbytes = copy(b, s[:i]) if r >= 0 { if r <= utf8.RuneSelf { b[nbytes] = byte(r) nbytes++ } else { nbytes += utf8.EncodeRune(b[nbytes:], r) } } if c == utf8.RuneError { // RuneError is the result of either decoding // an invalid sequence or '\uFFFD'. Determine // the correct number of bytes we need to advance. _, w := utf8.DecodeRuneInString(s[i:]) i += w } else { i += utf8.RuneLen(c) } s = s[i:] break } if b == nil { return s } for _, c := range s { r := mapping(c) // common case if (0 <= r && r <= utf8.RuneSelf) && nbytes < len(b) { b[nbytes] = byte(r) nbytes++ continue } // b is not big enough or r is not a ASCII rune. if r >= 0 { if nbytes+utf8.UTFMax >= len(b) { // Grow the buffer. nb := make([]byte, 2*len(b)) copy(nb, b[:nbytes]) b = nb } nbytes += utf8.EncodeRune(b[nbytes:], r) } } return string(b[:nbytes]) } // Repeat returns a new string consisting of count copies of the string s. // // It panics if count is negative or if // the result of (len(s) * count) overflows. func Repeat(s string, count int) string { // Since we cannot return an error on overflow, // we should panic if the repeat will generate // an overflow. // See Issue golang.org/issue/16237 if count < 0 { panic("strings: negative Repeat count") } else if count > 0 && len(s)*count/count != len(s) { panic("strings: Repeat count causes overflow") } b := make([]byte, len(s)*count) bp := copy(b, s) for bp < len(b) { copy(b[bp:], b[:bp]) bp *= 2 } return string(b) } // ToUpper returns a copy of the string s with all Unicode letters mapped to their upper case. func ToUpper(s string) string { return Map(unicode.ToUpper, s) } // ToLower returns a copy of the string s with all Unicode letters mapped to their lower case. func ToLower(s string) string { return Map(unicode.ToLower, s) } // ToTitle returns a copy of the string s with all Unicode letters mapped to their title case. func ToTitle(s string) string { return Map(unicode.ToTitle, s) } // ToUpperSpecial returns a copy of the string s with all Unicode letters mapped to their // upper case, giving priority to the special casing rules. func ToUpperSpecial(c unicode.SpecialCase, s string) string { return Map(func(r rune) rune { return c.ToUpper(r) }, s) } // ToLowerSpecial returns a copy of the string s with all Unicode letters mapped to their // lower case, giving priority to the special casing rules. func ToLowerSpecial(c unicode.SpecialCase, s string) string { return Map(func(r rune) rune { return c.ToLower(r) }, s) } // ToTitleSpecial returns a copy of the string s with all Unicode letters mapped to their // title case, giving priority to the special casing rules. func ToTitleSpecial(c unicode.SpecialCase, s string) string { return Map(func(r rune) rune { return c.ToTitle(r) }, s) } // isSeparator reports whether the rune could mark a word boundary. // TODO: update when package unicode captures more of the properties. func isSeparator(r rune) bool { // ASCII alphanumerics and underscore are not separators if r <= 0x7F { switch { case '0' <= r && r <= '9': return false case 'a' <= r && r <= 'z': return false case 'A' <= r && r <= 'Z': return false case r == '_': return false } return true } // Letters and digits are not separators if unicode.IsLetter(r) || unicode.IsDigit(r) { return false } // Otherwise, all we can do for now is treat spaces as separators. return unicode.IsSpace(r) } // Title returns a copy of the string s with all Unicode letters that begin words // mapped to their title case. // // BUG(rsc): The rule Title uses for word boundaries does not handle Unicode punctuation properly. func Title(s string) string { // Use a closure here to remember state. // Hackish but effective. Depends on Map scanning in order and calling // the closure once per rune. prev := ' ' return Map( func(r rune) rune { if isSeparator(prev) { prev = r return unicode.ToTitle(r) } prev = r return r }, s) } // TrimLeftFunc returns a slice of the string s with all leading // Unicode code points c satisfying f(c) removed. func TrimLeftFunc(s string, f func(rune) bool) string { i := indexFunc(s, f, false) if i == -1 { return "" } return s[i:] } // TrimRightFunc returns a slice of the string s with all trailing // Unicode code points c satisfying f(c) removed. func TrimRightFunc(s string, f func(rune) bool) string { i := lastIndexFunc(s, f, false) if i >= 0 && s[i] >= utf8.RuneSelf { _, wid := utf8.DecodeRuneInString(s[i:]) i += wid } else { i++ } return s[0:i] } // TrimFunc returns a slice of the string s with all leading // and trailing Unicode code points c satisfying f(c) removed. func TrimFunc(s string, f func(rune) bool) string { return TrimRightFunc(TrimLeftFunc(s, f), f) } // IndexFunc returns the index into s of the first Unicode // code point satisfying f(c), or -1 if none do. func IndexFunc(s string, f func(rune) bool) int { return indexFunc(s, f, true) } // LastIndexFunc returns the index into s of the last // Unicode code point satisfying f(c), or -1 if none do. func LastIndexFunc(s string, f func(rune) bool) int { return lastIndexFunc(s, f, true) } // indexFunc is the same as IndexFunc except that if // truth==false, the sense of the predicate function is // inverted. func indexFunc(s string, f func(rune) bool, truth bool) int { for i, r := range s { if f(r) == truth { return i } } return -1 } // lastIndexFunc is the same as LastIndexFunc except that if // truth==false, the sense of the predicate function is // inverted. func lastIndexFunc(s string, f func(rune) bool, truth bool) int { for i := len(s); i > 0; { r, size := utf8.DecodeLastRuneInString(s[0:i]) i -= size if f(r) == truth { return i } } return -1 } // asciiSet is a 32-byte value, where each bit represents the presence of a // given ASCII character in the set. The 128-bits of the lower 16 bytes, // starting with the least-significant bit of the lowest word to the // most-significant bit of the highest word, map to the full range of all // 128 ASCII characters. The 128-bits of the upper 16 bytes will be zeroed, // ensuring that any non-ASCII character will be reported as not in the set. type asciiSet [8]uint32 // makeASCIISet creates a set of ASCII characters and reports whether all // characters in chars are ASCII. func makeASCIISet(chars string) (as asciiSet, ok bool) { for i := 0; i < len(chars); i++ { c := chars[i] if c >= utf8.RuneSelf { return as, false } as[c>>5] |= 1 << uint(c&31) } return as, true } // contains reports whether c is inside the set. func (as *asciiSet) contains(c byte) bool { return (as[c>>5] & (1 << uint(c&31))) != 0 } func makeCutsetFunc(cutset string) func(rune) bool { if len(cutset) == 1 && cutset[0] < utf8.RuneSelf { return func(r rune) bool { return r == rune(cutset[0]) } } if as, isASCII := makeASCIISet(cutset); isASCII { return func(r rune) bool { return r < utf8.RuneSelf && as.contains(byte(r)) } } return func(r rune) bool { return IndexRune(cutset, r) >= 0 } } // Trim returns a slice of the string s with all leading and // trailing Unicode code points contained in cutset removed. func Trim(s string, cutset string) string { if s == "" || cutset == "" { return s } return TrimFunc(s, makeCutsetFunc(cutset)) } // TrimLeft returns a slice of the string s with all leading // Unicode code points contained in cutset removed. func TrimLeft(s string, cutset string) string { if s == "" || cutset == "" { return s } return TrimLeftFunc(s, makeCutsetFunc(cutset)) } // TrimRight returns a slice of the string s, with all trailing // Unicode code points contained in cutset removed. func TrimRight(s string, cutset string) string { if s == "" || cutset == "" { return s } return TrimRightFunc(s, makeCutsetFunc(cutset)) } // TrimSpace returns a slice of the string s, with all leading // and trailing white space removed, as defined by Unicode. func TrimSpace(s string) string { return TrimFunc(s, unicode.IsSpace) } // TrimPrefix returns s without the provided leading prefix string. // If s doesn't start with prefix, s is returned unchanged. func TrimPrefix(s, prefix string) string { if HasPrefix(s, prefix) { return s[len(prefix):] } return s } // TrimSuffix returns s without the provided trailing suffix string. // If s doesn't end with suffix, s is returned unchanged. func TrimSuffix(s, suffix string) string { if HasSuffix(s, suffix) { return s[:len(s)-len(suffix)] } return s } // Replace returns a copy of the string s with the first n // non-overlapping instances of old replaced by new. // If old is empty, it matches at the beginning of the string // and after each UTF-8 sequence, yielding up to k+1 replacements // for a k-rune string. // If n < 0, there is no limit on the number of replacements. func Replace(s, old, new string, n int) string { if old == new || n == 0 { return s // avoid allocation } // Compute number of replacements. if m := Count(s, old); m == 0 { return s // avoid allocation } else if n < 0 || m < n { n = m } // Apply replacements to buffer. t := make([]byte, len(s)+n*(len(new)-len(old))) w := 0 start := 0 for i := 0; i < n; i++ { j := start if len(old) == 0 { if i > 0 { _, wid := utf8.DecodeRuneInString(s[start:]) j += wid } } else { j += Index(s[start:], old) } w += copy(t[w:], s[start:j]) w += copy(t[w:], new) start = j + len(old) } w += copy(t[w:], s[start:]) return string(t[0:w]) } // EqualFold reports whether s and t, interpreted as UTF-8 strings, // are equal under Unicode case-folding. func EqualFold(s, t string) bool { for s != "" && t != "" { // Extract first rune from each string. var sr, tr rune if s[0] < utf8.RuneSelf { sr, s = rune(s[0]), s[1:] } else { r, size := utf8.DecodeRuneInString(s) sr, s = r, s[size:] } if t[0] < utf8.RuneSelf { tr, t = rune(t[0]), t[1:] } else { r, size := utf8.DecodeRuneInString(t) tr, t = r, t[size:] } // If they match, keep going; if not, return false. // Easy case. if tr == sr { continue } // Make sr < tr to simplify what follows. if tr < sr { tr, sr = sr, tr } // Fast check for ASCII. if tr < utf8.RuneSelf && 'A' <= sr && sr <= 'Z' { // ASCII, and sr is upper case. tr must be lower case. if tr == sr+'a'-'A' { continue } return false } // General case. SimpleFold(x) returns the next equivalent rune > x // or wraps around to smaller values. r := unicode.SimpleFold(sr) for r != sr && r < tr { r = unicode.SimpleFold(r) } if r == tr { continue } return false } // One string is empty. Are both? return s == t }
派生类型:
指针类型(Pointer) 结构化类型(struct)
数组类型
package main import "fmt" func main() { var a [2]string a[0] = "Hello" a[1] = "World" fmt.Println(a[0], a[1]) fmt.Println(a) primes := [6]int{2, 3, 5, 7, 11, 13} fmt.Println(primes) }
1.数组简介
数组,是指一系列同一类型数据的集合,数组长度是固定的
2.数组基本操作
2.1 数组声明
var 数组名 [元素数量] 类型
2.2 数组初始化
package main import "fmt" func main() { ///* //var Numbers[5] int = [5]int{1,2,3,4,5} // 下标是从0开始计算的。 //等效于 var Numbers = [5]int{1,2,3,4,5} // 下标是从0开始计算的。 fmt.Println(Numbers[3]) //*/ // 部分赋值 /* Numbers := [5]int{1,2} fmt.Println(Numbers[4]) */ // 指定某个元素初始化 /* Numbers :=[5]int{2:5,3:6} fmt.Println(Numbers[3]) */ //通过初始化确定数组长度 /* Numbers :=[...]int{7,8,5} //fmt.Println(len(Numbers)) fmt.Println(Numbers[0]) */ //var Numbers [5] int /* Numbers[0]=1 Numbers[1]=2 fmt.Println(Numbers[3]) */ //for i := 0; i < len(Numbers); i++ { // Numbers[i] = i + 1 //} //fmt.Println(Numbers[0]) }
3. 数组遍历
package main import "fmt" func main() { var Numbers [5] int = [5]int{1, 2, 3, 4, 5} /* for i := 0; i < len(Numbers); i++ { fmt.Println(Numbers[i]) } */ for _, v := range Numbers { // fmt.Println("下标:", i) fmt.Println("值:", v) } }
4. 数组做函数参数
package main import "fmt" func main() { var Numbers [5]int = [5]int{1, 2, 3, 4, 5} getPrint(Numbers) } func getPrint(n [5]int) { for i := 0; i < len(n); i++ { fmt.Println(n[i]) } }
5. 二维数组
package main import "fmt" func main() { // var arr [2][3]int = [2][3]int{{1, 2, 3}, {5, 6, 7}} // 全部初始化 // 部分初始化 // var arr [2][3]int = [2][3]int{{1,2},{6}} // 指定元素初始化 // var arr [2][3]int = [2][3]int{0:{1:6}} // 通过初始化确定二维数组行数 // arr := [...][3]int{{1, 2, 3}, {5, 6}} // 行的下标可以用"..."来代替,但是列的下标不能用"..."来代替。 // fmt.Println(arr) // 通过循环遍历的方式输出打印二维数组中的值。 var arr [2][3]int = [2][3]int{{1, 2, 3}, {5, 6, 7}} // fmt.Println(len(arr)) // 输出的是有几行。 //fmt.Println(len(arr[0])) // 输出有几列。 // fmt.Println(arr[0]) /* for i := 0; i < len(arr); i++ { // 遍历的行 for j := 0; j < len(arr[0]); j++ { // 遍历的是列。 fmt.Println(arr[i][j]) } } */ for _, v := range arr { //fmt.Println("i", i) //fmt.Println("v", v) for j, data := range v { fmt.Println("j:",j) fmt.Println("data:",data) } } /* arr[0][1]=123 arr[1][1]=456 fmt.Println(arr[0][1]) */ }
注意:由于是值传递,函数实参如果是数组,函数内部的处理要影响外部数组,一般形参不会采用数组的指针,而是数组的切片作实参或者形参为[]type
package main import ( "fmt" ) func printArray(arr []int) { arr[0] = 100 for i, v := range arr { fmt.Println(i, v) } } func main() { a := [...]int{1, 2, 3, 4, 5} s_a := a[:] printArray(s_a) fmt.Println(a) } ######### 0 100 1 2 2 3 3 4 4 5 [100 2 3 4 5]
package main import ( "fmt" ) func printArray(arr []int) { arr[0] = 100 for i, v := range arr { fmt.Println(i, v) } } func main() { a := []int{1, 2, 3, 4, 5} printArray(a) fmt.Println(a) } #### 0 100 1 2 2 3 3 4 4 5 [100 2 3 4 5]
package main import ( "fmt" ) func printArray(arr [5]int) { arr[0] = 100 for i, v := range arr { fmt.Println(i, v) } } func main() { a := [...]int{1, 2, 3, 4, 5} printArray(a) fmt.Println(a) } ############### 0 100 1 2 2 3 3 4 4 5 [1 2 3 4 5]
数组声明:
var name []type //声明不定长数组(或者说切片) var name [n]type //声明定长数组
数组的赋值和C风格一致,这里只阐述常用赋值方法
示例一:
package main //必须有个main包 import "fmt" func main() { //声明定义同时赋值,叫初始化 //1、全部初始化 var a [5]int = [5]int{1, 2, 3, 4, 5} fmt.Println("a = ", a) b := [5]int{1, 2, 3, 4, 5} fmt.Println("b = ", b) //部分初始化,没有初始化的元素,自动赋值为0 c := [5]int{1, 2, 3} fmt.Println("c = ", c) //指定某个元素初始化 d := [5]int{2: 10, 4: 20} fmt.Println("d = ", d) }
package main //必须有个main包 import "fmt" func main() { //有多少个[]就是多少维 //有多少个[]就用多少个循环 var a [3][4]int k := 0 for i := 0; i < 3; i++ { for j := 0; j < 4; j++ { k++ a[i][j] = k fmt.Printf("a[%d][%d] = %d, ", i, j, a[i][j]) } fmt.Printf("\n") } fmt.Println("a = ", a) //有3个元素,每个元素又是一维数组[4]int b := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}} fmt.Println("b = ", b) //部分初始化,没有初始化的值为0 c := [3][4]int{{1, 2, 3}, {5, 6, 7, 8}, {9, 10}} fmt.Println("c = ", c) d := [3][4]int{{1, 2, 3, 4}, {5, 6, 7, 8}} fmt.Println("d = ", d) e := [3][4]int{1: {5, 6, 7, 8}} fmt.Println("e = ", e) }
package main //必须有个main包 import "fmt" //数组做函数参数,它是值传递 //实参数组的每个元素给形参数组拷贝一份 //形参的数组是实参数组的复制品 func modify(a [5]int) { a[0] = 666 fmt.Println("modify a = ", a) } func main() { a := [5]int{1, 2, 3, 4, 5} //初始化 modify(a) //数组传递过去 fmt.Println("main: a = ", a) }
package main //必须有个main包 import "fmt" //p指向实现数组a,它是指向数组,它是数组指针 //*p代表指针所指向的内存,就是实参a func modify(p *[5]int) { (*p)[0] = 666 fmt.Println("modify *a = ", *p) } func main() { a := [5]int{1, 2, 3, 4, 5} //初始化 modify(&a) //地址传递 fmt.Println("main: a = ", a) }
示例二:
package main import "fmt" func printArray(arr [5]int) { arr[0] = 100 for i, v := range arr { fmt.Println(i, v) } } func main() { var arr1 [5]int arr2 := [3]int{1, 3, 5} arr3 := [...]int{2, 4, 6, 8, 10} var grid [4][5]int fmt.Println("array definitions:") fmt.Println(arr1, arr2, arr3) fmt.Println(grid) fmt.Println("printArray(arr1)") printArray(arr1) fmt.Println(arr1) fmt.Println("printArray(arr3)") printArray(arr3) fmt.Println(arr1) fmt.Println("arr1 and arr3") fmt.Println(arr1, arr3) } ###### array definitions: [0 0 0 0 0] [1 3 5] [2 4 6 8 10] [[0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0] [0 0 0 0 0]] printArray(arr1) 0 100 1 0 2 0 3 0 4 0 [0 0 0 0 0] printArray(arr3) 0 100 1 4 2 6 3 8 4 10 [0 0 0 0 0] arr1 and arr3 [0 0 0 0 0] [2 4 6 8 10]
package main import "fmt" var a []int func main() { //数组赋值 a = []int{1, 2, 3, 4, 5} //数组切片、打印 mySlice := a[:2] fmt.Println(mySlice) fmt.Println("len(mySlice):", len(mySlice)) fmt.Println("cap(mySlice):", cap(mySlice)) // 数组遍历 for i, v := range a { fmt.Println("Array element[", i, "]=", v) } // 数组遍历 for i := 0; i < len(a); i++ { fmt.Println("Element", i, "of array is", a[i]) } } #################### [1 2] len(mySlice): 2 cap(mySlice): 5 Array element[ 0 ]= 1 Array element[ 1 ]= 2 Array element[ 2 ]= 3 Array element[ 3 ]= 4 Array element[ 4 ]= 5 Element 0 of array is 1 Element 1 of array is 2 Element 2 of array is 3 Element 3 of array is 4 Element 4 of array is 5
package main import ( "fmt" ) func main() { //创建数组切片 mySlice := make([]int, 5, 10) fmt.Println(mySlice) fmt.Println("len(mySlice):", len(mySlice)) fmt.Println("cap(mySlice):", cap(mySlice)) mySlice = append(mySlice, 1, 2, 3) fmt.Println(mySlice) mySlice2 := []int{8, 9, 10} mySlice = append(mySlice, mySlice2...) fmt.Println(mySlice) //基于数组切片创建数组切片 againSlice := mySlice[:7] fmt.Println(againSlice) } ############# [0 0 0 0 0] len(mySlice): 5 cap(mySlice): 10 [0 0 0 0 0 1 2 3] [0 0 0 0 0 1 2 3 8 9 10] [0 0 0 0 0 1 2]
切片类型
一个切片是一个数组片段的描述。它包含了指向数组的指针,片段的长度, 和容量(片段的最大长度)。
1.切片简介
数组的问题 长度固定,不灵活 切片 长度是不固定的,可以追加元素,在追加时容量增大,可以理解成"动态数组"
数组的切片会自动转为切片类型
切片就像数组的引用
切片并不存储任何数据,它只是描述了底层数组中的一段。
更改切片的元素会修改其底层数组中对应的元素。
与它共享底层数组的切片都会观测到这些修改。
切片截取后返回新切片,对新切片的值进行修改,会影响原切片
package main func main() { // var nums[5]int=[5]int{1,2,3,4,5,8} }
2.切片创建
var 切片名 []数据类型 切片名 := []类型{} 使用make( )函数创建 make(切片类型, 长度, 容量) 长度是已经初始化的空间。容量是已经开辟的空间,包括已经初始化的空间和空闲的空间。 在使用make( )函数定义切片时,一定要注意,切片长度要小于容量 len( )函数返回长度,cap( )返回容量 make( )函数中的容量参数是可以省略掉的,这时容量与长度是相等的。
package main import "fmt" func main() { // var s[]int // s := []int{} s:=make([]int,3,5) // 长度不能大于容量。 fmt.Println(s) fmt.Println(len(s)) fmt.Println(cap(s)) }
3.切片初始化
var 切片名 []数据类型 可以使用append函数追加值 注意:切片名[下标]=值,表示的是修改值 切片名 := []类型{} 直接在大括号中添加值 可以使用append函数追加值 使用make( )函数创建 切片名[下标]=值 的方式完成初始化 可以使用append函数追加值 通过循环的方式完成初始化 循环结束条件是小于切片的长度,而不是容量
package main import "fmt" func main() { /* var s []int s = append(s, 1, 2, 3, 4, 5, 89, 90) s[3] = 88 // 通过这种方式可以完成某个值的修改。 fmt.Println(s[3]) fmt.Println(s) */ /* s := []int{8, 9, 7, 10, 12, 13} s = append(s, 99, 100) s[0] = 78 fmt.Println(s[0]) fmt.Println(s) */ s := make([]int, 3) for i:=0;i<len(s) ;i++ { s[i]=i+1 } s=append(s,80) s[3]=90 //s[4]=100 /* s[0]=10 s[1]=20 s[2]=30 */ fmt.Print(len(s), cap(s)) // s[3]=78 // 会造成下标越界。 //s[4]=80 fmt.Println(s) }
4.切片遍历
for...len( )方式进行遍历 for...range方式进行遍历
package main import "fmt" func main() { s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} /* for i := 0; i < len(s); i++ { fmt.Println(s[i]) } */ for _,v:= range s{ //fmt.Println("i=",i) fmt.Println("v=",v) } }
5.切片截取
概念 所谓截取就是从切片中获取指定的数据 截取操作 s[low:high:max] 第一个数(low)表示下标的起点(从该位置开始截取) 第二个数(high)表示取到哪结束,也就是下标的终点(不包含该位置) 第三个数用来计算容量,所谓容量:是指切片目前可容纳的最多元素个数 容量计算公式 max-low 长度计算公式 high-low
package main import "fmt" func main() { s := []int{3, 5, 6, 7, 8, 9} //第一个值:截取的起始位置 // 第二个值;截取的终止位置(不包含该值的) // 第三个值:用来计算容量,容量指的是切片中最多能够容纳多少元素。 //容量=第三个值减去第一个值。 //长度=第二个值减去第一个值 //s1 := s[1:3:5] //fmt.Println(s1) //fmt.Println(cap(s1)) //fmt.Println(len(s1)) //s1 := s[:] //fmt.Println(s1) //fmt.Println(len(s1)) //fmt.Println(cap(s1)) s1 := s[1:] fmt.Println(len(s1)) fmt.Println(cap(s1)) fmt.Println(s1) //s1:=s[:3] // fmt.Println(s1) // fmt.Println(len(s1)) // fmt.Println(cap(s1)) //s1:=s[1:3] //fmt.Println(len(s1)) //fmt.Println(cap(s1)) //fmt.Println(s1) }
6.切片值修改
切片截取后返回新切片,对新切片的值进行修改,会影响原切片
package main import "fmt" func main() { s := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} // s[0] = 30 s1 := s[2:5] //fmt.Println(s) s1[0] = 80 fmt.Println("s1=", s1) fmt.Println("s=",s) }
7.append函数使用
append函数基本使用
append函数向切片末尾追加数据
切片扩容
一般扩容方式为上一次:容量*2, 如果超过1024字节 每次扩容上一次的1/4
package main import "fmt" func main() { s := make([]int, 5, 8) s = append(s, 1) s = append(s, 2) s = append(s, 3) s = append(s, 4) fmt.Println("len=", len(s)) fmt.Println("cap=", cap(s)) fmt.Println(s) }
8.copy函数使用
基本语法
copy(切片1,切片2)
注意事项
拷贝的长度为两个切片中长度较小的长度值。
package main import "fmt" func main() { s1:=[]int{1,2} s2:=[]int{3,4,5,6,7} // copy(s1,s2) copy(s2,s1) fmt.Println(s2) }
9.切片作为函数参数
基本语法
func 函数名 (切片 ) {
函数体
}
函数名(切片)
注意事项
在函数中修改切片的值,会影响到原切片
package main import "fmt" func main() { //s := []int{1, 2, 3, 4, 5} s:=make([]int,10) Init(s) fmt.Println("s=",s) } func Init(num []int) { for i:=0;i<len(num) ;i++ { num[i]=i } /* for i := 0; i < len(num); i++ { fmt.Println(num[i]) } */ }
字典类型
1.map结构简介
数组,切片问题
获取数据比较麻烦
概念
map 是一种无序的键值对的集合。
map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。
注意
键不允许出现重复
2.map创建与初始化
map创建 var map名字 map[键的类型]值的类型 map名字 := map[键的类型]值的类型{ } make(map[键的类型]值的类型) map初始化 var map名字 map[键的类型]值的类型 map[键的类型]值的类型{键:值} 注意:键不允许重复 map名字 := map[键的类型]值的类型{ } 直接在大括号中添加键与值 注意:键不允许重复 make(map[键的类型]值的类型) map名字[键]=值 键的名字一致完成值的修改
package main import "fmt" func main() { var m =map[int]string{1:"张三",2:"李四",3:"王五",4:"itcast"} // key是唯一的。 m1 := map[int]string{1:"张三",2:"李四",3:"王五",4:"itcast"} m2 := make(map[string]int,10) m2["张三"]=12 m2["李四"]=15 m2["张三"]=16 //完成数据的修改。 fmt.Println(m) fmt.Println(m1) fmt.Println(len(m2)) // len()返回的是map中已有的键值对个数。 fmt.Println(m2) }
3.map键与值
通过key获取值 map名字[键] 通过key获取值时,判断是否存在 变量1,变量2 := map名字[键] 如果键是存在的”变量1”中存储对应的值,并”变量2”的值为true,否则为false 通过循环方式获取值 通过for...range方式进行遍历 通过key删除某个值 delete(map名字,键)
package main import "fmt" func main() { var m map[int]string = map[int]string{1: "王五", 2: "李四"} // fmt.Println(m[2]) value, ok := m[6] if ok { fmt.Println(value) } else { fmt.Println(value) fmt.Println("不存在") } /* for key, value := range m { fmt.Println(key) fmt.Println(value) } */ delete(m,2) fmt.Println(m) }
4.map作为函数参数
基本语法
func 函数名 (map ) {
函数体
}
函数名(map)
注意事项
在函数中修改map的值,会影响到原map
package main import "fmt" func main() { var m map[int]string = map[int]string{1: "张三", 2: "李四"} DeleteMap(m) PrintMap(m) } func PrintMap(m map[int]string) { for key, value := range m { fmt.Println(key) fmt.Println(value) } } func DeleteMap(m map[int]string) { delete(m, 2) }
5.更多demo
package main //必须有个main包 import "fmt" func main() { //定义一个变量, 类型为map[int]string var m1 map[int]string fmt.Println("m1 = ", m1) //对于map只有len,没有cap fmt.Println("len = ", len(m1)) //可以通过make创建 m2 := make(map[int]string) fmt.Println("m2 = ", m2) fmt.Println("len = ", len(m2)) //可以通过make创建,可以指定长度,只是指定了容量,但是里面却是一个数据也没有 m3 := make(map[int]string, 2) m3[1] = "mike" //元素的操作 m3[2] = "go" m3[3] = "c++" fmt.Println("m3 = ", m3) fmt.Println("len = ", len(m3)) //初始化 //键值是唯一的 m4 := map[int]string{1: "mike", 2: "go", 3: "c++"} fmt.Println("m4 = ", m4) }
package main //必须有个main包 import "fmt" func main() { m1 := map[int]string{1: "mike", 2: "yoyo"} //赋值,如果已经存在的key值,修改内容 fmt.Println("m1 = ", m1) m1[1] = "c++" m1[3] = "go" //追加,map底层自动扩容,和append类似 fmt.Println("m1 = ", m1) }
package main //必须有个main包 import "fmt" func main() { m := map[int]string{1: "mike", 2: "yoyo", 3: "go"} //第一个返回值为key, 第二个返回值为value, 遍历结果是无序的 for key, value := range m { fmt.Printf("%d =======> %s\n", key, value) } //如何判断一个key值是否存在 //第一个返回值为key所对应的value, 第二个返回值为key是否存在的条件,存在ok为true value, ok := m[0] if ok == true { fmt.Println("m[1] = ", value) } else { fmt.Println("key不存在") } }
package main //必须有个main包 import "fmt" func main() { m := map[int]string{1: "mike", 2: "yoyo", 3: "go"} fmt.Println("m = ", m) delete(m, 1) //删除key为1的内容 fmt.Println("m = ", m) }
package main import "fmt" func main() { //有一个英文字符串 统计每个字母出现的次数 var str string = "helloworld" // 1;循环整个字符串,取出每个字母 m := make(map[byte]int) for i := 0; i < len(str); i++ { ch := str[i] // ch='h' ch='e' ch='l' ch='l' m[ch] = m[ch] + 1 // m['h']=1 // m['e']=1 // m['l']=1 // m['l']=2 } // 2;统计 // 3:输出结果 // h 1 // e 1 //l 3 for key,value:= range m { fmt.Printf("%c:%d\n",key,value) } }
字典作为函数参数,传引用
package main // 规范:包名和入口函数必须是main import ( . "fmt" ) func test(m map[string]string) { delete(m, "France") } func main() { m := map[string] string {"France":"Paris","Italy":"Rome","Japan":"Tokyo","India":"New Delhi"} Println(m) test(m) Println(m) } /* map[India:New Delhi France:Paris Italy:Rome Japan:Tokyo] map[Italy:Rome Japan:Tokyo India:New Delhi] */
结构体类型
1.结构体简介
概念
结构体是由一系列具有相同类型或不同类型的数据构成的数据集合。
应用场景
结构体可以很好地管理一批有联系的数据,使用结构体可以提高程序的易读性
2.结构体创建与初始化
结构体创建
type 结构体名 struct {
成员名 数据类型
...................
}
初始化
顺序初始化
每个成员必须初始化,在初始化时,
值的顺序与结构体成员的顺序保持一致。
指定成员初始化
通过“结构体变量.成员” 完成初始化
3.结构体与数组
结构体数组定义 var 结构体数组名 [下标] 结构体类型 修改结构体成员的值 结构体数组名[下标].成员=值 循环遍历 for...len() 进行遍历 for...range 进行遍历
package main /* type Student struct { id int name string age int addr string } */ func main() { //var arr [3]Student = [3]Student{ // Student{101, "张三", 18, "北京"}, // Student{102, "李四", 18, "北京"}, // Student{103, "王五", 19, "北京"}, //} //fmt.Println(arr) /* fmt.Println(arr[0]) fmt.Println(arr[0].age) arr[0].age=20 fmt.Println(arr) // 通过循环来输出结构体数组中的内容。 for i:=0;i<len(arr) ;i++ { fmt.Println(arr[i].age) } */ //for k, v := range arr { // fmt.Println(k) // fmt.Println(v.age) //} }
4.结构体与切片
结构体切片定义 var 结构体切片名 [ ] 结构体类型 修改结构体成员的值 结构体切片名[下标].成员=值 循环遍历 for...len() 进行遍历 for...range 进行遍历 append函数使用 向切片末尾追加数据
package main /* type Student struct { id int name string age int addr string } */ func main() { //var arr [3]Student = [3]Student{ // Student{101, "张三", 18, "北京"}, // Student{102, "李四", 18, "北京"}, // Student{103, "王五", 19, "北京"}, //} //fmt.Println(arr) /* fmt.Println(arr[0]) fmt.Println(arr[0].age) arr[0].age=20 fmt.Println(arr) // 通过循环来输出结构体数组中的内容。 for i:=0;i<len(arr) ;i++ { fmt.Println(arr[i].age) } */ //for k, v := range arr { // fmt.Println(k) // fmt.Println(v.age) //} }
5.结构体与字典
结构体map定义 make(map[key的类型]值的类型) 初始化 map名字[键]=值 循环遍历 通过for...range方式进行遍历 删除map中的值 delete(map名字,键)
package main /* type Student struct { id int name string age int addr string } */ func main() { //var arr [3]Student = [3]Student{ // Student{101, "张三", 18, "北京"}, // Student{102, "李四", 18, "北京"}, // Student{103, "王五", 19, "北京"}, //} //fmt.Println(arr) /* fmt.Println(arr[0]) fmt.Println(arr[0].age) arr[0].age=20 fmt.Println(arr) // 通过循环来输出结构体数组中的内容。 for i:=0;i<len(arr) ;i++ { fmt.Println(arr[i].age) } */ //for k, v := range arr { // fmt.Println(k) // fmt.Println(v.age) //} }
6.结构体作为函数参数
基本语法
func 函数名 (结构体) {
函数体
}
函数名(结构体)
注意事项
在函数中修改结构体成员值,不会影响到原结构体
结构体作为函数参数案例
package main import "fmt" type Student struct { id int name string age int addr string } func main() { // 1: 输入学生信息 stu := make([]Student, 3) InitData(stu) // 2: 进行比较 GetMax(stu) // 3:打印结果 //stu := Student{101, "张三", 18, "北京"} //PrintDemo(stu) //fmt.Println(stu) } func InitData(stu []Student) { for i := 0; i < len(stu); i++ { fmt.Printf("请输入第%d个学生的详细信息\n", i+1) fmt.Scan(&stu[i].id, &stu[i].name, &stu[i].age, &stu[i].addr) } } func GetMax(stu []Student) { var max int = stu[0].age var maxIndex int // 记录最大年龄学生信息在整个切片中下标。 for i := 0; i < len(stu); i++ { if stu[i].age > max { max = stu[i].age maxIndex = i } } fmt.Println(stu[maxIndex]) } //func PrintDemo(stu Student) { // stu.age = 20 // // fmt.Println(stu) //}
指针类型
Go 拥有指针。指针保存了值的内存地址。
类型 *T
是指向 T
类型值的指针。其零值为 nil
。
1.指针简介
概念
指针也是一个变量,但它是一种特殊的变量,因为它存储的数据不仅仅是一个普通的值,如简单的整数或字符串,而是另一个变量的内存地址
2.指针定义
package main import "fmt" func main() { var a int = 10 var p *int p = &a //fmt.Printf("%p\n", &a) //fmt.Printf("%p", p) fmt.Println(*p) *p=222 fmt.Println("a=",a) }
3.指针操作注意事项
空指针
不要操作没有合法指向的内存
new 函数使用
开辟数据类型对应的内存空间 返回值为数据类型指针
package main func main() { var p *int //fmt.Println(p) *p = 78 //p = new(int) //*p = 67 //fmt.Println(*p) }
4.指针作为函数参数
package main import "fmt" func main() { var num int = 10 //Update(num) Update(&num) fmt.Println(num) } func Update(p *int) { *p = 60 } //func Update(n int) { // n = 60 //}
5.数组指针
package main import "fmt" func main() { nums:=[10]int{1,2,3,4,5,6,7,8,9,10} var p *[10]int p=&nums // fmt.Println(*p) // 获取整个数组中的内容。 // fmt.Println((*p)[3])// []的运算优先级高于* // fmt.Println(p[0]) //for i:=0;i<len(p) ;i++ { // fmt.Println(p[i]) //} UpdateArr(p) fmt.Println(nums) } func UpdateArr(p*[10]int) { p[0]=100 }
6.指针数组
package main import "fmt" func main() { nums:=[10]int{1,2,3,4,5,6,7,8,9,10} var p *[10]int p=&nums // fmt.Println(*p) // 获取整个数组中的内容。 // fmt.Println((*p)[3])// []的运算优先级高于* // fmt.Println(p[0]) //for i:=0;i<len(p) ;i++ { // fmt.Println(p[i]) //} UpdateArr(p) fmt.Println(nums) } func UpdateArr(p*[10]int) { p[0]=100 }
7.指针与切片
package main import "fmt" func main() { s:=[]int{1,2,3,4,5,6} var p *[]int p=&s //fmt.Println(*p) fmt.Println((*p)[0]) //fmt.Println(p[0]) //错误 //(*p)[0]=200 //fmt.Println(s) //for i:=0;i<len(*p) ; i++ { // fmt.Println((*p)[i]) //} for k,value:= range *p{ fmt.Println("k=",k) fmt.Println("value=",value) } }
8.指针与结构体
package main import "fmt" func main() { s:=[]int{1,2,3,4,5,6} var p *[]int p=&s //fmt.Println(*p) fmt.Println((*p)[0]) //fmt.Println(p[0]) //错误 //(*p)[0]=200 //fmt.Println(s) //for i:=0;i<len(*p) ; i++ { // fmt.Println((*p)[i]) //} for k,value:= range *p{ fmt.Println("k=",k) fmt.Println("value=",value) } }
9.多级指针
package main import "fmt" func main() { s:=[]int{1,2,3,4,5,6} var p *[]int p=&s //fmt.Println(*p) fmt.Println((*p)[0]) //fmt.Println(p[0]) //错误 //(*p)[0]=200 //fmt.Println(s) //for i:=0;i<len(*p) ; i++ { // fmt.Println((*p)[i]) //} for k,value:= range *p{ fmt.Println("k=",k) fmt.Println("value=",value) } }
new
new函数不太常用,使用new函数得到的是一个类型的指针,并且该指针对应的值为该类型的零值。
package main //必须有个main包 import "fmt" func main() { //a := 10 //整型变量a var p *int //指向一个合法内存 //p = &a //p是*int, 指向int类型 p = new(int) *p = 666 fmt.Println("*p = ", *p) q := new(int) //自动推导类型 *q = 777 fmt.Println("*q = ", *q) }
不要操作没有合法指向的内存
package main //必须有个main包 import "fmt" func main() { var p *int p = nil fmt.Println("p = ", p) //*p = 666 //err, 因为p没有合法指向 var a int p = &a //p指向a *p = 666 fmt.Println("a = ", a) }
流程控制
表达式条件不需要括号括起来
从根本上讲,流程控制只是为了控制程序语句的执行顺序,一般需要与各种条件配合,因此, 在各种流程中,会加入条件判断语句。流程控制语句一般起以下3个作用: 选择,即根据条件跳转到不同的执行序列; 循环,即根据条件反复执行某个序列,当然每一次循环执行的输入输出可能会发生变化; 跳转,即根据条件返回到某执行序列。 Go语言支持如下的几种流程控制语句: 条件语句,对应的关键字为if、 else和else if; 选择语句,对应的关键字为switch、 case和select(将在介绍channel的时候细说); 循环语句,对应的关键字为for和range; 跳转语句,对应的关键字为goto。但是不能跨函数使用 在具体的应用场景中,为了满足更丰富的控制需求, Go语言还添加了如下关键字: break、 continue和fallthrough。
IF
package main //必须有一个main包 import "fmt" func main() { s := "屌丝" //if和{就是条件,条件通常都是关系运算符 if s == "王思聪" { //左括号和if在同一行 fmt.Println("左手一个妹子,右手一个大妈") } //if支持1个初始化语句, 初始化语句和判断条件以分号分隔 if a := 10; a == 10 { //条件为真,指向{}语句 fmt.Println("a == 10") } }
package main //必须有一个main包 import "fmt" func main() { //1 a := 10 if a == 10 { fmt.Println("a == 10") } else { //else后面没有条件 fmt.Println("a != 10") } //2 if a := 10; a == 10 { fmt.Println("a == 10") } else { //else后面没有条件 fmt.Println("a != 10") } //3 a = 8 if a == 10 { fmt.Println("a == 10") } else if a > 10 { fmt.Println("a > 10") } else if a < 10 { fmt.Println("a < 10") } else { fmt.Println("这是不可能的") } //4 if a := 8; a == 10 { fmt.Println("a == 10") } else if a > 10 { fmt.Println("a > 10") } else if a < 10 { fmt.Println("a < 10") } else { fmt.Println("这是不可能的") } }
package main //必须有一个main包 import "fmt" func main() { //这种好 a := 10 if a == 10 { fmt.Println("a == 10") } else if a > 10 { fmt.Println("a > 10") } else if a < 10 { fmt.Println("a < 10") } b := 10 if b == 10 { fmt.Println("b == 10") } if b > 10 { fmt.Println("b > 10") } if b < 10 { fmt.Println("b < 10") } }
switch
package main //必须有一个main包 import "fmt" func main() { var num int fmt.Printf("请按下楼层:") fmt.Scan(&num) switch num { //switch后面写的是变量本身 case 1: fmt.Println("按下的是1楼") //break //go语言保留了break关键字,跳出switch语言, 不写,默认就包含 fallthrough //不跳出switch语句,后面的无条件执行 case 2: fmt.Println("按下的是2楼") //break fallthrough case 3: fmt.Println("按下的是3楼") //break fallthrough case 4: fmt.Println("按下的是4楼") //break fallthrough default: fmt.Println("按下的是xxx楼") } }
package main //必须有一个main包 import "fmt" func main() { //支持一个初始化语句, 初始化语句和变量本身, 以分号分隔 switch num := 4; num { //switch后面写的是变量本身 case 1: fmt.Println("按下的是1楼") case 2: fmt.Println("按下的是2楼") case 3, 4, 5: fmt.Println("按下的是yyy楼") case 6: fmt.Println("按下的是4楼") default: fmt.Println("按下的是xxx楼") } score := 85 switch { //可以没有条件 case score > 90: //case后面可以放条件 fmt.Println("优秀") case score > 80: //case后面可以放条件 fmt.Println("良好") case score > 70: //case后面可以放条件 fmt.Println("一般") default: fmt.Println("其它") } }
for
package main //必须有一个main包 import "fmt" func main() { //for 初始化条件 ; 判断条件 ; 条件变化 { //} //1+2+3 …… 100累加 sum := 0 //1) 初始化条件 i := 1 //2) 判断条件是否为真, i <= 100, 如果为真,执行循环体,如果为假,跳出循环 //3) 条件变化 i++ //4) 重复2, 3, 4 for i := 1; i <= 100; i++ { sum = sum + i } fmt.Println("sum = ", sum) }
package main //必须有一个main包 import "fmt" func main() { str := "abc" //通过for打印每个字符 for i := 0; i < len(str); i++ { fmt.Printf("str[%d]=%c\n", i, str[i]) } //迭代打印每个元素,默认返回2个值: 一个是元素的位置,一个是元素本身 for i, data := range str { fmt.Printf("str[%d]=%c\n", i, data) } for i := range str { //第2个返回值,默认丢弃,返回元素的位置(下标) fmt.Printf("str[%d]=%c\n", i, str[i]) } for i, _ := range str { //第2个返回值,默认丢弃,返回元素的位置(下标) fmt.Printf("str[%d]=%c\n", i, str[i]) } }
goto
package main //必须有一个main包 import "fmt" func main() { //break //break is not in a loop, switch, or select //continue//continue is not in a loop //goto可以用在任何地方,但是不能夸函数使用 fmt.Println("11111111111111") goto End //goto是关键字, End是用户起的名字, 他叫标签 fmt.Println("222222222222222") End: fmt.Println("3333333333333") }
类型别名
type bigint int64 type char rune //type( // bigint int64 // char rune //) var a bigint = 6 fmt.Println(a)
类型转换
使用strconv 包 Format 系列函数把其他类型的转换为字符串 Parse 系列函数把字符串转换为其他类型
package main import ( "strconv" "fmt" ) // 把其他类型的转换为字符串。 // 把字符串转换为其他类型 func main() { // bool // int // 将bool类型转换成字符串类型 //str:=strconv.FormatBool(true) //fmt.Println(str) // 将int类型转换成字符串类型 //str:=strconv.Itoa(123) //fmt.Println(str) // 把字符串转换为其他类型 // "true" --bool // "123" --int //b,err:=strconv.ParseBool("true") //if err!=nil{ // fmt.Println(err) //}else{ // fmt.Println(b) //} // 字符串转成整型 num, err := strconv.Atoi("123") if err != nil { fmt.Println(err) } else { fmt.Println(num) } }
遍历
1.range会复制对象,而不是不是直接在原对象上操作
2.使用range迭代遍历引用类型时,底层的数据不会被复制
package main import "fmt" func main(){ a := [3]int {1, 2, 3} for _, v := range a{ //复制一份a遍历[1, 2, 3] v += 100 //v是复制对象中的值,不会改变a数组元素的值 } fmt.Println(a) //1 2 3 }
package main import "fmt" func main(){ a := [3]int {1, 2, 3} for i, v := range a{ //i,v从a复制的对象里提取出 if i == 0{ a[1], a[2] = 200, 300 fmt.Println(a) //输出[1 200 300] } a[i] = v + 100 //v是复制对象里的元素[1, 2, 3] } fmt.Println(a) //输出[101, 102, 103] }
package main import "fmt" func main(){ a := []int {1, 2, 3} //改成slice for i, v := range a{ if i == 0{ a[1], a[2] = 200, 300 fmt.Println(a) //[1 200 300] } a[i] = v + 100 } fmt.Println(a) }
深浅拷贝
浅拷贝:
仅仅拷贝的是变量的值,没有对指向的空间进行任何的拷贝
go语言中赋值,函数传参, 函数返回值都是浅拷贝。
深拷贝:
将原有的变量的空间全部拷贝一份。