C++ 基础遗漏知识点:bool、sizeof、auto、nullptr 与 static

容易被忽略但常出问题的基础语法点:bool 与 boolalpha、sizeof 的指针陷阱、auto 的 const 剥离、nullptr 与 static。 1. bool 类型与 boolalpha 核心规则 场景 结果 说明 bool b = true 输出 1 默认输出数字 bool b = false 输出 0 默认输出数字 bool b = -8 输出 1 非零即为 true bool b = 0 输出 0 零即为 false boolalpha 操纵符 使用 std::boolalpha 可以让 cout 输出 true/false 而不是 1/0。 #include <iostream> using namespace std; int main() { bool isTrue = true; // 默认输出(数字) cout << "默认: " << isTrue << endl; // 输出: 1 // 使用 boolalpha(文字) cout << boolalpha; cout << "boolalpha: " << isTrue << endl; // 输出: true // 非零值也为 true bool b = -8; cout << "-8 转 bool: " << b << endl; // 输出: true return 0; } 常见陷阱 // 陷阱1:非零即为 true bool b1 = 42; // true bool b2 = -1; // true bool b3 = 0.001; // true bool b4 = nullptr; // false (C++11) // 陷阱2:bool 与整数混合运算 int a = true + true; // a = 2(true 被提升为 1) int b = true * 10; // b = 10 2. sizeof 运算符 基本用法 #include <iostream> using namespace std; int main() { cout << "char: " << sizeof(char) << endl; // 1 cout << "short: " << sizeof(short) << endl; // 2 cout << "int: " << sizeof(int) << endl; // 4 cout << "long: " << sizeof(long) << endl; // 4 或 8 cout << "long long: " << sizeof(long long) << endl; // 8 cout << "float: " << sizeof(float) << endl; // 4 cout << "double: " << sizeof(double) << endl; // 8 // 数组 int arr[10]; cout << "int[10]: " << sizeof(arr) << endl; // 40 // 指针 int* p; cout << "int*: " << sizeof(p) << endl; // 4 或 8 return 0; } 动态数组的 sizeof 陷阱 int* arr = new int[10]; cout << sizeof(arr) << endl; // 输出 4 或 8(指针大小,不是数组大小!) // 正确获取动态数组大小:需要额外传参或用 vector 3. auto 关键字(C++11) 基本用法 auto x = 42; // int auto y = 3.14; // double auto z = "hello"; // const char* auto flag = true; // bool // 推荐用于复杂类型 #include <vector> #include <map> std::vector<int> v = {1, 2, 3}; auto it = v.begin(); // std::vector<int>::iterator std::map<std::string, int> m; auto pair = std::make_pair("key", 1); 注意事项 // auto 会忽略顶层 const const int a = 10; auto b = a; // b 是 int,不是 const int const auto c = a; // c 是 const int // 引用形式的 auto 才保留底层 const const int& ref = a; auto ref2 = ref; // ref2 是 int 拷贝——普通 auto 剥离引用,连同底层 const 一起丢 auto& ref3 = ref; // ref3 是 const int&——auto& 保留底层 const(不能改 *ref3) 4. nullptr(C++11) 与 NULL 的区别 // NULL 在 C++ 中可能是 0 或 (void*)0 void func(int); void func(char*); func(NULL); // 编译错误!歧义 func(nullptr); // OK,调用 func(char*) 最佳实践 // 初始化指针 int* p = nullptr; // 判断指针 if (p != nullptr) { // 使用 p } // 函数参数 void foo(int* ptr) { if (ptr == nullptr) { return; } } 5. const 与 constexpr(简记) 特性 const constexpr 初始化时机 运行时 编译时 可用于函数返回值 是 是 编译期常量 否 是 const int x = 10; // OK constexpr int y = 20; // OK,编译期常量 // constexpr 函数 constexpr int square(int n) { return n * n; } constexpr int val = square(5); // 编译期计算 const 与 constexpr 的完整对比、constexpr 函数、if constexpr、C++20 的 consteval/constinit,见另一篇《C++ namespace、const 与 constexpr 知识点总结》。 ...

2026 年 10 月 13 日

Map(哈希表)

Go 的内置关联容器,键值对存储,底层是哈希表 一、概述 map 是 Go 的内置数据结构,用于存储键值对。它等价于 C++ 的 std::unordered_map——基于哈希表实现,不是红黑树(Go 没有 std::map 那样的有序容器)。 核心特性: 引用类型,赋值和传参共享底层数组 键必须支持 == 比较(可哈希) 遍历顺序不确定(哈希表特性) 并发读写会触发 fatal(不是 data race,直接崩程序) 二、C++ 对照速查表 Go C++ 说明 map[K]V 类型声明 std::unordered_map<K,V> 类型声明 声明一个 map 类型 m := map[string]int{} auto m = unordered_map<string,int>(); 字面量初始化 m := make(map[string]int) auto m = unordered_map<string,int>(); make 初始化 m := make(map[string]int, 64) auto m = unordered_map<string,int>(64); 预分配容量 m["key"] = 42 m["key"] = 42; 插入/修改 delete(m, "key") m.erase("key"); 删除键 val, ok := m["key"] auto it = m.find("key"); it != m.end() 查找(双返回值) len(m) m.size() 获取大小 nil map nullptr 的 map 未初始化的 map for k, v := range m for (auto& [k, v] : m) 遍历 无 std::map<K,V> Go 没有有序 map 三、声明与初始化 3.1 字面量初始化(最常用) package main import "fmt" func main() { // 字面量初始化:声明 + 赋值一步到位 ages := map[string]int{ "Alice": 30, "Bob": 25, "Carol": 28, } fmt.Println(ages) // map[Alice:30 Bob:25 Carol:28] // 空 map empty := map[string]int{} fmt.Println(len(empty)) // 0 } 3.2 make 初始化 // make 创建空 map,可选预分配容量(提高性能) scores := make(map[string]int) // 零值容量 scores2 := make(map[string]int, 64) // 预分配 64 个槽位 // 适用场景:已知大致大小,预分配可减少扩容次数 users := make(map[int]string, 1000) 3.3 nil map(未初始化) var m map[string]int // nil map,值为 nil fmt.Println(m) // map[] fmt.Println(len(m)) // 0 — len 对 nil map 返回 0 3.4 C++ 对照 Go C++ ages := map[string]int{"Alice": 30} auto ages = unordered_map<string,int>{{"Alice", 30}}; m := make(map[string]int) auto m = unordered_map<string,int>(); m := make(map[string]int, 64) auto m = unordered_map<string,int>(64); var m map[string]int (nil) unordered_map<string,int>* m = nullptr; 四、增删改查 4.1 增 / 改(赋值) m := map[string]int{} // 不存在的 key → 插入 m["apple"] = 5 m["banana"] = 3 // 已存在的 key → 修改 m["apple"] = 10 fmt.Println(m) // map[apple:10 banana:3] 4.2 查(双返回值 ok 模式) m := map[string]int{"x": 1, "y": 2} // 双返回值:值 + 是否存在(ok 模式) val, ok := m["x"] fmt.Println(val, ok) // 1 true val2, ok2 := m["z"] fmt.Println(val2, ok2) // 0 false(零值,不存在) // C++ 对比: // auto it = m.find("x"); // if (it != m.end()) { /* 找到 */ } 为什么需要 ok 模式? 如果值类型是 int,零值是 0,无法区分"值为 0"和"不存在"。ok 布尔值解决了这个问题。 ...

2026 年 10 月 13 日

包与导入

Go 的"包"是组织代码的唯一单元。一个目录就是一个包,没有头文件,没有 .cpp/.h 分离。 理解包的规则,是读懂 Go 项目结构的第一步。 1. 核心概念速览 package 声明 每个 .go 文件的第一行必须是 package 声明,且同一目录下所有文件必须声明相同的包名。 // 文件: mathutil/add.go package mathutil // 同目录的 subtract.go 也必须是: // package mathutil func Add(a, b int) int { return a + b } package main — 程序入口 package main 是特殊约定,表示可执行程序的入口,不是库包。Go 没有独立的入口函数声明——入口就是 package main 里的 func main()。 package main import "fmt" func main() { fmt.Println("Hello, Go!") } C++ 对照: package main ≈ 包含 int main() 的那个编译单元。 2. C++ 对照速查表 Go 概念 C++ 对应 差异说明 package namespace + #include 一个 package 同时提供命名空间和导入功能,不需要头文件 import #include import 加载的是编译好的包,不是文本包含 首字母大写 = 导出 public / protected 基于命名规则,不是关键字;小写 = 包内可见 首字母小写 = 私有 private 同上,没有 private 关键字 一个目录一个包 一个模块/目录 同目录必须同 package,不可混装 init() 全局变量的构造函数 更可靠,保证在 main() 之前执行 import . "fmt" using namespace std; 同样不推荐,污染命名空间 循环导入 循环 #include 两者都是编译错误 internal/ C++ 无直接对应 类似 friend 的简化版,限制子包的可见性 go.mod 依赖管理 CMake / Conan / vcpkg Go 原生支持,无需第三方工具 3. 导入语法 单行导入 package main import "fmt" func main() { fmt.Println("单行导入") } 分组导入(推荐) package main import ( "fmt" "os" "strings" // 标准库放一组 ) func main() { fmt.Println(strings.Join(os.Args[1:], " ")) } C++ 对照: Go 的 import 会自动加载包内所有导出符号,不需要逐个 include 头文件。 ...

2026 年 10 月 13 日

变量、常量与类型

从 C++ 到 Go:变量声明、常量、基本类型与类型系统的核心差异 一、简述 Go 的类型系统比 C++ 更简单、更严格:没有隐式类型转换,没有函数重载,没有模板/泛型(Go 1.18 后有泛型但用法不同)。这看似"退步",实际上大幅降低了代码的心智负担——编译器替你做了大量约束。 对于 C++ 开发者,最需要适应的三个变化: 所有未使用的变量都会报错(unused variable) 类型转换必须显式(没有 int x = 3.14 这种事) string 是值类型(赋值和传参都会拷贝,不像 std::string 有引用计数) 二、C++ 对照速查表 概念 C++ Go 备注 变量声明 int x; var x int Go 声明与赋值分离 类型推断 auto x = 42; x := 42 := = 声明 + 推断 + 赋值 常量 const int X = 10; const X = 10 Go 常量不需要写类型 编译期常量 constexpr int X = 10; const X = 10 Go 的 const 全部是编译期常量 枚举 enum Color { RED, GREEN }; const ( RED = iota; GREEN ) Go 用 iota 实现枚举 隐式转换 double d = 42;(合法) 不允许 Go 必须显式:float64(42) 字符串 std::string s = "hi";(引用语义) s := "hi"(值类型) Go string 赋值是拷贝 字符 char c = 'A'; var c byte = 'A' 或 var c rune = 'A' Go 没有 char 类型 多变量声明 int a, b; var a, b int 或 a, b := 1, 2 零值 未初始化(UB/随机值) 自动零值 Go 所有变量声明后都有零值 类型别名 using MyInt = int; type MyInt int Go 叫"类型定义",不是别名 打印 std::cout << x; fmt.Println(x) Go 用 fmt 包格式化输出 三、变量声明 3.1 var 声明 package main import "fmt" func main() { // 基本语法:var 变量名 类型 var name string // 声明但不赋值,零值为 "" var age int // 零值为 0 var active bool // 零值为 false // 声明并赋值 var score int = 100 var lang string = "Go" // 类型推断:省略类型,编译器从右侧推断 var version = 2 // 推断为 int var pi = 3.14 // 推断为 float64(Go 默认浮点类型) var greeting = "hello" // 推断为 string fmt.Println(name, age, active, score, lang, version, pi, greeting) } C++ 对照: ...

2026 年 10 月 13 日

函数、多返回值与闭包

Go 函数的设计哲学:多返回值、闭包、defer——简洁而强大 一、函数定义 1.1 基本语法 // 格式:func 函数名(参数名 类型, ...) 返回类型 func add(a int, b int) int { return a + b } C++ 对照: 概念 C++ Go 函数定义 int add(int a, int b) func add(a int, b int) int 类型位置 类型在前 int a 类型在后 a int 多返回值 std::pair<T1,T2> / std::tuple 直接返回 (int, string) 函数声明 头文件 + 源文件分离 同文件(包内导出靠大小写) 1.2 参数类型简写 相邻同类型参数可以合并: // 两种写法等价 func calc(a int, b int, c int) int { ... } func calc(a, b, c int) int { ... } // 更简洁 1.3 没有默认参数,没有函数重载 Go 不支持默认参数和函数重载。需要类似效果时用函数选项模式或可变参数: // 用 Option 模式替代默认参数(后面会详细讲) type ServerOption struct { Addr string Port int } // 或者更简单的做法:拆成多个函数 func NewServer(addr string) *Server { ... } func NewServerWithPort(addr string, port int) *Server { ... } 二、多返回值 这是 Go 最核心的设计之一,C++ 没有直接对应。 ...

2026 年 10 月 13 日

环境与工具链

Go 的安装、配置与核心工具链速览 一、Go 的安装与配置 1.1 下载安装 平台 方式 Windows 下载 .msi 安装包:go.dev/dl macOS brew install go 或下载 .pkg Linux 下载 .tar.gz 解压至 /usr/local/go 安装完成后验证: go version # go version go1.24.x windows/amd64 1.2 环境变量 Go 通过几个关键环境变量控制行为: 变量 作用 常用值 GOROOT Go 安装目录(一般自动检测) /usr/local/go GOPATH 工作区目录(存放模块缓存和编译缓存) ~/go(默认) GOBIN go install 安装的可执行文件目录 $GOPATH/bin GOPROXY 模块代理地址(国内必改) https://goproxy.cn,direct GO111MODULE 模块模式开关(1.16+ 默认开启) on 国内加速配置(重要): # 永久生效(PowerShell) go env -w GOPROXY=https://goproxy.cn,direct # 或设置环境变量 set GOPROXY=https://goproxy.cn,direct 1.3 工作区结构(GOPATH) GOPATH/ ├── bin/ # go install 编译出的可执行文件 ├── pkg/ # 编译缓存 └── src/ # [已废弃] 早期放源码,现在用 Go Modules Go 1.16+ 不再需要把代码放在 GOPATH/src 下。使用 Go Modules 后,项目可以放在任意目录。 ...

2026 年 10 月 13 日

结构体与方法

Go 没有 class,结构体(struct) + 方法(method) 是面向对象的核心替代方案 一、C++ 对照速查表 C++ 概念 Go 对应 说明 struct struct 默认 public,字段首字母大写才导出 class struct + 方法 Go 没有 class 关键字 this 指针 Receiver(值/指针) 显式声明,名字自取 构造函数 NewXxx() 工厂函数 没有构造函数语法 析构函数 defer + Close() 没有析构函数语法 继承 (is-a) 嵌入 (has-a) 组合优于继承,没有继承 运算符重载 不支持 Go 刻意不提供 访问控制 private/protected/public 首字母大小写 大写=导出,小写=包内可见 模板/泛型 泛型(Go 1.18+) func F[T any](x T) T 虚函数 / 多态 接口隐式实现 鸭子类型,无需声明 implements 二、结构体定义与零值 package main import "fmt" // 定义结构体:type + struct 关键字 // C++ 对比:类似 struct MyPoint { double x; double y; }; 但没有方法体 type Point struct { X float64 // 首字母大写 = 导出字段(其他包可访问) Y float64 name string // 首字母小写 = 未导出(仅本包内可访问) } func main() { // 零值:所有字段自动初始化为各自类型的零值 // float64 -> 0.0, int -> 0, string -> "", bool -> false, 指针/切片/map -> nil var p Point fmt.Printf("零值: %+v\n", p) // {X:0 Y:0 name:} } 三、结构体初始化 package main import "fmt" type User struct { Name string Age int Email string } func main() { // 方式1: 字面量(最常用,推荐) u1 := User{Name: "张三", Age: 25, Email: "zhangsan@example.com"} fmt.Println(u1) // 方式2: 按字段名初始化(部分字段,其余为零值) u2 := User{Name: "李四"} // Age=0, Email="" fmt.Println(u2) // 方式3: 按顺序初始化(不推荐!字段顺序变化会导致 bug) u3 := User{"王五", 30, "wangwu@example.com"} // 需要 import fmt 不会用到,编译器不检查字段名 fmt.Println(u3) // 方式4: new 返回指针,所有字段为零值 u4 := new(User) // *User 类型 u4.Name = "赵六" // Go 允许对指针直接访问字段,自动解引用 fmt.Println(u4) // 方式5: 声明后再赋值 var u5 User u5.Name = "钱七" u5.Age = 28 fmt.Println(u5) } NewXxx 工厂函数(替代构造函数) // Go 没有构造函数,惯用 NewXxx 工厂函数 func NewUser(name, email string, age int) *User { return &User{ Name: name, Age: age, Email: email, } } // 使用 // u := NewUser("张三", "zhangsan@example.com", 25) // 优点:返回指针,避免大结构体拷贝;可以做参数校验 四、结构体比较 package main import "fmt" type Point struct { X int Y int } func main() { a := Point{1, 2} b := Point{1, 2} c := Point{3, 4} // 同类型结构体可以用 == 比较(逐字段比较) fmt.Println(a == b) // true fmt.Println(a == c) // false // 包含不可比较字段(slice, map)的结构体不能用 == // type Bad struct { Data []int } // var x, y Bad // x == y // 编译错误!slice 不能比较 } 五、方法定义 package main import "fmt" type Rectangle struct { Width float64 Height float64 } // 值 Receiver:操作的是副本,不会修改原值 // C++ 对比:相当于 void method() const(不能修改成员) func (r Rectangle) Area() float64 { return r.Width * r.Height } // 指针 Receiver:操作的是原值,可以修改字段 // C++ 对比:相当于非 const 的 this 指针方法 func (r *Rectangle) SetWidth(w float64) { r.Width = w // 直接修改原结构体 } // String 方法实现 fmt.Stringer 接口 func (r Rectangle) String() string { return fmt.Sprintf("Rectangle(%.1f x %.1f)", r.Width, r.Height) } func main() { rect := Rectangle{Width: 3, Height: 4} fmt.Println(rect.Area()) // 12 rect.SetWidth(5) fmt.Println(rect) // Rectangle(5.0 x 4.0) // Go 会自动处理取地址/解引用: rectPtr := &Rectangle{Width: 1, Height: 2} rectPtr.Area() // 值方法:自动解引用 (*rectPtr).Area() (*rectPtr).Area() // 手动解引用,效果相同 rect2 := Rectangle{Width: 1, Height: 2} rect2.SetWidth(3) // 指针方法:自动取地址 (&rect2).SetWidth(3) } 六、值 Receiver vs 指针 Receiver 选择原则 场景 推荐 Receiver 原因 只读取,不修改字段 值 Receiver 避免不必要的拷贝开销(小结构体) 需要修改字段 指针 Receiver 必须修改原值 结构体较大 指针 Receiver 避免每次调用都拷贝 实现接口 同一类型保持一致 混用会导致方法集问题(见下文) slice/map 字段需修改 指针 Receiver 虽然 slice 底层是引用,但语义更清晰 关键区别 type Counter struct { Value int } // 值 Receiver:操作的是拷贝 func (c Counter) IncByValue() { c.Value++ // 修改的是副本,原值不变! } // 指针 Receiver:操作的是原值 func (c *Counter) IncByPtr() { c.Value++ // 直接修改原值 } func main() { c := Counter{Value: 0} c.IncByValue() fmt.Println(c.Value) // 0 —— 没变! c.IncByPtr() fmt.Println(c.Value) // 1 —— 变了 } 七、结构体嵌入(Embedding)—— 组合优于继承 package main import "fmt" // 基础结构体 type Person struct { Name string Age int } // 嵌入 Person —— 注意:没有字段名! // 这不是 "Person 字段",而是把 Person 的字段和方法"提升"到 Employee 中 type Employee struct { Person // 嵌入(embedding),不是字段 Company string Salary float64 } // Person 的方法也会被提升 func (p Person) Greet() string { return fmt.Sprintf("你好,我是 %s", p.Name) } // Employee 自己的方法 func (e Employee) Info() string { return fmt.Sprintf("%s 在 %s 工作,薪资 %.0f", e.Name, e.Company, e.Salary) } func main() { // 初始化:嵌入结构体可以逐层初始化 emp := Employee{ Person: Person{Name: "张三", Age: 30}, Company: "字节跳动", Salary: 30000, } // 字段提升:直接访问嵌入结构体的字段 fmt.Println(emp.Name) // "张三" —— Person 的字段被提升 fmt.Println(emp.Age) // 30 fmt.Println(emp.Company) // "字节跳动" // 方法也被提升 fmt.Println(emp.Greet()) // "你好,我是 张三" // 也可以显式指定 fmt.Println(emp.Person.Name) // "张三" } 嵌入接口 // 结构体可以嵌入接口 type Logger interface { Log(msg string) } type Server struct { Logger // 嵌入接口:Server 隐式实现了 Logger Addr string } // 使用时 Server 必须给 Logger 赋值,否则调用 Log 会 panic // srv := Server{Logger: someLoggerImpl, Addr: ":8080"} C++ 对比:嵌入 vs 继承 ...

2026 年 10 月 13 日

流程控制: if, for, switch

Go 的流程控制关键字精简而强大——没有 while、没有 do、switch 默认不穿透 一、C++ 对照速查表 概念 C++ Go 关键区别 条件判断 if (cond) {} if cond {} Go 不需要括号 初始化语句 if (int x = f(); x > 0) (C++17) if x := f(); x > 0 语法几乎等价 基本循环 for (init; cond; post) {} for init; cond; post {} Go 统一用 for while 循环 while (cond) {} for cond {} Go 无 while 关键字 do-while do {} while (cond); for { ...; if !cond { break } } Go 无 do-while 无限循环 for (;;) {} for {} Go 写法更简洁 范围遍历 for (auto& x : vec) {} for i, v := range slice {} range 可解构 key/value switch 穿透 默认穿透(需 break) 默认不穿透(需 fallthrough) 完全相反 switch 无条件 switch { case ...: } switch { case ...: } 两者相同 类型判断 dynamic_cast/typeid switch v := x.(type) Go 的 type switch 更优雅 延迟执行 RAII / 析构函数 defer defer 是函数级,非作用域级 goto goto label; goto label; 两者几乎相同 标号跳转 break label(跳出循环) break label(跳出标签所在的 for) Go 标签只对 for 有效 二、if 语句 2.1 基本用法——不需要括号 // Go: 条件表达式不需要括号 if x > 0 { fmt.Println("正数") } else if x == 0 { fmt.Println("零") } else { fmt.Println("负数") } C++ 对照:if (x > 0) { ... } — Go 省略了括号,且 { 不能另起一行(编译器自动插入分号机制)。 ...

2026 年 10 月 13 日

数组与切片

Go 的序列容器:固定长度的数组与动态的切片——切片是 Go 中最常用的数据结构 一、C++ 对照速查表 C++ Go 关键差异 std::array<T, N> [N]T Go 数组长度是类型的一部分,[3]int 和 [4]int 是不同类型 std::vector<T> []T(切片) 切片是底层数组的引用/视图,不是独立容器 vector::push_back append append 返回新切片,必须重新赋值 vector::size() / capacity() len(s) / cap(s) 完全对应 vector<T> 赋值 切片赋值 C++ vector 赋值是深拷贝;Go 切片赋值只是拷贝引用 std::vector<T> (值类型) 切片是引用语义 切片共享底层数组,修改会相互影响 数组 == 比较 [N]T 支持 == 切片不能用 == 比较(只能和 nil 比) 核心区别:C++ 的 std::vector 是一个拥有数据的容器;Go 的切片只是一个"窗口"——包含指针、长度、容量三个字段,指向某个底层数组。 二、数组 [N]T 2.1 基本语法 // 声明:长度在类型中,不可省略 var a [5]int // 零值:[0 0 0 0 0] b := [3]string{"hello", "go", "world"} // 字面量初始化 c := [...]int{1, 2, 3} // 编译器自动推断长度 = 3 // 长度是类型的一部分:[3]int 和 [4]int 是不同类型! // var x [3]int = [4]int{...} // 编译错误 2.2 数组是值类型(与 C++ std::array 一致) func main() { a := [3]int{1, 2, 3} b := a // 值拷贝,b 是 a 的副本 b[0] = 999 // 修改 b 不影响 a fmt.Println(a) // [1 2 3] fmt.Println(b) // [999 2 3] } 2.3 数组支持 == 比较 a := [3]int{1, 2, 3} b := [3]int{1, 2, 3} fmt.Println(a == b) // true——长度相同、元素逐个相等即可比较 实际使用:Go 中几乎不用数组,绝大多数场景用切片。数组主要用于固定长度的场景(如 RGB 颜色 [3]byte、矩阵维度等)。 ...

2026 年 10 月 13 日

指针 (Pointers)

Go 保留了指针的核心能力(间接寻址、传地址),但砍掉了指针算术和引用类型 &T,换来内存安全和 GC 兼容。 C++ 对照速查表 概念 C++ Go 差异说明 声明指针 int* p / int *p var p *int Go 把 * 放在类型前 取地址 &x &x 完全相同 解引用 *p *p 完全相同 指针算术 p++, p + n 不支持 Go 禁止,用切片代替 空指针 NULL / nullptr nil Go 没有 NULL 关键字 引用类型 int& ref = x 不存在 Go 没有引用,统一用指针 new new int (调用构造) new(int) → *int Go 的 new 只分配零值内存,不调用构造函数 struct 访问 ptr->field ptr.field Go 编译器自动解引用,不需要 -> make 无对应物 make([]int, 5) 仅用于 slice/map/channel,返回初始化后的值(不是指针) 1. 指针基础:&、*、*T package main import "fmt" func main() { x := 42 var p *int = &x // p 是指向 x 的指针,*int 表示"指向 int 的指针" fmt.Println("x 的值:", *p) // 解引用:通过指针读取值 → 42 fmt.Println("x 的地址:", p) // 打印地址 → 0xc0000b4008(类似) *p = 100 // 通过指针修改原变量 fmt.Println("修改后 x:", x) // → 100 // 零值:指针的零值是 nil var q *int fmt.Println("q == nil:", q == nil) // → true } 要点:*int 的 * 是类型修饰符(“int 的指针”),*p 的 * 是操作符(“解引用”)。 ...

2026 年 10 月 13 日