🍊Go基础语法-I
2024-4-11
| 2024-9-20
0  |  阅读时长 0 分钟
type
status
date
slug
summary
tags
category
icon
password
🍊
Go语言基本语法学习-I

目 录

Go语言特性

自动垃圾回收

以不支持垃圾回收的语言内存管理机制为例,如下面这段C语言代码
C系列编程语言由于不支持自动的内存回收机制,因此在声明并赋值一个内存区域后,需要在不使用该内存的时候及时释放,否则指针会变成“野指针”并且该程序进程所占内存会疯狂增长,导致程序崩溃。到目前为止,内存泄露的最佳解决方案是在语言级别引入自动垃圾回收算法(Garbage Collection,简称GC)。所谓垃圾回收,即所有的内存分配动作都会被在运行时记录,同时任何对该内存的使用也都会被记录,然后垃圾回收器会对所有已经分配的内存进行跟踪监测,一旦发现有些内存已经不再被任何人使用,就阶段性地回收这些没人用的内存。当然,因为需要尽量最小化垃圾回收的性能损耗,以及降低对正常程序执行过程的影响,现实中的垃圾回收算法要比这个复杂得多,比如为对象增加年龄属性等,但基本原理都是如此。

丰富的内置类型

Golang 除了支持简单的内置类型,如整型、浮点型、字符型等,还内置了一些高级类型,比如字符串类型、字典类型、数组类型、数组切片类型(动态增长的数组,类似c++中的vector)

函数多返回值

目前的主流语言除了Python外基本上都不支持函数拥有多返回值功能。然而,Golang 可以支持函数拥有多返回值,并且在接收函数返回值时,Go还可以实现像Python类似的使用下划线作为占位符来忽略其他不关心的返回值。

错误处理

Go语言引入了3个关键字用于标准的错误处理流程,这3个关键字分别为defer、panic 和 recover。整体上而言与C++和Java等语言中的异常捕获机制相比, Go语言的错误处理机制可以大量减少代码量,让开发者也无需仅仅为了程序安全性而添加大量一层套一层的try-catch语句。这对于代码的阅读者和维护者来说也是一件很好的事情,因为可以避免在层层的代码嵌套中定位业务代码。

匿名函数和闭包

在Go语言中,所有的函数也是值类型,可以作为参数传递。Go语言支持常规的匿名函数和闭包,比如下列代码就定义了一个名为f的匿名函数,开发者可以随意对该匿名函数变量进行传递和调用。

类型与接口

Go语言的类型定义非常接近于C语言中的结构(struct),甚至直接沿用了struct关键字。相比而言,Go语言并没有直接沿袭C++和Java的传统去设计一个超级复杂的类型系统,不支持继承和重载,而只是支持了最基本的类型组合功能。Go语言也不是简单的对面向对象开发语言做减法,它还引入了一个无比强大的“非侵入式” 接口的概念,让开发者从以往对C++和Java开发中的接口管理问题中解脱出来。
上述c++代码首先声明了一个接口,并且在实现类时,将类与接口紧密绑定,接口的修改会影响到所有实现了该接口的类。Go则不需要

并发编程

Go语言引入了goroutine概念,它使得并发编程变得非常简单。通过使用goroutine而不是裸用操作系统的并发机制,以及使用消息传递来共享内存而不是使用共享内存来通信,Go语言让并发编程变得更加轻盈和安全。
goroutine是一种比线程更加轻盈、更省资源的协程。Go语言通过系统的线程来多路派遣这些函数的执行,使得每个用go关键字执行的函数可以运行成为一个单位协程。当一个协程阻塞的时候,调度器就会自动把其他协程安排到另外的线程中去执行,从而实现了程序无等待并行化运行。而且调度的开销非常小,一颗CPU调度的规模不下于每秒百万次,这使得我们能够创建大量的goroutine,从而可以很轻松地编写高并发程序,达到我们想要的目的。

反射

反射(reflection)是在Java语言出现后迅速流行起来的一种概念。通过反射,你可以获取对象类型的详细信息,并可动态操作对象。反射最常见的使用场景是做对象的序列化(serialization,有时候也叫Marshal & Unmarshal)。例如,Go语言标准库的encoding/json、encoding/xml、encoding/gob、encoding/binary等包就大量依赖于反射功能来实现。

语言交互性

在Go代码中,可以按Cgo的特定语法混合编写C语言代码,然后Cgo工具可以将这些混合的C代码提取并生成对于C功能的调用包装代码。与Java中的JNI不同,Cgo的用法非常简单。

小结

主要是针对Go语言所具备的几点优势进行简单的汇总,后续会针对具体内容进行具体学习。

变量

变量声明

  • 声明单个变量: var 变量名 变量类型
  • 声明多个变量

变量初始化

变量赋值

匿名变量

常量

字面常量

指的是程序硬编码的常量。Go中的字面常量与其他语言不同,只要该常量在相应类型的值域范围内,就可以作为该类型的常量。例如, 就可以赋值给 int、uint、int32、int64、float32、float64、complex64、complex128等类型的变量。

常量定义

通过const关键字给字面常量定义一个名字, const 常量名 (常量类型) = 字面常量

预定义常量

iota比较特殊,可以被认为是一个可被编译器修改的常量,在每一个const关键字出现时被重置为0,然后在下一个const出现之前,每出现一次iota,其所代表的数字会自动增1。

枚举

类型

布尔类型

整型

类型名
长度(字节)
值范围
int8
1
1000 0000(-0/-128) 0111 1111(127) 1111 1111(-127)
int16
2
int32
4
int64
8
uint8
1
(1111 1111)
uint16
2
uint32
4
uint64
8
int
平台相关
平台相关
uint
平台相关
平台相关
uintptr
无符号整型,用于存放一个指针
在32位平台下为4字节,64字节平台下是8字节

浮点型

浮点型用于表示包含小数点的数据,比如1.234就是一个浮点型数据。Go语言中的浮点类型采用IEEE-754标准的表达方式。Go语言定义了两个类型float32和float64,其中float32等价于C语言的float类型,float64等价于C语言的double类型。
因为浮点数不是一种精确的表达方式,所以像整型那样直接用==来判断两个浮点数是否相等是不可行的,这可能会导致不稳定的结果。

复数类型

复数实际上由两个实数(在计算机中用浮点数表示)构成,一个表示实部(real),一个表示虚部(imag)。

字符串

  1. ASCII码集
    1. ASCII 使用 7 位代码来表示 128 个不同的字符。这些代码被分为 95 个可打印的字符,其中包括 26 个英文字母(A 到 Z,包括大写和小写)、10 个数字(0 到 9),以及各种标点符号和其他符号。还有 33 个不可打印的字符,其中包括控制字符,如回车和换行,以及其他各种用于格式化文本的字符
  1. UTF-8
    1. UTF-8 扩展了 ASCII 字符集,使用 8 位代码,允许多达 256 个不同的字符。这意味着 UTF-8 可以表示所有可打印的 ASCII 字符,以及不可打印的字符。UTF-8 还包括各种额外的国际字符,如中文字符和阿拉伯字符。
在Go语言中,字符串也是一种基本类型。相比之下, C/C++语言中并不存在原生的字符串类型,通常使用字符数组来表示,并以字符指针来传递。
双引号用来创建可解析的字符串,支持转义,但不能用来引用多行;
反引号用来创建原生的字符串字面量,可能由多行组成,但不支持转义,并且可以包含除反引号外的其他所有字符
notion image
字符串遍历

字符类型

在Go语言中支持两个字符类型,一个是byte(实际上是uint8的别名),代表UTF-8字符串的单个字节的值;另一个是rune,代表单个Unicode字符。

数组

数组是Go语言编程中最常用的数据结构之一。顾名思义,数组就是指一系列同一类型数据的集合。数组中包含的每个数据被称为数组元素(element),一个数组包含的元素个数被称为数组的长度。数组的长度在定义之后无法再次修改;数组是值类型,每次传递都将产生一份副本
  • 数组声明方法
在Go语言中,数组长度在定义后就不可更改,在声明时长度可以为一个常量或者一个常量表达式(常量表达式是指在编译期即可计算结果的表达式)。数组的长度是该数组类型的一个内置常量,可以用Go语言的内置函数len()来获取。
  • 数组元素遍历

数组切片

从底层实现的角度来看,数组切片实际上仍然使用数组来管理元素,因此它们之间的关系让C++程序员们很容易联想起STL中std::vector和数组的关系。基于数组,数组切片添加了一系列管理功能,可以随时动态扩充存放空间,并且可以被随意传递而不会导致所管理的元素被重复复制。
notion image
  • 创建数组切片
  • 数组切片元素遍历
  • 动态增减元素
可动态增减元素是数组切片比数组更为强大的功能。与数组相比,数组切片多了一个存储能力(capacity)的概念,即元素个数和分配的空间可以是两个不同的值。合理地设置存储能力的值,可以大幅降低数组切片内部重新分配内存和搬送内存块的频率,从而大大提高程序性能。数组切片支持Go语言内置的cap()函数和len()函数,cap()函数返回的是数组切片分配的空间大小,而len()函数返回的是数组切片中当前所存储的元素个数。
数组切片会自动处理存储空间不足的问题,如果追加的内容长度超过当前已分配的存储空间(即cap()函数返回的值),数组切片会自动分配一块足够大的内存。
  • 数组切片内容赋值
数组切片支持Go语言的另一个内置函数copy(),用于将内容从一个数组切片复制到另一个数组切片。如果加入的两个数组切片不一样大,就会按其中较小的那个数组切片的元素个数进行复制。
notion image

map类型

在C++/Java中,map一般都以库的方式提供,比如在C++中是STL的std::map<>,在C#中是Dictionary<>,在Java中是Hashmap<>,在这些语言中,如果要使用map,事先要引用相应的库。而在Go中,使用map不需要引入任何库,并且用起来也更加方便。map是一堆键值对的未排序集合。比如以身份证号作为唯一键来标识一个人的信息。
  • map变量声明:var map变量名 map[键类型] 值类型
  • 创建map
  • 创建并初始化
  • map赋值
  • map元素删除
  • map元素查找

运算符

算术运算符

A赋值为20,B赋值为10
运算符
描述
实例
+
相加
A + B 输出结果 30
-
相减
A - B 输出结果 -10
*
相乘
A * B 输出结果 200
/
相除
B / A 输出结果 2
%
求余
B % A 输出结果 0
++
自增
A++ 输出结果 11
--
自减
A-- 输出结果 9
notion image

逻辑运算符

A赋值为True,B赋值为False
运算符
描述
实例
&&
逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。
(A && B) 为 False
||
逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。
(A || B) 为 True
!
逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。
!(A && B) 为 True
notion image

关系运算符

A赋值为20,B赋值为10
运算符
描述
实例
==
检查两个值是否相等,如果相等返回 True 否则返回 False。
(A == B) 为 False
!=
检查两个值是否不相等,如果不相等返回 True 否则返回 False。
(A != B) 为 True
>
检查左边值是否大于右边值,如果是返回 True 否则返回 False。
(A > B) 为 False
<
检查左边值是否小于右边值,如果是返回 True 否则返回 False。
(A < B) 为 True
>=
检查左边值是否大于等于右边值,如果是返回 True 否则返回 False。
(A >= B) 为 False
<=
检查左边值是否小于等于右边值,如果是返回 True 否则返回 False。
(A <= B) 为 True
notion image

位运算符

p
q
p & q
p | q
p ^ q
0
0
0
0
0
0
1
0
1
1
1
1
1
1
0
1
0
0
1
1
假定 A 为60,B 为13
运算符
描述
实例
&
按位与运算符"&"是双目运算符。 其功能是参与运算的两数各对应的二进位相与。
(A & B) 结果为 12, 二进制为 0000 1100
|
按位或运算符"|"是双目运算符。 其功能是参与运算的两数各对应的二进位相或
(A | B) 结果为 61, 二进制为 0011 1101
^
按位异或运算符"^"是双目运算符。 其功能是参与运算的两数各对应的二进位相异或,当两对应的二进位相异时,结果为1。
(A ^ B) 结果为 49, 二进制为 0011 0001
<<
左移运算符"<<"是双目运算符。左移n位就是乘以2的n次方。 其功能把"<<"左边的运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补0。
A << 2 结果为 240 ,二进制为 1111 0000
>>
右移运算符">>"是双目运算符。右移n位就是除以2的n次方。 其功能是把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数。
A >> 2 结果为 15 ,二进制为 0000 1111

赋值运算符

运算符
描述
实例
=
简单的赋值运算符,将一个表达式的值赋给一个左值
C = A + B 将 A + B 表达式结果赋值给 C
+=
相加后再赋值
C += A 等于 C = C + A
-=
相减后再赋值
C -= A 等于 C = C - A
*=
相乘后再赋值
C *= A 等于 C = C * A
/=
相除后再赋值
C /= A 等于 C = C / A
%=
求余后再赋值
C %= A 等于 C = C % A
<<=
左移后赋值
C <<= 2 等于 C = C << 2
>>=
右移后赋值
C >>= 2 等于 C = C >> 2
&=
按位与后赋值
C &= 2 等于 C = C & 2
^=
按位异或后赋值
C ^= 2 等于 C = C ^ 2
|=
按位或后赋值
C |= 2 等于 C = C | 2

其他运算符

运算符
描述
实例
&
返回变量存储地址
&a; 将给出变量的实际地址。
*
指针变量。
*a; 是一个指针变量

运算符优先级

二元运算符的运算方向均是从左至右。下表列出了所有运算符以及它们的优先级,由上至下代表优先级由高到低:
优先级
运算符
5
* / % << >> & &^
4
+ - | ^
3
== != < <= > >=
2
&&
1
||

运算符注意

不同数据类型不能直接进行相关运算,如下
notion image

流程控制

条件语句

关于条件语句,需要注意以下几点:
  • 条件语句不需要使用括号将条件包含起来();
  • 无论语句体内有几条语句,花括号{}都是必须存在的;
  • 左花括号{必须与if或者else处于同一行;
  • 在if之后,条件语句之前,可以添加变量初始化语句,使用;间隔;

选择语句

在使用switch结构时,我们需要注意以下几点:
  • 左花括号{必须与switch处于同一行;
  • 条件表达式不限制为常量或者整数;
  • 单个case中,可以出现多个结果选项;
  • 与C语言等规则相反,Go语言不需要用break来明确退出一个case;
  • 只有在case中明确添加fallthrough关键字,才会继续执行紧跟的下一个case;
  • 可以不设定switch之后的条件表达式,在此种情况下,整个switch结构与多个if...else...的逻辑作用等同。

循环语句

与多数语言不同的是,Go语言中的循环语句只支持for关键字,而不支持while和do-while结构。关键字for的基本使用方法与C和C++中非常接近。
使用循环语句时,需要注意的有以下几点。
  • 左花括号{必须与for处于同一行。
  • Go语言中的for循环与C语言一样,都允许在循环条件中定义和初始化变量,唯一的区别是,Go语言不支持以逗号为间隔的多个赋值语句,必须使用平行赋值的方式来初始化多个变量。
  • Go语言的for循环同样支持continue和break来控制循环,但是它提供了一个更高级的break,可以选择中断哪一个循环

跳转语句

参考文献

注:本文大部分内容来源于《Go语言编程》这本书,非常感谢这本书在学习Go语言方面给我的指导!
注:To be Continued…… 🍊
☎️
有关这篇文章的任何问题,欢迎您在底部评论区留言,一起交流~ 🍊
 
Private Stateful Information RetrievalGo基础语法-II
  • Twikoo
目录