Skip to content

variables.go

文件信息

  • 📄 原文件:01_variables.go
  • 🔤 语言:go

完整代码

go
package main

import "fmt"

var Name string = "Go语言"

func main() {
	// ============================================================
	//                      变量声明
	// ============================================================
	// Go 是静态类型语言,变量必须有明确的类型
	// 声明变量有三种主要方式

	fmt.Println("=== 变量声明 ===")

	// ----------------------------------------------------------
	// 方式1: var 声明(显式指定类型)
	// ----------------------------------------------------------
	// 语法: var 变量名 类型 = 值
	// 【适用场景】需要明确类型时,或声明包级别变量时
	var age int = 15
	fmt.Println("var 显式类型:", Name, age)

	// ----------------------------------------------------------
	// 方式2: var 声明(类型推断)
	// ----------------------------------------------------------
	// 语法: var 变量名 = 值
	// 编译器根据右边的值自动推断类型
	// 【注意】整数默认推断为 int,浮点数默认推断为 float64
	var version = "1.21" // 推断为 string
	var count = 100      // 推断为 int(不是 int32 或 int64)
	var price = 9.99     // 推断为 float64(不是 float32)
	fmt.Println("var 类型推断:", version, count, price)

	// ----------------------------------------------------------
	// 方式3: 短声明 :=(最常用)
	// ----------------------------------------------------------
	// 语法: 变量名 := 值
	// 【重要】只能在函数内部使用,不能用于包级别变量
	// 【重要】:= 是声明+赋值,= 是纯赋值
	language := "Golang"
	score := 95.5
	fmt.Println("短声明 :=:", language, score)

	// 【易错点】:= 左边必须至少有一个新变量
	// language := "Python"  // 错误!language 已存在
	// language = "Python"   // 正确,这是赋值

	// 【技巧】:= 可以部分重新声明
	language, newVar := "Python", "new" // language 赋值,newVar 新声明
	fmt.Println("部分重新声明:", language, newVar)

	// ----------------------------------------------------------
	// 方式4: 多变量声明
	// ----------------------------------------------------------
	// 一行声明多个变量,用逗号分隔

	// 同类型多变量
	var a, b, c int = 1, 2, 3
	fmt.Println("同类型多变量:", a, b, c)

	// 不同类型多变量(短声明)
	x, y, z := 10, "hello", true
	fmt.Println("不同类型多变量:", x, y, z)

	// 【实用】交换变量值
	a, b = b, a
	fmt.Println("交换后 a, b:", a, b)

	// 【实用】函数多返回值
	// value, err := someFunction()

	// ----------------------------------------------------------
	// 方式5: 声明块(分组声明)
	// ----------------------------------------------------------
	// 用 () 将多个声明组织在一起,代码更整洁
	// 【适用场景】包级别变量、相关变量分组
	var (
		width  = 100
		height = 200
		title  = "窗口"
	)
	fmt.Println("声明块:", width, height, title)

	// ============================================================
	//                      基本数据类型
	// ============================================================
	fmt.Println("\n=== 基本类型 ===")

	// ----------------------------------------------------------
	// 整型(有符号)
	// ----------------------------------------------------------
	// int8:   -128 到 127
	// int16:  -32768 到 32767
	// int32:  -21亿 到 21亿
	// int64:  -922亿亿 到 922亿亿
	// int:    根据平台,32位系统是int32,64位系统是int64
	//
	// 【建议】一般情况用 int,除非有特殊需求(如节省内存、与C交互)

	var i8 int8 = 127          // 最大值 127
	var i16 int16 = 32767      // 最大值 32767
	var i32 int32 = 2147483647 // 约 21 亿
	var i64 int64 = 9223372036854775807
	var i int = 42 // 【推荐】通用整型

	fmt.Printf("int8=%d, int16=%d, int32=%d\n", i8, i16, i32)
	fmt.Printf("int64=%d, int=%d\n", i64, i)

	// 【注意】不同整型之间不能直接运算,需要显式转换
	// result := i32 + i64        // 错误!
	// result := int64(i32) + i64 // 正确

	// ----------------------------------------------------------
	// 整型(无符号)
	// ----------------------------------------------------------
	// uint8:  0 到 255(别名 byte)
	// uint16: 0 到 65535
	// uint32: 0 到 42亿
	// uint64: 0 到 1844亿亿
	// uint:   根据平台

	var ui uint = 42
	var ui8 uint8 = 255
	fmt.Printf("uint=%d, uint8=%d\n", ui, ui8)

	// ----------------------------------------------------------
	// 浮点型
	// ----------------------------------------------------------
	// float32: 单精度,约 7 位有效数字
	// float64: 双精度,约 15 位有效数字
	//
	// 【建议】默认使用 float64,精度更高
	// 【注意】浮点数有精度问题,不要用 == 比较

	var f32 float32 = 3.14
	var f64 float64 = 3.141592653589793

	fmt.Printf("float32: %.7f\n", f32)
	fmt.Printf("float64: %.15f\n", f64)

	// 【警告】浮点数精度问题
	fmt.Printf("0.1 + 0.2 = %.20f (不等于 0.3)\n", 0.1+0.2)

	// ----------------------------------------------------------
	// 布尔型
	// ----------------------------------------------------------
	// 只有 true 和 false 两个值
	// 【注意】不能用 0 和 1 代替,不能隐式转换
	// 【注意】不能用 !0 或 !!x 这种写法

	var isTrue bool = true
	var isFalse bool = false
	fmt.Printf("bool: %t, %t\n", isTrue, isFalse)

	// if 1 { }  // 错误!Go 不允许

	// ----------------------------------------------------------
	// 字符串
	// ----------------------------------------------------------
	// 字符串是不可变的字节序列
	// 使用双引号 "" 或反引号 ``(原始字符串)

	var str1 string = "Hello, 世界"       // 普通字符串,支持转义
	var str2 string = `C:\path\to\file` // 原始字符串,不转义
	var str3 string = `多行
字符串`

	fmt.Println("普通字符串:", str1)
	fmt.Println("原始字符串:", str2)
	fmt.Println("多行字符串:", str3)

	// 【重要】字符串操作
	fmt.Println("长度(字节数):", len(str1))     // 13(中文3字节)
	fmt.Println("字符数:", len([]rune(str1))) // 9
	fmt.Println("拼接:", "Hello"+" "+"World")
	fmt.Println("索引(字节):", str1[0]) // 72 ('H')

	// 【注意】字符串不可变
	// str1[0] = 'h'  // 错误!不能修改

	// ----------------------------------------------------------
	// byte 和 rune
	// ----------------------------------------------------------
	// byte: uint8 的别名,表示一个字节(ASCII字符)
	// rune: int32 的别名,表示一个 Unicode 码点(任意字符)
	//
	// 【重要】单引号 '' 表示字符,双引号 "" 表示字符串

	var b1 byte = 'A'   // ASCII 字符
	var b2 byte = 65    // 同上,'A' 的 ASCII 值
	var r1 rune = ''   // Unicode 字符
	var r2 rune = 20013 // 同上,'中' 的 Unicode 码点

	fmt.Printf("byte: %c = %d\n", b1, b1)
	fmt.Printf("byte: %c = %d\n", b2, b2)
	fmt.Printf("rune: %c = %d (U+%04X)\n", r1, r1, r1)
	fmt.Printf("rune: %c = %d\n", r2, r2)

	// 【技巧】遍历字符串的正确方式
	fmt.Print("遍历字符: ")
	for _, char := range "Go语言" {
		fmt.Printf("%c ", char)
	}
	fmt.Println()

	// ============================================================
	//                      常量
	// ============================================================
	fmt.Println("\n=== 常量 ===")

	// ----------------------------------------------------------
	// 常量声明
	// ----------------------------------------------------------
	// 使用 const 关键字,值在编译时确定,不能修改
	// 【注意】常量不能用 := 声明
	// 【注意】常量可以是无类型的,有更大的灵活性

	const pi = 3.14159        // 无类型常量
	const e float64 = 2.71828 // 有类型常量

	fmt.Println("pi:", pi)
	fmt.Println("e:", e)

	// 常量块
	const (
		StatusOK       = 200
		StatusNotFound = 404
		StatusError    = 500
	)
	fmt.Println("HTTP状态码:", StatusOK, StatusNotFound, StatusError)

	// ----------------------------------------------------------
	// iota 枚举生成器
	// ----------------------------------------------------------
	// iota 在 const 块中从 0 开始,每行自动 +1
	// 【技巧】用于创建枚举值

	const (
		Sunday    = iota // 0
		Monday           // 1
		Tuesday          // 2
		Wednesday        // 3
		Thursday         // 4
		Friday           // 5
		Saturday         // 6
	)
	fmt.Println("星期:", Sunday, Monday, Tuesday)

	// 【技巧】iota 表达式
	const (
		_  = iota             // 0,用 _ 丢弃
		KB = 1 << (10 * iota) // 1 << 10 = 1024
		MB                    // 1 << 20 = 1048576
		GB                    // 1 << 30
		TB                    // 1 << 40
	)
	fmt.Printf("KB=%d, MB=%d, GB=%d\n", KB, MB, GB)

	// 【技巧】跳过值
	const (
		A = iota // 0
		B        // 1
		_        // 2(跳过)
		D        // 3
	)
	fmt.Println("跳过值:", A, B, D)

	// ============================================================
	//                      零值(Zero Value)
	// ============================================================
	fmt.Println("\n=== 零值 ===")

	// Go 的变量声明后如果没有初始化,会有默认的零值
	// 这是 Go 的重要特性,避免了未初始化变量的问题

	var zeroInt int         // 0
	var zeroFloat float64   // 0.0
	var zeroBool bool       // false
	var zeroString string   // ""(空字符串)
	var zeroPointer *int    // nil
	var zeroSlice []int     // nil
	var zeroMap map[int]int // nil
	var zeroFunc func()     // nil

	fmt.Printf("int 零值: %d\n", zeroInt)
	fmt.Printf("float64 零值: %g\n", zeroFloat)
	fmt.Printf("bool 零值: %t\n", zeroBool)
	fmt.Printf("string 零值: %q\n", zeroString)
	fmt.Printf("指针零值: %v\n", zeroPointer)
	fmt.Printf("切片零值: %v (nil=%t)\n", zeroSlice, zeroSlice == nil)
	fmt.Printf("map零值: %v (nil=%t)\n", zeroMap, zeroMap == nil)
	fmt.Printf("函数零值: %v\n", zeroFunc)

	// 【重要】零值表
	// 数值类型:  0
	// 布尔类型:  false
	// 字符串:    ""
	// 指针/切片/map/channel/接口/函数: nil

	// ============================================================
	//                      类型转换
	// ============================================================
	fmt.Println("\n=== 类型转换 ===")

	// Go 没有隐式类型转换,必须显式转换
	// 语法: 目标类型(值)

	var intVal int = 42
	var floatVal float64 = float64(intVal) // int -> float64
	var intVal2 int = int(floatVal)        // float64 -> int(截断小数)

	fmt.Printf("int->float64: %d -> %f\n", intVal, floatVal)
	fmt.Printf("float64->int: %f -> %d\n", floatVal, intVal2)

	// 【注意】浮点数转整数会截断小数,不是四舍五入
	var pi64 float64 = 3.9
	fmt.Printf("截断示例: %.1f -> %d\n", pi64, int(pi64)) // 3

	// 字符串转换
	var num int = 65
	var char string = string(num) // 转为 Unicode 字符 "A"
	fmt.Printf("int->string: %d -> %s\n", num, char)

	// 【注意】数字和字符串互转要用 strconv 包
	// strconv.Itoa(42)      -> "42"
	// strconv.Atoi("42")    -> 42, error

	// ============================================================
	//                      类型别名与自定义类型
	// ============================================================
	fmt.Println("\n=== 类型别名与自定义类型 ===")

	// 类型别名(Type Alias): type 新名 = 原类型
	// 两者完全相同,可以互换使用
	type MyByte = byte
	var mb MyByte = 'X'
	fmt.Printf("类型别名: %c (类型完全相同)\n", mb)

	// 自定义类型(Type Definition): type 新类型 原类型
	// 创建全新类型,不能直接与原类型互换
	type Age int
	var myAge Age = 25
	// var otherAge int = myAge  // 错误!类型不同
	var otherAge int = int(myAge) // 需要显式转换
	fmt.Printf("自定义类型: %d (需要转换: %d)\n", myAge, otherAge)

	main02()
	main03()
	main04()
}

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布