Go:基础用法

1.程序结构

1.命名

关键字和内建字
在这里插入图片描述

在这里插入图片描述

2.声明

  • var:变量的声明
  • const:常量的声明
  • type:类型的声明
  • func:函数实体对象的声明

3.变量

变量声明(以字符串为例)

	var a string = "123"
	a  := "123"
	var a string
	a = "123"

变量默认值

  • 数值类型变量对应的零值是0
  • 布尔类型变量对应的零值是false
  • 字符串类型对应的零值是空字符串
  • 接口或引用类型(包括slice、map、chan和函数)变量对应的零值是nil
  • 数组或结构体等聚合类型对应的零值是每个元素或字段都是对应该类型的零值

注意点
“:=”是一个变量声明语句,而“=‘是一个变量赋值操作

指针
一个变量对应一个保存了变量对应类型值的内存空间。一个指针的值是另一个变量的地址。一个指针对应变量在内存中的存储位置
任何类型的指针的零值都是nil。如果 p != nil 测试为真,那么p是指向某个有效变量。指针之间也是可以进行相等测试的,只有当它们指向同一个变量全部是nil时才相等。
指针包含了一个变量的地址,因此如果将指针作为参数调用函数,那将可以在函数中通过该指针来更新变量的值
写法:

	p := &x // p是指向x的指针
	*p = 2 // *p代表x,这个表达式等价于x = 2

new函数
表达式new(T)将创建一个T类型的匿名变量,初始化为T类型的零值,然后返回变量地址,返回的指针类型为 *T 。
每次调用new函数都是返回一个新的变量的地址。

变量的回收思路
从每个包级的变量和每个当前运行函数的每一个局部变量开始,通过指针或引用的访问路径遍历,是否可以找到该变量。如果不存在这样的访问路径,那么说明该变量是不可达的,也就是说它是否存在并不会影响程序后续的计算结果。
因为一个变量的有效周期只取决于是否可达,因此一个循环迭代内部的局部变量的生命周期可能超出其局部作用域。同时,局部变量可能在函数返回之后依然存在。

关于go的垃圾回收和堆上分配、栈上分配的说法,可参考下面:

在这里插入图片描述

4.赋值

元组赋值

元组赋值也可以使一系列琐碎赋值更加紧凑,比如:i, j, k = 2, 3, 5
但如果表达式太复杂的话,应该尽量避免过度使用元组赋值;因为每个变量单独赋值语句的写法可读性会更好。
有一种函数用额外的返回值来表达某种错误类型,比如:

	v, ok = m[key] // map lookup
	v, ok = x.(T) // type assertion
	v, ok = <-ch // channel receive

可赋值性
赋值语句是显式的赋值形式,但是程序中还有很多地方会发生隐式的赋值行为:函数调用会隐式地将调用参数的值赋值给函数的参数变量,一个返回语句将隐式地将返回操作的值赋值给结果变量,一个复合类型的字面量(§4.2)也会产生赋值行为。

对于两个值是否可以用 == 或 != 进行相等比较的能力也和可赋值能力有关系:对于任何类型的值的相等比较,第二个值必须是对第一个值类型对应的变量是可赋值的,反之依然。

5.类型

一个类型声明语句创建了一个新的类型名称,和现有类型具有相同的底层结构。新命名的类型提供了一个方法,用来分隔不同概念的类型,这样即使它们底层类型相同也是不兼容的:

	type 类型名字 底层类型

对于每一个类型T,都有一个对应的类型转换操作T(x),用于将x转为T类型(译注:如果T是指针类型,可能会需要用小括弧包装T,比如 (*int)(0) )。只有当两个类型的底层基础类型相同时,才允许这种转型操作,或者是两者都是指向相同底层结构的指针类型,这些转换只改变类型而不会影响值本身。

6.包和文件

包的初始化
包的初始化首先是解决包级变量的依赖顺序,然后安照包级变量声明出现的顺序依次初始化
对于在包级别声明的变量,如果有初始化表达式则用表达式初始化,还有一些没有初始化表达式的,可以用一个特殊的init初始化函数来简化初始化工作。每个文件都可以包含多个init初始化函数

	func init() { /* ... */ }

这样的init初始化函数除了不能被调用或引用外,其他行为和普通函数类似。在每个文件中的init初始化函数,在程序开始执行时按照它们声明的顺序被自动调用。
每个包在解决依赖的前提下,以导入声明的顺序初始化,每个包只会被初始化一次。
初始化工作是自下而上进行的,main包最后被初始化。以这种方式,可以确保在main函数执行之前,所有依然的包都已经完成初始化工作了。

7.作用域

不要将作用域和生命周期混为一谈。声明语句的作用域对应的是一个源代码的文本区域;它是一个编译时的属性。一个变量的生命周期是指程序运行时变量存在的有效时间段,在此时间区域内它可以被程序的其他部分引用;是一个运行时的概念。


2.基础数据类型

Go语言将数据类型分为四类:基础类型、复合类型、引用类型和接口类型。

1.整型

  • 1、Go语言同时提供了有符号和无符号类型的整数运算。

  • 2、有int8、int16、int32和int64四种截 然不同大小的有符号整形数类型,分别对应8、16、32、64bit大小的有符号整形数,与此对 应的是uint8、uint16、uint32和uint64四种无符号整形数类型。

  • 3、其中int和int32也是不同的类 型,即使int的大小也是32bit

  • 4、运算符按照优先级递减(二元运算符有五种优先级。在同一个优先级,使用左优先结合规则,但是使用括号可以明确 优先顺序)
    在这里插入图片描述

  • 5、在Go语言中,%取模运算 符的符号和被取模数的符号总是一致的,因此 -5%3 和 -5%-3 结果都是-2。

  • 6、除法运算符 / 的 行为则依赖于操作数是否为全为整数,比如 5.0/4.0 的结果是1.25,但是5/4的结果是1,因为 整数除法会向着0方向截断余数。

  • 7、算术上,一个 x<<n 左移运算等价于乘以2 ,一个 x>>n 右移运算等价 于除以2 。左移运算用零填充右边空缺的bit位,无符号数的右移运算也是用0填充左边空缺的bit位,但是 有符号数的右移运算会用符号位的值填充左边空缺的bit位。因为这个原因,最好用无符号运算,这样你可以将整数完全当作一个bit位模式处理。

  • 8、无符号数往往只有在位运算或其它特殊的运算场景才会使用,就像bit集合分析二进制文件格式或者是哈希和加密操作等。它们通常并不用于仅仅是表达非负数量的场 合。

2.浮点数

  • 1、float32的有效bit位只有23个,其它 的bit位用于指数和符号;当整数大于23bit能表达的范围时,float32的表示将出现误差

3.复数

  • 1、Go语言提供了两种精度的复数类型:complex64和complex128,分别对应float32和float64两 种浮点数精度
  • 2、内置的complex函数用于构建复数,内建的real和imag函数分别返回复数的实 部和虚部

4.布尔型

  • 1、&&(AND)和||(OR)操作符,可能会有短路行为:如果运算符左边 值已经可以确定整个布尔表达式的值,那么运算符右边的值将不在被求值

5.字符串

  • 1、一个字符串是一个不可改变的字节序列。
  • 2、内置的len函数可以返回一个字符串中的字节数目(不是rune字符数目),索引操作s[i]返回第i 个字节的字节值,i必须满足0 ≤ i< len(s)条件约束。第i个字节并不一定是字符串的第i个字符,因为对于非ASCII字符的UTF8编码会要两个或多个 字节
  • 4、3、子字符串操作s[i:j]基于原始的s字符串的第i个字节开始到第j个字节(并不包含j本身)生成一 个新字符串。生成的新字符串将包含j-i个字节。不管i还是j都可能被忽略,当它们被忽略时将采用0作为开始位置,采用len(s)作为结束的位置。
  • 5、字符串可以用==和<进行比较;比较通过逐个字节比较完成的,因此比较的结果是字符串自然编码的顺序
  • 6、字符串的值是不可变的:一个字符串包含的字节序列永远不会被改变,追加或者剔除只会生成新的字符串
  • 7、因为字符串是不可修改的,因此尝试修改字符串内部数据的操作也是被禁止的,比如修改S[0] = ‘i’ 编译错误。
  • 8、不变性意味如果两个字符串共享相同的底层数据的话也是安全的,这使得复制任何长度的字 符串代价是低廉的。同样,一个字符串s和对应的子字符串切片s[7:]的操作也可以安全地共享相同的内存,因此字符串切片操作代价也是低廉的。在这两种情况下都没有必要分配新的内存。
发布了82 篇原创文章 · 获赞 15 · 访问量 3114

猜你喜欢

转载自blog.csdn.net/qq_34326321/article/details/104108008