vault backup: 2026-06-07 12:14:39
This commit is contained in:
@@ -1,118 +1,115 @@
|
||||
---
|
||||
tags:
|
||||
- Go
|
||||
- golang
|
||||
- go基础语法
|
||||
- go程序结构
|
||||
tags: [go, golang, go基础语法, go程序结构]
|
||||
create time: 2026-06-07 15:00
|
||||
---
|
||||
|
||||
# Go语言结构
|
||||
# Go 语言代码结构
|
||||
|
||||
> 初看,可能觉得文档有点复杂,但是有空写写程序,一切都豁然开朗了
|
||||
## 概述
|
||||
|
||||
每一门语言都有各自特有的组织结构,同样,在学习go语言的基础语法之前,我们先来了解Go语言最简单程序的结构。
|
||||
下面以hello world为例来简单讲解一下Go语言的组织结构
|
||||
本文介绍 Go 程序的最基本组成:包声明、导入、`main` 函数和注释,并通过 Hello World 示例带你跑通第一个 Go 程序。
|
||||
|
||||
## 正文
|
||||
|
||||
### 一个 Go 程序由什么构成?
|
||||
|
||||
在深入语法细节之前,先思考一个问题:**"如果让你写一个能打印 'Hello, World!' 的 Go 程序,你知道它最少需要几行代码?"**
|
||||
|
||||
答案是 **5 行**(不算空行和注释)。下面这个最小可运行的 Go 程序展示了所有核心要素:
|
||||
|
||||
## Hello World 实例
|
||||
Go 语言的基础组成有以下几个部分:
|
||||
- 包声明
|
||||
- 引入包
|
||||
- 函数
|
||||
- 变量
|
||||
- 语句 & 表达式
|
||||
- 注释
|
||||
```go
|
||||
package main
|
||||
package main // 1. 包声明
|
||||
|
||||
import "fmt"
|
||||
import "fmt" // 2. 导入包
|
||||
|
||||
func main() {
|
||||
/* 这是我的第一个简单的程序 */
|
||||
fmt.Println("Hello, World!")
|
||||
func main() { // 3. main 函数(程序入口)
|
||||
fmt.Println("Hello, World!") // 4. 调用函数 + 自动换行
|
||||
}
|
||||
```
|
||||
1. 第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
|
||||
2. 下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
|
||||
3. 下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。
|
||||
4. 下一行 /*...*/ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
|
||||
5. 下一行 fmt.Println(...) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。
|
||||
使用 fmt.Print("hello, world\n") 可以得到相同的结果。
|
||||
Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。
|
||||
6. 当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )
|
||||
|
||||
## 执行Go程序
|
||||
让我们来看下如何编写Go代码并执行它。步骤如下:
|
||||
1. 打开编辑器如Goland(参考前面环境安装章节),创建一个project,名字叫做gostudy
|
||||
2. 将以上代码添加到编辑器中。
|
||||
3. 将以上代码保存为 hello.go
|
||||
4. 打开命令行,并进入程序文件保存的目录中。
|
||||
5. 输入命令 go run hello.go 并按回车执行代码。
|
||||
6. 如果操作正确你将在屏幕上看到 "Hello World!" 字样的输出
|
||||
> [!question] ❓ 思考
|
||||
> - `package main` 为什么必须是第一行非注释代码?
|
||||
> - 如果去掉 `import "fmt"`,程序还能编译通过吗?
|
||||
> - `main()` 函数可以带参数吗?如果不写 `()` 会怎样?
|
||||
|
||||
每一行的作用如下:
|
||||
|
||||
| 要素 | 说明 |
|
||||
|------|------|
|
||||
| `package main` | 声明包名。`main` 包表示这是一个**可执行程序**(而非库) |
|
||||
| `import "fmt"` | 导入标准库 `fmt`,提供格式化输入输出功能 |
|
||||
| `func main()` | 程序的**唯一入口**。可执行程序的 `main` 包必须有且仅有一个 `main` 函数 |
|
||||
| `/* ... */` / `//` | 多行注释 / 单行注释,编译时被忽略 |
|
||||
| `fmt.Println(...)` | 将内容输出到控制台,末尾自动添加 `\n` |
|
||||
|
||||
> [!tip] 💡 Print vs Println
|
||||
> `fmt.Print` 不会自动换行,`fmt.Println` 会在末尾追加 `\n`。两者都支持变量插值:`fmt.Println(arr, name, age)` 会用默认格式打印所有参数。
|
||||
|
||||
### 运行你的第一个程序
|
||||
|
||||
保存为 `hello.go` 后,在终端执行:
|
||||
|
||||
```shell
|
||||
$ go run hello.go
|
||||
Hello, World!
|
||||
```
|
||||
我们还可以使用 go build 命令来生成二进制文件:
|
||||
|
||||
`go run` 会**编译并立即执行**。如果想单独生成二进制文件,用 `go build`:
|
||||
|
||||
```shell
|
||||
$ go build hello.go
|
||||
$ ls
|
||||
hello hello.go
|
||||
$ ./hello
|
||||
$ go build hello.go # 生成 ./hello 二进制文件
|
||||
$ ./hello # 运行
|
||||
Hello, World!
|
||||
```
|
||||
|
||||
## 文件结构
|
||||
关于包,有以下几点需要注意:
|
||||
- 文件名与包名没有直接关系,不一定要将文件名与包名定成同一个。
|
||||
- 文件夹名与包名没有直接关系,并非需要一致。
|
||||
- 同一个文件夹下的文件只能有一个包名,否则编译报错。
|
||||
> [!note] 📝 go run vs go build
|
||||
> `go run` 适合快速调试;`go build` 生成独立二进制文件,方便部署和分发。
|
||||
|
||||
现在在hello目录下有一个`helloworld.go`文件,在myMath目录下有两个文件,分别为`myMath1.go`和`myMath2.go`
|
||||
### 包与文件的关系
|
||||
|
||||
`helloworld.go`的包名为`main`,不必和文件夹名hello一致。`myMath1.go`和`myMath2.go`的包名必须一致,都为`mathClass`
|
||||
这是新手最容易困惑的地方。记住三条规则:
|
||||
|
||||
```shell
|
||||
hello
|
||||
--helloworld.go
|
||||
> [!warning] ⚠️ 常见误区
|
||||
> 1. **文件名 ≠ 包名**:`helloworld.go` 可以是 `package main`
|
||||
> 2. **文件夹名 ≠ 包名**:目录叫 `myMath`,包名可以是 `mathclass`
|
||||
> 3. **同目录 = 同包**:同一个文件夹下的 `.go` 文件必须属于同一个包,否则编译报错
|
||||
|
||||
myMath
|
||||
--myMath1.go
|
||||
--myMath2.go
|
||||
```mermaid
|
||||
graph LR
|
||||
A["项目根目录"] --> B["hello/"]
|
||||
B --> B1["helloworld.go → package main"]
|
||||
A --> C["mymath/"]
|
||||
C --> C1["myMath1.go → package mathClass"]
|
||||
C --> C2["myMath2.go → package mathClass"]
|
||||
```
|
||||
|
||||
helloworld.go
|
||||
**关键点**:`myMath1.go` 和 `myMath2.go` 虽然在不同文件中,但包名都是 `mathClass`,它们共享这个包的命名空间。
|
||||
|
||||
### 导出与未导出标识符
|
||||
|
||||
Go 没有 `public` / `private` 关键字,而是用**大小写**控制可见性:
|
||||
|
||||
> [!note] 📝 导出规则
|
||||
> - **大写字母开头** → 导出(exported),其他包可以访问,类似 `public`
|
||||
> - **小写字母开头** → 未导出(unexported),仅限本包内部使用,类似 `private`
|
||||
|
||||
```go
|
||||
// helloworld.go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gostudy/mymath"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Hello World!")
|
||||
fmt.Println(mathClass.Add(1, 1))
|
||||
fmt.Println(mathClass.Sub(1, 1))
|
||||
}
|
||||
```
|
||||
|
||||
myMath1.go
|
||||
```go
|
||||
// myMath1.go
|
||||
package mathClass
|
||||
func Add(x,y int) int {
|
||||
|
||||
// Add 是大写开头 → 其他包可以调用
|
||||
func Add(x, y int) int {
|
||||
return x + y
|
||||
}
|
||||
```
|
||||
|
||||
myMath2.go
|
||||
```go
|
||||
// myMath2.go
|
||||
package mathClass
|
||||
func Sub(x,y int) int {
|
||||
// sub 是小写开头 → 只能在 mathClass 包内使用
|
||||
func sub(x, y int) int {
|
||||
return x - y
|
||||
}
|
||||
```
|
||||
|
||||
> [!warning] ⚠️ 注意
|
||||
> 如果你导入了一个包却没用它的任何导出符号,Go 编译器会直接报错:`imported and not used`。这强制你保持代码整洁。
|
||||
|
||||
## 关联笔记
|
||||
- [[hzh/GolangStar/Go语言基础/Go语言命名规范]]
|
||||
- [[hzh/GolangStar/Go语言基础/Go语言变量]]
|
||||
|
||||
Reference in New Issue
Block a user