【Go语言学习系列08】Go基础语法(六):结构体

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

📚 原创系列: “Go语言学习系列”

🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。

🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Go技术文章。

📑 Go语言学习系列导航

本文是【Go语言学习系列】的第8篇,当前位于第一阶段(入门篇)

🚀 第一阶段:入门篇
  1. Go语言简介与环境搭建
  2. Go开发工具链介绍
  3. Go基础语法(一):变量与数据类型
  4. Go基础语法(二):流程控制
  5. Go基础语法(三):函数
  6. Go基础语法(四):数组与切片
  7. Go基础语法(五):映射
  8. Go基础语法(六):结构体 👈 当前位置
  9. Go基础语法(七):指针
  10. Go基础语法(八):接口
  11. 错误处理与异常
  12. 第一阶段项目实战:命令行工具

📚 查看完整Go语言学习系列导航

📖 文章导读

在本文中,您将学习:

  • 结构体的基本定义与初始化方法
  • 结构体的内存布局与字段对齐原理
  • 如何为结构体添加方法与实现封装
  • 结构体嵌套与组合的最佳实践
  • 结构体标签的使用场景与技巧
  • 优化结构体的性能与内存占用
  • 结构体在实战项目中的应用案例

结构体是Go语言中实现复杂数据组织与面向对象编程的基础,掌握好它既能写出高效优雅的代码,也是理解Go标准库与高级特性的关键。

Go结构体示意图


Go基础语法(六):结构体全面精通指南

结构体(struct)是Go语言中组织和管理相关数据的复合数据类型。它允许你将不同类型的数据字段组合成一个单一的、逻辑连贯的实体,是Go中实现复杂数据结构和面向对象编程的基础。本文将深入探讨结构体的各个方面,从基础概念到高级应用。

一、结构体基础

1.1 结构体的定义与声明

结构体通过typestruct关键字定义:

// 基本结构体定义
type Person struct {
    Name    string
    Age     int
    Email   string
    Address string
}

// 空结构体
type Empty struct{}

// 匿名结构体
var config = struct {
    Endpoint string
    Timeout  int
}{
    Endpoint: "https://api.example.com",
    Timeout:  30,
}

结构体的声明与初始化:

// 方式1:使用var声明,字段为零值
var person1 Person

// 方式2:使用新类型声明,并初始化全部字段
person2 := Person{
    Name:    "张三",
    Age:     30,
    Email:   "zhangsan@example.com",
    Address: "北京市朝阳区",
}

// 方式3:仅初始化部分字段(其他为零值)
person3 := Person{
    Name: "李四",
    Age:  25,
}

// 方式4:按字段顺序初始化(不推荐)
person4 := Person{"王五", 35, "wangwu@example.com", "上海市浦东新区"}

// 方式5:使用new函数(返回指针)
person5 := new(Person)

1.2 访问与修改结构体字段

使用点号操作符访问和修改结构体字段:

// 访问字段
fmt.Println(person2.Name)   // 输出: 张三
fmt.Println(person2.Age)    // 输出: 30

// 修改字段
person2.Age = 31
person2.Address = "北京市海淀区"

对于结构体指针,Go提供了便捷的语法:

p := &Person{Name: "赵六", Age: 28}

// 这两种写法等价
p.Name = "钱七"     // 简便写法
(*p).Name = "钱七"  // 完整写法

fmt.Println(p.Age) // 输出: 28

1.3 结构体的比较

结构体的可比较性取决于其字段:

type ComparableStruct struct {
    A int
    B string
}

// 这两个结构体可以直接比较
s1 := ComparableStruct{1, "hello"}
s2 := ComparableStruct{1, "hello"}
fmt.Println(s1 == s2) // true

type NonComparableStruct struct {
    A []int          // 切片不可比较
    B map[string]int // 映射不可比较
}

// 不能直接比较包含不可比较字段的结构体
// n1 := NonComparableStruct{}
// n2 := NonComparableStruct{}
// fmt.Println(n1 == n2) // 编译错误

对于不可直接比较的结构体,可以实现自定义比较逻辑:

func compareStructs(a, b NonComparableStruct) bool {
    // 比较切片
    if len(a.A) != len(b.A) {
        return false
    }
    for i, v := range a.A {
        if v != b.A[i] {
            return false
        }
    }
    
    // 比较映射
    if len(a.B) != len(b.B) {
        return false
    }
    for k, v := range a.B {
        if bv, ok := b.B[k]; !ok || bv != v {
            return false
        }
    }
    
    return true
}

二、结构体的高级特性

2.1 结构体的内存布局与对齐

Go结构体在内存中是按字段顺序分配的,但会考虑内存对齐:

type Example1 struct {
    a bool    // 1字节
    b int64   // 8字节
    c byte    // 1字节
}

type Example2 struct {
    a bool    // 1字节
    c byte    // 1字节
    b int64   // 8字节
}

func main() {
    fmt.Printf("Example1 size: %d\n", unsafe.Sizeof(Example1{})) // 输出可能是24
    fmt.Printf("Example2 size: %d\n", unsafe.Sizeof(Example2{})) // 输出可能是16
}

结构体的内存对齐规则:

  1. 每个字段按其类型的对齐要求对齐
  2. 结构体整体按其最大字段的对齐要求对齐
  3. 字段之间可能存在填充以满足对齐要求

优化字段顺序可以减小结构体大小:

// 未优化:32字节
type UnoptimizedStruct struct {
    a byte       // 1字节 + 7字节填充
    b int64      // 8字节
    c byte       // 1字节 + 7字节填充
    d int64      // 8字节
}

// 优化后:24字节
type OptimizedStruct struct {
    b int64      // 8字节
    d int64      // 8字节
    a byte       // 1字节
    c byte       // 1字节 + 6字节填充
}

2.2 嵌套结构体

Go支持结构体嵌套,可以组合更复杂的数据结构:

type Address struct {
    City    string
    Street  string
    ZipCode string
}

type Person struct {
    Name    string
    Age     int
    Address Address  // 嵌套结构体
}

// 初始化嵌套结构体
p := Person{
    Name: "张三",
    Age:  30,
    Address: Address{
        City:    "北京",
        Street:  "朝阳路",
        ZipCode: "100000",
    },
}

// 访问嵌套字段
fmt.Println(p.Address.City)  // 输出: 北京

2.3 匿名字段与字段提升

Go支持匿名嵌入其他结构体,实现类似继承的效果:

type Person struct {
    Name string
    Age  int
}

type Employee struct {
    Person      // 匿名嵌入Person
    Title       string
    Department  string
    Salary      float64
}

// 初始化
e := Employee{
    Person: Person{
        Name: "张三",
        Age:  30,
    },
    Title:      "软件工程师",
    Department: "研发部",
    Salary:     15000,
}

// 字段提升 - 可以直接访问嵌入结构体的字段
fmt.Println(e.Name)  // 等同于 e.Person.Name
fmt.Println(e.Age)   // 等同于 e.Person.Age

// 如果有同名字段,直接访问的是外层字段
type Manager struct {
    Person
    Title string  // 这个Title会覆盖任何嵌入结构体中的Title
}

匿名嵌入也可以是接口类型:

type Walker interface {
    Walk() string
}

type Runner interface {
    Run() string
}

// 嵌入接口
type Human struct {
    Name string
    Walker  // 匿名嵌入接口
    Runner  // 匿名嵌入接口
}

2.4 结构体标签

结构体标签是附加在结构体字段上的元数据,常用于JSON编码、数据库映射等:

type User struct {
    ID        int       `json:"id" db:"user_id"`
    Username  string    `json:"username" db:"username"`
    Email     string    `json:"email,omitempty" db:"email"`
    CreatedAt time.Time `json:"created_at" db:"created_at"`
}

使用反射获取标签:

import "reflect"

func main() {
    t := reflect.TypeOf(User{})
    field, _ := t.FieldByName("Email")
    fmt.Println(field.Tag.Get("json"))  // 输出: email,omitempty
    fmt.Println(field.Tag.Get("db"))    // 输出: email
}

常见标签用法:

  1. JSON标签

    `json:"fieldname,omitempty"` // omitempty表示值为零值时忽略
    
  2. 数据库标签

    `db:"column_name"`
    
  3. 表单标签

    `form:"field_name"`
    
  4. 验证标签

    `validate:"required,min=3,max=50"`
    

三、结构体方法

3.1 方法的定义

Go中的方法是特殊的函数,它与特定类型关联:

type Rectangle struct {
    Width  float64
    Height float64
}

// 值接收者的方法
func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// 指针接收者的方法
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    rect := Rectangle{Width: 10, Height: 5}
    
    // 调用方法
    area := rect.Area()
    fmt.Println("面积:", area)  // 输出: 面积: 50
    
    // 调用指针接收者方法
    rect.Scale(2)  // Go会自动取地址: (&rect).Scale(2)
    fmt.Printf("缩放后:宽度=%.2f, 高度=%.2f\n", rect.Width, rect.Height)
    // 输出: 缩放后:宽度=20.00, 高度=10.00
}

3.2 值接收者 vs 指针接收者

选择值接收者还是指针接收者是重要的设计决策:

值接收者

  • 方法操作的是值的副本
  • 适合不需要修改接收者的情况
  • 可以在值和指针上调用
  • 线程安全,因为每次调用使用的是副本

指针接收者

  • 方法可以修改接收者的内容
  • 避免大结构体复制的开销
  • 值和指针都可以调用指针接收者方法
  • 需要注意并发安全问题

选择准则:

// 小型结构体,不需要修改,使用值接收者
type Point struct {
    X, Y float64
}

func (p Point) Distance(q Point) float64 {
    return math.Sqrt((p.X-q.X)*(p.X-q.X) + (p.Y-q.Y)*(p.Y-q.Y))
}

// 大型结构体或需要修改的结构体,使用指针接收者
type Buffer struct {
    data [1024]byte
    // ... 更多字段
}

func (b *Buffer) Write(p []byte) (n int, err error) {
    // 写入数据并修改b
    // ...
    return len(p), nil
}

重要规则:为了保持一致性,如果类型的任何方法使用了指针接收者,那么该类型的所有方法都应使用指针接收者。

3.3 方法值与方法表达式

Go支持方法值和方法表达式,增加了方法使用的灵活性:

// 方法值
rect := Rectangle{Width: 5, Height: 10}
area := rect.Area     // 将方法绑定到特定接收者
fmt.Println(area())   // 调用方法值:50

// 方法表达式
scale := (*Rectangle).Scale   // 将方法转换为普通函数
scale(&rect, 2)               // 显式传递接收者:等同于rect.Scale(2)

四、结构体与组合

4.1 组合优于继承

Go不支持传统的继承,而是通过组合来实现代码复用:

// 基础功能结构体
type Logger struct {
    Level int
}

func (l *Logger) Log(message string) {
    fmt.Printf("[Level %d] %s\n", l.Level, message)
}

// 通过组合复用功能
type UserService struct {
    logger Logger
    // 其他字段
}

func (s *UserService) CreateUser(username string) {
    // 业务逻辑
    s.logger.Log("创建用户: " + username)
}

// 匿名组合更便捷
type OrderService struct {
    Logger  // 匿名字段,方法被提升
    // 其他字段
}

func (s *OrderService) PlaceOrder(orderID string) {
    // 业务逻辑
    s.Log("下单: " + orderID)  // 直接使用嵌入结构体的方法
}

4.2 实现"多重继承"

通过多个匿名嵌入,Go可以模拟多重继承:

type Sizer interface {
    Size() int
}

type Printer interface {
    Print()
}

// 实现Sizer
type File struct {
    size int
}

func (f *File) Size() int {
    return f.size
}

// 实现Printer
type Socket struct {}

func (s *Socket) Print() {
    fmt.Println("打印数据到套接字")
}

// 通过组合同时拥有两者的功能
type NetworkFile struct {
    *File
    *Socket
    name string
}

func main() {
    nf := &NetworkFile{
        File: &File{size: 1024},
        Socket: &Socket{},
        name: "network.dat",
    }
    
    // 调用来自File的方法
    fmt.Println("文件大小:", nf.Size())
    
    // 调用来自Socket的方法
    nf.Print()
}

4.3 处理方法冲突

当嵌入的多个结构体有同名方法时,需要显式指定:

type A struct{}
func (a A) Method() string { return "A" }

type B struct{}
func (b B) Method() string { return "B" }

type C struct {
    A
    B
}

func main() {
    c := C{}
    // c.Method() // 编译错误:ambiguous selector c.Method
    
    // 需要明确指定
    fmt.Println(c.A.Method()) // 输出: A
    fmt.Println(c.B.Method()) // 输出: B
    
    // 或者为C实现同名方法来解决冲突
}

五、实践应用

5.1 数据库模型映射

结构体常用于ORM(对象关系映射)中表示数据库表:

type User struct {
    ID        int64     `db:"id" json:"id"`
    Username  string    `db:"username" json:"username"`
    Email     string    `db:"email" json:"email"`
    CreatedAt time.Time `db:"created_at" json:"created_at"`
    UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}

// 查询用户
func GetUserByID(id int64) (*User, error) {
    user := &User{}
    err := db.QueryRow("SELECT id, username, email, created_at, updated_at FROM users WHERE id = ?", id).
        Scan(&user.ID, &user.Username, &user.Email, &user.CreatedAt, &user.UpdatedAt)
    if err != nil {
        return nil, err
    }
    return user, nil
}

5.2 配置管理

使用结构体管理应用程序配置:

type Config struct {
    Server   ServerConfig   `json:"server"`
    Database DatabaseConfig `json:"database"`
    Logging  LoggingConfig  `json:"logging"`
}

type ServerConfig struct {
    Host string `json:"host"`
    Port int    `json:"port"`
}

type DatabaseConfig struct {
    Host     string `json:"host"`
    Port     int    `json:"port"`
    Username string `json:"username"`
    Password string `json:"password"`
    DBName   string `json:"dbname"`
}

type LoggingConfig struct {
    Level      string `json:"level"`
    FilePath   string `json:"file_path"`
    MaxSize    int    `json:"max_size"`
    MaxBackups int    `json:"max_backups"`
}

// 加载配置
func LoadConfig(path string) (*Config, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    var config Config
    decoder := json.NewDecoder(file)
    if err := decoder.Decode(&config); err != nil {
        return nil, err
    }
    
    return &config, nil
}

5.3 中间件实现

使用结构体实现HTTP中间件:

type Middleware struct {
    next http.Handler
}

func NewMiddleware(next http.Handler) *Middleware {
    return &Middleware{next: next}
}

func (m *Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // 前置处理
    start := time.Now()
    
    // 调用下一个处理器
    m.next.ServeHTTP(w, r)
    
    // 后置处理
    duration := time.Since(start)
    log.Printf("请求处理时间: %v", duration)
}

// 使用中间件
func main() {
    handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })
    
    middleware := NewMiddleware(handler)
    http.ListenAndServe(":8080", middleware)
}

👨‍💻 关于作者与Gopher部落

"Gopher部落"专注于Go语言技术分享,致力于为开发者提供从入门到精通的完整学习路线。我们提供:

  • 📚 系统化的Go语言学习教程
  • 🔥 最新Go生态技术动态
  • 💡 实用开发技巧与最佳实践
  • 🚀 大厂项目实战经验分享

🎁 读者福利

关注"Gopher部落"微信公众号,即可获得:

  1. 完整Go学习路线图:从入门到高级的完整学习路径
  2. 面试题集锦:精选Go语言面试题及答案解析
  3. 项目源码:实战项目完整源码及详细注释
  4. 个性化学习计划:根据你的水平定制专属学习方案

如果您觉得这篇文章有帮助,请点赞、收藏并关注,这是对我们最大的支持!

Go语言入门教程】Go语言简介 Go语言(或Golang)起源于2007年,并在2009年正式对外发布。Go是非常年轻的一门语言,它的主要目标是“兼具Python等动态语言的开发速度和C/C++等编译型语言的性能与安全性”。Go语言是编程语言设计的又一次尝试,是对类C语言的重大改进,它不但能让你访问底层操作系统,还提供了强大的网络编程和并发编程支持。Go语言的用途众多,可以进行网络编程、系统编程、并发编程、分布式编程。Java或C++看起来并不相同。但是它通过接口(interface)的概念来实现多态性。... 阅读详情

相关推荐

最全的Go按照开发流程

Go是2009年开源的编程语言,Go语言具有以下特性:语法简洁、并发编程、编译迅速、数组安全、丰富的内置类型、错误处理、匿名函数和闭包、反射、函数多返回值、自动垃圾回收。**Go语言标识符与其它语言类似,第一个字符必须是字母、下划线,不能是数字。2、关键字Go语言的25个关键字或者保留字:Go语言的36个预定义标识符:

u013352320的博客 1549

Go语言全面解析:从入门到精通

Go语言是由Google开发的一种静态类型、编译型编程语言,具有简洁、高效、安全的特点。它特别适合高并发服务、云原生应用和网络编程等场景。Go采用Goroutine和Channel实现轻量级并发,性能接近C/C++。与其他语言相比,Go语法更简洁但类型更严格,编译速度快且跨平台支持好。虽然Go支持多种设计模式和编程范式,但其面向对象实现方式与传统语言不同。标准库丰富且拥有Gin、GORM等企业级框架。核心语法包括包管理、变量声明、函数定义、流程控制等,适合构建高性能后端服务和系统工具。

博哥哥的博客 4893

Golang基础语法-结构体和接口

在本文中,我们介绍了Golang结构体和接口,并讨论了一些高级特性,包括匿名字段、嵌套结构体和嵌入接口。这些特性可以使Golang代码更加模块化和可复用,从而提高代码的可维护性和可扩展性。结构体Golang中一种非常重要的数据类型,它可以用来定义自定义类型和复合类型,并且可以包含字段和方法。接口是Golang中另一种非常重要的数据类型,它可以用来定义行为和规范,并且可以让不同的类型之间实现相同的方法和行为。

rita_0567的博客 1043

Go语言详解

Go(The Google Programming Language)语言是谷歌2009年发布的第二款开源编程语言。Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。Go支持面向对象,而且具有真正的闭包(closures)和反射 (reflection)等功能。 特色 简洁 快速 安全 并行 有趣 ...

顺其自然~专栏 3万+

Go语言超全详解(入门级)

Go语言是谷歌2009年发布的第二款开源编程语言(系统开发语言),它是基于编译、垃圾收集和并发的编程语言。 Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美 C / C++代码的速度,而且更加安全、支持并行进程......

weixin_50941083的博客 14万+

Go语言学习系列00】Go语言学习系列导航

本文是Go语言学习系列的导航文档,提供了从入门到精通的学习路径,包含各阶段文章链接和内容概述,帮助读者系统化学习Go语言。

GopherTribe的博客 1562

AgentScope 2.0 实战指南:从终端调试到 4 步上线多智能体服务

AgentScope 2.0 是阿里通义实验室开源的生产级多智能体框架,要求 Python 3.11+。本文按"本地调试 → 隔离执行 → 权限管控 → 服务化上线"的完整链路展开,帮你把真正敢放心的 AI 智能体交付成多租户在线服务。 ## 三个落地场景:AgentScope 能拿来干什么 先看架构不如先想用途。下面三个场景在仓库里都有可直接运行的示例: **场景一:终端代码助手。** 给

gitblog_00994的博客 778

Go基础编程 - 08 - 结构体

Go 语言结构体,json序列化,Tag等详解

1095

Golang语法基础——深入解析Go语言的数据类型

Go语言(简称Golang)作为一门现代编程语言,以其简洁、强大的特性备受开发者青睐。在实际开发中,数据类型是语言的核心部分,理解和正确使用数据类型是编写高效、健壮代码的基础。本文将详细介绍Go语言中的基本数据类型及其特性,帮助读者更好地掌握Go语言的使用。

Linke的博客 1984

golang面试:golang基础语法(一)

最近忙于面试,就顺便把这个更新到blog上面方便随时查看,有些不对的请留意告知,内容大多数来源前辈的文章,因为当时做笔记也搞忘复制链接了,如果有知道的欢迎补充,我会补上链接。在初始化后长度是固定的,无法修改其长度。接口类型,是一种抽象的类型,它不会暴露它所代表的对象的内部值和对象支持的操作方法。数组是内置(build-in)类型,是一组同类型数据的集合,它是值类型,通过从0开始的下标索引访问元素值。数组的长度是数组类型的一部分,所以[3]int 和 [4]int 是两种不同的数组类型。

叫好与叫座虽然不是对立面,但想在同一个作品中达到双重效果很难。 339

(Golang) 牛客 在线编程 Go语言入门

本文为牛客的在线编程的GO语言入门题集的个人解析牛客网在线编程_语法篇_GO语言入门本人主语言C/C++ 来这边刷一下GO的入门语法题,这个题库很基础很入门真的就是纯考GO语法本文虽然是展示AC代码,但实际是为了记录GO的各种基础语法,便于以后遗忘了可以查看牛客的环境是版本:Go 1.14。

cuber-lotus的博客 2175

C++GO语言微服务项目之 go语言基础语法

C++GO语言微服务项目之 go语言基础语法

qq_27302885的博客 1276

Go:Go语言基础语法介绍

Go语言基础语法介绍,包括语句划分、标识符和注释、字符串连接、关键字、数据类型、变量和常量的声明、运算赋、条件语句、循环语句等的含义及语法格式,并提供了详细的代码示例

Liu_y_xin的博客 1280

Go语言学习记录

Go 的可执行文件都比相对应的源代码文件要大很多,这恰恰说明了 Go 的 runtime 嵌入到了每一个可执行文件当中。尽管 Go 编译器产生的是本地可执行代码,然而这些代码仍旧运行在 Go 的 runtime(这部分的代码可以在 runtime包中找到)当中。使用一门具有垃圾回收功能的编程语言不代表你可以避免内存分配所带来的问题,分配和回收内容都是消耗 CPU 资源的一种行为。/src:包含源代码构建脚本和标准库的包的完整源代码(Go 是一门开源语言)/os_arch:包含标准库的包的对象文件(.a)

sunny-blog 990

Golang基础语法学习与速成

快速学习Go语言

PleaSure乐事的博客 1074

08.Go-结构体

Go 中无法在结构体内部定义方法,这一点与 C 语言类似。package main import "fmt" // Lesson 定义一个名为 Lesson 的结构体 type Lesson struct {name , target string spend int } // PrintPersonInfo 定义一个与 Person 的绑定的方法 func(l Lesson) ShowLessonInfo() {

weixin_43633725的博客 498

[Go] 结构体不初始化仍然能够调用其方法

结构体不初始化仍然能够调用其方法

蜂蜜味的哈士奇 1303

go list指针_Go语言学习之路Day08

考试迫在眉睫,今天就简要总结下吧。结构体 Golang没有类(class)Go语言的结构体(struct)和其它编程语言的类(class)有同等的地位,你可以理解Golang是基于struct来实现OOP特性的。Golang仍然有面向对象编程的继承,封装和多态的特性,只是实现的方式和其它OOP语言不一样,比如继承:Golang没有extends关键字,继承是通过匿名字段来...

weixin_29728529的博客 263

Go语言-Day 19】深入理解Go自定义类型:Type、Struct、嵌套与构造函数实战

文章将从 `type` 关键字的基础用法(类型别名与新类型定义)切入,逐步引导读者掌握 `struct` 的定义、多种实例化方法、字段访问、以及匿名结构体和嵌套等高级技巧。最后,我们将讨论构造函数(工厂函数)这一重要的编程模式,帮助你编写出更健壮、更具可维护性的 Go 代码。无论你是 Go 新手还是希望巩固基础的开发者,本文都将为你提供清晰的指引和丰富的实战代码。

吴师兄大模型的博客 1052
上一篇: 【Go语言学习系列07】Go基础语法(五):映射
下一篇: 【Go语言学习系列09】Go基础语法(七):指针
Gopher部落
博客等级 码龄1年 784粉丝 92原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gopher部落

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值