Go语言入门 —— 基本语法 | 豆包MarsCode AI刷题

84 阅读9分钟

基础语法

Hello World!

package main  // 代表这个文件是main包,即程序的入口文件

// 导入包
import (
    "fmt"    // 导入fmt包,用于格式化输出
)

// 定义一个函数
func main() {
    fmt.Println("hello world")
}

执行方法:

// 直接运行
go run main.go
// 编译成二进制文件
go build main.go     // 会生成main可执行文件

Go的变量和数据类型

变量的声明和初始化

  1. var关键字:可以声明一个或多个变量,并分别初始化它们,或者同时声明并初始化。
var x int   // 声明一个变量
var (      // 声明多个变量
    y string
    z float64
)

// 初始化
x = 10
y = "hello"
z = 3.14

// 同时声明并初始化
var (
    d int    = 100 // 声明并初始化一个 int 类型的变量 d
    e string = "Go" // 声明并初始化一个 string 类型的变量 e
    f float64 = 1.618 // 声明并初始化一个 float64 类型的变量 f
)

// 直接初始化变量时,可以省略类型,Go 会根据赋值的值推导出变量的类型
var a = 42      // a 被推导为 int
var b = "Go"    // b 被推导为 string
var c = 3.14    // c 被推导为 float64
  1. 简短变量声明:使用<font style="color:rgb(77, 77, 77);">:=</font>声明并初始化一个或多个变量,这种方式只在函数内部有效****。
x, y, z := 10, "hello", 3.14
  1. 常量声明: 使用 const 关键字声明常量,这些常量的值在程序运行期间是不可变的。
const pi = 3.14
const greeting = "Hello, World!"
  1. 匿名变量: 在某些情况下,如果不需要使用某个返回值,可以使用匿名变量 _ 来丢弃它。
x, _ := someFunction()  // 忽略第二个返回值
  1. **make**函数:初始化切片、映射和通道(channel)这三种内置数据结构,返回已初始化的类型的值。
slice := make([]int, 5) // 创建一个长度为 5 的 int 类型切片
mapVar := make(map[string]int) // 创建一个 string 到 int 的映射
channel := make(chan int) // 创建一个 int 类型的通道

基本数据类型

Go语言是强类型语言,每个变量都会有固定的类型

整数类型
  • 有符号整数int(根据系统架构可为 32 或 64 位)、int8int16int32int64
  • 无符号整数uintuint8uint16uint32uint64
  • 特殊无符号类型uintptr,用于存储指针地址
浮点数类型
  • 单精度float32
  • 双精度float64
复数类型
  • 复数complex64(实部和虚部都是 float32)
  • 复数complex128(实部和虚部都是 float64)
布尔类型
  • 布尔bool(值为 truefalse
字符串类型
  • 字符串string,表示 UTF-8 编码的文本
其他类型
  • 字节类型byte(相当于 uint8
  • 字符类型rune(相当于 int32,用于表示 Unicode 码点)
#### 复合数据类型 复合数据类型是由基本数据类型组合而成的,更适合处理复杂数据结构。主要包括以下几种:
数组(Array)
  • 定义固定大小的元素序列,每个元素必须是相同的类型。
  • 数组长度是其类型的一部分,因此 [3]int[4]int 是不同的类型。
var arr [5]int = [5]int{1, 2, 3, 4, 5}
// 二维
var twoD [2][3]int
for i := 0; i < 2; i++ {
    for j := 0; j < 3; j++ {
        twoD[i][j] = i + j
    }
}
切片(Slice)
  • 动态大小的序列,比数组更灵活。切片不需要指定长度,可以动态增长。
  • 切片是对底层数组的引用,因此切片更为灵活且高效。
var s []int = []int{1, 2, 3, 4, 5}


s = append(s, 6, 7)

s = append(s, 6, 7)

映射(Map)
  • 键值对的数据结构,类似于其他语言的字典或哈希表。
  • 可以通过键快速查找对应的值。
var m map[string]int = map[string]int{"Alice": 23, "Bob": 25}
结构体(Struct)
  • 聚合多个不同类型的字段,适合表示复杂的数据结构。
  • 每个字段可以有不同的类型,可以定义为命名或匿名字段。
type Person struct {
    Name string
    Age  int
}
var p = Person{Name: "Alice", Age: 30}
接口(Interface)
  • 接口是一种抽象类型,定义了一组方法而不具体实现。
  • 可以通过接口实现多态,任何类型只要实现了接口定义的所有方法,就可以视为该接口的类型。
type Speaker interface {
    Speak() string
}
指针(Pointer)
  • 指针是存储变量地址的变量,允许在函数中传递大型结构体时避免拷贝以提高效率。
  • Go 没有指针运算,但可以通过 &* 操作符操作指针。
var a int = 10
var ptr *int = &a
### 基本语句语法 #### 条件语句 Go 使用 `if` 和 `else` 语句进行条件判断,注意与C++不同的时,`if`后面没有括号。
package main

import "fmt"

func main() {
    number := 10

    if number > 0 {   // 与C++的不同,没有括号
        fmt.Println("Positive number")
    } else if number < 0 {
        fmt.Println("Negative number")
    } else {
        fmt.Println("Zero")
    }
}

循环语句

Go 只有一种循环结构,即 for 循环,可以用于多种情况,包括传统的 for 循环和 while 风格的循环。

package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }
}

package main

import "fmt"

func main() {
    j := 0
    for j < 5 {
        fmt.Println(j)
        j++
    }
}

package main

import "fmt"

func main() {
    k := 0
    for {
        if k >= 5 {
            break
        }
        fmt.Println(k)
        k++
    }
}

switch语句

switch 语句用于替代多重 if 语句,根据不同的条件执行不同的代码块。与C++不同的是,Go中在执行完某个case时不需要加break关键字,不会执行到其他case中。

另外,Go中switch对变量类型没有限制。几乎可以替换if使用。

package main

import (
	"fmt"
	"time"
)

func main() {

	a := 2
	switch a {
	case 1:
		fmt.Println("one")     // 不需要加break
	case 2:
		fmt.Println("two")
	case 3:
		fmt.Println("three")
	case 4, 5:
		fmt.Println("four or five")
	default:
		fmt.Println("other")
	}

	t := time.Now()
	switch {			// 类似于 if else
	case t.Hour() < 12:
		fmt.Println("It's before noon")
	default:
		fmt.Println("It's after noon")
	}
}

range语句

在 Go 语言中,range 关键字用于遍历切片、数组、映射、字符串和通道等数据结构。它简化了循环操作,避免了手动索引。

package main

import "fmt"

func main() {
	nums := []int{2, 3, 4}
	sum := 0
	for i, num := range nums {
		sum += num
		if num == 2 {
			fmt.Println("index:", i, "num:", num) // index: 0 num: 2
		}
	}
	fmt.Println(sum) // 9

	m := map[string]string{"a": "A", "b": "B"}
	for k, v := range m {
		fmt.Println(k, v) // b 8; a A
	}
	for k := range m {
		fmt.Println("key", k) // key a; key b
	}
}

函数语句

Go语言中函数的参数和返回值的类型是跟在参数名后,而不是在前面。且函数可以返回多个值(类似于Python),这在处理错误或同时返回多个结果时非常方便。例如,可以返回计算结果和状态值。

package main

import "fmt"

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

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

func exists(m map[string]string, k string) (v string, ok bool) {
	v, ok = m[k]
	return v, ok
}

func main() {
	res := add(1, 2)
	fmt.Println(res) // 3

	v, ok := exists(map[string]string{"a": "A"}, "a")
	fmt.Println(v, ok) // A True
}

结构体方法

在 Go 语言中,结构体(struct)可以定义方法,以便与特定的结构体类型关联。方法是与特定类型(通常是结构体)相关联的函数,可以通过该类型的实例调用。这使得 Go 支持面向对象的编程风格。 (即类成员函数)



func (receiver Type) MethodName(parameters) ReturnType {
    // 方法体
}

/*
receiver:接收者,表示方法所属于的结构体类型。
Type:结构体类型。
MethodName:方法名。
parameters:方法参数。
ReturnType:返回值类型。
*/


// 示例
package main

import "fmt"

type user struct {
	name     string
	password string
}

func (u user) checkPassword(password string) bool {
	return u.password == password
}

func (u *user) resetPassword(password string) {
	u.password = password
}

func main() {
	a := user{name: "wang", password: "1024"}
	a.resetPassword("2048")  // 和成员函数一样
	fmt.Println(a.checkPassword("2048")) // true
}

错误处理

在 Go 语言中,错误处理是通过返回值实现的,通常函数的最后一个返回值是 error 类型。这种设计使得错误处理显式且简洁。 Go 的 error 类型是一个接口,定义如下:

type error interface {
    Error() string
}

任何实现了 Error() 方法的类型都可以被视为 error 类型。

错误处理示例:

package main

import (
	"errors"
	"fmt"
)

type user struct {
	name     string
	password string
}

func findUser(users []user, name string) (v *user, err error) {
	for _, u := range users {
		if u.name == name {
			return &u, nil
		}
	}
	return nil, errors.New("not found")
}

func main() {
	u, err := findUser([]user{{"wang", "1024"}}, "wang")
	if err != nil {	// 有错误
		fmt.Println(err)
		return
	}
	fmt.Println(u.name) // wang

	if u, err := findUser([]user{{"wang", "1024"}}, "li"); err != nil {
		fmt.Println(err) // not found
		return
	} else {
		fmt.Println(u.name)
	}
}

在 Go 语言中,nil 是一个预定义的标识符,表示“没有值”或“无效值”。 在错误处理中,一般用来表示没有错误。

字符串相关操作

在 Go 语言中,字符串是一个不可变的基本数据类型。Go 提供了丰富的内置函数和标准库来操作字符串,主要集中在 strings 包中。以下是一些常用的字符串操作函数和示例:

  • 长度:获取字符串的长度(字符数)。
str := "Hello, 世界"
length := len(str) // 字节长度
  • 拼接:使用 + 运算符或 strings.Join() 函数拼接字符串。
s1 := "Hello"
s2 := "World"
result := s1 + ", " + s2 // 使用 + 运算符

:::info **strings**** 包中的常用函数**

:::

import "strings"

// 包含
contains := strings.Contains("Hello, World", "World") // true

// 计数
count := strings.Count("Hello, Hello", "Hello") // 2

// 前缀和后缀
hasPrefix := strings.HasPrefix("Hello, World", "Hello") // true
hasSuffix := strings.HasSuffix("Hello, World", "World") // true

// 拆分和连接
parts := strings.Split("a,b,c", ",") // ["a", "b", "c"]
joined := strings.Join([]string{"a", "b", "c"}, ",") // "a,b,c"

// 去除空白
trimmed := strings.TrimSpace("  Hello, World  ") // "Hello, World"

// 转换大小写
lower := strings.ToLower("Hello, World") // "hello, world"
upper := strings.ToUpper("Hello, World") // "HELLO, WORLD"

// 替换
replaced := strings.Replace("Hello, World", "World", "Go", 1) // "Hello, Go"

格式化输出

在 Go 语言中,格式化输出通过 fmt 包提供的函数实现,特别是 fmt.Printf 函数。

**fmt.Printf**

  • fmt.Printf 函数用于格式化输出,可以控制输出的格式。
  • **%v**:默认格式,输出值的表示。
    • fmt.Printf("s=%v\n", s) 输出字符串的默认格式。
    • fmt.Printf("n=%v\n", n) 输出整数的默认格式。
    • fmt.Printf("p=%v\n", p) 输出结构体的默认格式。
  • **%+v**:输出结构体时包括字段名。
    • fmt.Printf("p=%+v\n", p) 输出字段名和字段值。
  • **%#v**:输出值的 Go 语法表示。
    • fmt.Printf("p=%#v\n", p) 输出结构体的完整类型信息和字段值。
  • **%.2f**:格式化浮点数,保留两位小数。
    • fmt.Printf("%.2f\n", f) 输出浮点数的格式化表示。
package main

import "fmt"

// 定义一个结构体 point
type point struct {
	x, y int
}

func main() {
	s := "hello" // 字符串
	n := 123     // 整数
	p := point{1, 2} // 结构体实例

	// 基本输出
	fmt.Println(s, n) // 输出: hello 123
	fmt.Println(p)    // 输出: {1 2}

	// 使用格式化输出
	fmt.Printf("s=%v\n", s)  // 输出: s=hello
	fmt.Printf("n=%v\n", n)  // 输出: n=123
	fmt.Printf("p=%v\n", p)  // 输出: p={1 2}
	fmt.Printf("p=%+v\n", p) // 输出: p={x:1 y:2}
	fmt.Printf("p=%#v\n", p) // 输出: p=main.point{x:1, y:2}

	f := 3.141592653 // 浮点数
	fmt.Println(f)          // 输出: 3.141592653
	fmt.Printf("%.2f\n", f) // 输出: 3.14(保留两位小数)
}