Go基础:下划线“_”、变量和常量、数组、slice
目录前言:Go语言的设计初衷Go语言很特别Go语言的主要特征GO概览Go基础Go语言声明下划线(很特殊)变量声明常量声明数组切片Slice前言:Go语言的设计初衷针对其他语言的痛点进行设计并加入并发编程为大数据、微服务、并发而生的通用编程语言Go语言很特别没有“对象”,没有继承多态、类等,没有泛型,没有try/cat...
目录
前言:
Go语言的设计初衷
- 针对其他语言的痛点进行设计
- 并加入并发编程
- 为大数据、微服务、并发而生的通用编程语言
Go语言很特别
- 没有“对象”,没有继承多态、类等,没有泛型,没有try/catch
- 有接口,函数式编程,CSP并发模型(goroutine+channel)
- 自带gc
- 静态编译,编译好后,扔服务器直接运行
- 学习Go语言很简单,因为语法简单
- 用好Go语言不容易,因为要调整三观
Go语言的主要特征
- 1.自动立即回收
- 2.更丰富的内置类型
- 3.函数多返回值
- 4.错误处理
- 5.匿名函数和闭包
- 6.类型和接口
- 7.并发编程
- 8.反射
- 9.语言交互性
GO概览
- 基本语法:变量,选择、循环,指针,数组,容器
- 面向接口:接口体,duck typing的概念,组合的思想
- 函数式编程:闭包的概念
- 工程化:资源管理、错误管理、测试和文档、性能调优
- 并发编程:理解调度器
Go基础
应用程序入口
应用程序入口
- 1. 必须是 main 包: package mai
- 2. 必须是 main 方法: func main()
- 3. ⽂文件名不不⼀一定是 main.go
退出返回值---与其他主要编程语⾔言的差异
- 1.Go 中 main 函数不不⽀支持任何返回值
- 2. 通过 os.Exit 来返回状态
获取命令⾏行行参数---与其他主要编程语⾔言的差异
- 1.main 函数不不⽀支持传⼊入参数
func main(
arg []string)
- 2.在程序中直接通过 os.Args 获取命令⾏行行参数
Go语言声明
- var(声明变量)
- const(声明常量)
- type(声明类型)
- func(声明函数)
1)声明在函数内部,是函数的本地值,类似private
2)声明在函数外部,是对当前包可见(包内所有.go文件都可见)的全局值,类似protect
3)声明在函数外部且首字母大写是所有包可见的全局值,类似public
下划线(很特殊)
“_”是特殊标识符,用来忽略结果。
- 下划线在import中:用【import _ 包路径】只是引用该包,仅仅是为了调用init()函数,所以无法通过包名来调用包中的其他函数
下划线在代码中:忽略这个变量,意思是那个位置本应赋给某个值,但是咱们不需要这个值。所以就把该值赋给下划线,意思是丢掉不要。
变量声明
- Go语言中的变量需要声明后才能使用,同一作用域内(可以放在函数内,也可以放在包内)不支持重复声明。并且Go语言的变量声明后必须使用。
- 使用var()集中定义常量
- 在函数内部,可以使用更简略的 := 方式声明并初始化变量。:=不能使用在函数外
- 没有char,只有rune: rune类型用来表示utf8字符,一个rune字符由一个或多个byte组成。
- 原生支持复数:complex64 complex128 (实部、虚部)
类型转化
- Go 语⾔言不允许隐式类型转换
- 别名和原有类型也不能进行隐式类型转换
类型的预定义值
- math.MaxInt64
- math.MaxFloat64
- math.MaxUint32
指针类型
- 不⽀持指针运算
- string 是值类型,其默认的初始化值为空字符串,而不是 nil
常量声明
- 常量的声明和变量声明非常类似,只是把
var
换成了const
,常量在定义的时候必须赋值- Go语言的常量不用大写,在go中大小写是有区别的
iota(自增值)
是go
语言的常量计数器,只能在常量的表达式中使用。
数组
Golang Array和以往认知的数组有很大不同。
1. 数组:是同一种数据类型的固定长度的序列。
2. 数组定义:var a [len]int,比如:var a [5]int,数组长度必须是常量,且是类型的组成部分。一旦定义,长度不能变。数量写在类型前
3. 长度是数组类型的一部分,因此,var a[5] int和var a[10]int是不同的类型。
4. 数组可以通过下标进行访问,下标是从0开始,最后一个元素下标是:len-1
for i := 0; i < len(a); i++ { } for index, v := range a { }
5. 访问越界,如果下标在数组合法范围之外,则触发访问越界,会panic
6. 调用func f(arr[10] int)会拷贝数组。数组是值类型,赋值和传参会复制整个数组,而不是指针。因此改变副本的值,不会改变本身的值。
7.支持 "=="、"!=" 操作符,因为内存总是被初始化过的。
8.指针数组 [n]*T,数组指针 *[n]T。
数组截取
a[开始索引(包含), 结束索引(不不包含)]
a := [...]int{1, 2, 3, 4, 5}
a[1:2] //2
a[1:3] //2,3
a[1:len(a)] //2,3,4,5
a[1:] //2,3,4,5
a[:3] //1,2,3
用 == 比较数组
- 相同维数且含有相同个数元素的数组才可以⽐比较
- 每个元素都相同的才相等
位运算符--&^ 按位置零
1 &^ 0 -- 1
1 &^ 1 -- 0
0 &^ 1 -- 0
0 &^ 0 -- 0
切片Slice
slice 并不是数组或数组指针。它通过内部指针和相关属性引用数组片段,以实现变长方案。
Slice本身没有数据,是对底层array的一个view
- 1. 切片:切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。
- 2. 切片的长度可以改变,因此,切片是一个可变的数组。
- 3. 切片遍历方式和数组一样,可以用len()求长度。表示可用元素数量,读写操作不能超过该限制。
- 4. cap可以求出slice最大扩张容量,不能超出数组限制。0 <= len(slice) <= len(array),其中array是slice引用的数组。
- 5. 切片的定义:var 变量名 []类型,比如 var str []string var arr []int。
- 6. 如果 slice == nil,那么 len、cap 结果都等于 0。
append超出原 slice.cap 限制,就会重新分配底层数组,即便原数组并未填满
package main
import (
"fmt"
"testing"
)
func TestEmptyInterfaceAssertion(t *testing.T) {
// 10: 0 使用冒号初始化元素--表示第10个位置值为0
data := [...]int{0, 1, 2, 3, 4, 10: 0}
//
s := data[:2:3]
fmt.Println(s)
s = append(s, 100, 200) // 一次 append 两个值,超出 s.cap 限制。
fmt.Println(s, data) // 重新分配底层数组,与原数组无关。
fmt.Println(&s[0], &data[0]) // 比对底层数组起始指针。
}
输出结果:
[0 1]
[0 1 100 200] [0 1 2 3 4 0 0 0 0 0 0]
0xc000086060 0xc000056060
append可能会导致cap不足,需要重新分配内存。此时需要修改slice指向的内存地址,len,cap。
但是由于append没法修改原slice变量的属性,这就出现了矛盾:需要修改原输入参数,但有不能产生副作用。
golang的解决方法是强制规定append的返回值必须赋值给slice。算是解决了忘记赋值的问题,但是太丑陋了。给slice提供一个append方法不是更简单直接吗?
数组 vs. 切片
- 容量是否可伸缩
- 是否可以进⾏比较
1 array
- 类型相同, 长度固定的数据集合. 长度无法修改
- 类型为: [len]T (len不能省略)
- 值是一段连续的内存区域和一些额外属性
属性示例:
- beg_addr 起始地址. 赋值时固定
- len 长度. 赋值时固定
- cap 保存实际长度. 赋值时固定,始终和len相同
要点:
- 编译时需要确定长度. 需要显式指定长度, 或者使用...由编译器自动推断
- go中都是值传递(pass by value). 因此赋值会复制数组本身.
- 引用需要借助pointer
2 slice
- 可以看作是内存的视图,展示内存的一段区域
- 类型为: []T
- 值是一个指向内存的数据结构, 类似指向数组的指针
属性示例:
- array 底层数组
- len 长度. 赋值(包括reslice)时设置
- cap 从起始地址开始,最多可以展示的内存长度. = 底层数组结束地址-起始地址
要点:
- 由于传递的是指针, 函数内可以修改外部slice指向的底层数组.
- 多个slice可以指向同一个底层数组.
更多推荐
所有评论(0)