当前位置: 首页 > news >正文

Golang语言基础篇001_常量变量与数据类型

在任何编程语言中,常量和变量都是最基本的概念之一。它们用于存储程序运行过程中需要的数据。在Go语言中,常量和变量有着不同的特性和使用方式。

本文将介绍Golang中常量、变量的概念、声明方式、命名规范等内容。另外,还会概述Golang中的数据类型。

1. 常量(Constant)

1.1. 什么是常量

常量是在程序编译时就确定的值,在程序运行过程中不能被修改。常量可以是数值类型(包括整数型、浮点型和复数型)、布尔型、字符串类型等。

1.2. 常量的声明

在Go语言中,使用const关键字来声明常量。常量的声明方式有以下几种:

1.2.1. 显式类型声明

// 声明并初始化单个常量
const <变量名> <类型> = <表达式 />// 声明并初始化同一类型的多个常量
const <变量名1>,<变量名2>,<变量名n> <类型> = <表达式1/1>,<表达式2/2>,<表达式n/值n>// 声明并初始化不同类型的多个常量
const (<变量名1> <类型1>  = <表达式1/1><变量名2> <类型2>  = <表达式2/2><变量名n> <类型n>  = <表达式n/值n>
)

示例:

// 声明并初始化单个常量
const a int = 1 + 1
// 声明并初始化同一类型的多个常量
const b, c, d int = 1, 2, 3
// 声明并初始化不同类型的多个常量
const (e string = ""f int    = 1
)

需要特别注意的是:在第三种声明并初始化常量的方式中,如果省略了值则表示和上面一行的值相同。

const (a int = 1 // a=1b // b=1c = 2 // c=2d // d=2
)

1.2.2. 隐式类型声明(类型推断)

常量声明与初始化时,如果类型推导的类型与目标类型一致,也可以忽略常量类型的编写。

const identifier = value

示例:

const pi = 3.14159
const appName = "MyApp"
const version = "1.0.0"
const maxBufferSize = 1024

当使用隐式类型声明时,Go编译器会根据赋值自动推断常量的类型。

1.3. iota枚举

iotago语言的常量计数器,只能在常量的表达式中使用。 iota在常量定义中出现时将被重置为0。批量定义常量时,const中每新增一行常量声明将使iota计数一次(iota可理解为const语句块中的行索引)。 使用iota能简化定义,在定义枚举时很有用。

  • 定义单个常量时,iota=0。

    const a = iota // a=0
    const b = iota // b=0
    const c // 报错		
    
  • 批量定义同一行的多个常量时,iota可以出现多次,但每次都是0.

    const a,b,c = iota,1,iota // a=0,b=1,c=0
    
  • 批量定义同一行的多个常量时,iota可以出现多次,但每次都是0.

    const (a = iota // a=0b // b=iota=1c // c=iota=2
    )const (a = iota*2 // a=0b // b=iota*2=1*1=2c // c=iota*2=2*2=4
    )const (a = 4 // a=4b = iota // b=iota=1c // c=iota=2
    )const (a = iota // a=iota=0b = 100 // b = 100c // c=100d = iota // d=iota=3e   // e=iota=4
    )const (a, b = iota+1, iota+2 // a=1,b=2c, d // c=2,d=3e, f // e=3,f=4
    )
    

1.4. 常量的命名规范

常量的命名遵循Go语言的命名规范:

  • 不导出的常量使用小驼峰命名法:maxBufferSize
  • 导出的常量使用大驼峰命名法:MaxBufferSize
  • 也可以使用全大写加下划线的传统风格:MAX_BUFFER_SIZE

2. 变量(Variable)

2.1. 什么是变量

变量是用来存储程序运行时可能发生变化的数据的标识符。

在Go语言中,每个变量都有特定的类型,决定了变量可以存储什么类型的值。

2.2. 变量的声明

在Go语言中,使用var关键字来声明变量。变量的声明方式有以下几种:

2.2.1. 显式类型声明

变量显式声明有如下三种格式:

// 声明单个变量
var <变量名> <变量类型>// 声明同一类型的多个变量
var <变量名1>,<变量名2>,<变量名n> <变量类型>// 声明不同类型的多个变量
var (<变量名1> <变量类型1><变量名2> <变量类型2><变量名n> <变量类型n>
)

示例:

// 声明单个变量
var name string
var age int
var isStudent bool// 声明同一个类型的多个变量
var maxAge,minAge int// 声明多个变量(不限类型)
var (name stringage  intisStudent bool
)

声明后,变量会被赋予对应类型的零值,比如:

  • 数值类型:0
  • 布尔类型:false
  • 字符串类型:“”

2.2.2. 声明时初始化

Go语言在声明变量的时候,会自动对变量对应的内存区域进行初始化操作。每个变量会被初始化成其类型的默认值,例如: 整型和浮点型变量的默认值为0。 字符串变量的默认值为空字符串。 布尔型变量默认为false。 切片、函数、指针变量的默认为nil。

当然我们也可在声明变量的时候为其指定初始值。变量初始化的格式如下:

// 声明并初始化单个变量
var <变量名> <变量类型> = <表达式 />// 声明并初始化同一类型的多个变量
var <变量名1>,<变量名2>,<变量名n> <变量类型> = <表达式1/1>,<表达式2/2>,<表达式n/值n>// 声明并初始化不同类型的多个变量
var (<变量名1> <变量类型1>  = <表达式1/1><变量名2> <变量类型2>  = <表达式2/2><变量名n> <变量类型n>  = <表达式n/值n>
)

示例:

// 声明并初始化单个变量
var a int = 1 + 1
// 声明并初始化同一类型的多个变量
var b, c, d int = 1, 2, 3
// 声明并初始化不同类型的多个变量
var (e string = ""f int    = 1
)

2.2.3. 隐式类型声明(类型推断)

在变量声明并初始化时,如果我们不设置变量类型,编译器会根据等号右边的值或表达式结果来推导变量的类型,完成初始化。

所以如果编译器推导的类型与我们的目标类型一致,就可以不编写变量类型:

var a int = 1 // 指定变量类型为int
var  a = 1 // 编译器推导变量类型为int
var a int32 = 1 // 指定变量类型为int32

在上述示例中,第一个语句和第二个语句效果完全一致。只有在第三种情况,编译器推导的变量类型与我们的目标变量类型不一致时,才有必要编写变量类型。

2.2.4. 短变量声明(:=)

在类型推导章节中,我们知道当编译器推导的类型与我们的目标类型一致时,var a int =1var a = 1效果完全一样。

在函数内部,可以将这两个表达式替换成更简略的:=方式声明并初始化变量:

<变量> := <>

示例:

a := 1

注意:短变量声明只能在函数内部使用,并且要求左侧至少有一个变量。

2.2.5. 匿名变量

当使用下划线占位符来代替变量名称时,即称该占位符代表的变量为匿名变量,表示忽略值。匿名变量最常用的方式是用来接收调用方法的返回值,表示忽略该返回值。

data,_:=json.Marshal(p )

2.3. 变量的赋值和重新赋值

变量声明后(不论是否初始化),都可以通过赋值语句进行重新赋值。

// 声明变量
var count int// 赋值
count = 10// 重新赋值
count = 20// 多重赋值
a, b := 1, 2
a, b = b, a  // 交换a和b的值

2.4. 变量的命名规范

变量的命名遵循Go语言的命名规范:

  • 全局变量:首字母大写表示导出,首字母小写表示私有
  • 局部变量:推荐使用小驼峰命名法
  • 避免使用下划线分隔符(特殊情况除外)
// 全局变量
var GlobalCounter int  // 导出变量
var localCounter int   // 私有变量// 局部变量
func processData() {userName := "张三"itemCount := 10
}

3. 常量与变量的区别

特性常量变量
关键字constvar 或 :=
修改性编译时确定,不可修改运行时可修改
存储位置符号表内存
性能更高(编译时优化)相对较低
使用场景固定不变的值可能变化的值

4. Go语言数据类型

Go语言是一种静态类型语言,这意味着每个变量都必须有一个明确的类型。Go语言提供了丰富的内置数据类型,主要包括基本数据类型和复合数据类型。

4.1. 基本数据类型

Go语言的基本数据类型包括数值类型、布尔类型和字符串类型。

4.1.1. 数值类型

Go语言提供了丰富的数值类型,包括整数类型和浮点数类型。

整数类型

整数类型分为有符号整数和无符号整数:

  • 有符号整数:int8、int16、int32、int64、int
  • 无符号整数:uint8、uint16、uint32、uint64、uint
  • 特殊整数类型:uintptr(用于存储指针值)
var a int8 = 127        // 8位有符号整数,范围:-128 到 127
var b int16 = 32767     // 16位有符号整数,范围:-32768 到 32767
var c int32 = 2147483647 // 32位有符号整数,范围:-2147483648 到 2147483647
var d int64 = 9223372036854775807 // 64位有符号整数var e uint8 = 255       // 8位无符号整数,范围:0 到 255
var f uint16 = 65535    // 16位无符号整数,范围:0 到 65535
var g uint32 = 4294967295 // 32位无符号整数,范围:0 到 4294967295
var h uint64 = 18446744073709551615 // 64位无符号整数var i int = 100         // 平台相关大小,32位系统上为int32,64位系统上为int64
var j uint = 100        // 平台相关大小,32位系统上为uint32,64位系统上为uint64
var k uintptr = 0x1234  // 用于存储指针值的整数类型
浮点数类型

Go语言提供了两种浮点数类型:

  • float32:32位浮点数(单精度)
  • float64:64位浮点数(双精度,默认类型)
var f1 float32 = 3.14
var f2 float64 = 3.141592653589793
var f3 = 3.14          // 默认为float64类型// 特殊浮点数值
var inf = math.Inf(1)   // 正无穷
var negInf = math.Inf(-1) // 负无穷
var nan = math.NaN()    // 非数字(Not a Number)
复数类型

Go语言原生支持复数类型:

  • complex64:实部和虚部都是float32类型
  • complex128:实部和虚部都是float64类型(默认类型)
var c1 complex64 = 3 + 4i
var c2 complex128 = 3 + 4i
var c3 = 3 + 4i         // 默认为complex128类型// 使用内置函数创建复数
c4 := complex(3.0, 4.0) // 创建复数3+4i// 获取复数的实部和虚部
realPart := real(c4)    // 获取实部
imagPart := imag(c4)    // 获取虚部

4.1.2. 布尔类型

布尔类型只有两个值:true和false。

var isTrue bool = true
var isFalse bool = false
var b1 = true           // 默认布尔类型// 布尔运算
result1 := true && false  // 与运算
result2 := true || false  // 或运算
result3 := !true          // 非运算

4.1.3. 字符串类型

Go语言中的字符串是UTF-8编码的不可变字节序列。

var str1 string = "Hello, 世界"
var str2 = "Hello, Go"     // 隐式类型声明
str3 := "Hello, World"     // 短变量声明// 多行字符串
str4 := `这是一个多行字符串
可以包含换行符
还可以包含"引号"`// 字符串连接
str5 := "Hello" + " " + "World"// 字符串长度
length := len(str1)// 字符串遍历
for i, char := range str1 {fmt.Printf("位置%d: %c\n", i, char)
}

4.1.4. 字符类型

Go语言没有专门的字符类型,字符是通过整数类型或rune类型表示的:

  • byte:uint8的别名,用于表示ASCII字符
  • rune:int32的别名,用于表示Unicode字符
var ch1 byte = 'A'      // ASCII字符
var ch2 rune = '世'     // Unicode字符// 字符与字符串的区别
char := 'A'             // 类型为rune或int32
str := "A"             // 类型为string// 遍历字符串中的字符
str := "Hello, 世界"
for i, r := range str {fmt.Printf("位置%d: %c\n", i, r)
}

4.2. 复合数据类型

Go语言提供了多种复合数据类型,用于组织和存储复杂的数据结构。

4.2.1. 数组(Array)

数组是固定长度的序列,所有元素必须是相同类型。

// 声明数组
var arr1 [5]int                     // 声明长度为5的整数数组
var arr2 [3]string = [3]string{"a", "b", "c"} // 声明并初始化
arr3 := [5]int{1, 2, 3, 4, 5}       // 短变量声明并初始化
arr4 := [...]int{1, 2, 3}           // 编译器自动计算长度// 访问数组元素
first := arr3[0]                    // 获取第一个元素
arr3[0] = 10                        // 修改元素值// 数组长度
length := len(arr3)                 // 获取数组长度// 多维数组
var matrix [3][3]int                // 3x3二维数组
matrix[0][0] = 1

4.2.2. 切片(Slice)

切片是对数组的封装,提供了动态数组的功能。

// 创建切片
var slice1 []int                    // 声明空切片
slice2 := make([]int, 5)            // 创建长度为5的切片
slice3 := make([]int, 3, 5)         // 创建长度为3,容量为5的切片
slice4 := []int{1, 2, 3, 4, 5}      // 直接初始化// 从数组创建切片
arr := [5]int{1, 2, 3, 4, 5}
slice5 := arr[1:4]                  // 从索引1到3创建切片
slice6 := arr[:3]                   // 从开始到索引2
slice7 := arr[2:]                   // 从索引2到结束// 切片操作
length := len(slice4)               // 切片长度
capacity := cap(slice4)             // 切片容量// 添加元素
slice8 := append(slice4, 6)         // 添加单个元素
slice9 := append(slice4, 7, 8, 9)   // 添加多个元素
slice10 := append(slice4, slice8...) // 合并切片// 复制切片
dest := make([]int, len(slice4))
copy(dest, slice4)

4.2.3. 映射(Map)

映射是一种无序的键值对集合。

// 创建映射
var map1 map[string]int             // 声明空映射
map2 := make(map[string]int)        // 使用make创建
map3 := map[string]int{             // 直接初始化"apple":  5,"banana": 3,"orange": 8,
}// 操作映射
map2["apple"] = 5                  // 添加或更新键值对
value, exists := map2["apple"]      // 获取值,exists表示键是否存在
delete(map2, "apple")               // 删除键值对// 遍历映射
for key, value := range map3 {fmt.Printf("%s: %d\n", key, value)
}// 映射长度
length := len(map3)

4.2.4. 结构体(Struct)

结构体是一种用户自定义的数据类型,可以包含多个不同类型的字段。

// 定义结构体
type Person struct {Name stringAge  intEmail string
}type Address struct {City, State stringZIP         int
}// 创建结构体实例
var person1 Person                  // 零值初始化
person2 := Person{Name: "张三", Age: 30, Email: "zhangsan@example.com"}
person3 := Person{"李四", 25, "lisi@example.com"}  // 按顺序初始化// 访问结构体字段
name := person2.Name
person2.Age = 31// 匿名结构体
person4 := struct {Name stringAge  int
}{Name: "王五",Age:  28,
}// 嵌套结构体
type Employee struct {PersonAddressSalary int
}emp := Employee{Person:  Person{Name: "赵六", Age: 35, Email: "zhaoliu@example.com"},Address: Address{City: "北京", State: "北京", ZIP: 100000},Salary:  10000,
}

4.2.5. 指针(Pointer)

指针存储了另一个变量的内存地址。

// 声明指针
var ptr *int                        // 声明指向int的指针
var value int = 42// 获取变量地址
ptr = &value                        // 获取value的地址// 解引用
fmt.Println(*ptr)                   // 输出指针指向的值
*ptr = 100                          // 修改指针指向的值// 指针与结构体
type Student struct {Name stringAge  int
}stu := Student{Name: "小明", Age: 18}
var stuPtr *Student = &stu
fmt.Println((*stuPtr).Name)         // 通过指针访问字段
fmt.Println(stuPtr.Name)            // 简化写法// 指针数组和数组指针
arr := [3]int{1, 2, 3}
var ptrArr [3]*int                  // 指针数组
for i := range arr {ptrArr[i] = &arr[i]
}var arrPtr *[3]int = &arr           // 数组指针
fmt.Println((*arrPtr)[0])

4.2.6. 通道(Channel)

通道用于goroutine之间的通信。

// 创建通道
var ch1 chan int                    // 声明通道
ch2 := make(chan int)               // 创建双向通道
ch3 := make(chan int, 5)            // 创建带缓冲的通道
sendCh := make(chan<- string)       // 只发送通道
receiveCh := make(<-chan string)    // 只接收通道// 通道操作
ch2 <- 42                           // 发送数据
data := <-ch2                       // 接收数据// 关闭通道
close(ch2)// 使用range遍历通道
ch4 := make(chan int, 3)
go func() {ch4 <- 1ch4 <- 2ch4 <- 3close(ch4)
}()for value := range ch4 {fmt.Println(value)
}// 选择语句
ch5 := make(chan string)
ch6 := make(chan string)go func() { ch5 <- "hello" }()
go func() { ch6 <- "world" }()select {
case msg1 := <-ch5:fmt.Println(msg1)
case msg2 := <-ch6:fmt.Println(msg2)
default:fmt.Println("没有可用的数据")
}

4.2.7. 接口(Interface)

接口是一种类型,它定义了一组方法的签名,但不提供具体实现。任何类型只要实现了接口中定义的所有方法,就被认为是实现了该接口。

4.2.7.1. 接口的定义
// 定义一个简单的接口
type Writer interface {Write([]byte) (int, error)
}// 定义一个包含多个方法的接口
type ReadWriter interface {Read([]byte) (int, error)Write([]byte) (int, error)
}// 定义一个空接口(可以表示任何类型)
type Any interface{}
// 或者使用Go 1.18+的any关键字
// var value any
4.2.7.2 接口的实现
// 定义结构体
type File struct {name string
}// 实现Writer接口
func (f *File) Write(data []byte) (int, error) {fmt.Printf("向文件 %s 写入数据: %s\n", f.name, string(data))return len(data), nil
}// 实现Reader接口
func (f *File) Read(data []byte) (int, error) {// 简化实现copy(data, []byte("从文件读取的数据"))return len(data), nil
}// 使用接口
func processData(w Writer) {w.Write([]byte("Hello, Interface!"))
}// 示例使用
func main() {file := &File{name: "test.txt"}processData(file)  // File实现了Writer接口// 接口变量var writer Writer = filewriter.Write([]byte("通过接口调用"))
}
4.2.7.3. 接口的组合
// 接口嵌入
type Reader interface {Read([]byte) (int, error)
}type Writer interface {Write([]byte) (int, error)
}type ReadWriter interface {Reader  // 嵌入Reader接口Writer  // 嵌入Writer接口
}// 实现ReadWriter接口的结构体
type File struct {name string
}func (f *File) Read(data []byte) (int, error) {copy(data, []byte("文件内容"))return len(data), nil
}func (f *File) Write(data []byte) (int, error) {fmt.Printf("写入文件 %s: %s\n", f.name, string(data))return len(data), nil
}
4.2.7.4. 空接口和类型断言
// 空接口可以存储任何类型的值
var x interface{}x = 42
fmt.Println(x)  // 42x = "Hello"
fmt.Println(x)  // Hellox = true
fmt.Println(x)  // true// 类型断言
str, ok := x.(string)
if ok {fmt.Printf("x 是字符串: %s\n", str)
} else {fmt.Println("x 不是字符串")
}// 类型选择
switch v := x.(type) {
case int:fmt.Printf("x 是整数: %d\n", v)
case string:fmt.Printf("x 是字符串: %s\n", v)
case bool:fmt.Printf("x 是布尔值: %t\n", v)
default:fmt.Printf("x 是其他类型: %T\n", v)
}
4.2.7.5. 接口的最佳实践
// 1. 倾向于定义小而专注的接口
type Stringer interface {String() string
}type Reader interface {Read([]byte) (int, error)
}// 2. 接受接口,返回结构体
func ProcessData(r Reader) []byte {data := make([]byte, 1024)r.Read(data)return data
}// 3. 使用接口作为函数参数实现解耦
type Animal interface {Speak() string
}type Dog struct{}
func (d Dog) Speak() string {return "汪汪"
}type Cat struct{}
func (c Cat) Speak() string {return "喵喵"
}func MakeAnimalSpeak(a Animal) {fmt.Println(a.Speak())
}// 使用示例
func main() {dog := Dog{}cat := Cat{}MakeAnimalSpeak(dog)  // 汪汪MakeAnimalSpeak(cat)  // 喵喵
}

4.3. 类型别名和自定义类型

Go语言允许创建类型别名和自定义类型。

// 类型别名(Go 1.9+)
type Celsius float64
type Fahrenheit float64
type ID = int32                    // 类型别名// 自定义类型
type UserID int32                  // 新类型,与int32不兼容
type MyInt int                     // 新类型,与int不兼容// 使用示例
var temp Celsius = 25.0
var id ID = 12345
var userId UserID = 67890// 类型转换
func CToF(c Celsius) Fahrenheit {return Fahrenheit(c*9/5 + 32)
}func FToC(f Fahrenheit) Celsius {return Celsius((f - 32) * 5 / 9)
}

4.4. 零值概念

在Go语言中,所有变量在声明时都会被赋予一个零值:

  • 数值类型:0
  • 布尔类型:false
  • 字符串类型:“”
  • 指针类型:nil
  • 切片、映射、通道:nil
  • 接口类型:nil
  • 结构体:各字段的零值
var i int                          // 0
var f float64                      // 0.0
var b bool                         // false
var s string                       // ""
var ptr *int                       // nil
var slice []int                    // nil
var m map[string]int               // nil
var ch chan int                    // nil// 结构体零值
type User struct {Name stringAge  intActive bool
}var user User                      // User{"", 0, false}

4.5. 类型转换

Go语言不支持隐式类型转换,必须显式进行类型转换。

// 基本类型转换
var i int = 42
var f float64 = float64(i)         // int转float64
var u uint = uint(f)               // float64转uint// 字符串与其他类型转换
import "strconv"// 数字转字符串
str := strconv.Itoa(42)            // int转字符串
str = strconv.FormatFloat(3.14, 'f', -1, 64) // float64转字符串// 字符串转数字
i, err := strconv.Atoi("42")       // 字符串转int
if err != nil {// 处理错误
}f, err = strconv.ParseFloat("3.14", 64) // 字符串转float64// 字节切片与字符串转换
str = "hello"
bytes := []byte(str)               // 字符串转字节切片
str = string(bytes)                // 字节切片转字符串

4.6. 类型断言

类型断言用于接口类型的转换。

var i interface{} = "hello"// 类型断言
s, ok := i.(string)                // 安全类型断言
if ok {fmt.Println(s)
}// 直接类型断言(如果类型不匹配会panic)
s = i.(string)// 类型选择
switch v := i.(type) {
case string:fmt.Println("字符串:", v)
case int:fmt.Println("整数:", v)
default:fmt.Println("未知类型")
}
http://www.dtcms.com/a/389306.html

相关文章:

  • pytest文档1-环境准备与入门
  • MySQL 专题(四):MVCC(多版本并发控制)原理深度解析
  • 【开发者导航】在终端中运行任意图形应用:term.everything
  • [Python]pytest是什么?执行逻辑是什么?为什么要用它测试?
  • Nginx set指令不能使用在http块里,可以使用map指令
  • LeetCode 1759.统计同质子字符串的数目
  • 揭秘Linux文件管理与I/O重定向核心
  • 【PyTorch】DGL 报错FileNotFoundError: Cannot find DGL C++ graphbolt library
  • Autoware不同版本之间的区别
  • 多轮对话-上下文管理
  • 在阿里云私网服务器(无公网IP)上安装 Docker 环境的完整指南
  • opencv DNN模块及利用实现风格迁移
  • 多层感知机:从感知机到深度神经网络的演进
  • centos7 docker compose 安装redis
  • ⸢ 肆-Ⅱ⸥ ⤳ 风险发现体系的演进(下):实践与演进
  • 18兆欧超纯水抛光树脂
  • 第三篇:C++的进化之旅:从C with Class到C++20
  • 机器视觉的手机FPC丝印应用
  • 在Windows上使用Claude Code并集成到PyCharm IDE的完整指南
  • MoPKL与SPAR的思考
  • Ubuntu 启动分配不到 ip 地址问题
  • iOS 推送证书配置 - p12
  • Qt QVPieModelMapper详解
  • 【MySQL数据库管理问答题】第1章 MySQL 简介
  • 铁头山羊视stm32-HAL库
  • iOS 26 帧率检测实战攻略 如何监控FPS、GPU渲染、Core Anima
  • AWS Lightsail vs 阿里云轻量:企业上云服务器选型深度对比
  • stm32中的位带操作的使用意义
  • Qt QStackedBarSeries详解
  • WebSocket Secure(WSS)在Django项目中的使用