Go:简单语法介绍
学习(1)运算符
Go:运算符学习
添加新的运算符
对于++,- -也有新的要求
返回值
指针的运算规则
/------------------------------------------------------------------------------
学习(2)数组与切片
数组的定义方法
切片的定义方法
数组和切片的对比及其使用
一、字符串尾部不包含NULL字符,与c/c++不一样
二、字符串是常量,可以访问字节单元但是不能修改某个字节单元的值;基于字符串创建的切片和原字符串指向相同的底层字符数组,一样不能修改
package main
import "fmt"
func main(){
s := "Hello World"
s[2] = 's' //error
a := s[:3]
fmt.Print(s,"\n",a)
a[2] = 's' //error
}
三、字符串和切片的转换
package main
import "fmt"
func main(){
s := "Hello World"
//字符串可以转换为字节数组,也可以转换为Unicode的字符数组
a0 := []rune(s) //[72 101 108 108 111 32 87 111 114 108 100]
a1 := []byte(s) //[72 101 108 108 111 32 87 111 114 108 100]
fmt.Print(a0,"\n",a1)
}
四、数组初始化
a := [3]int{1,2,3} //初始化长度和字面量 [1 2 3]
a := [...]int{1,2,3} //根据输入的数据个数确定长度 [1 2 3]
a := [3]int{1:1, 2:3} //指定总长度,通过索引值进行初始化,未初始化的元素使用类型默认值 [0 1 3]
a := [...]int{1:1, 2:3} //不指定总长度,通过索引值初始化,长度由最后一个索引值确定,
//未指定索引的元素被初始化为类型的零值 [0 1 3]
数组特点:
- 数组创建完长度就固定了,不可以再追加元素
- 数组是**值类型的 **,数组赋值或作为函数参数都是值拷贝
- 数组的长度是数组类型的组成部分
[10]int 和 [20]int
表示不同数组 - 可以根据数组创建切片
五、切片初始化
a := [5]int{1, 2, 3, 4, 5} //[1 2 3 4 5]
sl := a[1:] //通过数组创建;[2 3 4 5]
s2 := make([]int,5) //使用 make创建,len == cap = 5; [0 0 0 0 0]
var s3 []int //这是无意义的; []
/------------------------------------------------------------------------------
学习(3)map与结构体
一、map的定义与创建
(与切片一样,未创建的map是不能使用的)
//根据字面量创建
a := map[string]int{"a":1,"b":2}
fmt.Println(a) //map[a:1 b:2]
fmt.Println(a["a"]) //1
//使用 make 创建
b := make(map[string]string) //长度为默认值0
b2 :=make(map[string]string,10) //长度为 10
b3 := make(map[int]int)
b["name"] = "Tom" //增加元素后长度逐渐增加
b2["name"] = "John"
b2["sex"] = "M"
fmt.Println(b,len(b)) //map[name:Tom] ,len = 1
fmt.Println(b2["name"],b2["sex"],len(b2)) //John M ,len = 2
fmt.Println(b3,len(b3)) //map[], len = 0
map的增、删、改、查、遍历(无序)
二、结构体的定义与创建
(与切片和map不一样,定义了的结构体即可使用其变量)
//自定义结构体类型,并初始化
package main
import (
"fmt"
)
func main() {
type Person struct {
name string
age int
sex string //增加字段sex
}
//不推荐
a := Person{"Tom",1} //报错,因为没有初始化sex
fmt.Println(a)
//推荐
a :=&Person{
name: "Tom",
age: 0,
//未初始化的字段(sex)会被默认值初始化
}
fmt.Println(*a) //{Tom 0 }
}
结构体类型转换规则
匿名属性(结构体类型的匿名属性,及其访问规则)
/------------------------------------------------------------------------------
学习(4)函数
一、函数的特点
- 函数可以没有输入参数,也可以没有返回值
- 多个相邻的相同类型的参数可以使用简写模式
例如
func fun(a,b int,f float32){}
- 支持有名的返回值,最后return可以不带参数名直接返回
func fun(a,b int)(sum int){
sum = a + b
return //return sum 的简写形式
------------------------------
sum := a + b //如果是这样,则相当于新声明一个sum变量,将返回变量sum覆盖
return sum //最后要显式调用 return sum
}
- 不支持默认值参数
- 不支持函数重载
- 不支持函数嵌套,严格说是不支持命名函数的嵌套定义,但支持嵌套匿名函数,例如
//正确,使用了匿名函数嵌套
func add(a,b int)(sum int){
anonymous := func(x,y int)int{
return x + y
}
return anonymous(a, b)
}
//错误
func add(a,b int)(sum int){
func(x,y int)int{//写成func add2(x,y int)int 也是错误的
return x + y
}
return anonymous(a, b)
}
二、多值返回
定义多值返回时的返回参数列表要使用“()”包裹,并支持命名参数返回
func swap(a, b int)(int int){
return b ,a
}
三、不定参数
特点:
- 所有不定参数的类型必须是相同的
- 不定参数必须是函数的最后一个参数
- 不定参数名在函数体内相当于切片,对切片的操作同样适合对不定参数的操作
- 切片可以作为参数传递给不定参数,切片名后要加上“…"
package main
import "fmt"
func Sum(arr...int)(sum int){
for _,value:=range arr{ //此时arr就相当于切片,可以使用range访问
sum += value
}
return sum
}
func main(){
fmt.Println(Sum(1,2,3)) //6
a := [...]int{1,2,3,4}
slics := a[:]
fmt.Println(Sum(slics...)) //10
fmt.Println(Sum(a)) //error,数组不可以作为实参传递给不定参数函数中的参数
}
- 形参为不定参数的函数 和 形参为切片的函数类型不同
package main
import "fmt"
func suma(arr...int)(sum int){
for _,v:=range arr{
sum += v
}
return
}
func sumb(arr []int)(sum int){
for _,v:=range arr{
sum += v
}
return
}
func main(){
//suma 和 sumb 类型并不一样
fmt.Printf("%T\n",suma) //func(...int) int
fmt.Printf("%T\n",sumb) //func([]int) int
}
四、函数签名
- 函数类型又叫做函数签名,一个函数的类型就是函数定义首行去掉函数名、参数名和{,可以使用fmt.Printf的%T格式化参数打印函数类型,例如上面的代码
- 两个函数类型相同的条件是:拥有相同的形参列表和返回值列表(列表元素的次序、个数和类型都相同),形参名可以不同,例如
//两个函数类型相同
func add(a, b int)int{return a+b}
func Sum(x int, y int)(sum int){
sum = x+y
return
}
- 可以使用type 定义函数类型,函数类型变量可以作为函数的参数或返回值
package main
import "fmt"
func suma(arr...int)(sum int){
for _,v:=range arr{
sum += v
}
return
}
type op func(...int)int
func Hanshu(f op)int{
return f(1,2,3)
}
func main(){
fmt.Println(Hanshu(suma)) //6
}
五、匿名函数
匿名函数可以看作函数字面量,所有直接使用函数类型变量的地方都可以由匿名函数代替。
- 可以直接赋值给函数变量
package main
import (
"fmt"
)
func main() {
var f = func()int{
return 1
}
fmt.Printf("%T\n",f) //func() int
fmt.Println(f()) //1
}
- 可以当作实参
package main
import "fmt"
type op func(int)
func Prints(f op){
fmt.Printf("%T",f)
}
func main(){
Prints(func(i int) {}) //main.op
}
- 可以作为返回值
//典型例子斐波那契数列(使用闭包)
func Fib()func()int{
a:=-1
b:=1
return func()int{
a,b = b,a+b
return b
}
}
- 可以直接调用
package main
import "fmt"
func main(){
fmt.Printf("%T",func(int){}) //func(int)
}
六、闭包
对象是附有行为的数据,而闭包是附有数据的行为
概念:闭包是由函数及其相关引用环境组合而成的实体,一般通过在匿名函数中引用外部函数的局部变量或包全局变量构成
闭包 = 函数 + 引用环境
如果函数返回的闭包引用了该函数的局部变量(参数或函数内部变量):
- 多次调用该函数,返回的多个闭包所引用的外部变量是多个副本,原因是每次调用函数都会为局部变量分配内存
- 用一个闭包函数多次,如果该闭包修改了其引用的外部变量,则每次调用该闭包对该外部变量都有影响,因为闭包函数共享外部引用
- 如果闭包调用的是全局变量,则每次调用该闭包都会对全局变量产生影响,所以闭包引用全局变量并不是一个好的编程方式
//1.调用参数的闭包
package main
import "fmt"
func fa(n int) func() {
n++
return func() { //注意,这里闭包调用的是参数n,而没有调用函数内部变量,
//所以n++ 并不起作用,且由于闭包内未对n做处理,输出时就按原值输出
fmt.Println(n)
}
}
func fb(n int)func(){
return func(){ //这里闭包调用了参数且对其做了++处理
n++
fmt.Println(n)
}
}
func main(){
a := fa(1)
a() //2
a() //2
fmt.Println()
b := fb(1)
b() //2
b() //3
}
//2.调用函数内部变量的闭包
package main
func Sum()func()int{
a := 0
f := func()int {
a++
return a
}
return f
}
func main() {
a0 := Sum()
println(a0()) //1
println(a0()) //2
}
内函数对外函数的变量的修改,是对变量的引用
对其理解--------------------------------------------------------------------/
将拥有闭包的函数称为背包;
从背包外拿东西(传入参数),在背包内处理(利用闭包处理参数),
从背包内拿东西(函数内部变量),在背包内处理(利用闭包处理内部变量)
/------------------------------------------------------------------------------
学习(5)
指针的定义和使用规则
指向切片的指针(二级指针)
指针作为返回值的规则
Go:指针学习
学习(6)
面向对象,使用
“方法”进行绑定 +
组合(在c++中的继承)+
代码首字母的大小写
----------->进行封装
继承方式及规则和c++很像只是语法不同,包括继承的函数的重写…
Go:面向对象简单学习
一、指针接受者的方法
该知识点在于绑定方法时,实参接收者和形参接收者的选择
1.
package main
import "fmt"
type S struct {
x,y int
}
func (s S)Sum()int{ //形参接收者为 S 类型
return s.x+s.y
}
func main(){
fmt.Println(S{1,2}.Sum()) //实参接收者也为 S 类型
}
2.
package main
import "fmt"
type S struct {
x,y int
}
func (s *S)Sum()int{ //形参接收者为 *S 类型
return s.x+s.y
}
func main(){
t := &S{1,2}
fmt.Println(t.Sum()) //实参接收者也为 *S 类型
}
3.
package main
import "fmt"
type S struct {
x,y int
}
func (s *S)Sum()int{ //形参接收者为 *S 类型
return s.x+s.y
}
func main(){
var t = S{1,2}
fmt.Println(t.Sum()) //实参接收者为 S 类型
//此时 t 会被隐式转换为 &t
}
4.
package main
import "fmt"
type S struct {
x,y int
}
func (s S)Sum()int{ //形参接收者为 S 类型
return s.x+s.y
}
func main(){
var t = &S{1,2}
fmt.Println(t.Sum()) //实参接收者为 *S 类型
//此时 t 会被隐式转换为 *t
}