Go 语言

Go语言是云计算时代的语言

Go语言2007年诞生于Google,2009年开源,Go语言与区块链技术一样年轻

本文是对Go语言基本语法的总结

目录

Go语言简介

Hello World

Go词法单元

token

Go的token

标识符

内置数据类型标识符

常量值标识符

空白标识符

关键字

程序整体结构的关键字

声明复合结构的关键字

控制程序结构的关键字 

内置函数

操作符和分隔符

字面常量

整型字面量

浮点型字面量

字符型字面量

字符串字面量

变量

显式的完整声明

短类型声明

常量

Go的常量

预声明标识符

基本数据类型

布尔类型

整型

浮点型

复数类型

字符串

rune类型

复合数据类型

指针

数组

map

结构控制语句

if语句

switch语句

for语句

标签和跳转

函数

函数定义

传参

不定参数

函数签名和匿名函数

函数签名

匿名函数

defer

闭包

panic和recover

类型系统

命名类型和未命名类型

命名类型

未命名类型

方法

方法调用

一般调用


Go语言简介

罗布在 2007 年 9 月 25 号 回复给 肯、罗伯特的有关新的编程语言讨论主题的邮件说在开车回家的路上我得到了些灵感,给这门编程语言取名为“go”,它很简短,易书写。

Go语言的哲学:少即是多,世界是并行的,组合优于继承,面向接口编程

不像Python的含义是一条巨蟒,java的含义是爪哇岛的咖啡,Just go!

有点契合乔布斯所信奉的极简主义和禅宗哲学

乔布斯家曾经屋里只有一张爱因斯坦的照片、只有一盏最爱的蒂芙尼台灯,和一台影碟机,房间内没有沙发和椅子。他几乎没有什么家具,但是仅有的几项都是谨慎的选择。

物极必反,匮乏即是富足,自律才会产生喜悦,至繁归于简。

乔布斯一直以来对工艺有着非常严格的标准,他说,希望身边只出现他欣赏的东西。只留下一流的员工,只做没有瑕疵的产品。

水静极则形象明,心静极则智慧生。只有你的心灵和直觉知道你的真实想法。

When scientists climb a high mountain, but I found that the theologian had been sitting there for a long time.

                                                                                                        - Albert Einstein

下面开始介绍Go语言的语法

Hello World

先上一个Hello World

package main

import "fmt"

func main(){
    fmt.Println("Hello World")
}

Go语言编译器下载-Golang

GoLand开发工具安装

 

Go词法单元

token

token是源程序的基本的不可分割的单元

编译器先进行词法分析,将源程序分割成一个个token

Go的token

  • 操作符
  • 纯分隔符

操作符例如‘:=’,‘+’,‘a’

纯分隔符没有任何语法意义,如空格,制表符,换行符和回车符

标识符

标识变量,类型,常量等语法对象的符号名称

Go的标识符必须是字母或下划线开头,后面可以有数字

内置数据类型标识符

整型(12个):

        byte   int   int8   int16   int32   int64   

        uint    uint8    uint16   uint32    uint64    uintptr

浮点型(2个):

        float32   float32

复数型(2个):

        complex64   complex128

字符和字符串型(2个)

        string    rune

接口型:

        error

布尔型:

        bool

常量值标识符

true   false                       //表示bool类型的两常量值,真和假

iota                                //用在连续的枚举类型声明中

nil                                  //指针/引用型的变量的默认值是nil

空白标识符

_

用来声明一个匿名的变量,在赋值表达式的左端

关键字

有特定语法意义的标识符,用户不能使用重名的自定义标识符

Go只有25个关键字

程序整体结构的关键字

package   //定义包名

import     //导入包名

const      //常量声明

var        //变量声明

func      //函数定义

defer     //延迟执行

go        //并发语法糖

return        //函数返回

声明复合结构的关键字

struct        //定义结构类型

interface        //定义接口类型

map        //声明或创建map类型

chan        //声明或创建通道类型

控制程序结构的关键字 

if else for range continue break switch select type case default fallthrough switch goto

语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。

内置函数

内置函数就是高级语言的一种语法糖,不需要import引入

  1. append-用来追加元素到数组、slice中,返回修改后的数组、slice
  2. close-主要用来关闭channel
  3. delete-从map中删除key对应的value
  4. panic-停止常规的goroutine (panic和recover:用来做错误处理)
  5. recover-允许程序定义goroutine的panic动作
  6. real-返回complex的实部 (complex、real imag:用于创建和操作复数)
  7. imag-返回complex的虚部
  8. make-用来分配内存,返回Type本身(只能应用于slice, map, channel)
  9. new-用来分配内存,主要用来分配值类型,比如int、struct。返回指向Type的指针
  10. cap-capacity是容量的意思,用于返回某个类型的最大容量(只能用于切片和 map)
  11. copy-用于复制和连接slice,返回复制的数目
  12. len-来求长度,比如string、array、slice、map、channel ,返回长度
  13. print、println-底层打印函数,在部署环境中建议使用 fmt 包

操作符和分隔符

算术运算符含 义
+相加
-相减
*相乘
/相除
%取余数
++自增1
--自减1

比较运算符含 义
==相等
!=不相等
<小于
<=小于或等于
>大于
>=大于或等于

逻辑运算符含 义
&&逻辑与(AND),当运算符前后两个条件的结果均为 true 时,运算结果为 true
||逻辑或(OR),当运算符前后两个条件的结果中有一个为 true 时,运算结果为 true
!逻辑非(NOT),对运算符后面的条件的结果取反,当条件的结果为 true 时,整体运算结果为 false,否则为 true
位运算符含 义
&按位与(AND)操作,其结果是运算符前后的两数各对应的二进制位相与后的结果
|按位或(OR)操作,其结果是运算符前后的两数各对应的二进制位相或后的结果
^按位异或(XOR)操作,当运算符前后的两数各对应的二进制位相等时,返回 0;反之,返回 1
<<按位左移操作,该操作木质上是将某个数值乘以 2 的 n 次方,n 为左移位数。更直观地来看,其结果就是将某个数值的所有二进制位向左移了 n 个位置,并将超限的高位丢弃,低位补 0   
>>按位右移操作,该操作本质上是将某个数值除以 2 的 n 次方,n 为右移位数。更直观地来看,其结果就是将某个数值的所有二进制位向右移了 n 个位置,并将超限的低位丢弃,高位补 0   

赋值运算符含 义
=直接将运算符右侧的值赋给左侧的变量或表达式
+=先将运算符左侧的值与右侧的值相加,再将相加和赋给左侧的变量或表达式
-=赋给左侧的变量或表达式侧的值相减,再将相减差赋给左侧的变量或表达式
*=先将运算符左侧的值与右侧的值相乘,再将相乘结果赋给左侧的变量或表达式
/=先将运算符左侧的值与右侧的值相除,再将相除结果赋给左侧的变量或表达式
%=先将运算符左侧的值与右侧的值相除取余数,再将余数赋给左侧的变量或表达式
<<=先将运算符左侧的值按位左移右侧数值指定数量的位置,再将位移后的结果赋给左侧的变量或表达式
>>=先将运算符左侧的值按位右移右侧数值指定数量的位置,再将位移后的结果赋给左侧的变量或表达式
&=先将运算符左侧的值与右侧的值按位与,再将位运算后的结果赋给左侧的变量或表达式
|=先将运算符左侧的值与右侧的值按位或,再将位运算后的结果赋给左侧的变量或表达式
^=先将运算符左侧的值与右侧的值按位异或,再将位运算后的结果赋给左侧的变量或表达式

指针运算符含 义
&获取某个变量在内存中的实际地址
*声明一个指针变量

字面常量

整型字面量

表示具体的整形数值

698

0700

0xhello

浮点型字面量

表示一个浮点数值

0.

56.12

056.23

1e6

.25

.5239e5

字符型字面量

采用UTF-8编码,使用单引号括住

'a'

'豪'

'\000'

'\x07'

'\u12e4'

字符串字面量

使用双引号括住

"orz"

"大楚兴"

变量

显式的完整声明

var varName dataType [ = value]

示例

var a int = 1
var a int = 4*2
var a int = b

短类型声明

varName:=value
  • 短类型声明只能出现在函数内(包括在方法内)
  • Go语言编译器自动对数据类型进行判断
  • Go语言支持多个变量同时声明并赋值
a,b:=1,"hello"

如果一个对象的指针被多个方法或者线程引用时,那么我们成这个对象的指针发生了逃逸。

常量

Go的常量

  • 布尔型
  • 字符串型
  • 数值型

常量存储在程序的只读段里

.rodata section

ro就是read only(只读)

预声明标识符

iota

用在常量声明中,初始值是0,可以看成是一个自增枚举变量

const{
    a=iota        //a==0
    b=iota        //b==1
    c=iota        //c==2
}
/* 简写模式*/
const{
    a=iota        //a==0
    b             //b==1
    c             //c==2
}

基本数据类型

布尔类型

bool

只有true和false

不初始化,默认是false

整型

byte   int   int8   int16   int32   int64   

uint    uint8    uint16   uint32    uint64    uintptr

byte是uint8的别名

浮点型

float32        float64        

  1. 浮点数字面量被自动类型推断为float64类型
  2. 浮点数很难精确表示和存储,高精度科学计算应该使用math标准库

复数类型

complex64        complex128

complex128由两个float64组成

var value1 complex64 = 3.1 +5i

字符串

Go语言的字符串是一种基本的数据类型

  1. 字符串是常量,可以用数组索引形式访问当元素,但是不能修改某个字节的值
  2. 字符串转换成切片[]byte(s)要慎用,尤其是当数据量较大时
  3. 字符串结尾不含NULL字符
  4. 字符串的底层实现是一个二元数据结构
    type stringStruct struct{
        str unsafe.Pointer        //指向底层字节数组的指针
        len int                   //字节数组的长度
    }
  5. 基于字符串的切片和原字符串指向相同的底层字符数组,也不能修改
    a:="Hello World"
    b:=a[0:4]

遍历一个字符串

d:="Hello World"
for i:=0;i<=len(d);i++{
    fmt.Println(d[i])
}

统一码(Unicode),也叫万国码、单一码。

统一码是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。

将世界上所有的文字用2个字节统一进行编码。那样,像这样统一编码,2个字节就已经足够容纳世界上所有语言的大部分文字了。

rune类型

Go内置两种字符类型,一种是byte字节类型,一种是表示Unicode编码的字符rune

rune是uint32的别名

Go语言默认的字符编码是UTF-8类型

复合数据类型

Go语言基本的复合数据类型有指针,数组,切片,字典,通道,结构和接口

指针

声明类型为*T

Go语言支持多级指针**T

  1. *T出现在"="左边表示指针声明,*T出现在“=”表示指针指向的值
    var a = 11
    p:=&a
  2. 结构体的指针访问同C语言一样采用‘.’操作符
    type User struct{
        name string
        age int
    }
    
    stu:=User{
        name:"Peter",
        age:18,
    }
    
    p:=&stu
    
    fmt.Println(p.name)
  3. Go语言不允许指针的运算
    a:=4543
    p:=&a
    p++        //不允许此操作
  4. 允许函数返回局部变量的地址

数组

[n]elemetype

示例

var arr [2]int
array := [...]float64{7.0,8.5,9.1}

map

map创建

ma:=map[string]int{"a":1,"b":2}

结构控制语句

程序的执行在本质上就是顺序和跳转,循环也是一种跳转

if语句

  • if后面的条件判断子句不需要用小括号括起来
  • {必须放在if或者if else这行的行尾
  • Go语言没有三元运算符?:
  • if后面可以带一个简单的初始化语句,用分号来分割,作用域是整个if语句块

if x<=y{
    return y
}else{
    return x
}

if x:=f();x<y{
    return x
}else if x>z{
    return z
}else{
    return y
}

switch语句

  • 支持default语句
  • 条件表达式不一定是整数
  • fallthough语句来强制执行下一个case子句
  • switch后面也可以带一个初始化语句
switch{
    case score>=90:
        grade='A'
    case score>=80:
        grade='B'
    case score>=70:
        grade='C'
    case score>=60:
        grade='D'
    default:
        grade='F'
}

for语句

Go语言仅支持一种循环语句,for语句

Go语言的设计哲学,只提供一种方法将事情解决好

for init;condition;post{        //类似C语言中的for

}

for condition{            //类似C语言中的while

}

for{       //死循环

}

标签和跳转

goto语句用于函数内部的跳转,需要配合使用标签

goto Lable

break 跳出循环

continue  跳出for switch语句

continue  跳出本次循环

函数

函数定义

函数定义格式如图

func funcName(param-list)(result-list){
     function-body 
}
  • 函数可以没有输入参数和返回值
  • 多个相同的类型的参数相邻,可以使用简写模式
    func add(a,b int)(sum int){
        sum = a + b
        return //return sum的简写形式
        //sum:=a+b  相当于声明一个新的sum变量命名返回变量
    }
  • 支持有名的返回值
  • 不支持默认值参数
  • 不支持函数重载
  • 不支持命名函数的嵌套,支持嵌套匿名函数
  • Go语言支持多值返回
    func swap(a,b int)(int,int){
        return b,a
    }

传参

Go函数参数传递永远是传递一个副本,可能是指针,可能是变量的值

package main
import(
    "fmt"
)


func chvalue(a int) int{
    a=a+1
    return a
}

func chpointer(a *int){
    *a=*a+1
    return 
}

func main(){
    a:=10
    chvalue(a)      //传递的是值的副本
    fmt.Println(a)
    chpointer(&a)        //传递的是指针的副本
    fmt.Println(a)
}

不定参数

支持不定数目的形式参数

  1. 所有不定参数的类型必须是相同的
  2. 不定参数必须是函数的最后一个参数
  3. 不定参数名在函数体中相当于切片,对于切片不适宜的操作对于不定参数也不适宜
  4. 切片可以作为不定参数传递给不定参数,数组不可以
func sum(arr ...int)(sum int){
    for _,v:=range arr{
        sum+=v
    }
    return 
}

func main(){
    slice:=[]int{1,2,3,4}
    sum(slice...)
}

函数签名和匿名函数

函数签名

函数类型又叫函数签名,一个函数的类型就是函数定义首行去掉函数名,参数名和{,可以用fmt。Printf的“%T”打印一下函数的类型

package main

import "fmt"

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

func main(){
    fmt.Printf("%T\n",add)
}

匿名函数

匿名函数可以看做是函数字面量

直接使用函数类型变量的地方可以由匿名函数代替,匿名函数可以直接复制给函数变量,可以是实参,也可以是返回值

先看程序,尽力理解

package main

import "fmt"

//你命函数直接被赋值函数变量
var sum = func (a,b int) int {
    return a+b
}

func doinput(f func(int,int) int,a,b,int){
    return f(a,b)
}

func swap wrap(op string) func(int,int) int{
    switch op{
    case "add":
        return func(a,b int) int{
            return a+b
        }
    case "sub":
        return func(a,b int) int{
            return a+b
        }
    default:
        return nil
    }
}

func main(){
    defer func(){
        if err:=recover();err!=nil{
            fmt.Println(err)
        }
    }()
    sum(1,2)
    doinput(func(x,y int)) int{
        return x+y
    },1,2)
    opFunc:=wrap("add")
    re:=opFunc(2,3)
    fmt.Printf("%d\n",re)
}

defer

可以注册多个延迟调用,这些调用先进后出的顺序在函数返回前被执行

类似于Java语言在异常处理中的finaly子句

defer常用于保证一些资源最终一定能得到护手和释放

package main

func main(){
    defer func(){
        println("first")
    }()
    defer func(){    
        println("second")
    }()
    println("function body")
}

/*Output: function body
          second
          first*/

闭包

闭包就是能够读取其他函数内部变量的函数。

一般通过在匿名函数中引用外部函数的局部变量或包全局变量构成。

在本质上,闭包是将函数内部和函数外部连接起来的桥梁。

编译器检测到闭包,会将闭包引用的外部变量分配到堆上。

package main

func fa(a int) func(i int) int{
    return func(i int) int{
        println(&a,a)
        a=a+i
        return a
    }
}

func main(){
    f:=fa(1)
    g:=fa(1)
    println(f(1))
    println(f(1))
    println(g(1))
    println(g(1))
}
/*
output:

0x4200140b8 1
2
0x4200140b8 2
3
0x4200140c0 1
2
0x4200140c0 2
3
*/

不建议使用闭包的形式引用全局变量,因为每次调用闭包都会修改全局变量

同一函数返回的多个闭包共享函数的局部变量:

package main

func fa(base int)(func(int) int,func(int) int){
    println(&base,base)
    add:=func(i int) int{
        base +=i
        println(&base,base)
        return base
    }
    sub:=func(i int) int{
        base-=i
        println(&base,base)
        return base
    }
    return add,sub
}

func main(){
    f,g=fa(0)
    s,k=fa(0)
    println(f(1),g(2))
    println(s(1),k(1))
}


/*
Output:
0x4200140b8 0
0x4200140c0 0
0x4200140b8 1
0x4200140b8 -1
1 -1
0x4200140c0 1
0x4200140c0 -1
1 -1
*/

panic和recover

panic有两种情况,一种是程序主动调用panic函数,另一种是程序产生运行时错误,由运行时检测并抛出。

发生panic之后,程序会从调用panic函数的位置或者发生panic的地方立即返回,逐层向上执行函数的defer语句,然后逐层打印函数调用堆栈,直到recover捕获或运行到最外层函数而退出。

panic的参数是一个空接口类型interface{},所以任意类型的变量都可以传递给panic

recover用来捕获panic,阻止panic继续向上传递,recover()和defer一起使用,但是recover只要在defer后面的函数体内被直接调用才能捕获panic终止异常,否则返回nil

defer recover()

defer fmt.Println(recover())

defer func(){
        func(){
        println("defer inner")
        recover()
        }()
}()

defer func(){
    println("defer inner")
    recover()
}()

func except(){
    recover()
}

func test(){
    defer except()
    panic("test panic")
}

类型系统

命名类型和未命名类型

命名类型

类型可以通过标识符来表示,这种类型称为命名类型。Go语言的基本类型中有20个预声明简单类型都是命名类型。

未命名类型

一个类型由预声明类型,关键字和操作符组合而成,这个类型称为未命名类型。

数组,切片,字典,通道,指针,函数字面量,结构,接口都是未命名类型。

package main

import "fmt"

//type声明的是命名类型
type Person struct{
    name string
    age int
}

func main(){
//使用struct声明的是未命名类型
    a:=struct{
        name string
        age int
    }{"Peter",14}
    fmt.Println("%T\n",a)
    fmt.Println("%v\n",a)
    b:=Person{"tom",21}
    fmt.Println("%T\n",b)
    fmt.Println("%v\n",b)
}

方法

Go语言的类型方法是一种对类型行为的封装。

go语言的方法非常纯粹,可以看做特殊类型的函数

为命名类型定义方法的语法格式:

//类型方法接收者是值类型
func (t Typename)MethodName(ParamList)(Returnlist){
    //method body
}

//类型方法接收者是指针
func (t *TypeName)MethodName(ParamList)(Returnlist){
    //method body
}
  • t是接收者,可以自由指定名称
  • MethodName是方法名,是一个自定义标识符
  • ParamList是形参列表
  • ReturnList是返回值列表

Go语言的类型方法本质上就是一个函数,没有隐式的指针

方法调用

一般调用

TypeInstanceName.MethodName(ParaList)
  • TypeInstanceName是类型实例名或指向实例的指针变量名
  • Method是类型方法名
  • ParaList是方法实参
type T struct{
    a int
}

func (t T) Get() int{
    return t.a
}

func(t *T) Set(i int){
    t.a=i
}

var t=&T{}

t.Set(2)

t.Get()

本文包含了Go语言最基础的语法,变量类型和条件控制,函数

未完待续

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

烨鹰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值