当前位置: 首页 > news >正文

第四篇:《函数、指针与结构体:组织代码的核心工具》

变量是代码的“名词”,函数是代码的“动词”。在 Go 语言中,函数是第一公民,支持多返回值、命名返回值和闭包。指针提供了对内存的间接访问能力,但 Go 的指针比 C 更安全——不支持指针算术。结构体(struct)是 Go 组织数据的核心方式,结合方法(method)可以实现面向对象编程中的“类”概念。本文系统讲解函数、指针与结构体的核心语法与最佳实践,并通过一个“学生管理系统”的实战案例,帮你掌握组织 Go 代码的核心工具。

一、函数:Go 代码的组织单元
1.1 函数的基本声明
Go 函数的声明格式为:func 函数名(参数列表) 返回值列表 { 函数体 }

// 无参数,无返回值funcsayHello(){fmt.Println("Hello!")}// 带参数funcgreet(namestring){fmt.Printf("Hello, %s!\n",name)}// 带返回值funcadd(aint,bint)int{returna+b}// 参数类型相同时,可以省略前一个的类型声明funcmultiply(a,bint)int{returna*b}

1.2 多返回值(Go 的特色)
Go 是少数支持多返回值的语言之一,这是 Go 错误处理机制的基础:

// 返回两个值:商和余数funcdivide(a,bint)(int,int){quotient:=a/b remainder:=a%breturnquotient,remainder}funcmain(){q,r:=divide(10,3)fmt.Printf("商: %d, 余数: %d\n",q,r)// 商: 3, 余数: 1// 如果不需要某个返回值,使用空白标识符 _q2,_:=divide(20,6)fmt.Println("商:",q2)}

1.3 命名返回值
Go 允许为返回值命名,这在函数体中可以像普通变量一样使用,return 语句可以不带参数自动返回:

// 命名返回值funcdivideWithName(a,bint)(quotientint,remainderint){quotient=a/b remainder=a%breturn// 裸返回,返回 quotient 和 remainder}// 在文档中更清晰funcgetUserInfo(idint)(namestring,ageint,emailstring){// 实际开发中,这里会从数据库查询name="Alice"age=30email="alice@example.com"return}

💡 使用建议:对于短函数(1-3 行),命名返回值可能增加代码冗余;对于长函数或返回多个相同类型值时,命名返回值能提高可读性。

1.4 可变参数
使用 …T 语法接收任意数量的参数:

// 求和,支持任意数量的参数funcsum(nums...int)int{total:=0for_,num:=rangenums{total+=num}returntotal}funcmain(){fmt.Println(sum(1,2,3))// 6fmt.Println(sum(1,2,3,4,5))// 15// 传入切片时,使用 ... 展开nums:=[]int{1,2,3,4}fmt.Println(sum(nums...))// 10}

1.5 匿名函数与闭包
Go 支持在函数内部定义匿名函数,并可以捕获外部变量(闭包):

// 匿名函数直接调用funcmain(){func(msgstring){fmt.Println(msg)}("Hello from anonymous!")}// 闭包:函数捕获外部变量funccounter()func()int{count:=0returnfunc()int{count++returncount}}funcmain(){c:=counter()fmt.Println(c())// 1fmt.Println(c())// 2fmt.Println(c())// 3}

二、指针:内存的“门牌号”
Go 的指针提供了对变量内存地址的访问能力,但比 C 更安全——不支持指针算术运算(如 p++)。

2.1 指针的基本操作

funcmain(){x:=42p:=&x// p 是指向 x 的指针,类型为 *intfmt.Println(p)// 内存地址,如 0xc00000a0a8fmt.Println(*p)// 解引用,输出 42*p=100// 通过指针修改 x 的值fmt.Println(x)// 输出 100}

2.2 值传递 vs 指针传递
Go 中所有参数都是值传递(传递副本),但对于大型数据结构,使用指针传递可以避免拷贝开销,并能修改原值:

// 值传递:函数内修改不影响外部funcupdateValue(vint){v=100}// 指针传递:函数内修改影响外部funcupdatePointer(p*int){*p=100}funcmain(){a:=10updateValue(a)fmt.Println(a)// 10(未改变)updatePointer(&a)fmt.Println(a)// 100(已改变)}

2.3 指针接收者 vs 值接收者(与结构体方法相关)
在 Go 中,方法可以定义在结构体上,接收者可以是值或指针:

typePersonstruct{NamestringAgeint}// 值接收者:不能修改原值func(p Person)SayHello(){fmt.Printf("你好,我是 %s,今年 %d 岁\n",p.Name,p.Age)}// 指针接收者:可以修改原值func(p*Person)SetAge(ageint){p.Age=age}funcmain(){p:=Person{Name:"Alice",Age:30}p.SetAge(31)// 可以修改原值p.SayHello()// 你好,我是 Alice,今年 31 岁}

选择原则:

需要修改结构体字段 → 使用指针接收者

结构体较大(> 64 字节) → 使用指针接收者(避免拷贝开销)

结构体较小且不需要修改 → 可以使用值接收者

三、结构体(Struct):组合数据的载体
Go 没有类的概念,但通过结构体和方法,可以实现面向对象编程的核心能力。

3.1 定义与初始化

// 定义结构体typeStudentstruct{IDintNamestringAgeintScores[]int}// 多种初始化方式funcmain(){// 方式1:按字段顺序(不推荐,可读性差)s1:=Student{1,"Alice",20,[]int{90,85,92}}// 方式2:使用字段名(推荐)s2:=Student{ID:2,Name:"Bob",Age:21,}// 方式3:先声明再赋值(零值初始化)vars3 Student s3.ID=3s3.Name="Charlie"// 方式4:使用 new 关键字(返回指针)s4:=new(Student)// *Student 类型s4.ID=4s4.Name="David"}

3.2 匿名结构体
适用于一次性使用的场景:

funcmain(){// 匿名结构体,直接初始化config:=struct{HoststringPortint}{Host:"localhost",Port:8080,}fmt.Printf("%+v\n",config)// {Host:localhost Port:8080}}

3.3 结构体嵌套
Go 支持结构体嵌套,可以实现类似继承的效果:

typeAddressstruct{CitystringCountrystring}typeEmployeestruct{NamestringAgeintAddress Address// 嵌套结构体}funcmain(){e:=Employee{Name:"Alice",Age:30,Address:Address{City:"Beijing",Country:"China",},}// 访问嵌套字段fmt.Println(e.Address.City)// Beijing}

3.4 匿名字段(嵌入式结构体)
Go 支持“嵌入”结构体,被嵌入的字段可以直接访问:

typeEmployeestruct{NamestringAgeintAddress// 匿名字段(嵌入式)}funcmain(){e:=Employee{Name:"Alice",Age:30,Address:Address{City:"Beijing",Country:"China",},}// 可以直接访问嵌入的字段fmt.Println(e.City)// Beijing(不需要 e.Address.City)fmt.Println(e.Country)// China}

四、实战:学生管理系统
综合运用结构体、方法和函数,实现一个简单的学生管理系统:

packagemainimport"fmt"// 学生结构体typeStudentstruct{IDintNamestringScores[]float64}// 计算平均分func(s Student)Average()float64{iflen(s.Scores)==0{return0}total:=0.0for_,score:=ranges.Scores{total+=score}returntotal/float64(len(s.Scores))}// 判断是否及格(平均分 >= 60)func(s Student)IsPassed()bool{returns.Average()>=60}// 学生管理系统typeStudentManagerstruct{Studentsmap[int]Student}// 添加学生func(sm*StudentManager)AddStudent(s Student){ifsm.Students==nil{sm.Students=make(map[int]Student)}sm.Students[s.ID]=s}// 根据 ID 查找学生func(sm*StudentManager)FindByID(idint)(Student,bool){s,ok:=sm.Students[id]returns,ok}// 列出所有学生信息func(sm*StudentManager)ListAll(){iflen(sm.Students)==0{fmt.Println("暂无学生数据")return}fmt.Println("=== 学生列表 ===")for_,s:=rangesm.Students{fmt.Printf("ID: %d, 姓名: %s, 平均分: %.2f, 状态: %s\n",s.ID,s.Name,s.Average(),map[bool]string{true:"及格",false:"不及格"}[s.IsPassed()])}}funcmain(){sm:=StudentManager{}// 添加学生sm.AddStudent(Student{ID:1,Name:"Alice",Scores:[]float64{90,85,92}})sm.AddStudent(Student{ID:2,Name:"Bob",Scores:[]float64{50,60,55}})sm.AddStudent(Student{ID:3,Name:"Charlie",Scores:[]float64{70,75,80}})// 查找学生ifs,ok:=sm.FindByID(2);ok{fmt.Printf("找到学生: %s, 平均分: %.2f\n",s.Name,s.Average())}// 列出所有sm.ListAll()}

五、小结
函数:支持多返回值、命名返回值、可变参数和闭包,是 Go 代码的基本组织单元。

指针:使用 & 取地址,* 解引用,不支持指针算术。指针传递允许修改原值,避免拷贝开销。

结构体:组合数据的核心方式,支持嵌套和匿名字段。方法可以定义在值接收者或指针接收者上。

选择原则:需要修改字段时用指针接收者;不需要修改且结构体较小时用值接收者。

http://www.jsqmd.com/news/1244196/

相关文章:

  • Nativ:100%开源,让你在Mac本地运行AI模型,无需账户订阅!
  • 做公司PPT最烦的不是写内容,是套模板
  • B3843 [GESP202306 三级] 密码合规 题解
  • LLM输出稳定性差3倍?——从token级置信度、逻辑连贯性到领域适配度的8项硬指标深度拆解
  • 2026年经期记录小程序哪个好用?日常记录与周期预测实操经验 - 软件测评小帮手
  • 大数据hadoop的高校照明智慧监测预警系统
  • 2026 餐饮收银系统横向科普:二维火与客如云适配场景、能力差异解析
  • 2026 年文县专业的景区园林花岗岩石材订做厂家哪家专业,别让你的景区毁在石材选错上-天翔石业 - 行业推荐官[官方】--
  • AI全栈开发指南 2026 :从想法到代码只需几分钟:揭秘高效产品原型设计的AI工具栈
  • Codex工具生态与部署实践全解析
  • 崩坏星穹铁道自动化助手终极指南:如何让三月七小助手每天为你节省2小时游戏时间
  • 知网/万方/PubMed三库查重结果竟相差41.2%?揭秘AI搜索底层语义锚点偏移机制(含TensorFlow调试日志)
  • 性能/安全/异常场景提示词
  • Windows系统文件dwmghost.dll丢失找不到问题解决
  • 羽球搭子 HarmonyOS 实战(18):个人统计聚合与排行榜数据模型
  • IT66313:HDMI 2.1 Retimer
  • 数字身份的通行证:深入解析单点登录(SSO)的架构与艺术
  • 全中文作者命名Nature子刊!北京大学+天津大学+湖南大学Nature Communications:纳秒级超快存储器,中红外光电存储器的存算一体新突破
  • 2026济南空调维修哪家效率高?2家平台响应速度实测 - 简单到家
  • Hyprland 0.55 发布!带来 Lua 配置、自定义布局等重大更新!
  • CesiumJS终极指南:5大优势打造专业级WebGL地理可视化应用
  • 2026年7月最新泰格豪雅无锡万达广场(无锡惠山店)维修保养服务电话 - 亨得利钟表维修中心
  • 400电话底层技术拆解与企业落地实战:路由原理、呼叫架构、风控合规与对接方案
  • 科技查新怎么查?去哪查?线上平台与线下机构指南
  • “阅后即焚”的PrivateBin与乌龙事件
  • 解锁家庭音乐共享:用musikcube打造跨平台流媒体服务器
  • 如何快速掌握ARPL黑群晖安装:新手入门终极指南
  • IT66311:HDMI 2.0 Retimer
  • AgentScope 2.0:构建可观测、可理解、可信赖智能体的生产级架构实践
  • 命令行操作指南:从基础到高效开发实践