GO语言基础语法探究:简洁高效的编程之道2

简介: GO语言基础语法探究:简洁高效的编程之道

常量

常量使用一个名称来绑定一块内存地址,该内存地址中存放的数据类型由定义常量时指定的类型决定,而且该内存地址里面存放的内容不可以改变 。 Go 中常量分为布尔型、宇符串型和数值型常量。常量存储在程序的只读段里( .rodata section ) 。

预声明标识符 iota 用在常量声明中,其初始值为 0。一组多个常量同时声明时其值逐行增加, iota 可以看作自增的枚举变量,专 门用来初始化常量。

// 类似枚举的iota
  const (
    c0 = iota   // c0 == 0
    c1 = iota   // c1 == 1
    c2 = iota   // c2 == 2
  )
  // 简写模式
  const (
    a = iota // a == 0
    b        // b == 1
    c        // c == 2
  )
  // 注意iota 逐行递增
  const (
    // << 右移多少就是乘多少个2
    d = 1 << iota   // d == 1 (iota == 0)
    e = 1 << iota   // e == 2 (iota == 1)
    f = 3       // f == 3 (iota == 2, unused)
    g = 1 << iota   // g == 8 (iota == 3)
  )
  const (
    u = iota * 42       // u == 0     (untyped integer constant)
    v float64 = iota * 42 // v == 42.0  (float64 constant)
    w = iota * 42       // w == 84    (untyped integer constant)
  )
  // 分开的const语句,iota计数会被重置为0
  const x = iota    // x == 0
  const y = iota    // y == 0

基本数据类型

Go 是一种强类型的静态编译语言,类型是高级语言的基础,有了类型,高级语言才能对不同类型抽象出不同的运算,编程者才能在更高的抽象层次上操纵数据,而不用关注具体存储和运算细节。

  • Golang 是强类型语言

在赋值过程中, 类型必须保持一致

变量必须先定义后使用, 且必须被用到

Golang 会为每个变量设置默认值

变量不能重名

Golang 会根据值类型做变量类型推断

Go 语言内置七类基本数据类型( 20 个具体子类型)。


布尔类型: bool

整型 : byte int int8 intl6 init32 int64 uint uint8 uintl6 uint32 uint64 uintptr

浮点型 : float32 float64

复数: comlex64 complexl28

字符 : rune

字符串: string

错误类型:error

布尔类型

布尔类型关键字是 bool,布尔类型只有两个值: true 和 fasle,阳e 和 fals巳 是 Go 内置的两

个预声明标识符 。

var ok bool
ok = true

ok := false

布尔型数据和整型数据不能进行相互转换。

var a bool
a= 1 //error 1是整型字面量

比较表达式和逻辑表达式的结果都是布尔类型数据 。

var b bool = (x > y) && (x >0)

if 和 for i吾句的条件部分一定是布尔类型的值或表达式 。

if a <= b {
    print(b)
else {
    print(a)
    }
for ; true ; { //等价于 C 语言的 while (1)
}

声明的布尔型变量如不指定初始化值,则默认为 false 。

var b bool   // b is fals e

整型

Go 语言内置了12种整数类型,分别是 byte、int 、int8、int16、init32、int64、uint、uint8、uintl6、uint32、uint 64、uintptr。其 中 byte 是 uint8 的别名,不同类型的整型必须进行强制类型转换。

var a int = 1
var b int32 = 2
b = a        //error

整型支持算术运算和位操作,算术表达式和位操作表达式的结果还是整型。

var a int = (1+2)*3
var b int = 1000>>2

浮点型

浮点型用于表示包含小数点的数据 , Go 语言内置两种浮点数类型,分别是 float32 和 float64 。浮点数有两个注意事项:

  1. 浮点数字面量被自动类型推断为 float64 类型。
  2. 计算机很难进行浮点数的精确表示和存储 , 因此两个浮点数之间不应该使用=或 != 进行比较操作,高精度科学计算应该使用 math 标准库 。

复数类型

Go 语言内置的复数类型有两种,分别是 complex64 和 complex l28,复数在计算机里面使用两个浮点数表示 , 一个表示实部, 一个表示虚部。 complex64 是由两个 float32 构成的, complex l28是 由两个 float64 构成的。复数的字面量表示和数学表示法一样。

var value1 complex64 = 3.2 + 12i
value2 := 3.1 + 6i
//Go有三个内置函数处理复数
//complex、real和imag,分别返回复数的复数、实部和虚部
fmt.Println(complex(3, 2)) // "3+2i"
fmt.Println(real(3 + 2i))  // "3" 
fmt.Println(imag(3 + 2i))  // "2"

字符串

Go 语言将字符串作为一种原生的基本数据类型, 字符串的初始化可以使用字符串字面量。

例如 :

var a = "hello,world"
  1. 字符串是常量,可以通过类似数组 的索引访问其字节单元,但是不能修改某个字节的值。
    例如 :
var a = "hello,world "
b := a[0]
a[1] ='a' //error
  1. 宇符串转换为切片 []byte(s) 要慎用,尤其是当数据量较大时(每转换一次都需复制内容)。
    例如:
a := "hello,world!"
b : = []byte(a)
  1. 字符串尾部不包含 NULL 字符,这一点和 C/C++不一样。
  2. 字符串类型底层实现是一个二元的数据结构,一个是指针指 向字节数组的起点,另 一个是长度 。

  3. 基于字符串创建的切片和原字符串指向相同的底层字符数组 , 一样不能修改 , 对字符串的切片操作返回的子串仍然是string,而非 slice。
    例如 :
a := "hello,world!"
b := a[0 : 4]
c := a[1 : ]
d := a[ : 4)
  1. 字符串和切片的转换: 字符串可以转换为字节数组 ,也可以转换为 Unicode 的字数组。
    例如 :
a := "hello,世界!"
b := []byte(a)
c := []rune(a)
  1. 字符串的运算。
    例如 :
a := "hello"
b := "world"
// 字符串拼接
c := a + b
println(c)
e := len(a) // 字符串长度
println(e)
d := "hello 世界!"
for i := 0; i < len(d); i++ { // 遍历字节数组
  fmt.Println(d[i])
}
for i, ch := range d { // 遍历rune字符数组
  fmt.Println(i, ch)
}

rune类型

Go 内置两种字符类型 : 一种是 byte 的字节类类型( byte 是 uint 的别名),另一种是表示Unicode 编码的字符 rune。 rune 在 Go 内部是 int32 类型的别名,占用 4 个字节。 Go 语言默认的字符编码就是 UTF-8 类型的,如果需要特殊的编码转换,则使用 Unicode/UTF-8 标准包。


复合数据类型

复合数据类型就是由其他类型组合而成的类型。 Go 语言基本的复合数据类型有指针、数组、切片、字典( map )、通道、结构和接口,它们的字面量格式如下 :

* pointerType   //指针类型使用*后面跟其指向的类型名
[n] elementType   //数组类型使用[n]后面跟数组元素类型来表示,n表示该数组的长度
[] elementType    //切片类型使用[]后面跟切片元素类型来表示
map [keyType]valueType  //map 类型使用 map[键类型]值类型来表示
chan valueType    //通道使用 chan 后面跟通道元素类型来表示
struct {      //结构类型使用 struct{}将各个结构字段扩起来表示
  feildType feildType
  feildType feildYype
  ···
}
interface {     //接口类型使用 interface{}将各个方法括起来表示
  method1(inputParams)(returnParams)
  method1(inputParams)(returnParams)
  ···
}

指针

Go 语言支持指针,指针的声明类型为*T, Go 同样支持多级指针**T 。通过在变量名前加&来获取变量的地址。指针的特点如下。

  1. 在赋值语句中,*T 出现在 “=” 左边表示指针声明,*T 出现在 “=” 右边表示取指针指向的值( varName 为变量名)。示例如下 :
var a = 11
p : = &a  // *p 和 a 的值都是 11
  1. 结构体指针访问结构体字段仍然使用“ . ”点操作符, Go 语言没有“ ->”操作符 。 例如 :
type User struct {
  name string
  age  int
}
andes := User{
  name : "andes",
  age : 18,
}
p := &andes
fmt.Println(p.name) //通过“.”操作符来访问结构体的字段
  1. Go不支持指针的运算
    Go 由于支持垃圾回收,如果支持指针运算,则会给垃圾回收的实现带来很多不便,在 C和 C++里面指针运算很容易出现问题 , 因此 Go 直接在语言层面禁止指针运算。
a := 1234
p := &a
p++   //不允许,报non-numeric type *int 错误
  1. 函数中允许返回局部变量的地址 。
    Go 编译器使用“栈逃逸 ” 机制将这种局部变量的空间分配在堆上。 例如:
func sum(a, b int) *int {
  sum := a + b
  return &sum   //允许,sum 会分配在heap上
}

数组

数组的类型名是[n]elemetType,其中n是数组长度,elementType是数组元素类型。比如一个包含2个int类型元素的数组类型可表示为[2]int。数组一般在创建时通过字面量初始化,单独声明一个数组类型变量而不进行初始化是没有意义的。

数组初始化

var arr [2]int  //声明一个有两个整型的数组,但元素默认值都是0,一很少这样使用
array := [...]int{1,2,3}  //不指定长度,但是由后面的初始化列表数量来确定其长度
a := [3]int{1:1, 2:3}   //指定总长度,并通过索引值进行初始化,没有初始化元素时使用类型默认值
a := [...]int{1:1, 2:3}   //不指定总长度,通过索引值进行初始化,数组长度由最后一个索引值确定,没有指定索引的元素被初始化为类型的零值。

数组的特点

  • 数组创建完长度就固定了,不可以再追加元素。
  • 数组是值类型的,数组赋值或作为函数参数都是值拷贝。
  • 数组长度是数组类型的组成部分,[10]int 和 [20]int 表示不同的类型。
  • 可以根据数组创建切片。

数组相关操作

  1. 数组元素访问
a := [...]int{1,2,3}
b := a[0]
for i,v := range a {
}
  1. 数组长度
a := [...]int{1,2,3}
alengh := len(a)
for i := O; i < alengh ; i++ {
}

切片

Go 语言的数组的定长性和值拷贝限制了其使用场景, Go 提供了另一种数据类型 slice (切片),这是一种变长数组,其数据结构中有指向数组的指针,所以是一种引用类型 。

Go 为切片维护三个元素一一指向底层数组的指针、切片的元素数量和底层数组的容量。

切片的创建

由数组创建

创建语法如下:array[b:e],其中,array表示开始索引,可以不指定,默认是0;e表示结束索引,可以不指定,默认是len(array)。array[b:e]表示创建一个包含e-b个元素的切片,第一个元素是array[b],最后一个元素是array[e-1]。例如:

var array = [...]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} //创建有10个int类型的数组
var slice = array[2:5] //创建一个slice,从array[2]开始,到array[5]结束,但不包含array[5]
//slice的长度是3,容量是8
//slice的容量是从它的第一个元素开始,到其底层数组元素末尾的个数
var slice1 = array[2:5:7] 
//slice的长度是3,容量是5
var slice2 = array[2:] 
//slice的长度是8,容量是8
var slice3 = array[:5]
//slice的长度是5,容量是10
var slice4 = array[:]
//slice的长度是10,容量是10
fmt.Println(slice) //输出[3 4 5]
fmt.Println(slice1) //输出[3 4 5]
fmt.Println(slice2) //输出[3 4 5 6 7 8 9 10]
fmt.Println(slice3) //输出[1 2 3 4 5]
fmt.Println(slice4) //输出[1 2 3 4 5 6 7 8 9 10]
  • 通过内置函数make创建切片
    注意:由make创建的切片各元素被默认初始化为切片元素类型的零值。例如:
//len = 10, cap = 10
a := make([]int, 10)
//len = 5, cap = 10
b := make([]int, 5, 10)
fmt.Println(a) //输出[0 0 0 0 0 0 0 0 0 0]
fmt.Println(b) //输出[0 0 0 0 0]
// 直接声明切片类型变量是没有意义的
var c []int
fmt.Println(c) //输出[]

切片支持的操作

  • 内置函数len()返回切片长度
  • 内置函数cap()返回切片底层数组容量
  • 内置函数append()对切片追加元素
  • 内置函数copy()用于复制一个切片
a := [...]int{0,1,2,3,4,5,6}
b := make([]int,2,3)
c := a[2:5]
fmt.Println(len(b)) //2
fmt.Println(cap(b)) //3
b = append(b, 1)
fmt.Println(b)      //[0 0 1]
fmt.Println(len(b)) //3
fmt.Println(cap(b)) //3 
b = append(b, c...)
fmt.Println(b)      //[0 0 1 2 3 4]
fmt.Println(len(b)) //6
fmt.Println(cap(b)) //6 //容量不够,扩容成原来的2倍,底层的数组也会变成原来的2倍
d := make([]int, 2, 2)
copy(d,c) //copy函数,将c中的元素复制到d中,只会复制长度较小的那个slice的长度
fmt.Println(d)      //[2 3]
fmt.Println(len(d)) //2
fmt.Println(cap(d)) //2

字符串和切片的相关转换

str := "hello,世界" //通过字符串字面量初始化一个字符串str
a := []byte(str)  //将字符串转换为[]byte类型切片
b := []rune(str)  //将字符串转换为[]rune类型切片


相关文章
|
8天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
21 7
|
8天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
8天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
85 71
|
7天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
92 67
|
8天前
|
存储 Go
go语言中映射
go语言中映射
25 11
|
9天前
|
Go 索引
go语言使用range关键字
go语言使用range关键字
17 7
|
9天前
|
Go 索引
go语言修改元素
go语言修改元素
19 6
|
Go
go起步-语法控制语句
主要记录下go语言if、for siwtch循环控制语句
1355 0
|
10天前
|
Go 索引
go语言for遍历数组或切片
go语言for遍历数组或切片
84 62
|
12天前
|
并行计算 安全 Go
Go语言中的并发编程:掌握goroutines和channels####
本文深入探讨了Go语言中并发编程的核心概念——goroutine和channel。不同于传统的线程模型,Go通过轻量级的goroutine和通信机制channel,实现了高效的并发处理。我们将从基础概念开始,逐步深入到实际应用案例,揭示如何在Go语言中优雅地实现并发控制和数据同步。 ####