2. go 基本语法

语句

  1. go和js一样,语句不用加分号

数据类型

变量定义

与c和js有相同和不同之处,命令如下

var 变量 类型 = 表达式

如:

var i int = 10

编写程序测试

package main
import "fmt"
func main() {
    var i int = 10
    fmt.Println(i)
}

注意:go语言中定义变量必须使用,否则会报错。

go语言也可以像动态语言js一样初始化时不指定数据类型,go会自动推到类型。

var i = 10

一次声明多个变量

var {
    i int = 0
    j int = 1

}

var {
    i  = 0
    j  = 1

}

注意这种方式必须赋初值

变量赋值简写

变量名:=表达式

数据类型

基础类型
整形

go中整形最规范,直接写清楚了多少位,省去c中typedef,

有符号:

  • int
  • int8
  • int16
  • int32
  • int64

无符号:

  • uint
  • uint8 或者 byte
  • uint16
  • uint32
  • uint64

其中 int 和 uint 根据系统位数确定,64位就是64 32 就是 32

浮点数
  • float32
  • float64

整形和浮点型都属于数字类型,可以进行相互强制转换,语法与c类似,区别是括号加在被转换对象上类型(变量),如下:

var i int = 10;
i2f:=float32(i)
f2i:=int(i2f)
fmt.Println("i2f:",i2f)
fmt.Println("f2i:",f2i)
布尔型

与js相同,有两个值 true false

字符串
  • string
var str string = "hello go"

字符串与数字互转
与c类似,用到了ItoaAtoi函数,在’strconv’包下

  • Itoa:整形转字符串
  • Atoi:字符串转整形 返回两个结果 number,err
  • ParseFloat:字符串转浮点数
  • ParseBool: 字符串转布尔
  • FormatFloat:
  • FormatBool:
    var i int = 10
    
	//字符串转数字
	str: = strconv.Itoa(i);
	fmt.Println("整形转字符串结果 :",str)

	//数字转字符串
	number,err := strconv.Atoi(str);
	if err ==nil{
		fmt.Println("数字转字符串结果 :",number)
	}

与java相同 +号可以连接字符串

go 提供了strings包,有很多实用函数
c中常用几个字符串函数类比举例:

  • Compare :功能同c strcmp
  • Contains: 功能同c strstr
  • Index: 功能同c strstr
fmt.Println(strings.Compare("abcd","abcd"))
fmt.Println(strings.Contains("abcd","c"))



fmt.Println(strings.ToUpper("abcd"))
fmt.Println(strings.Index("abcd","c"))
fmt.Println(strings.HasPrefix("abcd","a"))

其他函数可以查看帮助文档https://golang.google.cn/pkg/strings/

零值

变量默认值,未赋初值时的值,数字为0,bool为false,字符串为空串

指针

与c 相同,&:取址符获取变量所在的地址, *p 为获取指针变量指向地址存储的内容

常量

与es6类似,关键字变为const
go中只允许整形、浮点型、布尔型、字符串基础变量作为常量

常量生成器 iota
go中iota是常量生成器, 用来生成多个常量并同时赋值,(与c的枚举有些相似)


const (
    a = itoa+1
    b 
    c
    d 
)

iota初值为0,所以整体效果为 abcd分别为1,2,3,4,注意:是小括号

控制结构

条件 if

if语句与c类似,不同之处在于条件没有小括号,且每个条件分支必须有大括号,大括号必须与条件在同一行

age := 10
if age < 10{
    fmt.Println("少年")
} else if age >= 10 && age < 30{
    fmt.Println("青年")
} else if age >30 && age < 40{
    fmt.Println("中年")
}else {
    fmt.Println("老年")
}

go 中if语句可以增加一个简单的赋值语句,用分号和条件语句隔开,上边可写为


if age := 10;age < 10{
    fmt.Println("少年")
} else if age >= 10 && age < 30{
    fmt.Println("青年")
} else if age >30 && age < 40{
    fmt.Println("中年")
}else {
    fmt.Println("老年")
}

该变量作用域只存在于 整个 if…else…包裹中

选择 switch

	var step int = 3
	switch step{
		case 0:
			fmt.Println("吸气")
		case 1:
			fmt.Println("压缩")

		case 2:
			fmt.Println("点火")
		case 3:
			fmt.Println("排气")
	}
  • switch 也没有小括号,大括号也必须和条件在一行
  • switch 自带break,不用写break;如果实在想执行多个条件,可以在break位置上写上 fallthrough
  • switch 也可以加一个简单赋值
	switch step:=3;step{
		case 0:
			fmt.Println("吸气")
		case 1:
			fmt.Println("压缩")

		case 2:
			fmt.Println("点火")
		case 3:
			fmt.Println("排气")
	}

todo: switch还支持类型选择

循环

sum:=0

for i:=1; i<=100; i++{
    sum+=i
} 
fmt.Println("sum",sum)

go中循环只有for,没有while,for简写实现while

sum:=0
i:=1;
for  i<=100{
    sum+=i
     i++
} 
fmt.Println("sum",sum)

或者

sum:=0
i:=1;
for {
    sum+=i
    i++
    if i>100 {
        break
    }
} 

fmt.Println("sum",sum)

循环中支持continuebreak与c同。

//计算奇数和
sum:=0
i:=1;
for  i<=100{
    if i%2==0{
        continue
    }
    sum+=i
    i++
} 
fmt.Println("sum",sum)

todo: for range

集合类型

Go 语言中,数组(array)、切片(slice)、映射(map)这些都是集合类型,用于存放同一类元素

数组 Array

go中数组存放同种类型元素,与c相同。在内存中连续存放

语法:

arr := [3]string{"apple","banana","organge"}
arr2 := [...]string{"apple","banana","organge"}

fmt.Println("arr第2个元素",arr[1])
for i:=0; i<3; i++{
    fmt.Printf("arr第%d个元素是:%s\r\n",i,arr[i])
}
  • 数组长度不指定时,需要在长度内些3个点,和c不同
  • go中数组可部分初始化,如下
arr := [5]string{1:"apple":3:"banana"}

for i:=0; i<3; i++{
    fmt.Printf("arr第%d个元素是:%s\r\n",i,arr[i])
}

未赋值的元素默认为零值

数组循环

for i,v:=range arr{
    fmt.Printf("for range 输出 arr第%d个元素是:%s\r\n",i,v)
}
  • i为序号,v为值,如果不用i可以用下划线丢弃
for _,v:=range arr{
    fmt.Printf("for range 输出 arr第%d个元素是:%s\r\n",i,v)
}

函数传递
go语言中函数传参都是值传递,如果数组作为形参传入函数,当数组长度大时会占用大量栈区,所以可以将数组的地址传入函数,这样只占用8个字节。

切片 slice

go中数组与c相同,一旦固定大小就不能改变了,所以新增了切片这种类型,切片是一个动态数组,可以动态扩容。

切片由三部分构成:

  1. 指向底层数组的指针
  2. 切片的长度:切片内元素个数
  3. 切片的容量:切片的空间,以动态扩充

切片定义

1.切片可以直接从数组赋值生成

切片变量:= 数组[开启索引:结束索引]
arr:=[5]int{1,2,3,4,5}
sli:=arr[2,5]
fmt.Println(slice)
  • 开始和结束索引可以省略,开始默认为0,结束默认为原数组最大长度

2.切片也可以采用make来生成

切片变量:= make([]类型,长度[,容量])
sli2:= make([]string,10)
sli3:= make([]string,10,15) //容量为15,长度为10
fmt.Println(len(sli3),cap(sli3))

切片还可以通过字面量生成

sli4:= []string{"1","2","3","4","5"}
fmt.Println(len(sli4),cap(sli4))

注意:当未指定长度使用...如 […] 代替长度时,定义的是数组,当使用空长度,如[] 定义的是切片。

slice :=[]int{1,2,3} //切片
slice :=[...]int{11,22,33}//数组

几个go自带的函数:

  • len 统计切片长度
  • cap 统计切片容量

切片扩充元素

sli5:= append(sli4,"6") //追加一个元素
sli6:= append(sli4,"6","7")//追加多个元素
sli7:= append(sli4,sli5...)//追加一个切片
  • append 函数返回的结果是一个新切片

切片底层是数组,在对切片增加数据的时候可能会创建新的数组,也可能不创建,是否创建在于切片初始化时的容量。如果想要实现只要对一个切片执行append就创建一个新的切片(底层新的数组创建),那么可以在创建切片时将长度和容量设为一致。如

sli8:= make([]string,3,3)
sli9:= append(sli8,"new")

此时新得到的sli9和sli8底层不是一个数组

注意:切片的容量不能小于切片的长度。

切片循环

for i,val:=range sli7{
    fmt.Println("第%d个元素,值为:%s")
}

String可以转换为[]byte

s:="hello go"
b:=]byte(s)
fmt.Println(b)

String 也可以直接用索引读取内容

	fmt.Println(s[0])

函数传参
与数组不同,切片作为参数传入时对栈区的占用不会收到切片大小的影响,因为切片存储的是底层数组的指针,而不是全部内容值,所以可以直接传入切片。
实际使用中,切片也

映射 map

map是无序键值对,其中键必须保持累心沟通一,值也必须保持类型统一。

map:=map[键类型]值类型{}

go中的map和js中object类似。

1.make来声明

map1:=make(map[string]string)

2.字面量shengming

map2:=map[string]int{}

操作map

map1["name"]="张三" //设置
map1["addr"]="北京"
fmt.Println(map1["name"]) //读取

//遍历输出
fmt.Println("map1大小为:",len(map1))
for k,v range map1{
    fmt.Printf("K:%s,V:%s\r\n",k,v);
}

delete(map1,"name")//删除

函数方法

函数

格式

func 函数名(参数) 返回值{

    函数体
}
  • 函数参数和返回值可以是多个,返回值可以省略
  • go中函数参数为值传递

例如:

func sum(a int,b int) int{
	return a+b
}

多值返回
go函数支持返回多个结果,go中可以利用第二个参数做出错处理

func div(a int, b int) (int, error) {
	//除数不能为0
	if b == 0 {
		return 0, errors.New("除数不能为0")
	}

	return a / b, nil
}

func main() {

	fmt.Println("sum:", sum(2, 3))

	res, err := div(5, 0)
	if err != nil {
		fmt.Println("除数为0,请检查")
	} else {
		fmt.Println("结果为:", res)
	}
}

返回参数命令

返回值可以有名称,且可在函数体内使用,这里就可以看出go比c简单

func sum2(a int,b int) ( sum int){
	sum = a+b
	return sum
}

可变参数
可变参数是指可以给函数传入数量不同的多个参数,要定义可变参数,只需要在参数类型前加上...即可。

注意:当有多个参数时,可变参数一定要放在最后一个位置。

func sum3(num ...int) (sum int) {
    sum = 0
    for _,i:=range num {
        sum += i;
    }
    return sum
}

//调用
fmt.Println(sum3(1,2))
fmt.Println(sum3(1,2,3))
fmt.Println(sum3(1,2,4))
fmt.Println(sum3(1,2,3,4,5))

一个包中如果要暴露一个函数只需要将函数名称首字母大写即可,如fmt包中的Println。首字母小写代表私有函数,只能在同一包中访问。

匿名函数
与js相同js支持匿名函数和闭包。
闭包是什么这个问题是前端常见面试题,简单来说闭包就是在一个函数内部可以访问函数外边的一个变量,一般常见应用于两层函数嵌套,返回内层函数,内层函数可以访问外层函数的变量。

匿名函数支持在函数内部再次编写函数,实现函数嵌套

func closure() func() int {
	i:= 0;
	return func() int {
		i++;
		return i;
	}
} 

//调用
cl:=closure()
fmt.Println(cl()); //输出1
fmt.Println(cl()); //输出2
fmt.Println(cl()); //输出3

上述例程使用了闭包,cl是函数类型,指向内部返回的函数,而内部返回的函数一直能访问外部函数中变量i,所以能实现结果暂存,多次调用结果不同。

注意:go中函数意识一种类型。可以作为变量、参数以及返回值类型。

方法

方法和函数唯一的区别是方法的方法名前有一个接受者,类似于java中的对象主体。一般在go中这个接受者就是自己定义的结构体。

结构体.方法() 类似于 对象.方法().

如下:

type Dog string
func (dog Dog) wang(){
    fmt.Printf("wang wang")
}

d:=Dog("大黄")
d.wang()

接受者类型
接受者可以是值,也可以是指针。
如果接受者是指针,那么对指针的修改会影响源接受者,如果是值,那么不会影响到接受者。所以一般用指针比较多。

一定要记住,go中都是值传递,那为什么纸质就可以影响源接受者呢?
实际上不管接受者是指还是指针,在方法内部都是对值或者指针副本的操作。区别是值得副本不会影响到源接受者,而指针的副本仍然指向源接受者,所以指针即使是副本,也能修改源接收者。

go中指针自动转换
与c不同的是,当利用指针访问源数据需要加信号*,惯性思维,接受者类型为指针时调用方法也要加星号。但实际却不用,go能自动转换

具体例子参考后续结构体笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值