404 Not Found

404 Not Found


nginx

变量与数据类型

想象你在厨房做饭:变量就是贴了标签的储物盒,标签是名字,盒子里装的是数据;数据类型就是盒子的形状——圆盒子装汤、方盒子装菜,不能搞混。Go 语言对"盒子"管理很严格:你必须先声明要什么盒子(类型),装什么内容(赋值),没装东西的盒子也有默认的东西(零值)。


1. 核心概念

1.1 变量声明:var vs :=

Go 提供两种声明变量的方式:

方式 语法 适用场景 作用域
var var name int 包级变量、需要显式类型 函数内/外均可
短变量 := name := 10 局部变量、类型推断 仅函数内

关键区别:

1.2 基本数据类型

类别 类型 说明 示例
整数 int, int8, int16, int32, int64 有符号整数 var a int = 42
无符号整数 uint, uint8, uint16, uint32, uint64 非负整数 var b uint = 100
浮点数 float32, float64 小数(默认 float64 var c float64 = 3.14
布尔 bool true / false var d bool = true
字符串 string 不可变的字节序列 var e string = "hello"
字节 byte uint8 的别名,表示一个字节 var f byte = 'A'
Unicode字符 rune int32 的别名,表示一个 Unicode 码点 var g rune = '中'

平台相关类型: intuint 在 32 位系统上是 32 位,在 64 位系统上是 64 位。日常开发中,大多数情况用 int 就够了。

1.3 零值机制

Go 中声明变量但不赋值时,变量会被自动初始化为零值,而不是 null 或未定义:

类型 零值
整数 (int, uint 等) 0
浮点数 (float32, float64) 0.0
布尔 (bool) false
字符串 (string) ""(空字符串)
指针、切片、map、channel、函数、接口 nil

这是 Go 的安全机制——你永远不会拿到一个"没有值"的变量,避免了很多空指针错误。

1.4 常量与 iota

1.5 类型转换

Go 不支持隐式类型转换,不同类型之间必须显式转换:

GO
var i int = 42
var f float64 = float64(i) // int → float64
var u uint = uint(f)        // float64 → uint

1.6 fmt 包格式化输出

fmt 是 Go 最常用的格式化包,主要函数:

函数 作用 示例
fmt.Println 打印并换行 fmt.Println("hello")
fmt.Printf 格式化打印(不换行) fmt.Printf("值=%d", 42)
fmt.Sprintf 格式化为字符串(不打印) s := fmt.Sprintf("%d", 42)

常用格式占位符:%d(整数)、%f(浮点)、%s(字符串)、%t(布尔)、%T(类型)、%v(通用值)、%q(带引号的字符串)。


2. 基本语法/用法

变量声明

GO
// 方式一:var 声明(可在函数内、外)
var name string       // 声明,零值 ""
var age int = 25      // 声明并赋值
var score = 98.5      // 类型推断为 float64

// 方式二:短变量 :=(仅函数内)
city := "北京"         // 声明并赋值,类型推断为 string
count := 100          // 类型推断为 int

// 批量声明
var (
    width  int     = 10
    height int     = 20
    area   float64
)
💡 提示: 如果你用 var 声明了变量但没使用它,Go 编译器会报错 declared and not used。这是 Go 的设计哲学:不允许无用代码存在。短变量 := 同理。

常量声明

GO
// 单个常量
const Pi = 3.14159
const Language string = "Go"

// 批量常量 + iota 枚举
const (
    Sunday    = iota // 0
    Monday           // 1
    Tuesday          // 2
    Wednesday        // 3
    Thursday         // 4
    Friday           // 5
    Saturday         // 6
)
💡 提示: iota 只在 const 块内有效,每新增一行自动 +1。如果某行用 _ 跳过,iota 仍然会自增。

类型转换

GO
var i int = 100
var f float64 = float64(i)  // 显式转换:int → float64
var s string = string(rune(i)) // int → rune → string(注意:不是把数字变字符串)
// 把数字转成字符串的正确方式:
s2 := fmt.Sprintf("%d", i)  // "100"
💡 提示: string(65) 得到的是字符 "A"(ASCII 码),不是 "65"。要把数字变成十进制字符串,请用 fmt.Sprintfstrconv.Itoa


3. 示例代码

示例 1:基础用法(难度⭐)

场景: 声明不同类型的变量,打印它们的值和类型。

GO
package main

import "fmt"

func main() {
    // ========== 变量声明 ==========

    // 1. var 声明 + 显式类型
    var name string = "张三"
    var age int = 28

    // 2. var 声明 + 类型推断(编译器根据值自动判断类型)
    salary := 15000.5 // 推断为 float64

    // 3. 短变量声明(最常用的方式)
    isEmployed := true
    city := "上海"

    // 4. 零值演示——声明但不赋值
    var defaultInt int        // 零值:0
    var defaultFloat float64  // 零值:0
    var defaultBool bool      // 零值:false
    var defaultString string  // 零值:""

    // ========== 打印输出 ==========
    fmt.Println("=== 个人信息 ===")
    fmt.Printf("姓名: %s (类型: %T)\n", name, name)
    fmt.Printf("年龄: %d (类型: %T)\n", age, age)
    fmt.Printf("工资: %.1f (类型: %T)\n", salary, salary)
    fmt.Printf("在职: %t (类型: %T)\n", isEmployed, isEmployed)
    fmt.Printf("城市: %s (类型: %T)\n", city, city)

    fmt.Println("\n=== 零值演示 ===")
    fmt.Printf("int 零值:    %d\n", defaultInt)
    fmt.Printf("float64 零值: %v\n", defaultFloat)
    fmt.Printf("bool 零值:   %t\n", defaultBool)
    fmt.Printf("string 零值: %q\n", defaultString) // %q 显示带引号的字符串
}
▶ 试一试

输出:

TEXT
=== 个人信息 ===
姓名: 张三 (类型: string)
年龄: 28 (类型: int)
工资: 15000.5 (类型: float64)
在职: true (类型: bool)
城市: 上海 (类型: string)

=== 零值演示 ===
int 零值:    0
float64 零值: 0
bool 零值:   false
string 零值: ""

要点:


示例 2:进阶用法(难度⭐⭐)

场景: 使用常量、iota 定义枚举,以及类型转换的实际应用。

GO
package main

import "fmt"

// ========== iota 枚举:定义角色权限 ==========

type Role int // 自定义类型,底层是 int

const (
    Guest    Role = iota // 0:访客
    Member               // 1:会员
    Admin                // 2:管理员
    SuperAdmin           // 3:超级管理员
)

// 给 Role 实现 String() 方法,方便打印
func (r Role) String() string {
    names := [...]string{"访客", "会员", "管理员", "超级管理员"}
    if int(r) < len(names) {
        return names[r]
    }
    return "未知角色"
}

// ========== iota 高级用法:位运算权限标志 ==========

type Permission int

const (
    Read    Permission = 1 << iota // 1 (001)
    Write                          // 2 (010)
    Execute                        // 4 (100)
)

// ========== 类型转换实战 ==========

func main() {
    // --- iota 枚举使用 ---
    var userRole Role = Admin
    fmt.Printf("用户角色: %s (值: %d)\n", userRole, userRole)

    // --- 位运算权限组合 ---
    userPerm := Read | Write // 组合权限:读 + 写 = 3 (011)
    fmt.Printf("\n用户权限值: %d (二进制: %03b)\n", userPerm, userPerm)
    fmt.Printf("有读权限? %t\n", userPerm&Read != 0)
    fmt.Printf("有执行权限? %t\n", userPerm&Execute != 0)

    // --- 类型转换 ---
    var temperature float64 = 36.6
    var intTemp int = int(temperature) // float64 → int(截断小数,不是四舍五入)
    fmt.Printf("\n体温: %.1f°C → 整数部分: %d\n", temperature, intTemp)

    // 数字 → 字符串的两种方式
    var code int = 65
    char := string(rune(code))      // 方式1:转成对应 Unicode 字符 → "A"
    numStr := fmt.Sprintf("%d", code) // 方式2:转成数字字符串 → "65"
    fmt.Printf("数字 %d → 字符: %s, 字符串: %q\n", code, char, numStr)

    // --- 字符串与字节/字符 ---
    s := "Hello,世界"
    fmt.Printf("\n字符串 %q 的长度: %d 字节\n", s, len(s))
    fmt.Println("逐字节遍历(不推荐处理中文):")
    for i := 0; i < len(s); i++ {
        fmt.Printf("  [%d] %d %c\n", i, s[i], s[i])
    }
    fmt.Println("逐 rune 遍历(正确处理中文):")
    for i, r := range s {
        fmt.Printf("  [%d] %U '%c'\n", i, r, r)
    }
}
▶ 试一试

输出:

TEXT
用户角色: 管理员 (值: 2)

用户权限值: 3 (二进制: 011)
有读权限? true
有执行权限? false

体温: 36.6°C → 整数部分: 36
数字 65 → 字符: A, 字符串: "65"

字符串 "Hello,世界" 的长度: 12 字节
逐字节遍历(不推荐处理中文):
  [0] 72 H
  [1] 101 e
  [2] 108 l
  [3] 108 l
  [4] 111 o
  [5] 44 ,
  [6] 228 ä
  [7] 184 ¸
  [8] 150 \x96
  [9] 228 ä
  [10] 150 \x96
  [11] 175 ¯
逐 rune 遍历(正确处理中文):
  [0] U+0048 'H'
  [1] U+0065 'e'
  [2] U+006C 'l'
  [3] U+006C 'l'
  [4] U+006F 'o'
  [5] U+002C ','
  [6] U+4E16 '世'
  [7] U+754C '界'

要点:


示例 3:综合应用(难度⭐⭐⭐)

场景: 模拟一个简单的用户注册信息处理系统,综合运用变量、常量、类型转换、格式化输出。

GO
package main

import (
	"fmt"
	"strings"
)

// ========== 常量定义 ==========

// 性别枚举
type Gender int

const (
	Unknown Gender = iota // 0:未知
	Male                  // 1:男
	Female                // 2:女
)

func (g Gender) String() string {
	return [...]string{"未知", "男", "女"}[g]
}

// 会员等级及对应折扣
const (
	LevelBronze   = 1
	LevelSilver   = 2
	LevelGold     = 3
	LevelPlatinum = 4
)

// 使用 iota 生成折扣比例(单位:百分比)
const (
	_                   = iota // 跳过 0
	DiscountBronze   = 95      // 95折
	DiscountSilver   = 90      // 90折
	DiscountGold     = 85      // 85折
	DiscountPlatinum = 80      // 80折
)

// ========== 用户结构体(预览,后续课程详解) ==========

type User struct {
	Name     string
	Age      int
	Gender   Gender
	Level    int
	Balance  float64
}

// ========== 核心逻辑 ==========

func main() {
	// 模拟用户数据
	users := []User{
		{Name: "李明", Age: 25, Gender: Male, Level: LevelGold, Balance: 1580.50},
		{Name: "王芳", Age: 30, Gender: Female, Level: LevelPlatinum, Balance: 3200.00},
		{Name: "张伟", Age: 17, Gender: Male, Level: LevelBronze, Balance: 200.00},
		{Name: "赵敏", Age: 22, Gender: Female, Level: LevelSilver, Balance: 800.75},
	}

	fmt.Println("╔══════════════════════════════════════════════════════════╗")
	fmt.Println("║                   用户信息注册系统                        ║")
	fmt.Println("╚══════════════════════════════════════════════════════════╝")
	fmt.Println()

	// 统计变量
	var totalAge int
	var totalBalance float64
	var maleCount, femaleCount int
	var adultCount int

	for _, u := range users {
		// 获取折扣
		discount := getDiscount(u.Level)
		discountAmount := u.Balance * float64(100-discount) / 100.0

		// 格式化输出用户信息
		ageTag := ""
		if u.Age >= 18 {
			ageTag = "✅ 成年"
			adultCount++
		} else {
			ageTag = "❌ 未成年"
		}

		fmt.Printf("👤 %-6s | 性别: %-2s | 年龄: %d岁 (%s) | 等级: Lv.%d | 余额: ¥%.2f\n",
			u.Name, u.Gender, u.Age, ageTag, u.Level, u.Balance)
		fmt.Printf("   💰 享受 %d 折优惠,可节省 ¥%.2f\n", discount, discountAmount)

		// 统计
		totalAge += u.Age
		totalBalance += u.Balance
		switch u.Gender {
		case Male:
			maleCount++
		case Female:
			femaleCount++
		}

		fmt.Println(strings.Repeat("-", 60))
	}

	// 输出统计信息
	avgAge := float64(totalAge) / float64(len(users))
	fmt.Println()
	fmt.Println("📊 统计信息:")
	fmt.Printf("   用户总数: %d 人\n", len(users))
	fmt.Printf("   男性: %d 人 | 女性: %d 人\n", maleCount, femaleCount)
	fmt.Printf("   成年人: %d 人 | 未成年: %d 人\n", adultCount, len(users)-adultCount)
	fmt.Printf("   平均年龄: %.1f 岁\n", avgAge)
	fmt.Printf("   总余额: ¥%.2f\n", totalBalance)
	fmt.Printf("   平均余额: ¥%.2f\n", totalBalance/float64(len(users)))

	// 演示类型转换和格式化
	fmt.Println()
	fmt.Println("🔧 类型转换演示:")
	demoTypeConversion()
}

// getDiscount 根据会员等级返回折扣
func getDiscount(level int) int {
	switch level {
	case LevelBronze:
		return DiscountBronze
	case LevelSilver:
		return DiscountSilver
	case LevelGold:
		return DiscountGold
	case LevelPlatinum:
		return DiscountPlatinum
	default:
		return 100 // 无折扣
	}
}

// demoTypeConversion 演示各种类型转换场景
func demoTypeConversion() {
	// 1. 数值类型互转
	var pi float64 = 3.14159
	var intPi int = int(pi) // 截断,不是四舍五入
	fmt.Printf("   float64 → int: %.5f → %d\n", pi, intPi)

	// 2. 数字 → 字符串(两种方式对比)
	var num int = 2024
	way1 := string(rune(num))      // Unicode 字符 → 不是你想要的
	way2 := fmt.Sprintf("%d", num) // 十进制字符串 → "2024"
	way3 := fmt.Sprintf("%x", num) // 十六进制字符串 → "7e8"
	fmt.Printf("   int → string (rune):  %q\n", way1)
	fmt.Printf("   int → string (%%d):    %q\n", way2)
	fmt.Printf("   int → string (%%x):    %q\n", way3)

	// 3. 字符串 → 数值(使用 fmt.Sscanf)
	var parsed int
	_, err := fmt.Sscanf("12345", "%d", &parsed)
	if err == nil {
		fmt.Printf("   string → int (Sscanf): %d\n", parsed)
	}

	// 4. bool → string
	b := true
	boolStr := fmt.Sprintf("%t", b)
	fmt.Printf("   bool → string: %t → %q\n", b, boolStr)

	// 5. rune 和 byte 的区别
	var r rune = '中'
	var b2 byte = 'A'
	fmt.Printf("   rune '中': 值=%d, 十六进制=%U, 占%d字节(UTF-8)\n", r, r, len(string(r)))
	fmt.Printf("   byte 'A': 值=%d, 十六进制=%U, 占1字节\n", b2, b2)
}
▶ 试一试

输出:

TEXT
╔══════════════════════════════════════════════════════════╗
║                   用户信息注册系统                        ║
╚══════════════════════════════════════════════════════════╝

👤 李明   | 性别: 男 | 年龄: 25岁 (✅ 成年) | 等级: Lv.3 | 余额: ¥1580.50
   💰 享受 85 折优惠,可节省 ¥237.08
------------------------------------------------------------
👤 王芳   | 性别: 女 | 年龄: 30岁 (✅ 成年) | 等级: Lv.4 | 余额: ¥3200.00
   💰 享受 80 折优惠,可节省 ¥640.00
------------------------------------------------------------
👤 张伟   | 性别: 男 | 年龄: 17岁 (❌ 未成年) | 等级: Lv.1 | 余额: ¥200.00
   💰 享受 95 折优惠,可节省 ¥10.00
------------------------------------------------------------
👤 赵敏   | 性别: 女 | 年龄: 22岁 (✅ 成年) | 等级: Lv.2 | 余额: ¥800.75
   💰 享受 90 折优惠,可节省 ¥80.08
------------------------------------------------------------

📊 统计信息:
   用户总数: 4 人
   男性: 2 人 | 女性: 2 人
   成年人: 3 人 | 未成年: 1 人
   平均年龄: 23.5 岁
   总余额: ¥5781.25
   平均余额: ¥1445.31

🔧 类型转换演示:
   float64 → int: 3.14159 → 3
   int → string (rune):  "\u07e8"
   int → string (%d):    "2024"
   int → string (%x):    "7e8"
   string → int (Sscanf): 12345
   bool → string: true → "true"
   rune '中': 值=20013, 十六进制=U+4E16, 占3字节(UTF-8)
   byte 'A': 值=65, 十六进制=U+0041, 占1字节

要点:


3. 常见应用场景

场景一:配置信息管理

GO
package main

import "fmt"

// 应用配置常量(编译期确定,不可修改)
const (
	AppName    = "GoWeb"
	Version    = "1.0.0"
	MaxRetries = 3
	DefaultTimeout = 30 // 秒
)

// 环境枚举
type Env int

const (
	Dev  Env = iota // 开发环境
	Staging         // 预发布环境
	Prod            // 生产环境
)

func (e Env) String() string {
	return [...]string{"开发", "预发布", "生产"}[e]
}

func main() {
	// 运行时变量
	currentEnv := Prod
	dbHost := "192.168.1.100"
	dbPort := 3306
	debug := currentEnv == Dev

	fmt.Printf("应用: %s v%s\n", AppName, Version)
	fmt.Printf("环境: %s\n", currentEnv)
	fmt.Printf("数据库: %s:%d\n", dbHost, dbPort)
	fmt.Printf("调试模式: %t\n", debug)
	fmt.Printf("最大重试: %d 次, 超时: %d 秒\n", MaxRetries, DefaultTimeout)
}

场景二:数据格式化与报表

GO
package main

import "fmt"

func main() {
	// 模拟学生成绩数据
	type Student struct {
		Name  string
		Score float64
		Grade string
	}

	students := []Student{
		{"小明", 92.5, ""},
		{"小红", 87.0, ""},
		{"小刚", 76.5, ""},
		{"小丽", 95.0, ""},
		{"小华", 63.0, ""},
	}

	// 根据分数自动评定等级
	for i := range students {
		switch {
		case students[i].Score >= 90:
			students[i].Grade = "A (优秀)"
		case students[i].Score >= 80:
			students[i].Grade = "B (良好)"
		case students[i].Score >= 70:
			students[i].Grade = "C (中等)"
		case students[i].Score >= 60:
			students[i].Grade = "D (及格)"
		default:
			students[i].Grade = "F (不及格)"
		}
	}

	// 打印成绩报表
	fmt.Println("┌──────┬────────┬──────────┐")
	fmt.Println("│ 姓名 │  分数  │   等级   │")
	fmt.Println("├──────┼────────┼──────────┤")
	var total float64
	for _, s := range students {
		fmt.Printf("│ %-4s │ %6.1f │ %-8s │\n", s.Name, s.Score, s.Grade)
		total += s.Score
	}
	fmt.Println("├──────┼────────┼──────────┤")
	fmt.Printf("│ 平均 │ %6.1f │          │\n", total/float64(len(students)))
	fmt.Println("└──────┴────────┴──────────┘")
}

❓ 常见问题

Q1:var:= 到底该用哪个?(概念混淆型)

A: 遵循简单原则:

场景 推荐方式 原因
函数内声明并赋值 := 更简洁,Go 社区惯例
函数内声明不赋值 var := 强制要求赋值
包级变量 var := 不能在函数外使用
需要显式指定类型 var 避免类型推断不符预期
GO
func example() {
    // ✅ 推荐:函数内用 :=
    name := "Go"
    count := 0

    // ✅ 必须用 var:声明时不赋值
    var result string
    if someCondition {
        result = "yes"
    } else {
        result = "no"
    }

    // ✅ 必须用 var:需要精确类型
    var b byte = 255 // 如果用 := 会推断为 int
}

Q2:为什么 string(65) 不是 "65"?(易错陷阱型)

A: string(int) 把整数当作 Unicode 码点,不是数字的文本表示:

GO
// ❌ 错误理解
s1 := string(65) // 期望 "65",实际得到 "A"(Unicode U+0041)

// ✅ 正确做法
s2 := fmt.Sprintf("%d", 65)  // "65"
s3 := strconv.Itoa(65)       // "65"(需要 import "strconv")

// string(rune) 的实际用途:Unicode 码点 → 字符
ch := string(rune(20013)) // "中"(U+4E16)

Q3:len("Hello,世界") 为什么不是 8?(易错陷阱型)

A: len() 返回的是字节数,不是字符数。Go 字符串默认 UTF-8 编码,中文占 3 个字节:

GO
s := "Hello,世界"
fmt.Println(len(s))          // 12(5 + 1 + 3×2 = 12 字节)
fmt.Println(utf8.RuneCountInString(s)) // 8(5 + 1 + 2 = 8 个字符)

// 正确计算字符数的方式
count := 0
for range s {
    count++
}
fmt.Println(count) // 8

Q4:如何快速交换两个变量?(实用技巧型)

A: Go 支持多重赋值,一行代码搞定:

GO
a, b := 10, 20
a, b = b, a // 交换:a=20, b=10

// 也可以用这种方式忽略某个值
_, err := someFunction() // 用 _ 忽略不需要的返回值

📖 小节


📝 作业

练习 1(⭐):变量声明与零值

声明以下变量并打印它们的零值:

预期输出:

TEXT
count: 0
price: 0
active: false
message: ""

练习 2(⭐⭐):iota 枚举与类型转换

定义一个 Color 类型(底层 int),用 iota 定义三种颜色:Red=0, Green=1, Blue=2。给 Color 实现 String() 方法。然后:

  1. 声明一个 Color 变量,赋值为 Green,打印它的名字和数值。
  2. float64 类型的 3.14159 转为 int,打印结果(应为 3)。
  3. 把数字 2025 转成字符串 "2025",打印结果。

练习 3(⭐⭐⭐):温度转换工具

编写一个程序,完成以下功能:

  1. 定义常量 AbsoluteZeroC = -273.15(绝对零度,摄氏)。
  2. 声明一组摄氏温度:-40, 0, 37, 100
  3. 遍历温度数组,将每个摄氏温度转换为华氏温度(公式:F = C × 9/5 + 32)。
  4. 格式化输出表格,要求:
    • 摄氏和华氏温度保留 1 位小数
    • 标注该温度是否低于绝对零度(物理上不可能)
    • 对齐列宽

预期输出:

TEXT
┌─────────┬─────────┬────────────┐
│ 摄氏(°C) │ 华氏(°F) │   状态     │
├─────────┼─────────┼────────────┤
│  -40.0  │  -40.0  │ 有效       │
│    0.0  │   32.0  │ 有效       │
│   37.0  │   98.6  │ 有效       │
│  100.0  │  212.0  │ 有效       │
└─────────┴─────────┴────────────┘

提示: 使用 constvar(数组/切片)、for 循环、fmt.Printf 格式化、类型转换(intfloat64)。


下一课

第三课:控制流 — if/else、switch、for 循环

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏