GO语言——基础语法
  y9EYnC7aLifI 2023年12月19日 13 0

从Hello Golang开始

package main 
import "fmt"
func main() {
    fmt.Println("Hello, Golang!")
}
  • main包是程序的入口包,在Go中,所有可执行文件都必须包含main
  • import导入所需要的包,将在后续介绍具体使用
  • func用于定义函数的关键字,main()则为主函数,运行时从此处开始
  • fmt.Println() 调用fmt包中的Println() 函数打印 Hello, Golang!

变量、常量的声明和初始化

若要声明变量,需要使用 var 关键字,<u>声明的类型放在后面</u>,如果你决定初始化某个变量,则不需要指定其类型,因为当你**<u>使用具体值初始化该变量时,Go 会推断出其类型。如果声明了变量但未使用,Go 会抛出错误</u>**

var name string
var (
    firstName, lastName string
    age int
)
var (
    firstName = "Lowell"
    lastName  = "Doe"
    age       = 32
)
var (
    firstName, lastName, age = "John", "Doe", 32	// 按顺序赋值
)
firstName, lastName := "John", "Doe"	// 声明同时初始化,这种方式用得多
age := 32

在定义变量名称后,需要在此处加入一个冒号等于号 (:=) 和相应的值。 <u>使用冒号等于号时,要声明的变量必须是新变量。 如果使用冒号等于号并已经声明该变量,将不会对程序进行编译</u>

声明常量的关键字为 const ,与变量一样,Go 可以通过分配给常量的值推断出类型。 在 Go 中,常量名称通常以混合大小写字母或全部大写字母书写。如果需要在一个块中声明多个常量,可以按如下所示执行:

const STATUS = 0
const (
    StatusOK              = 0
    StatusConnectionReset = 1
    StatusOtherError      = 2
)

可以在不使用常量的情况下声明常量,并且不会收到错误消息。 <u>不能使用冒号等于号来声明常量</u>。 如果采用这种方式,Go 会发出警告

Go 为常量定义了一个有趣的概念,即 iota,本模块未对此概念作进一步解释。 但你可以查看 GitHub 上的 Go wiki 了解更多信息。 请记住,iota 是一个关键字;如果这些值存在先后顺序,Go 可使用此关键字简化常量定义。

基本数据类型

Go 是一种强类型语言。声明的每个变量都绑定到特定的数据类型,并且只接受与此类型匹配的值。

四类数据类型:

  • 基本类型:数字、字符串和布尔值
  • 聚合类型:数组和结构
  • 引用类型:指针、切片、映射、函数和通道
  • 接口类型:接口

整数数字

一般来说,定义整数类型的关键字是 int。 但 Go 还提供了 int8int16int32int64 类型,其大小分别为 8、16、32 或 64 位的整数

var integer8 int8 = 127
var integer16 int16 = 32767
var integer32 int32 = 2147483647
var integer64 int64 = 9223372036854775807

类型转换

在 Go 中,intint32 不同,即使整数的自然大小为 32 位也是如此。 换句话说,需要强制转换时,你需要进行显式转换。 如果尝试在不同类型之间执行数学运算,将会出现错误。在Go中隐式类型转换不起作用,所所以我们的需要进行显示类型转换。如下:

var integer16 int16 = 127
var integer32 int32 = 32767
fmt.Println(integer16 + integer32)
//报错 invalid operation: integer16 + integer32 (mismatched types int16 and int32)

将代码改成如下所示便没问题

var integer16 int16 = 127
var integer32 int32 = 32767
fmt.Println(int32(integer16) + integer32)

另一种转换方法是使用 strconv 包。 例如,若要将 string 转换为 int,可以使用以下代码,反之亦然

package main
import (
    "fmt"
    "strconv"
)
func main() {
    i, _ := strconv.Atoi("-42")
    s := strconv.Itoa(-42)
    fmt.Println(i, s)
}

在前面的代码中,有一个下划线 (_) 用作变量的名称。 在 Go 中,_ 意味着我们不会使用该变量的值,而是要将其忽略。 否则,程序将不会进行编译,因为我们需要使用声明的所有变量。

浮点数字

Go 提供两种浮点数大小的数据类型:float32float64

当需要使用十进制数时,浮点类型也很有用。 例如,你可以编写类似于以下代码的内容:

const e = 2.71828
const Avogadro = 6.02214129e23
const Planck = 6.62606957e-34

布尔型

使用关键字 bool 声明布尔类型。 在 Go 中,不能将布尔类型隐式转换为 0 或 1。 你必须显式执行此操作。

var flag bool = true

字符串

在 Go 中,关键字 string 表示字符串数据类型。 若要初始化字符串变量,你需要在双引号(")中定义值。 单引号(')用于单个字符

var firstName string = "John"
lastName := "Doe"
fmt.Println(firstName, lastName)

默认值

在 Go 中,如果你不对变量初始化,所有数据类型都有默认值。 以下为几种数据类型的默认值

  • int 类型的 0(及其所有子类型,如 int64
  • float32float64 类型的 +0.000000e+000
  • bool 类型的 false
  • string 类型的空值

函数

下面为一个从键盘读取参数,两数相加的例子

func main() {
	// 直接相加
	number1, _ := strconv.Atoi(os.Args[1])
	number2, _ := strconv.Atoi(os.Args[2])
	fmt.Println("Sum = ", number1+number2)
	//调用自定义函数
	sum := sum(os.Args[1], os.Args[2])
	fmt.Println("Sum = ", sum)
}
func sum(number1 string, number2 string) int {
	int1, _ := strconv.Atoi(number1)
	int2, _ := strconv.Atoi(number2)
	return int1 + int2
}
func sum1(number1 string, number2 string) (result int) {
	int1, _ := strconv.Atoi(number1)
	int2, _ := strconv.Atoi(number2)
	restult = int1 + int2
	return
}

访问 Go 中的命令行参数,可以使用用于保存传递到程序的所有参数的 os 包os.Args 变量来执行操作。

在Go 中,你还可以为函数的返回值设置名称,将其当作一个变量,如下:

func name(parameters) (results) {
    body-content
}

上面创建函数的语法,在命名后,指定函数的参数列表,可以指定零个或多个参数

返回多个值

Go中可以函数返回多个值,可以采用类似于定义函数参数的方式来定义这些值,如下,将两个相加并且相乘返回

func main() {
	sum, mul := SumMul(os.Args[1], os.Args[2])
	fmt.Println("Sum = ", sum, "Mul = ", mul)
}

func SumMul(number1 string, number2 string) (sum int, mul int) {
	int1, _ := strconv.Atoi(number1)
	int2, _ := strconv.Atoi(number2)
	sum = int1 + int2
	mul = int1 * int2
	return
}

如果不需要函数的某个返回值,可以通过将返回值分配给 _ 变量来放弃该函数。 _ 变量是 Go 忽略返回值的惯用方式。 它允许程序进行编译。 因此,如果只需要求和,则可以使用以下代码:

func main() {
    sum, _ := AddMul(os.Args[1], os.Args[2])
    fmt.Println("Sum:", sum)
}

更改函数参数值(指针)

将值传递给函数时,该函数中的每个更改都不会影响调用方。 Go 是**“按值传递”**编程语言。 每次向函数传递值时,Go 都会使用该值并创建本地副本(内存中的新变量)。 在函数中对该变量所做的更改都不会影响你向函数发送的更改。

func main() {
	name := "Lowell"
	update(name)
	fmt.Println(name)
}
func update(name string) {
	name = "Tom"
}

即使你在函数中将该名称更改为 Tom,输出仍为 Lowell。由于 update 函数中的更改仅会修改本地副本,因此输出不会发生变化。 Go 传递变量的值,而不是变量本身。

使用指针在update函数中修改mainname值,向函数发送指针时,不是传递值,而是传递内存地址。 因此,对该变量所做的每个更改都会影响调用方。如下:

func main() {
	name := "Lowell"
	Update(&name)	//函数发送的是地址,所以要引用&
	fmt.Println(name)
}
func Update(name *string) {
	*name = "Tom"
}

了解包

main 包

当你使用 main 包时,程序将生成独立的可执行文件。 但当程序不是 main 包的一部分时,Go 不会生成二进制文件。 它生成包存档文件(具有 .a 扩展名的文件)。

在 Go 中,包名称需遵循约定。 包使用其导入路径的最后一部分作为名称。 例如,Go 标准库包含名为 math/cmplx 的包,该包提供用于处理复数的有用代码。 此包的导入路径为 math/cmplx

Go中没有private public等关键字,但是 须遵循以下两个简单规则:

  • 如需将某些内容设为专用内容,请以小写字母开始。
  • 如需将某些内容设为公共内容,请以大写字母开始。

创建calculator

package calculator

var logMessage = "[LOG]"	// 专用内容,只能从包内调用logMessage变量。

var Version = "1.0"		// 公共内容,可以从任何位置访问Version变量。 

func internalSum(number int) int {	// 专用内容,只能从包内调用 `internalSum` 函数。
    return number - 1
}

func Sum(number1, number2 int) int {	// 公共内容,可以从任何位置访问 `Sum` 函数。
    return number1 + number2
}

创建模块

Go 模块通常包含可提供相关功能的包。 包的模块还指定了 Go 运行组合在一起的代码所需的上下文。 此上下文信息包括编写代码时所用的 Go 版本。

此外,模块还有助于其他开发人员引用代码的特定版本,并更轻松地处理依赖项。 另一个优点是,我们的程序源代码无需严格存在于 $GOPATH/src 目录中。 如果释放该限制,则可以更方便地在其他项目中同时使用不同包版本

因此,若要为 calculator 包创建模块,请在根目录 ($GOPATH/src/calculator) 中运行以下命令:

go mod init path

控制流

if / else 语句

在 Go 中,你不需要在条件中使用括号。 else 子句可选。 但是,大括号是必需的,不能将语句和if 写在同一行。 Go 不支持三元 if 语句,因此每次都需要编写完整的 if 语句。

package main

import "fmt"

func main() {
    if num := 12; num < 0 {
        fmt.Println(num, "is negative")
    } else if num < 10 {
        fmt.Println(num, "has only one digit")
    } else {
        fmt.Println(num, "has multiple digits")
    }
}

Go 支持复合条件语句,在if分支中初始化num ,并且这个num的作用域仅限于这个 if-else-if 语句块,随后按条件执行输出:has multiple digits

switch 语句

if 语句一样,switch <u>条件不需要括号</u>。如下:

func main() {
    i := 3
    switch i {
    case 1 :
        fmt.Prntln("1st")
    case 2 :
        fmt.Println("2nd")
    case 3 :
        fmt.Println("3rd")
    default :
        fmt.Println("not match")
    }
}

Go 会比较 switch 语句的每个用例,直到找到与条件匹配的项。 如果都未匹配,则程序的输出为 not match

使用多个表达式

有时,多个表达式仅与一个 case 语句匹配。 在 Go 中,<u>如果希望 case 语句包含多个表达式,请使用逗号 (,) 来分隔表达式,避免代码重复。</u>

package main

import "fmt"

func location(city string) (string, string) {
    var region string
    var continent string
    switch city {
    case "Delhi", "Hyderabad", "Mumbai", "Chennai", "Kochi":
        region, continent = "India", "Asia"
    case "Lafayette", "Louisville", "Boulder":
        region, continent = "Colorado", "USA"
    case "Irvine", "Los Angeles", "San Diego":
        region, continent = "California", "USA"
    default:
        region, continent = "Unknown", "Unknown"
    }
    return region, continent
}
func main() {
    region, continent := location("Irvine")
    fmt.Printf("Lowell works in %s, %s\n", region, continent)
}

调用函数

switch 还可以调用函数。以下代码调用 time.Now() 函数。 它提供的输出取决于当前工作日。

package main

import (
    "fmt"
    "time"
)

func main() {
    switch time.Now().Weekday().String() {
    case "Monday", "Tuesday", "Wednesday", "Thursday", "Friday":
        fmt.Println("It's time to learn some Go.")
    default:
        fmt.Println("It's the weekend, time to rest!")
    }

    fmt.Println(time.Now().Weekday().String())
}

使逻辑进入到下一个 case

在C/C++,在每个 case 语句末尾写一个 break 关键字。 但在 Go 中,当逻辑进入某个 case 时,它会退出 switch 块,除非你显式停止它。 若要使逻辑进入到下一个紧邻的 case,请使用 fallthrough 关键字。

package main

import (
    "fmt"
)

func main() {
    switch num := 15; {
    case num < 50:
        fmt.Printf("%d is less than 50\n", num)
        fallthrough
    case num > 100:
        fmt.Printf("%d is greater than 100\n", num)
        fallthrough
    case num < 200:
        fmt.Printf("%d is less than 200", num)
    }
}
输出:
15 is less than 50
15 is greater than 100
15 is less than 200

我们可以发现在第二个case输出中有错误,是因为我们在第一个case中使用了 fallthrough 关键字,在判断满足第一个case的条件进入语句块,它会直接进入下一个case中,而不验证条件,因此慎重使用。

for语句

if 语句和 switch 语句一样,for 循环表达式不需要括号。 但是,大括号是必需的。

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

数组

<u>数组必须在声明或初始化它们时定义大小</u>。 此外,它们一旦创建,就无法调整大小。鉴于这些原因,数组在 Go 程序中并不常用,但它们是切片和映射的基础。如下:创建一个长度为3,类型为 int 的数组a,并给a[1]赋值为10。

package main
import "fmt"
func main() {
    var a [3]int
    a[1] = 10
    fmt.Println(a[0])
    fmt.Println(a[1])
    fmt.Println(a[len(a)-1])
}
输出:
0
10
0

可以看给a[0]赋值,但是打印了值,默认情况下,Go 会用默认数据类型初始化每个元素。 这样的话,int 的默认值为零。len 函数是 Go 中的内置函数,用于获取数组、切片或映射中的元素数。

你也可以声明的同时初始化,如下所示,最后一个位置包含一个空的字符串,因为它是字符串数据类型的默认值。

cities := [5]string{"New York", "Paris", "Berlin", "Madrid"}

切片

与数组一样,切片也是 Go 中的一种数据类型,它表示一系列类型相同的元素。 不过,<u>与数组更重要的区别是切片(slice)的大小是动态的,不是固定的。切片是数组或另一个切片之上的数据结构。</u> 我们将源数组或切片称为基础数组。 通过切片,可访问整个基础数组,也可仅访问部分元素。

切片有 3 个组件:

  • 指向基础数组中第一个可访问元素的指针。 此元素不一定是数组的第一个元素 array[0]
  • 切片的长度。 切片中的元素数目。
  • 切片的容量。 切片开头与基础数组结束之间的元素数目。

创建切片

slice1 := []int {1, 2, 3, 4, 5}    // 创建一个包含五个元素的整型切片
slice2 := make([]int, 5)		  // 创建一个长度为5的整型切片

由上可以看出,在初始化slice1时没有指定长度,而是由后面给出的初始值个数确定,而创建数组时长度就确定了

切片项

Go 支持切片运算符 s[i:p],其中:

  • s 表示数组。
  • i 表示指向要添加到新切片的基础数组(或另一个切片)的第一个元素的指针。 变量 i 对应于数组 array[i] 中索引位置 i 处的元素。 请记住,此元素不一定是基础数组的第一个元素 array[0]
  • p 表示创建新切片时要使用的基础数组中的元素数目,也表示元素位置。 变量 p 对应于可用于新切片的基础数组中的最后一个元素。 可在位置 array[i+1] 找到基础数组中位置 p 处的元素。 请注意,此元素不一定是基础数组的最后一个元素 array[len(array)-1]

假设你需要 4 个变量来表示一年的每个季度,并且你有一个包含 12 个元素的 months 切片。

package main
import "fmt"
func main() {
    months := []string{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}
    quarter1 := months[0:3]
    quarter2 := months[3:6]
    quarter3 := months[6:9]
    quarter4 := months[9:12]
    fmt.Println(quarter1, len(quarter1), cap(quarter1))
    fmt.Println(quarter2, len(quarter2), cap(quarter2))
    fmt.Println(quarter3, len(quarter3), cap(quarter3))
    fmt.Println(quarter4, len(quarter4), cap(quarter4))
}
输出:
[January February March] 3 12
[April May June] 3 9
[July August September] 3 6
[October November December] 3 3

使用 append 追加元素

slice := []int{1, 2, 3, 4}
slice = append(silce, 5)

映射(map)

Go 中的映射是一个哈希表,是**<u>键值对的集合</u>**。 映射中所有的键都必须具有相同的类型,它们的值也是如此。 不过,可对键和值使用不同的类型。

使用关键字 map 创建映射

studentsAge := map[string]int	// 键类型为string,值类型为int 

使用内置函数 make() 创建映射

studentsAge := make(map[string]int)	// 键类型为string,值类型为int 

映射是动态的。 创建项后,可添加、访问或删除这些项。

studentsAge["john"] = 32
fmt.Println(studentsAge["john"]) // 打印键值对中,键为"john"的值
studentsAge["Lowell"] = 20
delete(studentsAge, "Lowell")	// 删除键为"Lowell"的键值对

映射中的循环

range 会首先生成项的键,然后再生成该项的值。

package main
import (
    "fmt"
)
func main() {
    studentsAge := make(map[string]int)
    studentsAge["john"] = 32
    studentsAge["bob"] = 31
    for name, age := range studentsAge {
        fmt.Printf("%s\t%d\n", name, age)
    }
}
输出:
john    32
bob     31

结构体

Go 中的结构,它可包含零个或多个任意类型的字段,并将它们表示为单个实体。声明结构,需要使用 struct 关键字

type Employee struct {
    ID        int
    Name  string
    Age int
}

定义了一个Employee 结构体,包含ID,Name,Age三个字段,随后我们可以做如下操作

employee := Employee{1, Lowell, 20}		// 初始化一个结构体
var employee1
employee1.ID = 2 
employee1.Name = "Mike"
employee1.Age = 19
【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月19日 0

暂无评论

推荐阅读
  hyrB1Ag4eVs8   2024年04月15日   38   0   0 Go
  YFCZjJLTjJgW   14天前   19   0   0 Go
  YFCZjJLTjJgW   18小时前   6   0   0 Go
  uGYzDadp0Cs7   2024年04月16日   93   0   0 Go
  YFCZjJLTjJgW   18小时前   4   0   0 Go
y9EYnC7aLifI