Go语言的使用

发布于:2024-11-02 ⋅ 阅读:(18) ⋅ 点赞:(0)

✨ Go的基本语法

1. 基本数据类型

在 Go 语言中,不同数据类型占用的内存大小是固定的

数据类型 占用内存大小 描述
bool 1字节 布尔值,truefalse
int8 1字节 8位有符号整数,范围 -128127
uint8 1字节 8位无符号整数,范围 0255
int16 2字节 16位有符号整数,范围 -3276832767
uint16 2字节 16位无符号整数,范围 065535
int32 4字节 32位有符号整数,范围 -21474836482147483647
uint32 4字节 32位无符号整数,范围 04294967295
int64 8字节 64位有符号整数,范围 -92233720368547758089223372036854775807
uint64 8字节 64位无符号整数,范围 018446744073709551615
int 平台相关 通常为 int32int64
uint 平台相关 通常为 uint32uint64
uintptr 平台相关 通常为 uint32uint64,用于存储指针值
float32 4字节 32位浮点数
float64 8字节 64位浮点数
complex64 8字节 两个 float32 组成的复数
complex128 16字节 两个 float64 组成的复数
byte 1字节 uint8 的别名
rune 4字节 int32 的别名,用于表示 Unicode 码点

复合数据类型

数据类型 占用内存大小 描述
string 变长 字符串,存储实际字符的字节数加上一些额外的元数据(如长度)
[]T 变长 切片,包含指向底层数组的指针、长度和容量
[n]T 固定 数组,大小为 n * sizeof(T)
map[K]V 变长 映射,包含哈希表的元数据和实际存储的数据
struct 固定 结构体,大小为各字段大小之和,可能包含填充字节以对齐
chan T 变长 通道,包含通道的元数据和缓冲区(如果有)
interface{} 变长 接口,包含类型信息和值指针
*T 平台相关 指针,通常为 4 字节或 8 字节
package main

import "fmt"

func main() {
    var i int = 42
    var f float64 = float64(i)  // 类型转换
    var u uint = uint(f)

    fmt.Printf("i: %d, f: %f, u: %d\n", i, f, u)
}

fmt 包格式化选项

fmt 包的格式化选项整理成表格形式,以便更清晰地查看和理解。以下是一个详细的表格:

格式化符 描述 示例输入 示例输出
%d 十进制整数 123 123
%x 十六进制整数 123 7b
%o 八进制整数 123 173
%b 二进制整数 123 1111011
%c 对应整数的 Unicode 字符 123 {
%f 浮点数,小数点后默认6位 123.456 123.456000
%e 科学记数法,小数点前一位,小数点后默认6位 123.456 1.234560e+02
%E 科学记数法,小数点前一位,小数点后默认6位,使用大写E 123.456 1.234560E+02
%g 根据数值大小自动选择 %e%f,去掉尾部的零 123.456 123.456
%s 字符串 "hello" hello
%q 带引号的字符串,适合打印字符串字面量 "hello" "hello"
%t 布尔值,truefalse true true
%p 指针地址 &s 0xc0000160a0
%v 默认格式,适用于任何类型 123 123
%T 类型的名称 123 int
%% 输出百分号本身 %% %
%w 指定最小宽度,不足时用空格填充 %5d 123
%.p 指定精度,对于浮点数是小数点后的位数,对于字符串是最大长度 %.2f 123.46
%-w 左对齐 %-5d 123
package main

import (
    "fmt"
)

func main() {
    // 整数
    fmt.Printf("十进制: %d, 十六进制: %x, 八进制: %o, 二进制: %b, 字符: %c\n", 123, 123, 123, 123, 123)
    // 浮点数
    fmt.Printf("浮点数: %f, 科学记数法: %e, 科学记数法大写E: %E, 自动选择: %g\n", 123.456, 123.456, 123.456, 123.456)
    // 字符串
    fmt.Printf("字符串: %s, 带引号: %q\n", "hello", "hello")
    // 布尔值
    fmt.Printf("布尔值: %t\n", true)
    // 指针
    var s string = "hello"
    fmt.Printf("指针地址: %p\n", &s)
    // 宽度和精度
    fmt.Printf("右对齐: %5d, 左对齐: %-5d, 小数点后两位: %.2f\n", 123, 123, 123.456)
    // 其他选项
    fmt.Printf("默认格式: %v, 类型名称: %T\n", 123, 123)
}

输出结果

十进制: 123, 十六进制: 7b, 八进制: 173, 二进制: 1111011, 字符: {
浮点数: 123.456000, 科学记数法: 1.234560e+02, 科学记数法大写E: 1.234560E+02, 自动选择: 123.456
字符串: hello, 带引号: "hello"
布尔值: true
指针地址: 0xc0000160a0
右对齐:   123, 左对齐: 123  , 小数点后两位: 123.46
默认格式: 123, 类型名称: int

2. 变量和常量

package main

import "fmt"

func main() {
    // 变量声明
    var a int = 10
    var b = 20         // 自动推断类型
    c := 30            // 简短声明(只能在函数内部使用)

    // 常量
    const pi = 3.14

    fmt.Println("a:", a, "b:", b, "c:", c, "pi:", pi)
}
2.1 短声明(short variable declaration)是一种简洁的方式来声明并初始化变量。短声明使用 := 操作符,语法如下:

// variable := value

package main

import "fmt"

func main() {
    // 短声明
    a := 42
    b := "hello"
    c := true

    fmt.Println(a, b, c) // 输出: 42 hello true
}

多变量声明

package main

import "fmt"

func main() {
    x, y := 10, 20
    fmt.Println(x, y) // 输出: 10 20
}

函数返回值

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    // 初始化随机数生成器
    rand.Seed(time.Now().UnixNano())

    // 接收函数返回值
    num := rand.Intn(100)
    fmt.Println(num) // 输出一个 0 到 99 之间的随机数
}
  1. 作用域:短声明只能在函数内部使用,不能在包级别的声明中使用。

  2. 重复声明:在一个作用域内,同一个变量名不能被多次短声明。例如:

    package main
    
    import "fmt"
    
    func main() {
        a := 42
        a := 100 // 编译错误: cannot declare and initialize the same variable twice in the same block
        fmt.Println(a)
    }
    

    但是,如果你已经声明了一个变量,可以在同一个作用域内重新赋值:

    package main
    
    import "fmt"
    
    func main() {
        a := 42
        a = 100 // 合法
        fmt.Println(a) // 输出: 100
    }
    

多变量赋值

package main

import "fmt"

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result) // 输出: Result: 5
    }
}

短声明是 Go 语言中非常实用的特性,可以使代码更加简洁和易读。但在使用时需要注意作用域和重复声明的问题。

2.2 有符号整数的环绕

在编程中,整数环绕(Integer Wraparound)是指当一个整数变量超出其最大或最小值范围时,它的值会自动“环绕”到另一个极端值。这种现象通常发生在有符号整数和无符号整数之间。

有符号整数(如 int8, int16, int32, int64)在超过其最大值时会变为最小值,反之亦然。这是由于它们的内部表示方式(通常是二进制补码)。

示例
package main

import (
	"fmt"
)

func main() {
	var i8 int8 = 127 // int8 的最大值
	fmt.Println("i8 before increment:", i8)
	i8++
	fmt.Println("i8 after increment:", i8) // 输出 -128

	var i8Min int8 = -128 // int8 的最小值
	fmt.Println("i8Min before decrement:", i8Min)
	i8Min--
	fmt.Println("i8Min after decrement:", i8Min) // 输出 127
}
无符号整数的环绕

无符号整数(如 uint8, uint16, uint32, uint64)在超过其最大值时会变为0,反之亦然。这是因为无符号整数没有负数表示,只有正数和0。

package main

import (
	"fmt"
)

func main() {
	var u8 uint8 = 255 // uint8 的最大值
	fmt.Println("u8 before increment:", u8)
	u8++
	fmt.Println("u8 after increment:", u8) // 输出 0

	var u8Zero uint8 = 0 // uint8 的最小值
	fmt.Println("u8Zero before decrement:", u8Zero)
	u8Zero--
	fmt.Println("u8Zero after decrement:", u8Zero) // 输出 255
}
类型 最大值 最小值 环绕示例
int8 127 -128 127 + 1 = -128
-128 - 1 = 127
int16 32,767 -32,768 32,767 + 1 = -32,768
-32,768 - 1 = 32,767
int32 2,147,483,647 -2,147,483,648 2,147,483,647 + 1 = -2,147,483,648
-2,147,483,648 - 1 = 2,147,483,647
int64 9,223,372,036,854,775,807 -9,223,372,036,854,775,808 9,223,372,036,854,775,807 + 1 = -9,223,372,036,854,775,808
-9,223,372,036,854,775,808 - 1 = 9,223,372,036,854,775,807
uint8 255 0 255 + 1 = 0
0 - 1 = 255
uint16 65,535 0 65,535 + 1 = 0
0 - 1 = 65,535
uint32 4,294,967,295 0 4,294,967,295 + 1 = 0
0 - 1 = 4,294,967,295
uint64 18,446,744,073,709,551,615 0 18,446,744,073,709,551,615 + 1 = 0
0 - 1 = 18,446,744,073,709,551,615
  1. 溢出检测:在关键应用中,应该注意整数溢出的问题,可以通过检查变量是否在预期范围内来避免潜在的错误。
  2. 编译器优化:某些编译器可能会优化掉显式的溢出检查,因此在编写代码时要特别小心。
  3. 安全编程:使用更大的整数类型(如 int32int64)可以减少溢出的风险,但在某些情况下可能会增加内存使用。
3.2 math/big 包提供了用于高精度计算的整数、有理数和浮点数类型。

这些类型可以处理任意大小的数值,非常适合需要高精度计算的场景。以下是对 math/big 包中主要类型的介绍和使用示例。

  1. big.Int:用于表示任意大小的整数。
  2. big.Rat:用于表示任意精度的有理数(分数)。
  3. big.Float:用于表示任意精度的浮点数。

big.Int 的使用

big.Int 是最常用的类型,用于处理大整数。以下是一些基本操作的示例:

导入包
import "math/big"

big.Int 实例

// 通过 NewInt 创建一个 big.Int 实例
zero := big.NewInt(0)
one := big.NewInt(1)

// 通过 SetInt64 创建一个 big.Int 实例
largeNumber := big.NewInt(0).SetInt64(1234567890123456789)

// 通过 SetString 创建一个 big.Int 实例
veryLargeNumber, ok := new(big.Int).SetString("123456789012345678901234567890", 10)
if !ok {
    panic("invalid number")
}
// 加法
sum := new(big.Int).Add(one, one) // sum = 2
// 减法
difference := new(big.Int).Sub(one, zero) // difference = 1
// 乘法
product := new(big.Int).Mul(one, largeNumber) // product = 1234567890123456789
// 除法
quotient := new(big.Int).Div(veryLargeNumber, largeNumber) // quotient = 100000000000000000000000000
// 模运算
remainder := new(big.Int).Mod(veryLargeNumber, largeNumber) // remainder = 0
// 幂运算
power := new(big.Int).Exp(largeNumber, big.NewInt(2), nil) // power = 1234567890123456789^2
// 比较
cmp := veryLargeNumber.Cmp(largeNumber) // cmp > 0 表示 veryLargeNumber > largeNumber

big.Rat 用于表示任意精度的有理数(分数)。

// 通过 NewRat 创建一个 big.Rat 实例
half := big.NewRat(1, 2)
// 通过 SetFloat64 创建一个 big.Rat 实例
piRat := new(big.Rat).SetFloat64(3.141592653589793)
// 通过 SetString 创建一个 big.Rat 实例
fraction, ok := new(big.Rat).SetString("1/3")
if !ok {
    panic("invalid fraction")
}
// 加法
sumRat := new(big.Rat).Add(half, half) // sumRat = 1
// 减法
differenceRat := new(big.Rat).Sub(half, fraction) // differenceRat = 1/6
// 乘法
productRat := new(big.Rat).Mul(half, piRat) // productRat = 1.5707963267948965
// 除法
quotientRat := new(big.Rat).Quo(piRat, half) // quotientRat = 6.283185307179586
// 比较
cmpRat := piRat.Cmp(fraction) // cmpRat > 0 表示 piRat > fraction

big.Float 用于表示任意精度的浮点数。

// 通过 NewFloat 创建一个 big.Float 实例
pi := big.NewFloat(3.141592653589793)

// 通过 SetPrec 设置精度
pi.SetPrec(100) // 设置精度为 100 位

// 通过 SetString 创建一个 big.Float 实例
largeFloat, _, err := big.ParseFloat("12345678901234567890.12345678901234567890", 10, 0, big.ToNearestEven)
if err != nil {
    panic("invalid float")
}
// 加法
sumFloat := new(big.Float).Add(pi, pi) // sumFloat = 6.283185307179586
// 减法
differenceFloat := new(big.Float).Sub(pi, pi) // differenceFloat = 0
// 乘法
productFloat := new(big.Float).Mul(pi, pi) // productFloat = 9.869604401089358
// 除法
quotientFloat := new(big.Float).Quo(pi, pi) // quotientFloat = 1
// 比较
cmpFloat := pi.Cmp(largeFloat) // cmpFloat < 0 表示 pi < largeFloat

math/big 包提供了强大的工具来处理大整数、有理数和浮点数,适用于需要高精度计算的场景。通过这些示例,您可以更好地理解和使用 math/big 包中的主要类型和方法。

2.4 Unicode 码点(Code Point)

可以使用不同的编码方式来表示。常见的 Unicode 编码方式包括 UTF-8、UTF-16 和 UTF-32。每种编码方式都有其特点和适用场景。以下是这三种编码方式的详细介绍:

UTF-8 是一种可变长度的编码方式,广泛用于互联网和现代操作系统中。它具有以下特点:

  • 兼容 ASCII:对于 ASCII 字符(码点范围 U+0000U+007F),UTF-8 编码与 ASCII 完全相同,使用 1 个字节。

  • 可变长度:对于其他 Unicode 码点,UTF-8 使用 1 到 4 个字节来编码。

  • 自同步:可以从任意位置开始解码,即使中间有错误也不会影响后续的解码。

  • 1 字节0xxxxxxxU+0000U+007F

  • 2 字节110xxxxx 10xxxxxxU+0080U+07FF

  • 3 字节1110xxxx 10xxxxxx 10xxxxxxU+0800U+FFFF

  • 4 字节11110xxx 10xxxxxx 10xxxxxx 10xxxxxxU+10000U+10FFFF

UTF-8 示例
package main

import (
    "fmt"
    "unicode/utf8"
)

func main() {
    str := "Hello, 世界!"

    for i, c := range str {
        fmt.Printf("Index: %d, Rune: %U, Char: %c, UTF-8: % x\n", i, c, c, []byte(string(c)))
    }
}
//Index: 0, Rune: U+0048, Char: H, UTF-8: 48
//Index: 1, Rune: U+0065, Char: e, UTF-8: 65

UTF-16 示例
package main

import (
    "fmt"
    "encoding/json"
)

func main() {
    str := "Hello, 世界!"

    // 将字符串转换为 UTF-16 编码
    utf16 := json.RawMessage(str).EncodeUTF16()

    for i, c := range utf16 {
        fmt.Printf("Index: %d, UTF-16: %04x\n", i, c)
    }
}
//Index: 0, UTF-16: 0048
//Index: 1, UTF-16: 0065
...
UTF-32 示例
package main

import (
    "fmt"
)

func main() {
    str := "Hello, 世界!"

    // 将字符串转换为 UTF-32 编码
    utf32 := []rune(str)

    for i, c := range utf32 {
        fmt.Printf("Index: %d, UTF-32: %08x\n", i, uint32(c))
    }
}
//Index: 0, UTF-32: 00000048
//Index: 1, UTF-32: 00000065
...
  • UTF-8:可变长度,兼容 ASCII,广泛用于互联网和现代操作系统。
  • UTF-16:变长编码,主要用于 Windows 和 Java,对于 BMP 字符使用 2 个字节,对于辅助平面字符使用 4 个字节。
  • UTF-32:固定长度,每个码点使用 4 个字节,编码和解码简单直接。
2.5 字符串是一种不可变的字节序列。

创建字符串

// 直接赋值
str1 := "Hello, World!"

// 使用反引号创建多行字符串
str2 := `This is a
multi-line
string.`

// 使用 fmt.Sprintf 创建格式化字符串
str3 := fmt.Sprintf("The answer is %d", 42)

字符串拼接

// 使用 + 运算符
str4 := "Hello" + " " + "World"

// 使用 fmt.Sprintf
str5 := fmt.Sprintf("%s %s", "Hello", "World")

// 使用 strings.Join
parts := []string{"Hello", "World"}
str6 := strings.Join(parts, " ")

字符串长度

length := len("Hello, World!") // length = 13

字符串索引

str := "Hello, World!"
firstChar := str[0] // 'H'
lastChar := str[len(str)-1] // '!'

子字符串

str := "Hello, World!"
subStr1 := str[0:5]   // "Hello"
subStr2 := str[7:]    // "World!"
subStr3 := str[:5]    // "Hello"
subStr4 := str[len(str)-6:] // "World!"

字符串比较

str1 := "Hello"
str2 := "Hello"
str3 := "World"

isEqual := str1 == str2 // true
isNotEqual := str1 != str3 // true

字符串查找

str := "Hello, World!"

// 查找子字符串的位置
index := strings.Index(str, "World") // index = 7

// 查找子字符串是否存在
contains := strings.Contains(str, "World") // true

// 查找前缀
hasPrefix := strings.HasPrefix(str, "Hello") // true

// 查找后缀
hasSuffix := strings.HasSuffix(str, "World!") // true

字符串替换

str := "Hello, World!"
newStr := strings.Replace(str, "World", "Go", 1) // "Hello, Go!"

字符串分割

str := "a,b,c,d"
parts := strings.Split(str, ",") // []string{"a", "b", "c", "d"}

字符串修剪

str := "  Hello, World!  "
trimmed := strings.TrimSpace(str) // "Hello, World!"

字符串转换

str := "Hello, World!"

// 转换为小写
lower := strings.ToLower(str) // "hello, world!"

// 转换为大写
upper := strings.ToUpper(str) // "HELLO, WORLD!"

// 转换为驼峰式
camel := strings.Title(str) // "Hello, World!"

字符串格式化

// 使用 fmt.Sprintf 进行格式化
formatted := fmt.Sprintf("The answer is %d", 42) // "The answer is 42"

字符串遍历

str := "Hello, 世界!"

// 遍历每个字节
for i := 0; i < len(str); i++ {
    fmt.Printf("%c ", str[i])
}

// 遍历每个 Unicode 码点
for i, c := range str {
    fmt.Printf("Index: %d, Rune: %c\n", i, c)
}

3. 控制结构

条件语句
package main

import "fmt"

func main() {
    age := 18

    if age >= 18 {
        fmt.Println("You are an adult.")
    } else {
        fmt.Println("You are a minor.")
    }

    // switch语句
    day := 2
    switch day {
    case 1:
        fmt.Println("Monday")
    case 2:
        fmt.Println("Tuesday")
    default:
        fmt.Println("Other day")
    }
}
循环语句

Go语言中只有一种循环结构:for

package main

import "fmt"

func main() {
    // 基本的for循环
    for i := 0; i < 5; i++ {
        fmt.Println(i)
    }

    // 类似while循环
    count := 0
    for count < 3 {
        fmt.Println(count)
        count++
    }

    // 遍历数组
    arr := []string{"apple", "banana", "cherry"}
    for index, value := range arr {
        fmt.Printf("index: %d, value: %s\n", index, value)
    }
}

4. 函数和多返回值

package main

import "fmt"

// 定义一个有返回值的函数
func add(x int, y int) int {
    return x + y
}

// 多返回值函数
func divide(dividend, divisor int) (int, int) {
    quotient := dividend / divisor
    remainder := dividend % divisor
    return quotient, remainder
}

func main() {
    sum := add(10, 5)
    fmt.Println("Sum:", sum)

    quotient, remainder := divide(10, 3)
    fmt.Printf("Quotient: %d, Remainder: %d\n", quotient, remainder)
}

5. 指针

package main

import "fmt"

func main() {
    x := 10
    ptr := &x            // 获取变量的地址
    fmt.Println("Address of x:", ptr)
    fmt.Println("Value of x:", *ptr)  // 通过指针访问变量值
}

6. 结构体与方法

package main

import "fmt"

// 定义一个结构体
type Person struct {
    Name string
    Age  int
}

// 结构体方法
func (p Person) Greet() {
    fmt.Printf("Hello, my name is %s and I am %d years old.\n", p.Name, p.Age)
}

func main() {
    person := Person{Name: "Alice", Age: 25}
    person.Greet()
}

7. 接口和多态

package main

import "fmt"

// 定义一个接口
type Animal interface {
    Speak() string
}

// Dog类型实现了Animal接口
type Dog struct{}
func (d Dog) Speak() string {
    return "Woof!"
}

// Cat类型实现了Animal接口
type Cat struct{}
func (c Cat) Speak() string {
    return "Meow!"
}

func main() {
    animals := []Animal{Dog{}, Cat{}}
    for _, animal := range animals {
        fmt.Println(animal.Speak())
    }
}

8. 并发编程:goroutines 和 channels

package main

import (
    "fmt"
    "time"
)

// 函数,用于展示goroutine
func printMessage(message string) {
    for i := 0; i < 3; i++ {
        fmt.Println(message)
        time.Sleep(500 * time.Millisecond)
    }
}

func main() {
    go printMessage("Hello from goroutine")  // 启动goroutine
    printMessage("Hello from main")

    // 使用通道
    ch := make(chan string)
    go func() {
        ch <- "Data sent to channel"
    }()

    msg := <-ch  // 从通道接收数据
    fmt.Println("Received:", msg)
}

9. 错误处理

package main

import (
    "errors"
    "fmt"
)

func divide(a, b int) (int, error) {
    if b == 0 {
        return 0, errors.New("cannot divide by zero")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 0)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", result)
    }
}

这些示例展示了Go语言的一些基本语法和特色特性。在实际开发中,还可以结合Go的标准库及第三方库来实现更多功能。


网站公告

今日签到

点亮在社区的每一天
去签到