• Go/Golang语言学习实践[回顾]教程13--详解Go语言的词法解析单元


      经过前面章节的初步实践,相信大家对 Go 语言已经有了一个初步的印象,也可以编写简单的例程。但是要达到使用 Go 语言编写相对复杂实用的真实项目,还需要有些扎实的 Go 语言基础和更全面更深入的了解。那么接下来从本节开始,我们就相对系统的深入实践 Go 语言编程。

    Go语言的词法解析单元

      编译器在编译源代码时,首先要做的就是要把源代码分割成可以识别处理的独立 标记(token),这些标记是不可再分割的基本单元,这样编译器才可以进行下一步。这个过程就叫词法分析,每个标记就是一个词法单元。

      分割这些 标记(token) 的依据我们称之为 分隔符。在 Go 语言中有两类分隔符:

    分隔符

      在 Go 语言中有两类分隔符:

      ▲ 纯分隔符:只做分割标记(token)只用,自身没有任何语法含义。有 空格、制表符、回车符、换行符,连续多个空格或制表符编译器会看做一个分隔符来处理。如下面两行代码的编译结果是一样的:

    package main
    
    package    main
    

      这两行代码编译器最终都是分离出两个 token,分别是 package 和 main。

      ▲ 操作符:本身是一个 token,具有语法含义,但也是分隔符,具备分割其他 token 的作用。例如以下语句:

    a:=b+19
    

      注意上面语句中并没有写空格,但编译器也会分割成5个 token:a、:=、b、+、19。其中 a 和 b 是用操作符 := 分割的,b 和 19 是用操作符 + 分割的。具体有哪些操作符后面章节介绍。

      那么Go语言具体有哪些类的 token 呢,接下来就介绍这些 token。

    标识符

      标识符分为 语言设计者预留的 和 编程者自定义的 两大类,用来标识语法对象的符号名。
      语言设计者预留的:包括语言设计者预先声明的标识符以及用于后续扩展的保留字,通常称为预声明的
      编程者自定义的:编程者在编程过程中自己定义的变量名、常量名、函数名等。
      自定义标识符名称规则:只能以字母或下划线开头,区分大小写,不可以与语言设计者预留的重名。看下面自定义的标识符例举:

    8num     // 不合法,不是以字母或下划线开头
    -num     // 不合法,不是以字母或下划线开头
    _num     // 合法
    num      // 合法
    num1     // 合法
    num_1    // 合法
    

      那么Go语言有哪些预声明的标识符呢,下面我们逐一介绍。

    关键字

      关键字是语言设计者预留的使用其标识语法含义及控制程序结构的标志 token,各自代表不同语义。Go 语言总计只有25个关键字,分为如下三类:

      ▲ 用于引导定义程度整体结构的8个:

    package     // 声明定义包名,在行首
    import      // 导入包,在行首
    var         // 声明定义变量,在行首
    const       // 声明定义常量,在行首
    func        // 声明定义函数,在行首
    return      // 函数返回,结束函数运行
    defer       // 相较于其他语句延迟执行,最先 defer 的最后执行,在行首
    go          // 并发执行后面的代码,在行首
    

      ▲ 用于流程控制的12个:

    if            // 条件判断语句,结果为真(True)执行其后面或括号包裹的代码
    else          // 与 if 联合使用,是 if 的否定条件,也可以 else if,表示在前面条件不成立时继续判断
    for           // 循环,可使用循环计数变量,可配合 range 遍历集合类型数据,也可以仅用 for 关键字做无限循环
    range         // 与 for 联合使用,用于遍历数组(array)、切片(slice)、通道(channel)、映射(map)的元素
    break         // 在 for 循环中表示跳出当前循环,在 case 语句中出现表示跳过当前 case 语句
    continue      // 仅在 for 循环中实用,表示结束本轮循环,进入下一轮
    switch        // 与 case 联合使用,若 switch 后面有变量则case后面的值等于变量值为该 case 成立,否则 case 后面判断布尔值
    select        // 与 switch 类似,但是专门为监听通道通信设计的,每个 case 必须是一个通信操作,要么是发送要么是接收
    case          // 与 switch 或 select 关键字联合使用,是 switch 语句的条件分支,满足条件则执行该段代码块
    default       // 与 case 同时出现在 switch 或select 语句中,表示所有 case 都不成立时要执行的代码块
    fallthrough   // 在 switch 或 select 语句的某个或多个 case 中最后一行使用,继续执行后面一个 case 的代码
    goto          // 无条件跳转语句,要配合标签(lable)使用,Go语言允许用标签标注代码位置
    

      ▲ 用于声明符合数据结构5个:

    map         // 声明定义映射,类似于键值对数组或俗称的对照表,有固定类型的键名和值的集合类型
    struct      // 声明定义结构体,是有成员名且各成员数据类型无需相同的集合类型
    type        // 声明定义新的数据类型,通常与 struct 一起使用,也可以声明数据类型的别名
    chan        // 声明定义通道,通道是使用 go 关键字启动的各协程之间通信的桥梁
    interface   // 声明定义接口,接口可以让变量不仅有值,还有了方法,实现了面向对象且更灵活
    

    内置基本数据类型

      有了基本数据类型,用户才能定义变量、返回值以及自定义数据类型等,Go 语言有20个基本数据类型:

      ▲ 整数型13个:

    int       // 32 位或 64 位有符号整数,位数随操作系统。
    int8      // 8 位有符号整数,-128 ~ 127
    int16     // 16 位有符号整数,-32768 ~ 32767
    int32     // 32 位有符号整数,-2147483648 ~ 2147483647
    int64     // 64 位有符号整数,-9223372036854775808 ~ 9223372036854775807
    uint      // 32 位或 64 位无符号整数,位数随操作系统。
    uint8     // 8 位无符号整数,0 ~ 255
    uint16    // 16 位无符号整数,0 ~ 65535
    uint32    // 32 位无符号整数,0 ~ 4294967295
    uint64    // 64 位有符号整数,0 ~ 18446744073709551615
    byte      // uint8 的别名 ,用于表示一个字节
    rune      // 符文,uint32 的别名 ,用于表示一个 Unicode 码
    uintptr   // 无符号整数 ,一个能足够容纳指针位数大小的整数类型
    

      ▲ 浮点型2个:

    float32   // 单精度浮点数,占用32位长度
    float64   // 双精度浮点数,占用64位长度
    

      ▲ 复数型2个:

    complex64    // 64 位复数,由两个float32构成,一个表示实部,一个表示虚部
    complex128   // 128 位复数,由两个float64构成,一个表示实部,一个表示虚部
    

      ▲ 字符串型1个:

    string   // 字符串,UTF-8 字符序列,ASCII 码表中字符占 1 个字节,其它字符占用 2-4 个字节,汉字三个字节  
    

      ▲ 布尔型1个:

    bool    // 布尔,仅有两个值 true、false,通常代表 真/假、有/无、是/否、1/0等
    

      ▲ 接口型1个:

    error   // 错误信息
    

    内置常量值标识符

      有着特殊含义的常量值,Go 语言设计者为它们预先声明了标识符来代替其实际值:

    true    // 表示布尔类型里的 真 值
    false   // 表示布尔类型里的 假 值
    nil     // 表示空值(实际不存在),如引用或指针类型的默认值
    iota    // 常量计数器,只能在常量的表达式中使用
    

    内置空白标识符

      空白标识符的意义主要是占位并不需要起名字:

    _      // 通常用于忽略函数的返回值、不使用导入的包名等
    

    内置函数

      内置函数,不需要 import 导入,可以直接使用,且全局可见。虽然都是小写字母开头,但不影响全局特性。总计有15个:

    	append     // 在切片(slice)末尾追加元素,返回修改后的新切片(slice)
        close      // 关闭通道 channel
        delete     // 从一个 map 中删除 key 对应的 value
        panic      // 停止常规的协程 goroutine(panic 和 recover 用来做错误处理)
        recover    // 允许程序定义协程 goroutine 的 panic 动作
        complex    // 构建复数
        real       // 返回 complex 的实部   (complex、real imag:用于创建和操作复数)
        imag       // 返回 complex 的虚部
        make       // 用来分配内存,返回 Type 本身,只应用于slice、map、channel
        new        // 用来分配内存,返回指向 Type 的指针,主要用来分配值类型,比如int、struct
        cap        // 返回某个类型的最大容量,只能用于切片(slice)和映射(map)
        copy       // 用于复制和连接 slice,返回复制的数目
        len        // 返回 string、array、slice、map、channel 的长度(集合类型是元素数)
        print      // 底层打印函数,在部署环境中建议使用 fmt 包内的 print
        println    // 底层打印函数(自动换行),在部署环境中建议使用 fmt 包内的 println
    

    操作符

      操作符是用于运算、显示分隔、语法辅助的符号。操作符毕竟本身具有语义,同时也具有分隔其他 token 的作用。Go 语言总计有以下47个操作符:

    算术运算符7个:

    +     // a + b,两边都是数字则为相加,都是字符串则是拼接
    -     // a - b,两数相减
    *     // a * b,两数相乘
    /     // a / b,两数相除,两数中有任何一个是浮点数,结果则为浮点数,否则结果只保留整数部分
    %     // a % b,求两数相除后的余数
    ++    // a++,自身值增加1,Go语言将其降级为语句
    --    // a--,自身值减少1,Go语言将其降级为语句
    

    位运算符6个:

      假设 a = 3 二进制表示 00000011,b = 5 二进制表示 00000101

    &    // 按位与运算,两数二进制同位都为 1 结果该位为 1,否则为 0;a & b 结果为 1 (00000001)
    |    // 按位或运算,两数二进制同位有一个为 1 结果该位就为 1,都是 0 为 0;a | b 结果为 7 (00000111)
    ^    // 按位异或运算,两数二进制同位不一样则结果该位就为 1,否则为 0;a ^ b 结果为 6 (00000110)
    &^   // 位清零运算,将第一个数对应第二个数为1的位置清0;a &^ b 结果为 2 (00000010)
    <<   // 按位左移运算符,a<< 1 结果为 6 (00000110) ,b << 1 结果为 10 (00001010)
    >>   // 按位右移运算符,a >> 1 结果为 1 (00000001) ,b >> 1 结果为 2 (00000010)
    

    赋值运算符13个:

    :=    // 声明并初始化一个变量,左侧是变量,右侧是初始化值
    =     // 简单赋值运算,将右侧的值赋给左侧
    +=    // 左侧与右侧相加后再赋给左侧;a += b, 等同于 a = a + b
    -=    // 左侧与右侧相减后再赋给左侧;a -= b, 等同于 a = a - b
    *=    // 左侧与右侧相乘后再赋给左侧;a *= b, 等同于 a = a * b
    /=    // 左侧与右侧相除后再赋给左侧;a /= b, 等同于a = a / b
    %=    // 左侧与右侧相除取余后再赋给左侧;a %= b, 等同于 a =a % b
    &=    // 左侧与右侧按位相与后再赋给左侧;a &= b, 等同于 a = a & b
    |=    // 左侧与右侧按位相或后再赋给左侧;a |= b, 等同于 a = a | b
    ^=    // 左侧与右侧按位异或后再赋给左侧;a ^= b, 等同于 a = a ^ b
    &^=   // 左侧按右侧位清零后再赋给左侧;a &^= b,等同于 a = a &^ b
    <<=   // 左侧左移右侧值那么多位后再赋给左侧;a <<= 3, 等同于 a = a << 3
    >>=   // 左侧右移右侧值那么多位后再赋给左侧;a >>= 3, 等同于 a = a >> 3
    

    比较(关系)运算符6个:

    ==    // a == b,如果 a 等于 b 则返回 True,否则返回 False
    !=    // a != b,如果 a 不等于 b 则返回 True,否则返回 False
    >     // a > b,如果 a 大于 b 则返回 True,否则返回 False
    >=    // a >= b,如果 a 大于或等于 b 则返回 True,否则返回 False
    <     // a < b,如果 a 小于 b 则返回 True,否则返回 False
    <=    // a <= b,如果 a 小于或等于 b 则返回 True,否则返回 False
    

    逻辑运算符3个:

    &&    // 并且:a && b,如果 a 为 True 并且 B 为 True,则条件为 True,否则条件为 False
    ||    // 或者:a || b,如果 a 为 True 或者 B 为 True,则条件为 True,否则条件为 False
    !     // 取反:!a,如果 a 为 True,则条件反转为 False;如果 a 为 False,则条件反转为 True
    

    指针运算符2个:

    &    // 引用,返回变量的存储地址而不是变量的值。&a,这将得到变量 a 的实际地址
    *    // 定义指针变量。*a,表示 a 是一个指针变量
    

    括号语法辅助界定符6个:

    ()    // 小括号
    {}    // 花括号
    []    // 方括号
    

    其他操作符6个:

    :     // 冒号,主要用于 map 类型数据的键与值之间的分隔
    ,     // 逗号,主要用于多个元素之间的分隔
    ;     // 分号,Go 已不需要分号做语句的结尾,但在 for 循环这类一行有多个语句的时候,还是需要写的
    .     // 点,主要用于访问结构体类型的成员或包内的成员
    ...   // 三点省略号,表示不确定数量,或将切片打散传递
    <-    // 向通道发送值或从通道中读值
    

    字面常量(简称字面量)

      在源代码文件中,直接写出来固定值的符号叫字面量。Go语言的字面量只能是基本类型的值,不支持用户自定义类型的字面量。基本有以下几类:

      ▲ 整型字面量,示例如下:

    386
    18446744073709551615
    0600
    0xCa0Fbe6
    

      ▲ 浮点型字面量,示例如下:

    0.0
    0.
    63.9
    063.9
    3.14159
    1.e+0
    3E9
    .23i
    

      ▲ 字符型字面量,Go语言的源码使用的是 UTF-8 的编码,占用的字节数在1 ~ 4 个之间,rune 字符常量表现形式也有很多种,但都要用英文单引号包起来。\ 是转义符。示例如下:

    'f'
    '我'
    '\t'
    '\001'
    '\x02'
    '\xff'
    'u13e5'
    'u00103242'
    

      ▲ 字符串字面量,需要用英文的双引号包起来。\ 是转义符。示例如下:

    "\n"        // 表示换行
    "\""        // 表示一个英文引号
    "OK"        // 表示 OK
    "Yes!\n"    // 表示 Yes! 且后面还跟一个换行
    "编程语言"  // 表示 编程语言
    

    .
    .
    上一节:Go/Golang语言学习实践[回顾]教程12–快速体验Go语言的并发之美

    下一节:Go/Golang语言学习实践[回顾]教程14–详解Go语言代码结构、包、作用域、变量、常量
    .

  • 相关阅读:
    洛谷P6672 你的生命已如风中残烛
    搜索技术领域的“奥林匹克”,飞桨支持“第二届百度搜索创新大赛”正式启动!...
    循环链表3
    Boomi入选《Inc.》杂志2022年5000家增速最快私营公司排行榜
    面试题——网址 (url) 的组成、url模块、querystring模块、mime模块、各种路径、静态资源托管、网页的加载流程
    AndroidT(13) -- natvie LOG 输出的实现(三)
    day7_C++
    使用ensp搭建路由拓扑,并使用ospf协议实现网络互通实操
    【Python】PySpark 数据计算 ③ ( RDD#reduceByKey 函数概念 | RDD#reduceByKey 方法工作流程 | RDD#reduceByKey 语法 | 代码示例 )
    【缓存】Spring全家桶中@CacheEvict无效情况共有以下几种
  • 原文地址:https://blog.csdn.net/yyykj/article/details/127105669