{"id":13456257,"url":"https://github.com/coderit666/GoGuide","last_synced_at":"2025-03-24T09:31:50.662Z","repository":{"id":39092779,"uuid":"425227344","full_name":"coderit666/GoGuide","owner":"coderit666","description":"「Go语言学习指南」一份涵盖大部分 Golang 程序员所需要掌握的核心知识，拥有 Go语言教程、Go开源书籍、Go语言入门教程、Go语言学习路线。零基础学习 Go语言、Go编程，首选 GoGuide。","archived":false,"fork":false,"pushed_at":"2023-10-11T11:47:42.000Z","size":822,"stargazers_count":2984,"open_issues_count":10,"forks_count":338,"subscribers_count":28,"default_branch":"main","last_synced_at":"2025-03-24T00:18:41.581Z","etag":null,"topics":["git","go","golang","study"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/coderit666.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-11-06T11:32:28.000Z","updated_at":"2025-03-21T17:44:17.000Z","dependencies_parsed_at":"2024-07-31T08:24:20.920Z","dependency_job_id":null,"html_url":"https://github.com/coderit666/GoGuide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderit666%2FGoGuide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderit666%2FGoGuide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderit666%2FGoGuide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/coderit666%2FGoGuide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/coderit666","download_url":"https://codeload.github.com/coderit666/GoGuide/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245243374,"owners_count":20583614,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git","go","golang","study"],"created_at":"2024-07-31T08:01:18.726Z","updated_at":"2025-03-24T09:31:50.623Z","avatar_url":"https://github.com/coderit666.png","language":null,"funding_links":[],"categories":["学习资源","Repositories","Others","Programming Language Tutorials","资源","miscellaneous"],"sub_categories":["学习项目","Go","书籍"],"readme":"\u003e 1. **介绍**：**GoGuide** 致力于打造最易懂的 Go语言教程，`让天下没有难学的 Go 语言` \n\u003e 2. **PDF版本** ： [**GoGuide** PDF1.0 版本下载](https://pan.baidu.com/s/1GRzdSG1cfvkL0AFvnK3N6Q )  提取码：dmqx \n\u003e 3. **转载须知** ：以下所有文章与视频教程皆为我的原创，转载请联系我们，如发现恶意抄袭/搬运，会动用法律武器维护自己的权益，让我们一起维护一个良好的技术创作环境！\n\u003e 4. **Star/Fork 支持**：开源不易，如果开源项目帮助你打开 Go 语言的学习大门，希望你能 **Star** 支持我们，你的支持就是我们持续更新的动力。\n\u003e 5. **视频教程**：待开源 ......\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://mp.weixin.qq.com/s/PpeblHk6Ml4w8iNrrw8UAA\"\u003e\u003cimg src=\"https://img.shields.io/badge/%E5%9C%A8%E7%BA%BF%E9%98%85%E8%AF%BB-read-brightgreen\" alt=\"在线阅读\"\u003e\u003c/a\u003e\n  \u003ca href=\"#公众号\"\u003e\u003cimg src=\"https://img.shields.io/badge/%E5%BE%AE%E4%BF%A1%E5%85%AC%E4%BC%97%E5%8F%B7-%E4%BB%A3%E7%A0%81%E6%83%85%E7%BC%98-orange\" alt=\"公众号\"\u003e\u003c/a\u003e\n  \u003ca href=\"#公众号\"\u003e\u003cimg src=\"https://img.shields.io/badge/PDF-Go%E8%AF%AD%E8%A8%80%E4%BF%9D%E5%A7%86%E7%BA%A7%E6%95%99%E7%A8%8B-orange\" alt=\"PDF\"\u003e\u003c/a\u003e\n  \u003ca href=\"#联系我\"\u003e\u003cimg src=\"https://img.shields.io/badge/%E8%81%94%E7%B3%BB%E6%88%91%E4%BB%AC-weChat-orange\" alt=\"联系我\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\n# **免责声明**\n\n\u003e GoGuide 致力于打造最易懂的 Go 学习之旅，设计该项目的过程中可能存在勘误，请认真斟酌识别，学习路线仅供参考，记住，适合自己的才是最好的。\n\n# 给我一个 Star！ ⭐\n\n如果您喜欢或者打算使用这个项目来学习或者来开始你的 Go 语言学习之路，给我一个 Star，谢谢！\n\n# GoGuide 学习路线图\n\n![](https://cdn.nlark.com/yuque/0/2021/png/12653647/1636272970231-97d51ea2-1e4c-4f91-becb-f101e9d9b026.png?x-oss-process=image%2Fresize%2Cw_1100%2Climit_0)\n\n# C 语言\n\n**C 语言知识点详解已经开源，请移步 CNote 开源项目：**\n\n[CNote 开源项目地址](https://github.com/coderit666/CNote)\n\n# Go 语言\n\n# Go语言基础\n\n## 什么是Go语言\n- Go语言(Golang)是Google公司2009年推出的一门\"高级编程言语\", 目的是为了解决:\n  + \"现有主流编程语言\"明显**落后于硬件发展速度**的问题\n  + **不能合理利用多核CPU**的优势提升软件系统性能的问题\n  + 软件复杂度越来越高, ***维护成本也越来越高***的问题\n  + 企业开发中不得不在***快速开发和性能之间艰难抉择***的问题\n\u003e 科普小知识:\n\u003e 1.静态语言：\n\u003e1.1一般都需要通过编译器（compiler）将源代码翻译成机器码，之后才能执行。程序被编译之后无论是程序中的数据类型还是程序的结构都不可以被改变\n\u003e1.2静态语言的性能和安全性都非常好, 例如C和C++、Go, 但是C和C++的缺点是开发速度慢, 维护成本高\n\u003e2.动态语言\n\u003e2.1一般不需要通过编译器将源代码翻译成机器码，在运行程序的时候才逐行翻译。程序在运行的过程中可以动态修改程序中的数据类型和程序的结构\n\u003e2.2动态语言开发速度快,维护成本低,例如Ruby和Python, 但是Ruby和Python的性能和安全性又略低\n- Go语言专门针对多核CPU进行了优化, **能够充分使用硬件多核CPU的优势**, 使得通过Go语言编写的**软件系统性能能够得到很大提升**\n- Go语言编写的程序,既可以媲美C或C++代码的运行速度, 也可以媲美Ruby或Python开发的效率\n- 所以Go语言很好的解决了\"现有主流编程语言\"存在的问题, 被誉\"现代化的编程语言\"\n\n---\n## Go语言优势\n- 简单易学\n  + Go语言的作者都有C的基因，Go自然而然也有了C的基因，但是Go的语法比C还简单, 并且几乎支持大多数你在其他语言见过的特性：封装、继承、多态、反射等\n- 丰富的标准库\n  + Go目前已经内置了大量的库，特别是网络库非常强大\n  + 前面说了作者是C的作者，所以Go里面也可以直接包含c代码，利用现有的丰富的C库\n- 跨平台编译和部署\n  + Go代码可直接编译成机器码，不依赖其他库，部署就是扔一个文件上去就完事了. 并且Go代码还可以做到跨平台编译(例如: window系统编译linux的应用)\n- 内置强大的工具\n  + Go语言里面内置了很多工具链，最好的应该是gofmt工具，自动化格式化代码，能够让团队review变得如此的简单，代码格式一模一样，想不一样都很困难\n- 性能优势: Go 极其地快。其性能与 C 或 C++相似。在我们的使用中，Go 一般比 Python 要快 30 倍左右\n  +  语言层面支持并发，这个就是Go最大的特色，天生的支持并发，可以充分的利用多核，很容易的使用并发 \n  +  内置runtime，支持垃圾回收\n- ... ...\n\n\u003eGo语言的吉祥物是地鼠\n\u003e地鼠的特点是速度快、成群结队、头脑简单\n\u003e而Go语言的特点正好也是编程速度快、并发性好、简单易学\n\u003e![](https://img-blog.csdnimg.cn/img_convert/e5fec66717b4fff665daabbcedae7d76.png)\n\n\n---\n## Go语言发展史\n```\n2007年，谷歌工程师Rob Pike, Ken Thompson和Robert Griesemer开始设计一门全新的语言，这是Go语言的最初原型。\n2009年11月10日，Go语言以开放源代码的方式向全球发布。\n2011年3月16日，Go语言的第一个稳定(stable)版本r56发布。\n2012年3月28日，Go语言的第一个正式版本Go1发布。\n2013年4月04日，Go语言的第一个Go 1.1beta1测试版发布。\n2013年4月08日，Go语言的第二个Go 1.1beta2测试版发布。\n2013年5月02日，Go语言Go 1.1RC1版发布。\n2013年5月07日，Go语言Go 1.1RC2版发布。\n2013年5月09日，Go语言Go 1.1RC3版发布。 \n2013年5月13日，Go语言Go 1.1正式版发布。\n2013年9月20日，Go语言Go 1.2RC1版发布。\n2013年12月1日，Go语言Go 1.2正式版发布。\n2014年6月18日，Go语言Go 1.3版发布。\n2014年12月10日，Go语言Go 1.4版发布。\n2015年8月19日，Go语言Go 1.5版发布，本次更新中移除了”最后残余的C代码”。\n2016年2月17日，Go语言Go 1.6版发布。\n2016年8月15日，Go语言Go 1.7版发布。\n2017年2月17日，Go语言Go 1.8版发布。\n2017年8月24日，Go语言Go 1.9版发布。\n2018年2月16日，Go语言Go 1.10版发布。\n```\n---\n## Go作者\n- Go语言是UNIX作者、C语言作者、谷歌V8引擎作者携手打造的, 由谷歌公司2009年推出的一门高级编程言语。\n![](https://img-blog.csdnimg.cn/img_convert/c81254e9173496f3e7db176191c493c0.png)\n\u003e 跟着谷歌走吃喝啥都有\n---\n## Go语言现状\n- 多次获得TIOBE年度最佳语言\n![](https://img-blog.csdnimg.cn/img_convert/0f2100411dec241d1055d364b6a7ce62.png)\n\n- 2018年Go语言一度超过Java, [进入编程语言排行榜前三名](https://www.hntrends.com/2018/jul-top-ten-programming-languages.html). \n  ![](https://img-blog.csdnimg.cn/img_convert/62c7d630590f5caabdeac1a3af5383af.png)\n- 从公司角度:\n  + [许多大厂](https://github.com/golang/go/wiki/GoUsers)都已经拥抱 Go 语言，包括阿里巴巴、京东、今日头条、小米、滴滴、七牛云、360等明星公司, 也包括知乎、轻松筹、快手、探探、美图、猎豹移动等等。同时，创业公司也很喜欢 Go 语言，主要因为其入门快、程序库多、运行迅速，很适合快速构建互联网软件产品。\n- 从业务维度:\n  + Go 程序可以在装有 Windows、Linux、FreeBSD 等操作系统的服务器上运行，并用于提供基础软件支撑、API 服务、Web 服务、网页服务等等。\n  + 在云计算、微服务、大数据、区块链、物联网等领域，Go 语言早已蓬勃发展. 除了语法简单, 性能优越以外, K8S底层架构在云计算的领导地位(```K8S就是Go开发的```), 也让这些各大公司不得不拥抱Go语言。 \n  + 区块链的崛起更进一步带动了Go工程师的需求,市面上大部分区块链明星项目都是用Go开发的, 足以说明Go在分布式系统中的地位，这也就是为什么今年开始，大批金融公司开始招聘Go工程师的重要原因。\n- 从薪资角度来看\n  + 应届生普遍在`4~8K`,  1年左右普遍在`10K左右`, 2年~3年普遍在`20K左右`\n  ![](https://img-blog.csdnimg.cn/img_convert/59565893664975591eed97861f2c3034.png)\n\n---\n## Go语言应用场景\n- 网络编程，这一块目前应用最广，包括Web应用、API应用、下载应用、内存数据库等\n- 云平台开发，目前国外很多云平台在采用Go开\n- 服务器编程, 以前你如果使用Java或者C++做的那些事情，都可以用Go来做\n- 分布式系统，数据库代理器等\n- 它可以做从底层到前端的任何工作\n\n---\n## 如何学习Go语言\n- Go语言被称之为现代化的C语言, 所以无论是从语法特性, 还是作者本身, Go语言都与C语言有着莫大的关系, 所以学习本套课程之前如果你有C语言的基础, 那么将会事半功倍\n- 对于初学者而言, 学习编程的捷径只有一条, 那就是**多动手**\n\n\u003e 竹子用了4年的时间， 仅仅长了3cm， 从第五年开始， 以每天30cm的速度疯狂地生长， 仅仅用了六周的时间就长到了15米。 其实，在前面的四年， 竹子将根在土壤里延伸了数百平米。 做人做事亦是如此， 不要担心你此时此刻的付出得不到回报， 因为这些付出都是为了扎根。 \n\n---\n\n## 源文件对比\n- C语言源文件\n\n|文件扩展名|源类型|\n|---|---|\n|.h|头文件，存放代码声明|\n|.c|C语言源文件，存放代码实现|\n\n- Go语言源文件\n\n|文件扩展名|源类型|\n|---|---|\n|.go|Go语言源文件，存放代码实现|\n\n---\n## 代码管理对比\n- C语言中通过文件来管理代码\n  + 想使用某一个函数时,只需要include导入对应的.h文件即可\n- Go语言中通过包来管理代码\n  + Go语言没有.h文件的概念, 在Go中想使用某一个函数时, 只需要import导入对应的包即可\n- C语言中函数、变量公私有管理\n  + 通过extern和static实现是否公开函数和变量\n- Go语言中函数、变量公私有管理\n  + 通过函数名称首字母大小写实现是否公开函数\n  + 通过变量名称首字母大小写实现是否公开变量\n---\n## 关键字对比\n- C语言中一共有32个关键字\n\n|1|2|3|4|5|6|7|8|\n|---|---|---|---|---|---|---|---|\n|***if***|***else***|***switch***|***case***|***default***|***break***|***return***|***goto***|\n|do|while|***for***|***continue***|typedef|***struct***|enum|union|\n|char|short|int|long|float|double|void|sizeof|\n|signed|unsigned|***const***|auto|register|static|extern|volatile|\n\n- Go语言中一共有25个关键字\n\n|1|2|3|4|5|6|7|8|\n|---|---|---|---|---|---|---|---|\n|***if***|***else***|***switch***|***case***|***default***|***break***|***return***|***goto***|\n|fallthrough|***for***|***continue***|type|***struct***|var|***const***|map|\n|func|interface|range|import|package|defer|go|select|\n|chan|\n\n---\n## 数据类型对比\n- C语言数据类型\n![](https://img-blog.csdnimg.cn/img_convert/73b03f1ce2062d094514e574bf3d0089.png)\n- Go语言数据类型\n![](https://img-blog.csdnimg.cn/img_convert/f538fc2dd608df743fd734f6b5678a63.png)\n\n---\n- C语言各数据类型占用内存空间\n\n|类型|32位编译器|64位编译器|\n|--|--|--|\n|char|1|1|\n|int|4|4|\n|float|4|4|\n|double|8|8|\n|short|2|2|\n|long|4|8|\n|long long|8|8|\n|void*|4|8|\n\n- Go语言各数据类型占用内存空间\n\n|类型|32位编译器|64位编译器|本质|\n|--|--|--|--|\n|int8/uint8|1|1|signed char/unsigned char|\n|int16/uint16|2|2|signed short/unsigned short|\n|int32/uint32|4|4|signed int/unsigned int|\n|int64/uint64|8|8|signed long long int/unsigned long long int|\n|byte|1|1|uint8/unsigned char|\n|rune|4|4|int32/signed int|\n|int|4|8|根据机器位数决定长度|\n|uintptr|4|8|根据机器位数决定长度 uint32/uint64|\n|float32|4|4|float|\n|float64|8|8|double|\n|true|1|1|char类型的整型|\n|false|1|1|char类型的整型|\n\n- 和C语言一样,Go语言也提供了Sizeof计算变量的内存空间\n  + 1.导入import \"unsafe\"包\n  + 2.通过unsafe.Sizeof()计算变量内存空间\n---\n- Go语言基本数据类型内部实现\n  + [golang官方网站下载go1.4版本源代码](https://github.com/golang/go/releases)\n    + 越老版本的代码越纯粹,越适合新手学习\n    + 随着代码的更新迭代会逐步变得非常复杂, 所以此处建议下载1.4版本\n  + 解压后打开路径: ```go\\src\\runtime\\runtime.h```\n   ![](https://img-blog.csdnimg.cn/img_convert/8aceda5f69371db5ffafffe0d927d2c1.png)\n  + 得到如下实现代码\n```go\n// 第8行到35行\ntypedef\tsigned char\t\tint8;\ntypedef\tunsigned char\t\tuint8;\ntypedef\tsigned short\t\tint16;\ntypedef\tunsigned short\t\tuint16;\ntypedef\tsigned int\t\tint32;\ntypedef\tunsigned int\t\tuint32;\ntypedef\tsigned long long int\tint64;\ntypedef\tunsigned long long int\tuint64;\ntypedef\tfloat\t\t\tfloat32;\ntypedef\tdouble\t\t\tfloat64;\n\n#ifdef _64BIT\ntypedef\tuint64\t\tuintptr;\ntypedef\tint64\t\tintptr;\ntypedef\tint64\t\tintgo; // Go's int\ntypedef\tuint64\t\tuintgo; // Go's uint\n#else\ntypedef\tuint32\t\tuintptr;\ntypedef\tint32\t\tintptr;\ntypedef\tint32\t\tintgo; // Go's int\ntypedef\tuint32\t\tuintgo; // Go's uint\n#endif\n\n#ifdef _64BITREG\ntypedef\tuint64\t\tuintreg;\n#else\ntypedef\tuint32\t\tuintreg;\n#endif\n\n// 第153行到157行\nenum\n{\n\ttrue\t= 1,\n\tfalse\t= 0,\n};\n```\n\u003e install B 时刻:\n\u003e Go本质就是用C语言编写的一门高级编程语言\n\u003e 所以江哥前面教你C语言就是为了今天能让你看懂Go的实现代码,做到知其然知其所以然\n---\n## 常量变量对比\n- C语言定义常量和变量格式\n```\n数据类型 变量名称 = 值;\nconst 数据类型 常量名称 = 值;\n```\n- Go语言定义常量和变量格式\n  + 除了以下标准格式外,Go语言还提供了好几种简单的语法糖\n```\nvar 变量名称 数据类型 = 值;\nconst 变量名称 数据类型 = 值;\n```\n---\n## 注释对比\n- 和C语言一样,Go语言也支持单行注释和多行注释, 并且所有注释的特性都和C语言一样\n  + 单行注释 ```// 被注释内容```\n  + 多行注释 ```/* 被注释内容*/```\n- 在Go语言中,官方更加推荐使用单行注释,而非多行注释(详情可以直接查看Go官方源码)\n\n---\n## 运算符对比\n- 算数运算符和C语言几乎一样\n  + Go语言中++、--运算符不支持前置\n      + 错误写法: ++i; --i;\n  + Go语言中++、--是语句,不是表达式,所以必须独占一行\n      + 错误写法: a = i++;  return i++;\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|+\t|相加\t|A + B |\n|-\t|相减\t|A - B |\n|*\t|相乘\t|A * B |\n|/\t|相除\t|B / A |\n|%\t|求余\t|B % A |\n|++\t|自增\t|A++|\n|--\t|自减\t|A-- |\n\n---\n- 关系算符和C语言一样\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|==\t|检查两个值是否相等，如果相等返回 True 否则返回 False。\t|A == B|\n|!=\t|检查两个值是否不相等，如果不相等返回 True 否则返回 False。\t|A != B|\n|\u003e\t|检查左边值是否大于右边值，如果是返回 True 否则返回 False。\t|A \u003e B |\n|\u003c\t|检查左边值是否小于右边值，如果是返回 True 否则返回 False。\t|A \u003c B|\n|\u003e=\t|检查左边值是否大于等于右边值，如果是返回 True 否则返回 False。\t|A \u003e= B|\n|\u003c=\t|检查左边值是否小于等于右边值，如果是返回 True 否则返回 False。\t|A \u003c= B |\n\n---\n- 逻辑运算符和C语言一样\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|\u0026\u0026\t|如果两边的操作数都是 True，则条件 True，否则为 False。\t|A \u0026\u0026 B|\n|```\\|\\|```\t|如果两边的操作数有一个 True，则条件 True，否则为 False。\t|A \\|\\| B|\n|  ! |如果条件为 True，则逻辑 NOT 条件 False，否则为 True。\t|!A |\n\n---\n- 位运算符和C语言几乎一样\n  + 新增一个\u0026^运算符\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|\u0026\t|参与运算的两数各对应的二进位相与, 对应位只要都是1结果就为1| A \u0026 B |\n|```\\|```|参与运算的两数各对应的二进位相或,对应位只要其中一个是1结果就为1 | A \\| B |\n|^\t|参与运算的两数各对应的二进位相异或,对应位只要不同结果就是1| A ^ B |\n|\u003c\u003c\t|左移运算符,左移n位就是乘以2的n次方| A \u003c\u003c 2|\n|\u003e\u003e\t|右移运算符,右移n位就是除以2的n次方| B \u003e\u003e 2|\n|\u0026^|逻辑清零运算符, B对应位是1,A对应位清零,B对应位是0, A对应位保留原样| A \u0026^ B|\n\n```\nint main(){\n\t/*\n\t  0110      a\n\t\u0026^1011      b 如果b位位1,那么结果为0, 否则结果为a位对应的值\n\t----------\n\t  0100\n\t*/\n\ta1 := 6\n\tb1 := 11\n\tres1 := a1 \u0026^ b1\n\tfmt.Println(\"res1 = \", res1) // 4\n\n\t/*\n\t  1011      a\n\t\u0026^1101      b 如果b位位1,那么结果为0, 否则结果为a位对应的值\n\t----------\n\t  0010\n\t*/\n\ta2 := 11\n\tb2 := 13\n\tres2 := a2 \u0026^ b2\n\tfmt.Println(\"res2 = \", res2) // 2\n}\n```\n---\n- 赋值运算符和C语言几乎一样\n  + 新增一个\u0026^=运算符\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|=\t|将右边赋值给左边|\tC = A + B 将 A + B 表达式结果赋值给 C|\n|+=\t|相加后再赋值\t|C += A 等于 C = C + A|\n|-=\t|相减后再赋值\t|C -= A 等于 C = C - A|\n|*=\t|相乘后再赋值\t|C *= A 等于 C = C * A|\n|/=\t|相除后再赋值\t|C /= A 等于 C = C / A|\n|%=\t|求余后再赋值\t|C %= A 等于 C = C % A|\n|\u003c\u003c=\t|左移赋值\t|C \u003c\u003c= 2 等于 C = C \u003c\u003c 2|\n|\u003e\u003e=\t|右移赋值\t|C \u003e\u003e= 2 等于 C = C \u003e\u003e 2|\n|\u0026=\t|位逻辑与赋值\t|C \u0026= 2 等于 C = C \u0026 2|\n|^=\t|位逻辑或赋值\t|C ^= 2 等于 C = C ^ 2|\n|```\\|=```\t|位逻辑异或赋值\t|C \\|= 2 等于 C = C \\| 2|\n|\u0026^=|位逻辑清零赋值|C \u0026^= 2 等于 C = C \u0026^ 2|\n\n---\n## 流程控制语句对比\n- C语言流程控制中的if、switch、for在Go语言都可以使用\n- C语言中的四大跳转语句return、break、continue、goto在Go语言都可以使用\n- Go语言除了实现C语言中if、switch、for、return、break、continue、goto的基本功能以外,还对if、switch、for、break、continue进行了增强\n  + 例如: if 条件表达式前面可以添加初始化表达式\n  + 例如: break、continue可以指定标签\n  + 例如: switch语句可以当做if/elseif来使用\n  + ... ...\n- 值得注意的是Go语言中没有while循环和dowhile循环, 因为它们能做的Go语言中的for循环都可以做\n\n---\n## 函数和方法对比\n- C语言定义函数格式\n```c\n返回值类型 函数名称(形参列表) {\n        函数体相关语句;\n        return 返回值;\n}\n```\n- Go语言定义函数格式\n```go\nfunc  函数名称(形参列表)(返回值列表) {\n        函数体相关语句;\n        return 返回值;\n}\n```\n- C语言中没有方法的概念, 但是Go语言中有方法\n  + 对于初学者而言,可以简单的把方法理解为一种特殊的函数\n```go\nfunc  (接收者 接受者类型)函数名称(形参列表)(返回值列表) {\n        函数体相关语句;\n        return 返回值;\n}\n```\n---\n## 编程思想对比\n- C语言是一门面向过程的编程语言\n  + 面向过程: 按部就班, 亲力亲为,关注的是我应该怎么做?\n  + 做饭例子: 面向过程做饭\n    + 1.上街买菜\n    + 2.摘菜\n    + 3.洗菜\n    + 4.切菜\n    + 5.开火炒菜\n    + 6.淘米煮饭\n    + 7.吃饭\n- Go语言是门面向对象的编程语言\n  + 面向对象:化繁为简, 能不自己干自己就不干,关注的是我应该让谁来做?\n  + 做饭例子: 面向对象做饭\n  + 1.找个会做饭女朋友 or 男朋友\n  + 2.老婆我饿了 or 老公我饿了\n  + 3.躺着...等她/他把饭做好\n  + 4.吃饭\n- 不要把面向过程和面向对象想象得那么神奇, 它们只是思考问题的方式不同而已\n\n---\n## 其它新增特性\n- 接口\n- 并发\n- 反射\n- 异常处理\n- .... \n## Go语言SDK安装和配置\n- **什么是SDK**\n  + 软件开发工具包（外语首字母缩写：**SDK**、外语全称：**SoftwareDevelopmentKit）**一般都是一些软件工程师为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件时的开发工具的集合\n  + 如果不安装SDK, 你可以编写Go语言代码, 但是你不能编译执行编写好的Go语言代码\n\n- **如何安装?**\n- 1.下载SDK安装包。地址: https://golang.google.cn/dl/\n  + 由于新版本一般不太稳定, 所以我们选择下载上一个版本\n   ![](https://img-blog.csdnimg.cn/img_convert/9a17d37918a1fe000d8f666743579d69.png)\n- 2.**运行图形化安装包**\n ![](https://img-blog.csdnimg.cn/img_convert/c9e7a7740b72838cb1a80427e3e31429.png)\n  ![](https://img-blog.csdnimg.cn/img_convert/9aaf1c69b803b9a8d5262e77d774776b.png)![](https://img-blog.csdnimg.cn/img_convert/1f1029fa7db70869c3cccb190de1c3e6.png)\n  ![](https://img-blog.csdnimg.cn/img_convert/ab502aea014130e55edbec2f3f4da404.png)\n  ![](https://img-blog.csdnimg.cn/img_convert/2624422e682cd52b2ecca2dfb32d342e.png)\n  ![](https://img-blog.csdnimg.cn/img_convert/7db673ed916989e079ec363dcf93b457.png)\n- **3.检测配置环境变量**\n  ![](https://img-blog.csdnimg.cn/img_convert/563086d782d416ef6e77e19dfb72b7d7.png)\n  ![](https://img-blog.csdnimg.cn/img_convert/46fa77ce612e3ec81e4febc884d646e7.png)\n  ![](https://img-blog.csdnimg.cn/img_convert/6588f6f8b5edfbeda00d11db44cb9a61.png)\n  + **3.1.添加GOROOT环境变量**\n    + 用于告诉操作系统，我们把Go语言SDK安装到哪了\n    ![](https://img-blog.csdnimg.cn/img_convert/9109058a12cfed7d2bbde194cbf3f894.png)\n+ **3.2.配置GOPATH环境变量**\n    + 用于告诉操作系统，将来我们要在哪里编写Go语言程序\n    ![image.png](https://img-blog.csdnimg.cn/img_convert/c987306e9e77063f7d17a6e559aefd7c.png)\n    ![](https://img-blog.csdnimg.cn/img_convert/730e4237129539e44b4a5a3c5f565d0f.png)\n+ **3.3.配置GoBin环境变量**\n    + 用于告诉操作系统，去哪查找Go语言提供的一些应用程序\n    ![](https://img-blog.csdnimg.cn/img_convert/338a499d59417f6804172f3944aa1a5d.png)\n    ![](https://img-blog.csdnimg.cn/img_convert/611f99113ec3a799935ea973a9a5e49d.png)\n+ **最终结果**\n![](https://img-blog.csdnimg.cn/img_convert/139b934d704a1745b17b338afaad30dd.png)\n+ ***4.检查是否安装配置成功***\n    + 4.1打开CMD\n    ![](https://img-blog.csdnimg.cn/img_convert/b7089d32887309f642cc50bb59bdd68d.png)\n    + 4.2输入```go version```\n    ![](https://img-blog.csdnimg.cn/img_convert/70c440555b3c099d17339e28fb7f1ebf.png)\n    + 4.3输入```go env```\n    ![](https://img-blog.csdnimg.cn/img_convert/6d2fd0bfb0bdd777c6ab114471cbce14.png)\n---\n## 安装Go语言开发工具\n- 记事本(开发效率极低)\n- Vim(初学者入门门槛高)\n- VSCode(不喜欢)\n- Sublime Test(不喜欢)\n- GoLand(喜欢,当收费)\n- LiteIDE（开源免费， 跨平台运行，轻量级）\n- 生男生女都一样， 最关键是你中意哪个就用哪个\n---\n## Goland安装\n- 下载安装包: [点我下载Goland](https://pan.baidu.com/s/1gQIt7JyEx7UpdqZeBOnxhA )\n- 提取码：lm7v \n- 运行安装文件\n![](https://img-blog.csdnimg.cn/img_convert/1d5d134a1f556831f9ca1a7af28cfc2c.png)\n- 疯狂下一步\n![](https://img-blog.csdnimg.cn/img_convert/5e831e0e7583cb2b1267b3b9e457249b.png)\n![](https://img-blog.csdnimg.cn/img_convert/53a9c0af87a62feeb2ea17972dc340bb.png)\n![](https://img-blog.csdnimg.cn/img_convert/d41219ee9f9cd10b5b3d7299ece59926.png)\n![](https://img-blog.csdnimg.cn/img_convert/46ce46884f71204d77d18d9ca268b6d8.png)\n![](https://img-blog.csdnimg.cn/img_convert/5ff9e681f2b90b859c0dec599203dfc5.png)\n\u003e- 激活程序: 自行淘宝`JetBrains 激活`(仅供学生党参考, 在职人员请支持正版)\n![](https://img-blog.csdnimg.cn/img_convert/46044730772e908e4a81214d8d55630e.png)\n\u003e- 看不习惯英文的可以自行百度`Goland汉化包`\n\n- 打开项目文件夹\n![](https://img-blog.csdnimg.cn/img_convert/27dda98c291cfb0653aa2c27eb46ed09.png)\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20210612213125704.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDYxNzk2OA==,size_16,color_FFFFFF,t_70#pic_center)\n![](https://img-blog.csdnimg.cn/img_convert/baf0766f1d04fd157578f763a1af1b17.png)\n![](https://img-blog.csdnimg.cn/img_convert/30603a4a35d8df03daf244c15b201528.png)\n- 测试开发工具是否安装正确\n![](https://img-blog.csdnimg.cn/img_convert/9d6c239a4a4ea75376c5448796e3e823.png)\n![](https://img-blog.csdnimg.cn/img_convert/82f203e4ad1874f3478f105c72b5d345.png)\n ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210612213308431.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDYxNzk2OA==,size_16,color_FFFFFF,t_70#pic_center)\n![](https://img-blog.csdnimg.cn/img_convert/2f468dbedc7347d008f7d28deb6e3b53.png)\n![在这里插入图片描述](https://img-blog.csdnimg.cn/20210612213356265.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDYxNzk2OA==,size_16,color_FFFFFF,t_70#pic_center)\n---\n- 其它问题:\n  +  提示没有安装JVM\n  ![](https://img-blog.csdnimg.cn/img_convert/c552128ac16865d96f312cee0560e677.png)\n  + 下载Java SDK [点我下载](https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html)\n  ![](https://img-blog.csdnimg.cn/img_convert/e52617f4e58e64346e9b4006ab073012.png)\n  + 安装即可\n\n## Go语言程序组成\n- 和C语言程序一样，Go语言程序也是由众多函数组成的\n- 和C语言程序一样，程序运行时系统会***自动调用***名称叫做***main的函数***\n- 和C语言程序一样，如果一个程序***没有主函数***，则这个程序***不具备运行能力***\n- 和C语言程序一样，一个Go语言程序***有且只能有一个主函数***\n\n---\n## Go语言程序主函数定义格式\n- C语言main函数格式\n```go\nint main(int argc, const char * argv[]) {\n    return 0;\n}\n```\n- Go语言main函数格式\n  + func 告诉系统这是一个函数\n  + main主函数固定名称\n  + 函数左括号必须和函数名在同一行\n  + main函数必须在main包中\n```go\n// 告诉系统当前编写的代码属于哪个包\npackage main\n// 定义了一个名称叫做main的函数\nfunc main() {\n}\n```\n---\n## Go语言HelloWorld\n```go\npackage main // 告诉系统当前代码属于main这个包\nimport \"fmt\" // 导入打印函数对应的fmt包\nfunc main() {\n        // 通过包名.函数名称的方式, 利用fmt包中的打印函数输出语句\n\tfmt.Println(\"Hello World!!!\")\n}\n```\n---\n## Go语言HelloWorld和C语言HelloWorld异同\n-  ***1.文件类型不同***\n    + C语言代码保存在.c为后缀的文件中\n    + Go语言代码保存在.go为后缀的文件中\n- ***2.代码管理方式不同***\n    + C语言程序用***文件***的方式管理代码\n      + C语言会把不同类型的代码放到不同的.c文件中, 然后再编写对应的.h文件\n      + 需要使用时直接通过#include导入对应文件的.h文件即可\n      ![](https://img-blog.csdnimg.cn/img_convert/b2f858242a32c55ae7b4fe6791a09898.png)\n  + Go语言程序用**包**的形式管理代码\n    + 我们会把不同类型的代码放到不同的.go文件中,然后通过package给该文件指定一个包名\n    + 需要使用时直接通过import导入对应的包名即可\n      ![](https://img-blog.csdnimg.cn/img_convert/adf488f9d219e4302544bce1ed21ddd7.png)\n-  **3.main函数书写文件不同**\n  + C语言中main函数可以写在任意文件中, 只要保证一个程序只有一个main函数即可\n  + Go语言中main函数只能写在包名为main的文件夹中, 同样需要保存一个程序只有一个main函数\n-  ***4.函数编写的格式不同***\n    + C语言中函数的格式为\n      + 注意:C语言函数的左括号可以和函数名称在同一行, 也可以不在同一行\n```go\n返回值类型 函数名称(形参列表) {\n        函数体相关语句;\n        return 返回值;\n}\n```\n\n+ Go语言函数定义格式\n      注意:Go语言函数的左括号必须和函数名称在同一行,否则会报错\n```go\nfunc  函数名称(形参列表)(返回值列表) {\n        函数体相关语句;\n        return 返回值;\n}\n```\n-  ***5.函数调用的格式不同***\n   + C语言通过#include导入.h文件后,直接通过函数名称调用函数\n   + Go语言通过import导入对应的包后,需要通过包名.函数名称的方式调用\n```c\n#include \u003cstdio.h\u003e\n#include \"calculate.h\"\nint main()\n{\n    int res = sum(2, 3); // 直接利用函数名称调用函数\n    printf(\"res = %d!\\n\", res);\n    return 0;\n}\n```\n```go\npackage main\nimport (\n\t\"fmt\"\n\t\"lesson_1/calculate\"\n)\nfunc main() {\n\tres := calculate.Sum(2, 3) // 使用包名.函数名称调用函数\n\tfmt.Println(\"res1 = \", res)\n}\n```\n-  ***6.语句的结束方式不同***\n    + C语言中每条语句都必须以分号结尾\n    + Go语言中每条语句后面不用添加分号(编译器会自动添加)\n```c\n#include \u003cstdio.h\u003e\n#include \"calculate.h\"\nint main()\n{\n    int res = sum(2, 3); // 不写分号会报错\n    printf(\"res = %d!\\n\", res); // 不写分号会报错\n    return 0; // 不写分号会报错\n}\n```\n```go\npackage main\nimport (\n\t\"fmt\"\n\t\"lesson_1/calculate\"\n)\nfunc main() {\n\tres := calculate.Sum(2, 3) // 不用写分号\n\tfmt.Println(\"res1 = \", res) // 不用写分号\n}\n```\n---\n## Go语言注释\n- 和C语言一样,Go语言也支持单行注释和多行注释, 并且所有注释的特性都和C语言一样\n  + 单行注释 ```// 被注释内容```\n  + 多行注释 ```/* 被注释内容*/```\n- 在Go语言中,官方更加推荐使用单行注释,而非多行注释(详情可以直接查看Go官方源码)\n\n---\n## Go语言编码风格\n- 1.go程序编写在.go为后缀的文件中\n- 2.包名一般使用文件所在文件夹的名称\n- 2.包名应该简洁、清晰且全小写\n- 3.main函数只能编写在main包中\n- 4.每一条语句后面可以不用编写分号(推荐)\n- 5.如果没有编写分号,一行只能编写一条语句\n- 6.函数的左括号必须和函数名在同一行\n- 7.导入包但没有使用包编译会报错\n- 8.定义局部变量但没有使用变量编译也会报错\n- 9.定义函数但没有使用函数不会报错\n- 10.给方法、变量添加说明,尽量使用单行注释\n\n## 关键字\n- Go语言中的关键字和C语言中的关键字的含义样, 是指被Go语言赋予特殊含义的单词\n- Go语言中关键字的特征和C语言也一样\n  + 全部都是小写\n  + 在开发工具中会显示特殊颜色\n- Go语言中关键字的注意点和C语言也一样\n  + 因为关键字在C语言中有特殊的含义, 所以不能用作变量名、函数名等\n\n---\n## C语言关键字和Go语言关键字对比\n- C语言中一共有32个关键字\n\n|1|2|3|4|5|6|7|8|\n|---|---|---|---|---|---|---|---|\n|***if***|***else***|***switch***|***case***|***default***|***break***|***return***|***goto***|\n|do|while|***for***|***continue***|typedef|***struct***|enum|union|\n|char|short|int|long|float|double|void|sizeof|\n|signed|unsigned|***const***|auto|register|static|extern|volatile|\n\n- Go语言中一共有25个关键字\n\n|1|2|3|4|5|6|7|8|\n|---|---|---|---|---|---|---|---|\n|***if***|***else***|***switch***|***case***|***default***|***break***|***return***|***goto***|\n|fallthrough|***for***|***continue***|type|***struct***|var|***const***|map|\n|func|interface|range|import|package|defer|go|select|\n|chan|\n\n---\n- Go语言中除了关键字以外,还有30多个`预定义标识符`\n\n|内建常量||||\n|--|--|--|--|\n|true|false|iota|nil|\n\n|內建类型||||\n|--|--|--|--|\n|int|int8|int16|int32|\n|int64|uint|uint8|uint16|\n|uint32|uint64|uintptr|float32|\n|float64|complex64|complex128|bool|\n|byte|rune|string|error|\n\n|內建函数||||\n|--|--|--|--|\n|make|len|cap|new|\n|append|copy|delete|real|\n|imag|panic|recover|complex|\n---\n## 标识符\n- Go语言中的标识符和C语言中的标识符的含义样, 是指程序员在程序中自己起的名字(变量名称、函数名称等)\n\n---\n- 和C语言一样Go语言标识符也有一套`命名规则`, Go语言标识符的命名规则几乎和C语言一模一样\n  + 只能由字母(a~z、 A~Z)、数字、下划线组成\n  + 不能包含除下划线以外的其它特殊字符串\n  + 不能以数字开头\n  + 不能是Go语言中的关键字\n  + 标识符严格区分大小写, test和Test是两个不同的标识符\n- 和C语言标识符命名规则不同的是\n  + Go语言中_单独作为标识符出现时, 代表`空标识符`, 它对应的值会被忽略\n  ```go\n\tpackage main\n\t\n\timport \"fmt\"\n\t\n\tfunc main() {\n\t\t// 将常量10保存到名称叫做num的变量中\n\t\tvar num int = 10\n\t\tfmt.Println(\"num = \", num)\n\t\n\t\t// 忽略常量20,不会分配存储空间,也不会保存常量20\n\t\t//var _ int = 20\n\t\t//fmt.Println(\"_ = \", _) // cannot use _ as value\n\t\n\t\t// Go语言中如果定义了变量没有使用, 那么编译会报错(sub declared and not used)\n\t\t// 所以如果我们只使用了sum,没有使用sub会报错\n\t\t// 为了解决这个问题, 我们可以使用_忽略sub的值\n\t\t//var sum, sub int = calculate(20, 10)\n\t\tvar sum, _ int = calculate(20, 10)\n\t\tfmt.Println(\"sum = \", sum)\n\t\n\t}\n\t\n\tfunc calculate(a, b int)(int, int)  {\n\t\tvar sum int = a + b\n\t\tvar sub int = a - b\n\t\treturn sum, sub\n\t}\n  \n  ​```go\n  + Go语言默认的编码方式就是UTF-8, 所以Go语言支持中文, 所以可以用中文作为标识符(非常非常非常不推荐)\n  ​```go\n  package main\n  \n  import \"fmt\"\n  \n  func main() {\n\t  // 不会报错, 可以正常运行\n\t  var 年龄 int = 33\n\t  fmt.Println(\"年龄 = \", 年龄) // 33\n  \n\t  // 不会报错, 可以正常运行\n\t  var 结果 int = 计算器(10, 20)\n\t  fmt.Println(\"结果 = \", 结果) // 30\n  }\n  func 计算器(第一个变量, 第二个变量 int)int  {\n\t  return 第一个变量 + 第二个变量\n  }\n  ```\n---\n- 和C语言一样,标识符除了有`命名规则`以外,还有标识符`命名规范`\n  + 规则必须遵守, 规范不一定要遵守, 但是建议遵守\n  + Go语言的命名规范和C语言一样, 都是采用驼峰命名, 避免采用_命名\n    + 驼峰命名: sendMessage / sayHello\n    + _命名: send_message / say_hello\n\n## Go语言数据类型\n- Go语言本质是用C语言编写的一套高级开发语言, 所以Go语言中的数据类型大部分都是由C语言演变而来的\n\n- C语言数据类型\n![](https://img-blog.csdnimg.cn/img_convert/73b03f1ce2062d094514e574bf3d0089.png)\n- Go语言数据类型\n![](https://img-blog.csdnimg.cn/img_convert/7b5697c6ce19a1826aa8144ffef0fec3.png)\n\n---\n- C语言各数据类型占用内存空间\n\n|类型|32位编译器|64位编译器|\n|--|--|--|\n|char|1|1|\n|int|4|4|\n|float|4|4|\n|double|8|8|\n|short|2|2|\n|long|4|8|\n|long long|8|8|\n|void*|4|8|\n\n- Go语言各数据类型占用内存空间\n\n|类型|32位编译器|64位编译器|本质|\n|--|--|--|--|\n|int8/uint8|1|1|signed char/unsigned char|\n|int16/uint16|2|2|signed short/unsigned short|\n|int32/uint32|4|4|signed int/unsigned int|\n|int64/uint64|8|8|signed long long int/unsigned long long int|\n|byte|1|1|uint8/unsigned char|\n|rune|4|4|int32/signed int|\n|int|4|8|根据机器位数决定长度|\n|uintptr|4|8|根据机器位数决定长度 uint32/uint64|\n|float32|4|4|float|\n|float64|8|8|double|\n|true|1|1|char类型的整型|\n|false|1|1|char类型的整型|\n\n- 和C语言一样,Go语言也提供了Sizeof计算变量的内存空间\n  + 1.导入import \"unsafe\"包\n  + 2.通过unsafe.Sizeof()计算变量内存空间\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"unsafe\"\n)\n\nfunc main() {\n\tfmt.Println(\"int size = \", unsafe.Sizeof(int(0)))\n\tfmt.Println(\"int8 size = \", unsafe.Sizeof(int8(0)))\n\tfmt.Println(\"int16 size = \", unsafe.Sizeof(int16(0)))\n\tfmt.Println(\"int32 size = \", unsafe.Sizeof(int32(0)))\n\tfmt.Println(\"int64 size = \", unsafe.Sizeof(int64(0)))\n\tfmt.Println(\"uint size = \", unsafe.Sizeof(uint(0)))\n\tfmt.Println(\"uint8 size = \", unsafe.Sizeof(uint8(0)))\n\tfmt.Println(\"uint16 size = \", unsafe.Sizeof(uint16(0)))\n\tfmt.Println(\"uint32 size = \", unsafe.Sizeof(uint32(0)))\n\tfmt.Println(\"uint64 size = \", unsafe.Sizeof(uint64(0)))\n\tfmt.Println(\"uintptr size = \", unsafe.Sizeof(uintptr(0)))\n\tfmt.Println(\"byte size = \", unsafe.Sizeof(byte(0)))\n\tfmt.Println(\"rune size = \", unsafe.Sizeof(rune(0)))\n\tfmt.Println(\"float32 size = \", unsafe.Sizeof(float32(0)))\n\tfmt.Println(\"float64 size = \", unsafe.Sizeof(float64(0)))\n\tfmt.Println(\"true size = \", unsafe.Sizeof(true))\n\tfmt.Println(\"false size = \", unsafe.Sizeof(false))\n\n}\n```\n---\n- Go语言基本数据类型内部实现\n  + [golang官方网站下载go1.4版本源代码](https://github.com/golang/go/releases)\n    + 越老版本的代码越纯粹,越适合新手学习\n    + 随着代码的更新迭代会逐步变得非常复杂, 所以此处建议下载1.4版本\n  + 解压后打开路径: ```go\\src\\runtime\\runtime.h```\n  ![](https://img-blog.csdnimg.cn/img_convert/8aceda5f69371db5ffafffe0d927d2c1.png)\n  + 得到如下实现代码\n```go\n// 第8行到35行\ntypedef\tsigned char\t\tint8;\ntypedef\tunsigned char\t\tuint8;\ntypedef\tsigned short\t\tint16;\ntypedef\tunsigned short\t\tuint16;\ntypedef\tsigned int\t\tint32;\ntypedef\tunsigned int\t\tuint32;\ntypedef\tsigned long long int\tint64;\ntypedef\tunsigned long long int\tuint64;\ntypedef\tfloat\t\t\tfloat32;\ntypedef\tdouble\t\t\tfloat64;\n\n#ifdef _64BIT\ntypedef\tuint64\t\tuintptr;\ntypedef\tint64\t\tintptr;\ntypedef\tint64\t\tintgo; // Go's int\ntypedef\tuint64\t\tuintgo; // Go's uint\n#else\ntypedef\tuint32\t\tuintptr;\ntypedef\tint32\t\tintptr;\ntypedef\tint32\t\tintgo; // Go's int\ntypedef\tuint32\t\tuintgo; // Go's uint\n#endif\n\n#ifdef _64BITREG\ntypedef\tuint64\t\tuintreg;\n#else\ntypedef\tuint32\t\tuintreg;\n#endif\n\n// 第153行到157行\nenum\n{\n\ttrue\t= 1,\n\tfalse\t= 0,\n};\n```\n\u003e install B 时刻:\n\u003e Go本质就是用C语言编写的一门高级编程语言\n\u003e 所以江哥前面教你C语言就是为了今天能让你看懂Go的实现代码,做到知其然知其所以然\n\u003e 注意点: 企业开发中一般使用int, 因为int会根据你当前的操作系统自动转换为int32和int64\n---\n## Go语言变量\n- Go语言中变量的概念和C语言中也一样, 所以我们直接来看下如何定义和使用变量即可\n- C语言中定义变量的格式\n```\n数据类型 变量名称;\n数据类型 变量名称1, 变量名称2;\n```\n```c\n#include \u003cstdio.h\u003e\n\nint main(int argc, const char * argv[])\n{\n    int num1; // 先定义\n    num1 = 10; // 后初始化\n    printf(\"num1 = %d\\n\", num1);\n\n    int num2 = 20; // 定义的同时初始化\n    printf(\"num2 = %d\\n\", num2);\n\n    // 注意: 同时定义多个变量,不支持定义时初始化, 只能先定义后初始化\n    int num3, num4; //同时定义多个变量\n    num3 = 30;\n    num4 = 40;\n    printf(\"num3 = %d\\n\", num3);\n    printf(\"num4 = %d\\n\", num4);\n\n    return 0;\n}\n```\n- Go语言中定义变量有三种格式\n```go\n// 标准格式\nvar 变量名称 数据类型 = 值;\n// 自动推到类型格式\nvar 变量名称 = 值;\n// 简短格式(golang官方推荐格式)\n变量名称 := 值;\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tvar num1 int // 先定义\n\tnum1 = 10 // 后赋值\n\tfmt.Println(\"num1 = \", num1)\n\n\tvar num2 int = 20 // 定义的同时赋值\n\tfmt.Println(\"num2 = \", num2)\n\n\tvar num3  = 30 // 定义的同时赋值, 并省略数据类型\n\tfmt.Println(\"num3 = \", num3)\n    \n\tnum4  := 40 // 定义的同时赋值, 并省略关键字和数据类型\n\t/*\n\tnum4  := 40 等价于\n\tvar num4 int\n\tnum4 = 40\n\t*/\n\tfmt.Println(\"num4 = \", num4)\n}\n```\n- 和C语言一样,除了可以定义单个变量以外,还支持一次性定义多个变量\n  + 方式一, 连续定义\n   ```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar num1, num2 int // 先定义\n\t\tnum1 = 10\t// 后赋值\n\t\tnum2 = 20\n\t\tfmt.Println(\"num1 = \", num1)\n\t\tfmt.Println(\"num2 = \", num2)\n\t\n\t\tvar num3, num4 int = 30, 40 // 定义的同时赋值\n\t\tfmt.Println(\"num3 = \", num3)\n\t\tfmt.Println(\"num4 = \", num4)\n\t\n\t\tvar num5, num6 = 50, 60 // 定义的同时赋值, 并省略数据类型\n\t\tfmt.Println(\"num5 = \", num5)\n\t\tfmt.Println(\"num6 = \", num6)\n\t\n\t\tnum7, num8 := 70, 80 // 定义的同时赋值, 并省略关键字和数据类型\n\t\tfmt.Println(\"num7 = \", num7)\n\t\tfmt.Println(\"num8 = \", num8)\n\t}\n   ```\n  + 方式二, 变量组\n  ```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar( // 先定义\n\t\t\tnum1 int\n\t\t\tnum2 float32\n\t\t)\n\t\tnum1 = 10 // 后赋值\n\t\tnum2 = 3.14\n\t\tfmt.Println(\"num1 = \", num1)\n\t\tfmt.Println(\"num2 = \", num2)\n  \n\t\tvar( // 定义的同时赋值\n\t\t\tnum3 int = 30\n\t\t\tnum4 float32 = 6.66\n\t\t)\n\t\tfmt.Println(\"num3 = \", num3)\n\t\tfmt.Println(\"num4 = \", num4)\n  \n\t\tvar( // 定义的同时赋值, 并省略数据类型\n\t\t\tnum5 = 50\n\t\t\tnum6 = 7.77\n\t\t)\n\t\tfmt.Println(\"num5 = \", num5)\n\t\tfmt.Println(\"num6 = \", num6)\n  \n\t\tvar( // 一行定义多个\n\t\t\tnum7, num8 = 70, 80\n\t\t\tnum9, num10 = 9.99, 100\n\t\t)\n\t\tfmt.Println(\"num7 = \", num7)\n\t\tfmt.Println(\"num8 = \", num8)\n\t\tfmt.Println(\"num9 = \", num9)\n\t\tfmt.Println(\"num10 = \", num10)\n\t}\n  ```\n---\n## Go语言变量定义注意点\n- 简短模式的含义是定义的同时初始化\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tnum := 10\n\tnum := 20 // 编译报错, 重复定义\n\tfmt.Println(\"num = \", num)\n}\n```\n- 一定不要把:=当做赋值运算符来使用\n```go\npackage main\nimport \"fmt\"\nvar num = 10 // 定义一个全局变量\nfunc main() {\n\tnum := 20 // 定义一个局部变量\n\tfmt.Println(\"num = \", num)\n        test()\n}\nfunc test() {\n\tfmt.Println(\"num = \", num) // 还是输出10\n}\n\n```\n- :=只能用于定义局部变量,不能用于定义全局变量\n```go\npackage main\nimport \"fmt\"\nnum := 10 // 编译报错\nfunc main() {\n\tfmt.Println(\"num = \", num)\n}\n```\n- 使用:=定义变量时,不能指定var关键字和数据类型\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\t//var num int := 10 // 编译报错\n\t//var num := 10 // 编译报错\n\tnum int := 10 // 编译报错\n\tfmt.Println(\"num = \", num)\n\tfmt.Println(\"num = \", num)\n}\n```\n- 变量组中不能够使用:=\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tvar(\n\t\tnum := 10 // 编译报错\n\t)\n\tfmt.Println(\"num = \", num)\n}\n```\n- 通过:=同时定义多个变量, 必须给所有变量初始化\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\t//num1, num2 := 666, 888 // 正确\n\tnum1, num2 := 666 // 报错\n\tfmt.Printf(\"%d, %d\\n\", num1, num2)\n}\n```\n- 通过:=同时定义多个变量, 只要任意一个变量没有定义过,都会做退化赋值操作\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\t// 定义一个变量num1\n\tnum1 := 10\n\t// 同时定义两个变量num1和num2, 由于num2从来没有定义过,\n\t// 所以对于num1来说:=退化为赋值运算符, 而对于num2来说:=仍然是定义+赋值\n\tnum1, num2 := 20, 30\n\tfmt.Println(\"num1 = \", num1)\n\tfmt.Println(\"num2 = \", num2)\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tnum1 := 10\n\tnum2 := 20\n\t// 报错, 因为num1,和num2都已经被定义过\n\t// 至少要有任意一个变量没有被定义过,才会退化赋值\n\tnum1, num2 := 30, 40\n\tfmt.Println(\"num1 = \", num1)\n\tfmt.Println(\"num2 = \", num2)\n}\n```\n- 定义的局部变量或者导入的包没有被使用, 那么编译器会报错,无法编译运行,但是定义的全局变量没有被使用,编译器不会报错, 可以编译运行\n---\n## 局部变量和全局变量\n- 和C语言一样,按照变量的作用域,我们可以把变量划分为局部变量和全局变量\n- Go语言中局部变量的概念以及全局变量的概念和C语言一模一样\n- 局部变量：\n  + 定义在函数内部的变量以及函数的形参称为局部变量\n  + 作用域：从定义哪一行开始直到与其所在的代码块结束\n  + 生命周期:从程序运行到定义哪一行开始分配存储空间到程序离开该变量所在的作用域\n- 全局变量：\n  + 定义在函数外面的变量称为全局变量\n  + 作用域范围：从定义哪行开始直到文件结尾\n  + 生命周期:程序一启动就会分配存储空间,直到程序结束\n\n- 和C语言不同的是, C语言中可以定义相同名称的全局变量, 而Go语言中无论全局变量还是局部变量, 只要作用域相同都不能出现同名的变量\n```go\npackage main\nimport \"fmt\"\n//var num1 int\n//var num1 int // 报错, 重复定义\nvar num3 int\nfunc main() {\n\t//var num2\n\t//var num2 // 报错, 重复定义\n\t\n\tvar num3 int // 不报错, 因为作用域不同\n\tfmt.Println(\"num3 = \", num3)\n}\n```\n---\n- C语言中全局变量没有赋值,那么默认初始值为0, 局部变量没有赋值,那么默认初始值是随机值\n- Go语言中无论是全局变量还是局部变量,只要定义了一个变量都有默认的0值\n  + int/int8/int16/int32/int64/uint/uint8/uint16/uint32/uint64/byte/rune/uintptr的默认值是0\n  + float32/float64的默认值是0.0\n  + bool的默认值是false\n  + string的默认值是\"\"\n  + pointer/function/interface/slice/channel/map/error的默认值是nil\n  + 其它复合类型array/struct默认值是内部数据类型的默认值\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tvar intV int // 整型变量\n\tvar floatV float32 // 实型变量\n\tvar boolV bool // 布尔型变量\n\tvar stringV string // 字符串变量\n\tvar pointerV *int // 指针变量\n\tvar funcV func(int, int)int // function变量\n\tvar interfaceV interface{} // 接口变量\n\tvar sliceV []int // 切片变量\n\tvar channelV chan int // channel变量\n\tvar mapV map[string]string // map变量\n\tvar errorV error // error变量\n\n\tfmt.Println(\"int = \", intV) // 0\n\tfmt.Println(\"float = \", floatV) // 0\n\tfmt.Println(\"bool = \", boolV) // false\n\tfmt.Println(\"string = \", stringV) // \"\"\n\tfmt.Println(\"pointer = \", pointerV) // nil\n\tfmt.Println(\"func = \", funcV) // nil\n\tfmt.Println(\"interface = \", interfaceV) // nil\n\tfmt.Println(\"slice = \", sliceV) // []\n\tfmt.Println(\"slice = \", sliceV == nil) // true\n\tfmt.Println(\"channel = \", channelV) // nil\n\tfmt.Println(\"map = \", mapV) // map[]\n\tfmt.Println(\"map = \", mapV == nil) // true\n\tfmt.Println(\"error = \", errorV) // nil\n\n\tvar arraryV [3]int // 数组变量\n\ttype Person struct{\n\t\tname string\n\t\tage int\n\t}\n\tvar structV Person // 结构体变量\n\tfmt.Println(\"arrary = \", arraryV) // [0, 0, 0]\n\tfmt.Println(\"struct = \", structV) // {\"\" 0}\n}\n```\n---\n## 数据类型转换\n- C语言中数据可以隐式转换或显示转换, 但是Go语言中数据`只能显示转换`\n- C语言隐式转换\n```c\n#include \u003cstdio.h\u003e\nint main(){\n  // 隐式转换:自动将实型10.6转换为整型后保存\n   int a = 10.6;\n // 自动类型提升: 运算时会自动将小类型转换为大类型后运算\n  double b = 1.0 / 2; // 等价于1.0 / 2.0\n}\n```\n- C语言显示转换(强制转换)\n```c\n#include \u003cstdio.h\u003e\nint main(){\n  // 显示转换:强制将实型10.6转换为整型后保存\n  int a = (int)10.5;\n}\n```\n- Go语言数值类型之间转换\n  + 格式: `数据类型(需要转换的数据)`\n  + 注意点: 和C语言一样数据可以从大类型转换为小类型, 也可以从小类型转换为大类型. 但是大类型转换为小类型可能会丢失精度\n  ```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar num0 int = 10\n\t\tvar num1 int8 = 20\n\t\tvar num2 int16\n\t\t//num2 = num0 // 编译报错, 不同长度的int之间也需要显示转换\n\t\t//num2 = num1 // 编译报错, 不同长度的int之间也需要显示转换\n\t\tnum2 = int16(num0)\n\t\tnum2 = int16(num1)\n\t\tfmt.Println(num2)\n\t\n\t\tvar num3 float32 = 3.14\n\t\tvar num4 float64\n\t\t//num4 = num3 // 编译报错, 不同长度的float之间也需要显示转换\n\t\tnum4 = float64(num3)\n\t\tfmt.Println(num4)\n\t\n\t\tvar num5 byte = 11\n\t\tvar num6 uint8 // 这里不是隐式转换, 不报错的原因是byte的本质就是uint8\n\t\tnum6 = num5\n\t\tfmt.Println(num6)\n\t\n\t\tvar num7 rune = 11\n\t\tvar num8 int32\n\t\tnum8 = num7 // 这里不是隐式转换, 不报错的原因是byte的本质就是int32\n\t\tfmt.Println(num8)\n\t}\n  ```\n\n---\n## 数值类型和字符串类型之间转换\n- Go语言中不能通过 数据类型(变量)的格式将数值类型转换为字符串, 也不能通过 数据类型(变量)的格式将字符串转换为数值类型\n```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar num1 int32 = 65\n\t\t// 可以将整型强制转换, 但是会按照ASCII码表来转换\n\t\t// 但是不推荐这样使用\n\t\tvar str1 string = string(num1)\n\t\tfmt.Println(str1)\n\n\t\tvar num2 float32 = 3.14\n\t\t// 不能将其它基本类型强制转换为字符串类型\n\t\tvar str2 string = string(num2)\n\t\tfmt.Println(str2)\n\n\t\tvar str3 string = \"97\"\n\t\t// 不能强制转换, cannot convert str2 (type string) to type int\n\t\tvar num3  int = int(str3)\n\t\tfmt.Println(num3)\n\t}\n```\n\n  + **数值类型转字符串类型`strconv..FormatXxx()`**\n  ```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar num1 int32 = 10\n\t\t// 第一个参数: 需要被转换的整型,必须是int64类型\n\t\t// 第二个参数: 转换为几进制,  必须在2到36之间\n\t\t// 将32位十进制整型变量10转换为字符串,并继续保留10进制格式\n\t\tstr1 := strconv.FormatInt(int64(num1), 10)\n\t\tfmt.Println(str1) // 10\n\t\t// 将32位十进制整型变量10转换为字符串,并转换为2进制格式\n\t\tstr2 := strconv.FormatInt(int64(num1), 2)\n\t\tfmt.Println(str2) // 1010\n\n\t\tvar num5 float64 = 3.1234567890123456789\n\t\t// 第一个参数: 需要转换的实型, 必须是float64类型\n\t\t// 第二个参数: 转换为什么格式,f小数格式, e指数格式\n\t\t// 第三个参数: 转换之后保留多少位小数, 传入-1按照指定类型有效位保留\n\t\t// 第四个参数: 被转换数据的实际位数,float32就传32, float64就传64\n\t\t// 将float64位实型,按照小数格式并保留默认有效位转换为字符串\n\t\tstr3 := strconv.FormatFloat(num5, 'f', -1, 64)\n\t\tfmt.Println(str3) // 3.1234567\n\t\tstr4 := strconv.FormatFloat(num5, 'f', -1, 64)\n\t\tfmt.Println(str4) // 3.1234567890123457\n\t\t// 将float64位实型,按照小数格式并保留2位有效位转换为字符串\n\t\tstr5 := strconv.FormatFloat(num5, 'f', 2, 64)\n\t\tfmt.Println(str5) // 3.12\n\t\t// 将float64位实型,按照指数格式并保留2位有效位转换为字符串\n\t\tstr6 := strconv.FormatFloat(num5, 'e', 2, 64)\n\t\tfmt.Println(str6) // 3.12\n\n\t\tvar num6 bool = true\n\t\tstr7 := strconv.FormatBool(num6)\n\t\tfmt.Println(str7) // true\n\t}\n  ```\n  + **字符串类型转数值类型`strconv.ParseXxx()`**\n  ```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar str1 string = \"125\"\n\t\t// 第一个参数: 需要转换的数据\n\t\t// 第二个参数: 转换为几进制\n\t\t// 第三个参数: 转换为多少位整型\n\t\t// 注意点: ParseInt函数会返回两个值, 一个是转换后的结果, 一个是错误\n\t\t// 如果被转换的数据转换之后没有超出指定的范围或者不能被转换时,\n\t\t// 那么错误为nil, 否则错误不为nil\n\t\t// 将字符串\"125\"转换为10进制的int8\n\t\tnum1, err := strconv.ParseInt(str1, 10, 8)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t}\n\t\tfmt.Println(num1)\n\n\t\tvar str2 string = \"150\"\n\t\t// 将字符串\"150\"转换为10进制的int8\n\t\t// 由于int8的取值范围是-128~127, 所以转换之后超出了指定的范围, error不为nil\n\t\tnum2, err := strconv.ParseInt(str2, 10, 8)\n\t\tif err != nil {\n\t\t\tfmt.Println(err)\n\t\t}\n\t\tfmt.Println(num2)\n\n\t\tvar str3 string = \"3.1234567890123456789\"\n\t\t// 第一个参数: 需要转换的数据\n\t\t// 第二个参数: 转换为多少位小数, 32 or 64\n\t\t// ParseFloat同样有两个返回值, 如果能够正常转换则错误为nil, 否则不为nil\n\t\tnum3, err := strconv.ParseFloat(str3, 32)\n\t\tif err != nil {\n\t\t\t// 例如: 把字符串\"3.14abc\"转换为小数就会报错, 因为\"3.14abc\"不是一个小数\n\t\t\tfmt.Println(err)\n\t\t}\n\t\tfmt.Println(num3)\n\n\t\tvar str4 string = \"true\"\n\t\t// 第一个参数: 需要转换的数据\n\t\t// ParseBool同样有两个返回值, 如果能够正常转换则错误为nil, 否则不为nil\n\t\tnum4, _ := strconv.ParseBool(str4)\n\t\tfmt.Println(num4)\n\t}\n  ```\n- 字符串类型转换为数值类型时,如果不能转换除了返回error以外,还会返回对应类型的默认值\n```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar str1 string = \"abc\"\n\t\tnum1, _ := strconv.ParseInt(str1, 10, 32)\n\t\tfmt.Println(num1) // 0\n\n\t\tnum2, _ := strconv.ParseFloat(str1, 32)\n\t\tfmt.Println(num2) // 0\n\n\t\tnum3, _ := strconv.ParseBool(str1)\n\t\tfmt.Println(num3) // false\n\t}\n```\n- 看完上面的代码有没有种想打人的感觉? 如果有那么请继续往下看\n- 字符串类型和整型快速转换\n ```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar num1 int32 = 110\n\t\t// 快速将整型转换为字符串类型\n\t\t// 注意:Itoa方法只能接受int类型\n\t\tvar str1 string = strconv.Itoa(int(num1))\n\t\tfmt.Println(str1)\n\n\t\tvar str2 string = \"666\"\n\t\t// 快速将字符串类型转换为整型\n\t\t// 注意: Atoi方法返回两个值, 一个值是int,一个值是error\n\t\t// 如果字符串能被转换为int,那么error为nil, 否则不为nil\n\t\tnum2, err := strconv.Atoi(str2)\n\t\tif err != nil{\n\t\t\tfmt.Println(err)\n\t\t}\n\t\tfmt.Println(num2)\n\t}\n ```\n-  **数值类型转字符串类型其它方式**\n  ```go\n\tpackage main\n\timport \"fmt\"\n\tfunc main() {\n\t\tvar num1 int32 = 110\n\t\t// Sprintf函数和Printf函数很像, 只不过不是输出而将格式化的字符串返回给我们\n\t\tvar str1 string = fmt.Sprintf(\"%d\", num1)\n\t\tfmt.Println(str1)\n  \n\t\tvar num2 float32 = 3.14\n\t\tvar str2 string = fmt.Sprintf(\"%f\", num2)\n\t\tfmt.Println(str2)\n  \n\t\tvar num3 bool = true\n\t\tvar str3 string = fmt.Sprintf(\"%t\", num3)\n\t\tfmt.Println(str3)\n\t}\n  ```\n---\n## Go语言常量\n- 和C语言一样Go语言中的常量也分为`整型常量`、`实型常量`、`字符常量`、`字符串常量`、`自定义常量`\n\n- 自定义常量\n  + C语言自定义常量: ```const 数据类型 常量名称 = 值;```\n  ```go\n    #include \u003cstdio.h\u003e\n    int main(int argc, const char * argv[])\n    {\n        const float PI = 998;\n        PI = 110; // 报错\n        printf(\"PI = %d\\n\", PI );\n        return 0;\n    }\n  ```\n  + Go语言自定义常量: ```const 常量名称 数据类型 = 值```or  ```const 常量名称  = 值```\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t//const PI float32 = 3.14\n\t//PI = 110 // 报错\n\t//fmt.Println(\"PI = \", PI )\n  \n\tconst PI = 3.14\n\tPI = 110 // 报错\n\tfmt.Println(\"PI = \", PI )\n  }\n  ```\n  + 除此之外Go语言还支持`一次性定义多个常量`\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t// 多重赋值方式\n\tconst num1, num2 int  = 100, 200\n\tfmt.Println(\"num1 = \", num1)\n\tfmt.Println(\"num2 = \", num2)\n  \n\t// 常量组方式\n\tconst (\n\t\tnum3 = 100\n\t\tnum4 = 200\n\t)\n\tfmt.Println(\"num3 = \", num3)\n\tfmt.Println(\"num4 = \", num4)\n  \n\t// 常量组+多重赋值\n\tconst (\n\t\tnum5, num6 = 100, 200\n\t\tnum7 = 300\n\t)\n\tfmt.Println(\"num5 = \", num5)\n\tfmt.Println(\"num6 = \", num6)\n\tfmt.Println(\"num7 = \", num7)\n  }\n  ```\n---\n- Go语言自定义常量注意点\n  + 定义的局部变量或者导入的包没有被使用, 那么编译器会报错,无法编译运行\n  + 但是定义的常量没有被使用,编译器不会报错, 可以编译运行\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t// 可以编译运行\n\tconst PI float32 = 3.14\n  }\n  ```\n  + 在常量组中, 如果上一行常量有初始值,但是下一行没有初始值, 那么下一行的值就是上一行的值\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\tconst (\n\t\tnum1 = 998\n\t\tnum2 // 和上一行的值一样\n\t\tnum3 = 666\n\t\tnum4 // 和上一行的值一样\n\t\tnum5 // 和上一行的值一样\n\t)\n\tfmt.Println(\"num1 = \", num1) // 998\n\tfmt.Println(\"num2 = \", num2) // 998\n\tfmt.Println(\"num3 = \", num3) // 666\n\tfmt.Println(\"num4 = \", num4) // 666\n\tfmt.Println(\"num5 = \", num5) // 666\n  \n\tconst (\n\t\tnum1, num2 = 100, 200\n\t\tnum3, num4  // 和上一行的值一样, 注意变量个数必须也和上一行一样\n\t)\n\tfmt.Println(\"num1 = \", num1)\n\tfmt.Println(\"num2 = \", num2)\n\tfmt.Println(\"num3 = \", num3)\n\tfmt.Println(\"num4 = \", num4)\n  }\n  ```\n---\n- 枚举常量\n  + C语言中枚举类型的本质就是整型常量  \n  + Go语言中没有C语言中明确意义上的enum定义, 但是可以借助iota标识符来实现枚举类型\n- C语言枚举格式:\n```go\n enum　枚举名　{\n    枚举元素1,\n    枚举元素2,\n    … …\n };\n```\n-  C语言枚举中,如果没有指定初始值,那么从0开始递增\n  ```c\n  #include \u003cstdio.h\u003e\n  int main(int argc, const char * argv[])\n  {\n      enum Gender{\n          male,\n          female,\n          yao,\n      };\n  //    enum Gender g = male;\n  //    printf(\"%d\\n\", g); // 0\n  //    enum Gender g = female;\n  //    printf(\"%d\\n\", g); // 1\n      enum Gender g = yao;\n      printf(\"%d\\n\", g); // 2\n      return 0;\n  }\n  ```\n  + C语言枚举中, 如果指定了初始值,那么从指定的数开始递增\n  ```c\n  #include \u003cstdio.h\u003e\n  int main(int argc, const char * argv[])\n  {\n      enum Gender{\n          male = 5,\n          female,\n          yao,\n      };\n  //    enum Gender g = male;\n  //    printf(\"%d\\n\", g); // 5\n  //    enum Gender g = female;\n  //    printf(\"%d\\n\", g); // 6\n      enum Gender g = yao;\n      printf(\"%d\\n\", g); // 7\n      return 0;\n  }\n  ```\n---\n- Go语言实现枚举格式\n```go\nconst(\n  枚举元素1 = iota\n  枚举元素2 = iota\n  ... ...\n)\n```\n-  利用iota标识符标识符实现从0开始递增的枚举\n    ```go\n    package main\n    import \"fmt\"\n    func main() {\n\t\tconst (\n\t\t\tmale = iota\n\t\t\tfemale = iota\n\t\t\tyao = iota\n\t\t)\n\t\tfmt.Println(\"male = \", male) // 0\n\t\tfmt.Println(\"male = \", female) // 1\n\t\tfmt.Println(\"male = \", yao) // 2\n    }\n    ```\n-  iota注意点:\n     + 在同一个常量组中,iota从0开始递增, `每一行递增1`\n     + 在同一个常量组中,只要上一行出现了iota,那么后续行就会自动递增\n      ```go\n      package main\n      import \"fmt\"\n      func main() {\n\t\tconst (\n\t\t\tmale = iota // 这里出现了iota\n\t\t\tfemale // 这里会自动递增\n\t\t\tyao\n\t\t)\n\t\tfmt.Println(\"male = \", male) // 0\n\t\tfmt.Println(\"male = \", female) // 1\n\t\tfmt.Println(\"male = \", yao) // 2\n      }\n      ```\n      + 在同一个常量组中,如果iota被中断, 那么必须显示恢复\n      ```go\n    package main\n    import \"fmt\"\n    func main() {\n\t\tconst (\n\t\t\tmale = iota \n\t\t\tfemale = 666 // 这里被中断, 如果没有显示恢复, 那么下面没有赋值的常量都和上一行一样\n\t\t\tyao\n\t\t)\n\t\tfmt.Println(\"male = \", male) // 0\n\t\tfmt.Println(\"male = \", female) // 666\n\t\tfmt.Println(\"male = \", yao) // 666\n    }\n     ```\n      ```go\n    package main\n    import \"fmt\"\n    func main() {\n\t\tconst (\n\t\t\tmale = iota \n\t\t\tfemale = 666 // 这里被中断\n\t\t\tyao = iota // 这里显示恢复, 会从当前常量组第一次出现iota的地方开始,每一行递增1, 当前是第3行,所以值就是2\n\t\t)\n\t\tfmt.Println(\"male = \", male) // 0\n\t\tfmt.Println(\"male = \", female) // 666\n\t\tfmt.Println(\"male = \", yao) // 2\n    }\n      ```\n      + iota也支持常量组+多重赋值, 在同一行的iota值相同\n      ```go\n      package main\n      import \"fmt\"\n      func main() {\n\t    const (\n\t\t  a, b = iota, iota\n\t\t  c, d = iota, iota\n\t    )\n\t    fmt.Println(\"a = \", a) // 0\n\t    fmt.Println(\"b = \", b) // 0\n\t    fmt.Println(\"c = \", c) // 1\n\t    fmt.Println(\"d = \", d) // 1\n      }\n      ```\n      + iota自增默认数据类型为int类型, 也可以显示指定类型\n      ```go\n      package main\n      import \"fmt\"\n      func main() {\n\t  const (\n\t\t  male float32 = iota // 显示指定类型,后续自增都会按照指定类型自增\n\t\t  female\n\t\t  yao\n\t  )\n\tfmt.Printf(\"%f\\n\", male) // 0.0\n\tfmt.Printf(\"%f\\n\", female) // 1.0\n\tfmt.Printf(\"%f\\n\", yao) // 2.0\n\t  fmt.Println(\"male = \", reflect.TypeOf(female)) // float32\n      }\n      ```\n- Go语言fmt包实现了类似C语言printf和scanf的格式化I/O, 格式化动作源自C语言但更简单\n##输出函数\n- func Printf(format string, a ...interface{}) (n int, err error)\n  + 和C语言用法几乎一模一样, 只不过新增了一些格式化符号\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t  name := \"微信搜索：代码情缘\"\n\t  age := 33\n\t  fmt.Printf(\"name = %s, age = %d\\n\", name, age) // name = lnj, age = 33\n  }\n  ```\n  + 值得注意的是,输出十进制只能通过%d,不能像C语言一样通过%i\n  + 除了和C语言一样,可以通过%o、%x输出八进制和十六进制外,`还可以`直接通过%b输出二进制\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t  num := 15\n\t  fmt.Printf(\"十进制 = %d\\n\", num)\n\t  fmt.Printf(\"八进制 = %o\\n\", num)\n\t  fmt.Printf(\"十六进制 = %x\\n\", num)\n\t  fmt.Printf(\"二进制 = %b\\n\", num)\n  }\n  ```\n  + 除此之外,Go语言还增加了%T控制符, 用于输出值的类型\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\ttype Person struct {\n\t\tname string\n\t\tage int\n\t}\n\tnum1 := 10\n\tnum2 := 3.14\n\tper := Person{\"lnj\", 33}\n\tfmt.Printf(\"num1 = %T\\n\", num1) // int\n\tfmt.Printf(\"num2 = %T\\n\", num2) // float64\n\tfmt.Printf(\"per = %T\\n\", per) // main.Person\n  }\n  ```\n  + 除此之外,Go语言还增加了%v控制符,用于打印所有类型数据\n    + Go语言中输出某一个值,很少使用%d%f等, 一般都使用%v即可\n    + 输出复合类型时会自动生成对应格式后再输出\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\ttype Person struct {\n\t\tname string\n\t\tage int\n\t}\n\tnum1 := 10\n\tnum2 := 3.14\n\tper := Person{\"lnj\", 33}\n\t// 此时相当于把%v当做%d\n\tfmt.Printf(\"num1 = %v\\n\", num1) // 10\n\t// 此时相当于把%v当做%f\n\tfmt.Printf(\"num2 = %v\\n\", num2) // 3.14\n  }\n  ```\n  + Go语言Printf函数其它特性,如宽度、标志、精度、长度、转移符号等,和C语言一样.\n---\n- func Println(a ...interface{}) (n int, err error)\n  + 采用默认格式将其参数格式化并写入标准输出, \n    + 输出之后`会`在结尾处添加换行\n    + 传入多个参数时, 会自动在相邻参数之间添加空格\n    + 传入符合类型数据时, 会自动生成对应格式后再输出\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\tnum1 := 10\n\tnum2 := 3.14\n\tfmt.Println(num1, num2) // 10 3.14\n\tfmt.Println(\"num1 =\", num1, \"num2 =\", num2) // num1 = 10 num2 = 3.14\n  \n\ttype Person struct {\n\t\tname string\n\t\tage int\n\t}\n\tper := Person{\"lnj\", 33}\n\tfmt.Println(per) // {lnj 33}\n  }\n  ```\n---\n- func Print(a ...interface{}) (n int, err error)\n  + 和Println几乎一样\n    + 输出之后`不会`在结尾处添加换行\n    + 传入多个参数时, 只有两个相邻的参数`都不是`字符串,才会在相邻参数之间添加空格\n    + 传入符合类型数据时, 会自动生成对应格式后再输出\n  ```gi\n  package main\n  import \"fmt\"\n  func main() {\n\tnum1 := 10\n\tnum2 := 3.14\n\tfmt.Print(num1, num2) // 10 3.14\n\tfmt.Print(\"num1 =\", num1, \"num2 =\", num2) // num1 =10 num2 =3.14\n  \n\ttype Person struct {\n\t\tname string\n\t\tage int\n\t}\n\tper := Person{\"lnj\", 33}\n\tfmt.Print(per) // {lnj 33}\n  }\n  ```\n---\n- 以下三个函数和Printf/Println/Print函数一样, 只不过上面三个函数是输出到标准输出, 而下面三个函数可以通过w指定输出到什么地方\n- func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error)\n- func Fprintln(w io.Writer, a ...interface{}) (n int, err error)\n- func Fprint(w io.Writer, a ...interface{}) (n int, err error)\n```go\npackage main\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n)\nfunc main() {\n\t// os.Stdout 写入到标准输出\n\tname := \"lnj\"\n\tage := 33\n\t// 第一个参数: 指定输出到什么地方\n\t// 第二个参数: 指定格式控制字符串\n\t// 第三个参数: 指定要输出的数据\n\tfmt.Fprintf(os.Stdout, \"name = %s, age = %d\\n\", name, age)\n\n\t// http.ResponseWriter 写入到网络响应\n\thttp.HandleFunc(\"/\", func(writer http.ResponseWriter, request *http.Request) {\n\t\tfmt.Fprintf(writer, \"name = %s, age = %d\\n\", name, age)\n\t})\n\thttp.ListenAndServe(\":8888\", nil)\n}\n```\n---\n- 以下三个函数和Printf/Println/Print函数一样, 只不过上面三个函数是输出到标准输出, \n而下面三个函数不会输出,而是将字符串返回给我们\n- func Sprintf(format string, a ...interface{}) string\n- func Sprint(a ...interface{}) string\n- func Sprintln(a ...interface{}) string\n```go\npackage main\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"os\"\n)\nfunc main() {\n\tname := \"lnj\"\n\tage := 33\n\t// 按照指定的格式生成字符串\n\tstr := fmt.Sprintf(\"name = %s, age = %d\\n\", name, age)\n\t// 输出生成的字符串\n\tfmt.Println(str)\n}\n```\n---\n## 输入函数\n- func Scanf(format string, a ...interface{}) (n int, err error)\n  + 和C语言用法几乎一模一样, 但是只能输入一行数据\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t  var num1 int\n\t  var num2 int\n\t  fmt.Scanf(\"%d%d\", \u0026num1, \u0026num2)\n\t  fmt.Println(num1, num2)\n  }\n  ```\n- func Scan(a ...interface{}) (n int, err error)\n  + 和C语言scanf函数几乎一样, 只不过不用指定格式化字符串\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\tvar num1 int\n\tvar num2 int\n\tfmt.Scan(\u0026num1, \u0026num2)\n\tfmt.Println(num1, num2)\n  \n\tvar num3 float32\n\tvar num4 float32\n\tfmt.Scan(\u0026num3, \u0026num4)\n\tfmt.Println(num3, num4)\n  }\n  ```\n- func Scanln(a ...interface{}) (n int, err error)\n  + 和C语言用法几乎一模一样, 只不过不用指定格式化字符串, 并且只能输入一行数据\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t  var num1 int\n\t  var num2 int\n\t  fmt.Scanln(\u0026num1, \u0026num2)\n\t  fmt.Println(num1, num2)\n  }\n  ```\n---\n- 以下三个函数和Scan/Scanln/Scanf函数一样, 只不过上面三个函数是从标准输入读取数据, 而下面三个函数可以通过r指定从哪读取数据\n- func Fscanf(r io.Reader, format string, a ...interface{}) (n int, err error)\n- func Fscanln(r io.Reader, a ...interface{}) (n int, err error)\n- func Fscan(r io.Reader, a ...interface{}) (n int, err error)\n```go\n  package main\n  import \"fmt\"\n  func main() {\n\t  var num1 int\n\t  var num2 int\n\t  // 第一个参数: 指定从哪读取数据\n\t  // 第二个参数: 指定格式控制字符串\n\t  // 第三个参数: 指定要输出的数据\n\t  fmt.Fscanf(os.Stdin, \"%d%d\", \u0026num1, \u0026num2)\n\t  fmt.Println(num1, num2)\n\n\n\t  s := strings.NewReader(\"lnj 33\")\n\t  var name string\n\t  var age int\n\t  // 从指定字符串中扫描出想要的数据\n\t  // 注意:\n\t  fmt.Fscanf(s, \"%s%d\", \u0026name, \u0026age)\n\t  fmt.Println(\"name =\",name, \"age =\",age)\n  }\n```\n- 以下三个函数和Scan/Scanln/Scanf函数一样, 只不过上面三个函数是从标准输入读取数据, 而下面三个函数是从字符串中读取数据\n- func Sscan(str string, a ...interface{}) (n int, err error)\n- func Sscanf(str string, format string, a ...interface{}) (n int, err error)\n- func Sscanln(str string, a ...interface{}) (n int, err error)\n```go\n  package main\n  import \"fmt\"\n  func main() {\n\tstr := \"lnj 33\"\n\tvar name string\n\tvar age int\n\t//fmt.Sscanf(str, \"%s %d\",\u0026name, \u0026age)\n\t//fmt.Sscanln(str,\u0026name, \u0026age)\n\tfmt.Sscan(str,\u0026name, \u0026age)\n\tfmt.Println(\"name =\",name, \"age =\",age)\n  }\n```\n## go命令行操作指令\n- 标准go语言项目文件目录格式\n  + 项目文件夹就是GOPATH指向的文件夹\n  + src文件夹是专门用于存放源码文件的\n    + main文件夹是专门用于存储package main包相关源码文件的\n    + 其它文件夹是专门用于存储除package main包以外源码文件的\n  + bin文件夹是专门用于存储编译之后的可执行程序的\n  + pag文件夹是专门用于存储编译之后的.a文件的\n\n\u003e |---项目文件夹\n\u003e -----------|--------src文件夹\n\u003e-----------------------------|--------main文件夹\n\u003e-----------------------------|--------其它文件夹\n\u003e -----------|--------bin文件夹\n\u003e -----------|--------pkg文件夹\n\n---\n- `go version` 查看当前安装的go版本\n- `go env` 查看当前go的环境变量\n- `go fmt` 格式化代码\n  + 会将指定文件中凌乱的代码按照go语言规范格式化\n- `go run 命令文件` 编译并运行go程序\n  + package main包中包含main函数的文件, 我们称之为命令文件\n  + 其它包中的文件, 我们称之为源码文件\n- `go build` 编译检查\n  + 对于非命令文件只会执行编译检查, 不会产生任何文件\n  + 对于命令文件除了编译检查外,还会在当前目录下生成一个可执行文件\n  + 对应只想编译某个文件, 可以在命令后面指定文件名称`go build 文件名称 `\n- `go install` 安装程序  \n  + 对于非命令文件会执行编译检查, 并生成.a结尾的包, 放到 $GOPATH/pkg目录中\n  + 对于命令文件会执行编译检查, 并生成可执行程序, 放到$GOPATH/bin目录中\n\n---\n## 通过os包获取命令行参数\n- C语言中的命令行参数\n  + argc: argv中保存数据的个数\n  + argv: 默认情况下系统只会传入一个值, 这个值就是main函数执行文件的路径\n  + 我们可以通过配置开发工具,或者命令行运行时以`空格+参数`形式传递其它参数\n  + 注意点: 无论外界传入的是什么类型, 我们拿到的都是`字符串类型`\n```c\n#include \u003cstdio.h\u003e\nint main(int argc, const char * argv[])\n{\n    for(int i = 0; i \u003c argc; i++){\n        printf(\"argv[%d] = %s\\n\", i, argv[i]);\n    }\n    return 0;\n}\n```\n![](https://img-blog.csdnimg.cn/img_convert/b51ac94964e5f39a88bb8f2289dcffd2.png)\n\n- Go语言中的命令行参数\n  + Go语言中main函数没有形参, 所以不能直接通过main函数获取命令行参数\n  + 想要获取命令行参数必须导入os包, 通过os包的Args获取\n  + 注意点: 无论外界传入的是什么类型, 我们拿到的都是`字符串类型`\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\" // 用于获取命令行参数的包\n)\nfunc main() {\n\t// 1.获取传入参数个数\n\tnum := len(os.Args)\n\t// 2.打印所有获取到的参数\n\tfor i := 0; i \u003c num; i++ {\n\t\tfmt.Println(os.Args[i])\n\t}\n}\n```\n![](https://img-blog.csdnimg.cn/img_convert/9f072135aa57cbdc2efc3774117f6522.png)\n\n---\n## 通过flag包获取命令行参数\n- Go语言中除了可以通过os包获取命令行参数以外,还可以通过flag包获取命令行参数\n```go\npackage main\nimport (\n\t\"flag\"\n\t\"fmt\"\n)\nfunc main() {\n\t/*\n    flag.Xxxx(name, value, usage)\n\t第一个参数: 命令行参数名称\n\t第二个参数: 命令行参数对应的默认值\n\t第三个参数: 命令行参数对应的说明\n\t*/\n\t// 1.设置命令行参数\n\tname := flag.String(\"name\", \"lnj\", \"请输入人的姓名\")\n\tage := flag.Int(\"age\", 33, \"请输入人的年龄\")\n\t// 2.将命令行参数解析到注册的参数\n\tflag.Parse()\n\t// 3.使用命令行参数\n\t// 注意flag对应方法返回的都是指针类型, 所以使用时必须通过指针访问\n\tfmt.Println(\"name = \", *name)\n\tfmt.Println(\"age = \", *age)\n}\n```\n![](https://img-blog.csdnimg.cn/img_convert/0613d4246191b848d976395aa038ca43.png)\n\n- flag获取命令行参数第二种写法\n```go\npackage main\nimport (\n\t\"flag\"\n\t\"fmt\"\n)\nfunc main() {\n\t/*\n\tflag.Xxxx(*type, name, value, usage)\n\t第一个参数:保存命令行参数变量地址\n\t第二个参数: 命令行参数名称\n\t第三个参数: 命令行参数对应的默认值\n\t第四个参数: 命令行参数对应的说明\n\t*/\n\t// 1.定义变量,保存命令行参数的值\n\tvar name string\n\tvar age int\n\t// 2.设置命令行参数\n\tflag.StringVar(\u0026name, \"name\", \"lnj\", \"请输入人的姓名\")\n\tflag.IntVar(\u0026age, \"age\", 33,\"请输入人的姓名\")\n\t// 3.注册解析命令行参数\n\tflag.Parse()\n\t// 4.使用命令行参数\n\tfmt.Println(\"name = \", name)\n\tfmt.Println(\"age = \", age)\n}\n```\n---\n## os包和flag包获取命令行参数对比\n- 通过os包获取命令行参数\n  + 如果用户没有传递参数`会`报错\n  + `需要`严格按照代码中的顺序传递参数, 否则`会`造成数据混乱\n  + `不能`指定参数的名称\n  + 获取到的数据都是`字符串`类型\n```go\npackage main\nimport (\n\t\"os\"\n\t\"fmt\"\n)\nint main(){\n\tname := os.Args[1]\n\tage := os.Args[2]\n\tfmt.Println(\"name = \", name)\n\tfmt.Println(\"age = \", age)\n}\n```\n- ![](https://img-blog.csdnimg.cn/img_convert/fbd12cc2764ad6666e51e42cac7ab0e9.png)\n\n- 通过flag包获取命令行参数\n  + 如果用户没有传递参数`不会`报错\n  + `不需要`严格按照代码中的顺序传递参数, `不会`造成数据混乱\n  + `可以`指定参数的名称\n  + 获取到的数据是我们自己指定的类型\n```go\npackage main\nimport (\n\t\"flag\"\n\t\"fmt\"\n)\nint main(){\n\tname := flag.String(\"name\", \"lnj\", \"请输入人的姓名\")\n\tage := flag.Int(\"age\", 33, \"请输入人的年龄\")\n\t// 2.注册解析命令行参数\n\tflag.Parse()\n\t// 3.使用命令行参数\n\t// 注意flag对应方法返回的都是指针类型, 所以使用时必须通过指针访问\n\tfmt.Println(\"name = \", *name)\n\tfmt.Println(\"age = \", *age)\n}\n```\n![](https://img-blog.csdnimg.cn/img_convert/fd54bbf6b671155d3ae69add5144dbbc.png)\n## 算数运算符\n- 算数运算符和C语言几乎一样\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|+\t|相加\t|A + B |\n|-\t|相减\t|A - B |\n|*\t|相乘\t|A * B |\n|/\t|相除\t|B / A |\n|%\t|求余\t|B % A |\n|++\t|自增\t|A++|\n|--\t|自减\t|A-- |\n\n- 注意点:\n  + 只有相同类型的数据才能进行运算\n```go\npackage main\nimport \"fmt\"\nint main(){\n\tvar num1 int32 = 10\n\t//var num2 int64 = num1 // 类型不同不能进行赋值运算\n\tvar num2 int64 = int64(num1) // 类型不同不能进行赋值运算\n\tfmt.Println(num2)\n\n\tvar num3 int32 = 10\n\tvar num4 int64 = 20\n\t//var res int64 = num3 + num4 // 类型不同不能进行算数运算\n\tvar res1 int64 = int64(num3) + num4 // 类型不同不能进行算数运算\n\tfmt.Println(res1)\n\n\tvar num5 int32 = 10\n\tvar num6 int64 = 20\n\t//var res2 bool = (num5 == num6) // 类型不同不能进行关系运算\n\tvar res2 bool = (num5 == int32(num6)) // 类型不同不能进行关系运算\n\tfmt.Println(res2)\n\n\t// ... ... 其它以此类推\n}\n```\n+ Go语言中++、--运算符不支持前置\n     + 错误写法: ++i; --i;\n+ Go语言中++、--是语句,不是表达式,所以必须独占一行\n     + 错误写法: a = i++;  return i++;\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tnum1 := 0\n\tnum1++\n\tfmt.Println(num1)\n\t//++num1 // 编译报错, Go语言中++只能后置,不能前置\n\t//fmt.Println(num1)\n\t//var num2 int = num1++ // 编译报错, num1++是语句不是表达式, 所以必须独占一行\n\t//fmt.Println(num2)\n}\n```\n- Go语言中字符串支持利用+号进行拼接\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tstr := \"abc\" + \"def\"\n\t//fmt.Println(str)\n}\n```\n---\n## 关系算符\n- 关系算符和C语言一样\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|==\t|检查两个值是否相等，如果相等返回 True 否则返回 False。\t|A == B|\n|!=\t|检查两个值是否不相等，如果不相等返回 True 否则返回 False。\t|A != B|\n|\u003e\t|检查左边值是否大于右边值，如果是返回 True 否则返回 False。\t|A \u003e B |\n|\u003c\t|检查左边值是否小于右边值，如果是返回 True 否则返回 False。\t|A \u003c B|\n|\u003e=\t|检查左边值是否大于等于右边值，如果是返回 True 否则返回 False。\t|A \u003e= B|\n|\u003c=\t|检查左边值是否小于等于右边值，如果是返回 True 否则返回 False。\t|A \u003c= B |\n\n- 注意点:\n  + 和C语言不通的是, Go语言中关系运算符只能返回true和false\n---\n## 逻辑运算符\n- 逻辑运算符和C语言一样\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|\u0026\u0026\t|如果两边的操作数都是 True，则条件 True，否则为 False。\t|A \u0026\u0026 B|\n|```\\|\\|```\t|如果两边的操作数有一个 True，则条件 True，否则为 False。\t|A \\|\\| B|\n|  ! |如果条件为 True，则逻辑 NOT 条件 False，否则为 True。\t|!A |\n\n- 注意点:\n  + 和C语言不通的是, Go语言中关系运算符只能返回true和false\n  + 逻辑非只能用于true和false\n---\n## 位运算符\n - 位运算符和C语言几乎一样\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|\u0026\t|参与运算的两数各对应的二进位相与, 对应位只要都是1结果就为1| A \u0026 B |\n|```\\|```|参与运算的两数各对应的二进位相或,对应位只要其中一个是1结果就为1 | A \\| B |\n|^\t|参与运算的两数各对应的二进位相异或,对应位只要不同结果就是1| A ^ B |\n|\u003c\u003c\t|左移运算符,左移n位就是乘以2的n次方| A \u003c\u003c 2|\n|\u003e\u003e\t|右移运算符,右移n位就是除以2的n次方| B \u003e\u003e 2|\n|\u0026^|逻辑清零运算符, B对应位是1,A对应位清零,B对应位是0, A对应位保留原样| A \u0026^ B|\n\n + 新增一个\u0026^运算符\n```c\nint main(){\n\t/*\n\t  0110      a\n\t\u0026^1011      b 如果b位位1,那么结果为0, 否则结果为a位对应的值\n\t----------\n\t  0100\n\t*/\n\ta1 := 6\n\tb1 := 11\n\tres1 := a1 \u0026^ b1\n\tfmt.Println(\"res1 = \", res1) // 4\n\n\t/*\n\t  1011      a\n\t\u0026^1101      b 如果b位位1,那么结果为0, 否则结果为a位对应的值\n\t----------\n\t  0010\n\t*/\n\ta2 := 11\n\tb2 := 13\n\tres2 := a2 \u0026^ b2\n\tfmt.Println(\"res2 = \", res2) // 2\n}\n```\n---\n## 赋值运算符\n- 赋值运算符和C语言几乎一样\n     +  新增一个\u0026^=运算符\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|=\t|将右边赋值给左边|\tC = A + B 将 A + B 表达式结果赋值给 C|\n|+=\t|相加后再赋值\t|C += A 等于 C = C + A|\n|-=\t|相减后再赋值\t|C -= A 等于 C = C - A|\n|*=\t|相乘后再赋值\t|C *= A 等于 C = C * A|\n|/=\t|相除后再赋值\t|C /= A 等于 C = C / A|\n|%=\t|求余后再赋值\t|C %= A 等于 C = C % A|\n|\u003c\u003c=\t|左移赋值\t|C \u003c\u003c= 2 等于 C = C \u003c\u003c 2|\n|\u003e\u003e=\t|右移赋值\t|C \u003e\u003e= 2 等于 C = C \u003e\u003e 2|\n|\u0026=\t|位逻辑与赋值\t|C \u0026= 2 等于 C = C \u0026 2|\n|^=\t|位逻辑或赋值\t|C ^= 2 等于 C = C ^ 2|\n|```\\|=```\t|位逻辑异或赋值\t|C \\|= 2 等于 C = C \\| 2|\n|\u0026^=|位逻辑清零赋值|C \u0026^= 2 等于 C = C \u0026^ 2|\n\n---\n## 其它运算符\n\n|运算符\t|描述\t|实例|\n|--|--|--|\n|\u0026\t|返回变量存储地址\t|\u0026a; 将给出变量的实际地址|\n|*\t|访问指针指向内存\t|*p; 访问指针p指向内存|\n\n```go\npackage main\nimport \"fmt\"\nint main(){\n\tvar num int = 666\n\tvar p *int = \u0026num\n\tfmt.Println(num)\n\tfmt.Println(*p)\n\tnum = 777\n\tfmt.Println(num)\n\t*p = 999\n\tfmt.Println(num)\n}\n```\n\n- 注意点\n  - 指针类型只支持相等运算, 不能做加减运算\n```c\n#include \u003cstdio.h\u003e\nint main()\n{\n    int ages[3] = {19, 23, 22};\n    int *arrayP = \u0026ages[0];\n    printf(\"ages[0] = %i\\n\", *(arrayP + 0)); // *(arrayP + 0) == *arrayP\n    printf(\"ages[1] = %i\\n\", *(arrayP + 1));\n    printf(\"ages[2] = %i\\n\", *(arrayP + 2));\n    return 0;\n}\n```\n```go\npackage main\nimport \"fmt\"\nint main(){\n\tvar ages [3]int = [3]int{19, 23, 22}\n\tvar p *int = \u0026ages[0]\n\t//fmt.Println(\u0026ages[0])\n\t//fmt.Println(*p) // 19\n\tfmt.Println(*(p + 0)) // 编译报错\n}\n```\n## 运算符优先级\n- 和C语言一样, 只需记住()优先级最高即可\n## Go语言流程控制基本概念\n- Go语言流程控制和C语言一样, 也有三大流程控制结构\n  + 顺序结构(默认结构)\n  + 选择结构(if / switch)\n  + 循环结构(for)\n\n---\n## 选择结构if\n- 和C语言不同的的是\n  + 条件表达式的值必须是布尔类型(Go语言中没有非零即真的概念)\n  + 条件表达式前面可以添加初始化语句\n  + 不需要编写圆括号\n  + 左大括号必须和条件语句在同一行\n- 第一种格式:\n  + 条件表达式结果为true,那么执行if后面{}中代码\n```c\nif 初始化语句; 条件表达式{\n    语句块;\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\t// 如果后续需要用到age变量, 可以将变量放到if外面\n\tage := 18\n\tif age \u003e= 18{\n\t\tfmt.Println(\"成年人\")\n\t}\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\t// 如果后续不需要用到age变量, 可以将变量放到条件表达式前面\n\tif age := 18; age \u003e= 18{\n\t\tfmt.Println(\"成年人\")\n\t}\n}\n\n```\n- 第二种格式:\n  + 条件表达式结果为true,那么执行if后面{}中代码\n  + 否则执行else后面{}中代码\n```c\nif 初始化语句; 条件表达式{\n    语句块;\n}else{\n    语句块;\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tif age := 18;age \u003e= 18 {\n\t\tfmt.Println(\"成年人\")\n\t}else{\n\t\tfmt.Println(\"未成年人\")\n\t}\n}\n```\n- 第三种格式:\n  + if后面条件表达式结果为true,那么执行if后面{}中代码\n  + 否则判断else if条件表达式是否为true,为true执行else if后面{}中代码\n  + 否则依次判断后续else if条件表达式是否为true,哪个为true就执行哪个else if后面{}中代码\n  + 都不满足则执行else后面{}中代码\n```c\nif 初始化语句; 条件表达式{\n    语句块;\n}else if 条件表达式{\n    语句块;\n}\n... ...\nelse{\n    语句块;\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tif age := 18;age \u003e 55{\n\t\tfmt.Println(\"老年人\")\n\t}else if age \u003e= 40{\n\t\tfmt.Println(\"中年人\")\n\t}else if age \u003e= 18{\n\t\tfmt.Println(\"成年人\")\n\t}else{\n\t\tfmt.Println(\"未成年人\")\n\t}\n}\n```\n- 值得一提的是Go语言中没有C语言中的三目运算符, 所以C语言中三目能干的在Go语言中都只能通过if else的形式来完成\n---\n## 选择结构switch\n- 和C语言不同的的是\n  + 和if一样,表达式前面可以添加初始化语句\n  + 和if一样,不需要编写圆括号\n  + 和if一样,左大括号必须和表达式在同一行\n  + case表达式的值不一定要是常量, 甚至可以不用传递\n  + 一个case后面可以有多个表达式, 满足其中一个就算匹配\n  + case后面不需要添加break\n  + 可以在case语句块最后添加fallthrough,实现case穿透\n  + case后面定义变量不需要添加{}明确范围\n- 格式\n```c\nswitch 初始化语句; 表达式{\n  case 表达式1, 表达式2:\n        语句块;\n  case 表达式1, 表达式2:\n        语句块;\n  default:\n        语句块;\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tswitch num := 3;num {\n\t\tcase 1:\n\t\t\tfmt.Println(\"星期一\")\n\t\tcase 2:\n\t\t\tfmt.Println(\"星期二\")\n\t\tcase 3:\n\t\t\tfmt.Println(\"星期三\")\n\t\tcase 4:\n\t\t\tfmt.Println(\"星期四\")\n\t\tcase 5:\n\t\t\tfmt.Println(\"星期五\")\n\t\tcase 6:\n\t\t\tfmt.Println(\"星期六\")\n\t\tcase 7:\n\t\t\tfmt.Println(\"星期日\")\n\t\tdefault:\n\t\t\tfmt.Println(\"Other...\")\n\t}\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tswitch num := 3;num {\n\tcase 1,2,3,4,5:\n\t\tfmt.Println(\"工作日\")\n\tcase 6,7:\n\t\tfmt.Println(\"非工作日\")\n\tdefault:\n\t\tfmt.Println(\"Other...\")\n\t}\n}\n```\n- 注意点:\n  + case后面不用编写break, 不会出现case穿透问题\n  + 如果想让case穿透,必须在case语句块最后添加fallthrough关键\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tswitch num := 3;num {\n\tcase 1:\n\t\tfallthrough\n\tcase 2:\n\t\tfallthrough\n\tcase 3:\n\t\tfallthrough\n\tcase 4:\n\t\tfallthrough\n\tcase 5:\n\t\tfmt.Println(\"工作日\")\n\tcase 6:\n\t\tfallthrough\n\tcase 7:\n\t\tfmt.Println(\"非工作日\")\n\tdefault:\n\t\tfmt.Println(\"Other...\")\n\t}\n}\n```\n  + case后面不仅仅可以放常量,还可以放变量和表达式\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tvalue := 2\n\tswitch num:=1; num {\n\tcase value: // 变量\n\t\tfmt.Println(\"num等于value\")\n\tdefault:\n\t\tfmt.Println(\"num不等于value\")\n\t}\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tvalue := 2\n\tswitch num:=1; num {\n\tcase getValue(): // 函数\n\t\tfmt.Println(\"num等于value\")\n\tdefault:\n\t\tfmt.Println(\"num不等于value\")\n\t}\n}\nfunc getValue() int {\n\treturn 1\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tswitch num:=18; {\n\t\tcase num \u003e=0 \u0026\u0026 num \u003c=10: // 表达式\n\t\t\tfmt.Println(\"num是一个0~10之间的数\")\n\t\tcase num \u003e10 \u0026\u0026 num \u003c=20:\n\t\t\tfmt.Println(\"num是一个11~20之间的数\")\n\t\tdefault:\n\t\t\tfmt.Println(\"num是一个大于20的数\")\n\t}\n}\n```\n  + case后面定义变量不用添加{}明确作用于范围\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n    switch num := 1;num {\n\t  case 1:\n\t\tvalue := 10 // 不会报错\n\t\tfmt.Println(value)\n\t  default:\n\t\tfmt.Println(\"Other...\")\n\t}\n}\n```\n  + 其它特性和C语言一样\n---\n## 循环结构for\n- 和C语言不同的的是\n  + 和if一样,条件表达式的值必须是布尔类型\n  + 和if一样,不需要编写圆括号\n  + 和if一样,左大括号必须和表达式在同一行\n- 格式:\n```\nfor 初始化表达式；循环条件表达式；循环后的操作表达式 {\n    循环体语句;\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tfor i:=0; i\u003c10; i++{\n\t\tfmt.Println(i)\n\t}\n}\n```\n- Go语言中没有while/dowhile循环, 所以可以通过如下格式实现C语言中while循环用法\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\ti:=0\n\tfor i\u003c10 {\n\t\tfmt.Println(i)\n\t\ti++\n\t}\n}\n```\n- 最简单死循环\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tfor{\n\t\tfmt.Println(\"根本停不下来\")\n\t}\n}\n```\n- 除了实现基本的循环结构以外,Go语言还实现了一种高级for循环`for...range循环`\n  + `for...range循环`可以快速完成对字符串、数组、slice、map、channel遍历\n- 格式\n```c\nfor 索引, 值 := range 被遍历数据{\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\t// 1.定义一个数组\n\tarr := [3]int{1, 3, 5}\n\t// 2.快速遍历数组\n\t// i用于保存当前遍历到数组的索引\n\t// v用于保存当前遍历到数组的值\n\tfor i, v := range arr{\n\t\tfmt.Println(i, v)\n\t}\n}\n```\n---\n## 四大跳转\n- 和C语言一样,Go语言中也有四大跳转语句, 分别是return、break、continue、goto\n- break语句\n  + Go语言中的break语句可以用于,立即跳出switch、for和select\n  + 但不同的是Go语言中的break语句可以指定标签\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tfor i:=0; i\u003c10; i++{\n\t\tif(i == 5){\n\t\t\tbreak // 跳出所在循环\n\t\t}\n\t\tfmt.Println(i)\n\t}\n}\n```\n- 利用break跳转到指定标签\n  + 标签必须在使用之前定义\n  + 标签后面只能跟switch和循环语句, 不能插入其它语句\n  + 跳转到标签之后switch和循环不会再次被执行\n```go\npackage main\nimport \"fmt\"\nfunc main() {\nouter:\n\tswitch num:=2; num {\n\tcase 1:\n\t\tfmt.Println(1)\n\tcase 2:\n\t\tfmt.Println(2)\n\t\tbreak outer\n\tdefault:\n\t\tfmt.Println(\"other\")\n\t}\n\tfmt.Println(\"come here\")\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\nouter:\n\tfor i:=0; i\u003c5; i++{\n\t\tfor j:=0; j\u003c10; j++ {\n\t\t\tif (j == 5) {\n\t\t\t\tbreak outer// 跳出到指定标签\n\t\t\t}\n\t\t\tfmt.Println(j)\n\t\t}\n\t}\n\tfmt.Println(\"come here\")\n}\n```\n- continue语句\n  + Go语言中的continue语句可以用于,立即进入下一次循环\n  + 但不同的是Go语言中的continue语句可以指定标签\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tfor i:=0; i\u003c5; i++{\n\t\tif (i == 2) {\n\t\t\tcontinue\n\t\t}\n\t\tfmt.Println(i)\n\t}\n}\n```\n- 利用continue 跳转到指定标签\n  + 标签必须在使用之前定义\n  + 标签后面只能跟循环语句, 不能插入其它语句\n  + 对于单层循环和直接编写continue一样\n  + 对于多层循环,相当于跳到最外层循环继续判断条件执行\n```go\npackage main\nimport \"fmt\"\nfunc main() {\nouter:\n\tfor i:=0; i\u003c5; i++{\n\t\tfmt.Println(\"i =\", i) // 0 1 2 3 4\n\t\tfor j:=0; j\u003c10; j++ {\n\t\t\tif (j == 5) {\n\t\t\t\tcontinue outer// 跳出到指定标签\n\t\t\t}\n\t\t}\n\t}\n\tfmt.Println(\"come here\")\n}\n```\n- goto语句\n  + Go语言中的goto和C语言中用法一样, 用于在同一个函数中瞎跳\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tnum := 1\nouter:\n\tif(num \u003c= 10){\n\t\tfmt.Println(num)\n\t\tnum++\n\t\tgoto outer // 死循环\n\t}\n\tfmt.Println(\"come here\")\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tnum := 1\n\tif(num \u003c= 10){\n\t\tfmt.Println(num)\n\t\tnum++\n\t\tgoto outer // 死循环\n\t}\nouter:\n\tfmt.Println(\"come here\")\n}\n```\n- Go语言中的return语句和C语言一模一样,都是用于结束函数,将结果返回给调用者\n\n# Go 语言入门\n\n## 函数\n- Go语言和C语言一样也有函数的概念, Go语言中函数除了定义格式和不用声明以外,其它方面几乎和C语言一模一样\n- 格式:\n```go\nfunc 函数名称(形参列表)(返回值列表){\n    函数体;\n}\n```\n- 无参数无返回值函数\n```go\nfunc say()  {\n\tfmt.Println(\"Hello World!!!\")\n}\n```\n- 有参数无返回值函数\n```go\nfunc say(name string)  {\n\tfmt.Println(\"Hello \", name)\n}\n```\n- 无参数有返回值函数\n```go\nfunc sum() int { // 只有一个返回值时,返回值列表的()可以省略\n\treturn 1 + 1\n}\n```\n- 有参数有返回值函数\n```go\nfunc sum(a int, b int) int {\n\treturn a + b\n}\n```\n---\n## 和C语言函数差异\n- 和C语言不同的是,Go语言中可以给函数的返回值指定名称\n```c\n// 给返回值指定了一个名称叫做res, return时会自动将函数体内部res作为返回值\n// 其实本质就是提前定义了一个局部变量res, 在函数体中使用的res就是这个局部变量,返回的也是这个局部变量\nfunc sum() (res int) { \n\tres = 1 + 1\n\treturn\n}\n```\n- 和C语言不同的是,Go语言中的函数允许有多个返回值函数\n```go\nfunc calculate(a int, b int) (sum int, sub int) {\n\tsum = a + b\n\tsub = a - b\n\treturn\n}\n```\n- 相邻同类型形参OR返回值类型可以合并, 可以将数据类型写到最后一个同类型形参OR返回值后面\n```go\n// a, b都是int类型, 所以只需要在b后面添加int即可\nfunc calculate(a, b int) (sum, sub int) {\n\tsum = a + b\n\tsub = a - b\n\treturn\n}\n```\n- 和C语言不同的是Go语言中的函数不需要先声明在使用\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tsay();\n}\nfunc say()  { // 在后面定义也可以在前面使用\n\tfmt.Println(\"Hello World!!!\")\n}\n```\n---\n## 值传递和引用传递\n- Go语言中`值类型`有: int系列、float系列、bool、string、数组、结构体\n  + 值类型通常在栈中分配存储空间\n  + 值类型作为函数参数传递, 是拷贝传递\n  + 在函数体内修改值类型参数, 不会影响到函数外的值\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tnum := 10\n\tchange(num)\n\tfmt.Println(num) // 10\n}\nfunc change(num int)  {\n\tnum = 998\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tarr := [3]int{1, 3, 5}\n\tchange(arr)\n\tfmt.Println(arr) // 1, 3, 5\n}\nfunc change(arr [3]int)  {\n\tarr[1] = 8\n}\n```\n```go\npackage main\nimport \"fmt\"\ntype Person struct {\n\tname string\n\tage int\n}\nfunc main() {\n\tp := Person{\"lnj\", 33}\n\tchange(p)\n\tfmt.Println(p.name) // lnj\n}\nfunc change(p Person)  {\n\tp.name = \"zs\"\n}\n```\n- Go语言中`引用类型`有: 指针、slice、map、channel\n  + 引用类型通常在堆中分配存储空间\n  + 引用类型作为函数参数传递,是引用传递\n  + 在函数体内修改引用类型参数,会影响到函数外的值\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tnum := 10\n\tchange(\u0026num)\n\tfmt.Println(num) // 998\n}\nfunc change(num *int)  {\n\t*num = 998\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tarr := []int{1, 3, 5}\n\tchange(arr)\n\tfmt.Println(arr) // 1, 8, 5\n}\nfunc change(arr []int)  {\n\tarr[1] = 8\n}\n```\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tmp := map[string]string{\"name\":\"lnj\", \"age\":\"33\"}\n\tchange(mp)\n\tfmt.Println(mp[\"name\"]) // zs\n}\nfunc change(mp map[string]string)  {\n\tmp[\"name\"] = \"zs\"\n}\n```\n---\n## 匿名函数\n- 匿名函数也是函数的一种, 它的格式和普通函数一模一样,只不过没有名字而已\n  + 普通函数的函数名称是固定的, 匿名函数的函数名称是系统随机的\n- 匿名函数可以定义在函数外(全局匿名函数),也可以定义在函数内(局部匿名函数), Go语言中的普通函数不能嵌套定义, 但是可以通过匿名函数来实现函数的嵌套定义\n  + 全局匿名函数\n  ```go\n  package main\n  import \"fmt\"\n  // 方式一\n  var a = func()  {\n  \tfmt.Println(\"hello world1\")\n  }\n  // 方式二\n  var (\n  \t  b  = func()  {\n  \t    fmt.Println(\"hello world2\")\n  \t  }\n  )\n  func main() {\n  \t  a()\n\t  b()\n  }\n  ```\n- 一般情况下我们很少使用全局匿名函数, 大多数情况都是使用局部匿名函数, 匿名函数可以直接调用、保存到变量、作为参数或者返回值\n- 直接调用\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tfunc(s string){\n\t\tfmt.Println(s)\n\t}(\"hello lnj\")\n}\n```\n- 保存到变量\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\ta := func(s string) {\n\t\tfmt.Println(s)\n\t}\n\ta(\"hello lnj\")\n}\n```\n- 作为参数\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\ttest(func(s string) {\n\t\tfmt.Println(s)\n\t})\n}\nfunc test(f func(s string))  {\n\tf(\"hello lnj\")\n}\n```\n- 作为返回值\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\tres := test()\n\tres(10, 20)\n}\nfunc test() func(int, int) {\n\treturn func(a int, b int) {\n\t\tfmt.Println(a + b)\n\t}\n}\n```\n- 匿名函数应用场景\n  + 当某个函数只需要被调用一次时, 可以使用匿名函数\n  + 需要执行一些不确定的操作时,可以使用匿名函数\n```go\npackage main\nimport \"fmt\"\nfunc main() {\n\t// 项目经理的一天\n\twork(func() {\n\t\tfmt.Println(\"组织部门开会\")\n\t\tfmt.Println(\"给部门员工分配今天工作任务\")\n\t\tfmt.Println(\"检查部门员工昨天提交的代码\")\n\t\tfmt.Println(\"... ...\")\n\t})\n\t// 程序员的一天\n\twork(func() {\n\t\tfmt.Println(\"参加部门会议\")\n\t\tfmt.Println(\"修改测试提交的BUG\")\n\t\tfmt.Println(\"完成老大今天安排的任务\")\n\t\tfmt.Println(\"... ...\")\n\t})\n}\n// 假设我们需要编写一个函数,用于描述一个人每天上班都需要干嘛\n// 职场中的人每天上班前,上班后要做的事几乎都是相同的, 但是每天上班过程中要做的事确实不确定的\n// 所以此时我们可以使用匿名函数来解决, 让上班的人自己觉得自己每天上班需要干什么\nfunc work(custom func())  {\n\t// 上班前\n\tfmt.Println(\"起床\")\n\tfmt.Println(\"刷牙\")\n\tfmt.Println(\"洗脸\")\n\tfmt.Println(\"出门\")\n\tfmt.Println(\"上班打卡\")\n\tfmt.Println(\"开电脑\")\n\n\t// 上班中\n\tcustom()\n\n\t// 上班后\n\tfmt.Println(\"关电脑\")\n\tfmt.Println(\"下班打卡\")\n\tfmt.Println(\"出门\")\n\tfmt.Println(\"到家\")\n\tfmt.Println(\"吃饭\")\n\tfmt.Println(\"睡觉\")\n\n}\n```\n  + 为了提升代码的可读性,我们还可以将这个大函数拆解为独立的匿名函数\n```go\nfunc work(custom func())  {\n\t// 这种写法的好处是代码层次清晰,并且如果有一些变量\n\t// 只需要在上班前或上班后使用,还可以将这些变量隔离,不对外界造成污染\n\t// 上班前\n\tfunc(){\n\t\tfmt.Println(\"起床\")\n\t\tfmt.Println(\"刷牙\")\n\t\tfmt.Println(\"洗脸\")\n\t\tfmt.Println(\"出门\")\n\t\tfmt.Println(\"上班打卡\")\n\t\tfmt.Println(\"开电脑\")\n\t}()\n\n\t// 上班中\n\tcustom()\n\n\t// 上班后\n\tfunc(){\n\t\tfmt.Println(\"关电脑\")\n\t\tfmt.Println(\"下班打卡\")\n\t\tfmt.Println(\"出门\")\n\t\tfmt.Println(\"到家\")\n\t\tfmt.Println(\"吃饭\")\n\t\tfmt.Println(\"睡觉\")\n\t}()\n\n}\n```\n```go\nfunc work(custom func())  {\n\t// 前提条件是这个函数只在work函数中使用, 两者有较强的关联性, 否则建议定义为普通函数\n\tpre := func(){\n\t\tfmt.Println(\"起床\")\n\t\tfmt.Println(\"刷牙\")\n\t\tfmt.Println(\"洗脸\")\n\t\tfmt.Println(\"出门\")\n\t\tfmt.Println(\"上班打卡\")\n\t\tfmt.Println(\"开电脑\")\n\t}\n\tlatter := func(){\n\t\tfmt.Println(\"关电脑\")\n\t\tfmt.Println(\"下班打卡\")\n\t\tfmt.Println(\"出门\")\n\t\tfmt.Println(\"到家\")\n\t\tfmt.Println(\"吃饭\")\n\t\tfmt.Println(\"睡觉\")\n\t}\n\t\n\t// 上班前\n\tpre()\n\t// 上班中\n\tcustom()\n\t// 上班后\n\tlatter()\n}\n```\n---\n## 闭包\n- 闭包是一个特殊的匿名函数, 它是匿名函数和相关引用环境组成的一个整体\n  + 也就是说只要匿名函数中用到了外界的变量, 那么这个匿名函数就是一个闭包\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t  num := 10\n\t  a := func() {\n\t\t  num++ // 在闭包中用到了main函数中的num, 所以这个匿名函数就是一个闭包\n\t\t  fmt.Println(num) // 11\n\t  }\n\t  a()\n  }\n  ```\n  + 闭包中使用的变量和外界的变量是同一个变量, 所以可以闭包中可以修改外界变量\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t  num := 10\n\t  a := func() {\n\t\t  num = 6 // 在闭包中用到了main函数中的num, 所以这个匿名函数就是一个闭包\n\t\t  fmt.Println(num) // 6\n\t  }\n\t  fmt.Println(\"执行闭包前\", num) // 10\n\t  a()\n\t  fmt.Println(\"执行闭包后\", num) // 6\n  }\n  ```\n  + 只要闭包还在使用外界的变量, 那么外界的变量就会一直存在\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\tres := addUpper() // 执行addUpper函数,得到一个闭包\n\tfmt.Println(res()) // 2 \n\tfmt.Println(res()) // 3\n\tfmt.Println(res()) // 4\n\tfmt.Println(res()) // 5\n  }\n  func addUpper() func() int {\n\t  x := 1\n\t  return func() int {\n\t\t  x++ // 匿名函数中用到了addUpper中的x,所以这是一个闭包\n\t\t  return x\n\t  }\n  }\n  ```\n---\n## 延迟调用\n- Go语言中没有提供其它面向对象语言的析构函数, 但是Go语言提供了defer语句用于实现其它面向对象语言析构函数的功能\n- defer语句常用于`释放资源`、`解除锁定`以及`错误处理`等\n  + 例如C语言中我们申请了一块内存空间,那么不使用时我们就必须释放这块存储空间  \n  + 例如C语言中我们打开了一个文件,那么我们不使用时就要关闭这个文件\n  + 例如C语言中我们打开了一个数据库, 那么我们不使用时就要关闭这个数据库\n  + 这一类的操作在Go语言中都可以通过defer语句来完成\n- 无论你在什么地方注册defer语句,它都会在所属函数执行完毕之后才会执行, 并且如果注册了多个defer语句,那么它们会按照`后进先出`的原则执行\n  + 正是因为defer语句的这种特性, 所以在Go语言中关闭资源不用像C语言那样用完了再关闭, 我们完全可以打开的同时就关闭, 因为无论如何defer语句都会在所属函数执行完毕之后才会执行\n```go\n  package main\n  import \"fmt\"\n  func main() {\n\tdefer fmt.Println(\"我是第一个被注册的\") // 3\n\tfmt.Println(\"main函数中调用的Println\") // 1\n\tdefer fmt.Println(\"我是第二个被注册的\") // 2\n  }\n```\n---\n## init函数\n- golang里面有两个保留的函数：\n  + init函数（能够应用于所有的package）\n  + main函数（只能应用于package main）\n  + 这两个函数在定义时不能有任何的参数和返回值\n- go程序会自动调用init()和main()，所以你`不能`在任何地方调用这两个函数\n- package main必须包含一个main函数, 但是每个package中的init函数都是可选的\n- 一个package里面可以写任意多个init函数，但这无论是对于可读性还是以后的可维护性来说，我们都强烈建议用户在一个package中每个文件只写一个init函数\n- 单个包中代码执行顺序如下\n  +  ***`main包--\u003e常量--\u003e全局变量--\u003einit函数--\u003emain函数--\u003eExit`***\n```go\npackage main\nimport  \"fmt\"\nconst constValue  = 998 // 1\nvar gloalVarValue int = abc() // 2\nfunc init() { // 3\n\tfmt.Println(\"执行main包中main.go中init函数\")\n}\nfunc main() { // 4\n\tfmt.Println(\"执行main包中main.go中main函数\")\n}\nfunc abc() int {\n\tfmt.Println(\"执行main包中全局变量初始化\")\n\treturn 998\n}\n```\n- 多个包之间代码执行顺序如下\n![](https://img-blog.csdnimg.cn/img_convert/b17558afeb98b97b90f2c45e2faea7aa.png)\n\n- init函数的作用\n  + init函数用于处理当前文件的初始化操作, 在使用某个文件时的一些准备工作应该放到这里\n\n## 数组\n- 和C语言一样,Go语言中也有数组的概念, Go语言中的数组也是用于保存一组`相同类型`的数据\n- 和C语言一样,Go语言中的数组也分为`一维数组`和`多维数组`\n---\n## 一维数组\n- 格式:`var arr [元素个数]数据类型`\n  + 和C语言中数组不同, Go语言中数组定义之后就`有默认的初始值`\n  + 默认初始值就是保存数据类型的默认值(零值)\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t  // 1.定义一个数组\n\t  var arr [3]int\n\t  // 2.打印数组\n\t  fmt.Println(arr) //[0 0 0]\n  \n\t  // 1.定义一个数组\n\t  var arr [3]bool\n\t  // 2.打印数组\n\t  fmt.Println(arr) //[false false  false]\n  }\n  ```\n  + 和C语言一样,Go语言中的数组也提供了好几种初始化方式\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t\t// 1.定义的同时完全初始化\n\t\tvar arr1 [3]int = [3]int{1, 3, 5}\n\t\t// 2.打印数组\n\t\tfmt.Println(arr1) // [1 3 5]\n  \n\t\t// 1.定义的同时部分初始化\n\t\tvar arr4 [3]int = [3]int{8, 9}\n\t\t// 2.打印数组\n\t\tfmt.Println(arr4) // [8 9 0]\n  \n\t\t// 1.定义的同时指定元素初始化\n\t\tvar arr5 [3]int = [3]int{0:8, 2:9}\n\t\t// 2.打印数组\n\t\tfmt.Println(arr5) // [8 0 9]\n  \n\t\t// 1.先定义再逐个初始化\n\t\tvar arr3 [3]int\n\t\tarr3[0] = 1\n\t\tarr3[1] = 2\n\t\tarr3[2] = 3\n\t\t// 2.打印数组\n\t\tfmt.Println(arr3) // [1 2 3]\n  }\n  ```\n  + 和C语言中的数组不同,Go语言中数组除了可以定义的同时初始化以外,还`可以先定义再一次性初始化`\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t\t// 1.先定义再一次性初始化\n\t\tvar arr2 [3]int\n\t\tarr2 = [3]int{2, 4, 6}\n\t\t// 2.打印数组\n\t\tfmt.Println(arr2) // [2 4 6]\n  }\n  ```\n  + 和C语言一样,Go语言中如果定义数组的同时初始化,那么元素个数可以省略,但是必须使用`...`来替代\n    + ...会根据初始化元素个数自动确定数组长度\n  ```go\n  package main\n  import \"fmt\"\n  func main() {\n\t// 1.定义的同时完全初始化\n\tvar arr1  = [...]int{1, 3, 5}\n\t// 2.打印数组\n\tfmt.Println(arr1) // [1 3 5]\n  \n\t// 1.定义的同时指定元素初始化\n\tvar arr2  = [...]int{6:5}\n\t// 2.打印数组\n\tfmt.Println(arr2) // [0 0 0 0 0 0 5]\n  }\n  ```\n- Go�","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoderit666%2FGoGuide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcoderit666%2FGoGuide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcoderit666%2FGoGuide/lists"}