网站优化是外包还是自己做,搜索引擎禁止的方式优化网站,移动端友好网站,免费建网站教程1. 常量
Go语言中的常量使用关键字const定义#xff0c;用于存储不会改变的数据#xff0c;常量是在编译时被创建的#xff0c;即使定义在函数内部也是如此#xff0c;并且只能是布尔型、数字型#xff08;整数型、浮点型和复数#xff09;和字符串型。
由于编译时的限…
1. 常量
Go语言中的常量使用关键字const定义用于存储不会改变的数据常量是在编译时被创建的即使定义在函数内部也是如此并且只能是布尔型、数字型整数型、浮点型和复数和字符串型。
由于编译时的限制定义常量的表达式必须为能被编译器求值的常量表达式。
声明格式
const name [type] value例如
const pi 3.14159type可以省略
和变量声明一样可以批量声明多个常量
const (e 2.7182818pi 3.1415926
)所有常量的运算都可以在编译期完成这样不仅可以减少运行时的工作也方便其他代码的编译优化当操作数是常量时一些运行时的错误也可以在编译时被发现例如整数除零、字符串索引越界、任何导致无效浮点数的操作等。 常量间的所有算术运算、逻辑运算和比较运算的结果也是常量对常量的类型转换操作或以下函数调用都是返回常量结果len、cap、real、imag、complex 和 unsafe.Sizeof。
因为它们的值是在编译期就确定的因此常量可以是构成类型的一部分
如果是批量声明的常量除了第一个外其它的常量右边的初始化表达式都可以省略如果省略初始化表达式则表示使用前面常量的初始化表达式对应的常量类型也是一样的。例如
const (a 1bc 2d
)
fmt.Println(a, b, c, d) // 1 1 2 21.1 iota 常量生成器 常量声明可以使用 iota 常量生成器初始化它用于生成一组以相似规则初始化的常量但是不用每行都写一遍初始化表达式。 在一个 const 声明语句中在第一个声明的常量所在的行iota 将会被置为 0然后在每一个有常量声明的行加1
比如定义星期日到星期六从0-6
const (Sunday iota //0MondayTuesdayWednesdayThursdayFridaySaturday //6
)2. 指针
指针pointer在Go语言中可以被拆分为两个核心概念
类型指针允许对这个指针类型的数据进行修改传递数据可以直接使用指针而无须拷贝数据类型指针不能进行偏移和运算。切片由指向起始元素的原始指针、元素数量和容量组成。
受益于这样的约束和拆分Go语言的指针类型变量即拥有指针高效访问的特点又不会发生指针偏移从而避免了非法修改关键性数据的问题。
同时垃圾回收也比较容易对不会发生偏移的指针进行检索和回收。
切片比原始指针具备更强大的特性而且更为安全。
切片在发生越界时运行时会报出宕机并打出堆栈而原始指针只会崩溃。
2.1 如何理解指针 var a int 10 如果用大白话来解释上述语句
在内存中开辟了一片空间空间内存放着数值10这片空间在整个内存当中有一个唯一的地址用来进行标识指向这个地址的变量就称为指针
如果用类比的说明
内存比作酒店每个房间就是一块内存上述代码表示为定了一间房间a让10住进了房间房间有一个门牌号px这个px就是房间的地址房卡可以理解为就是指针指向这个地址。 一个指针变量可以指向任何一个值的内存地址它所指向的值的内存地址在 32 和 64 位机器上分别占用 4 或 8 个字节占用字节的大小与所指向的值的大小无关。 当一个指针被定义后没有分配到任何变量时它的默认值为 nil。
每个变量在运行时都拥有一个地址这个地址代表变量在内存中的位置。
Go语言中使用在变量名前面添加操作符前缀来获取变量的内存地址取地址操作格式如下
//其中 v 代表被取地址的变量变量 v 的地址使用变量 ptr 进行接收ptr 的类型为*T称做 T 的指针类型*代表指针。
ptr : v // v 的类型为 Tpackage main
import (fmt
)
func main() {var cat int 1var str string ms的go教程fmt.Printf(%p %p, cat, str)
}变量、指针和地址三者的关系是每个变量都拥有地址指针的值就是地址 当使用操作符对普通变量进行取地址操作并得到变量的指针后可以对指针使用*操作符也就是指针取值 // 指针与变量var room int 10 // room房间 里面放的 变量10var ptr room // 门牌号px 指针 0xc00000a0a8fmt.Printf(%p\n, room) // 变量的内存地址 0xc00000a0a8fmt.Printf(%T, %p\n, ptr, ptr) // *int, 0xc00000a0a8fmt.Println(指针地址,ptr) // 0xc00000a0a8fmt.Println(指针地址代表的值, *ptr) // 10取地址操作符和取值操作符*是一对互补操作符取出地址*根据地址取出地址指向的值
变量、指针地址、指针变量、取地址、取值的相互关系和特性如下
对变量进行取地址操作使用操作符可以获得这个变量的指针变量。指针变量的值是指针地址。对指针变量进行取值操作使用*操作符可以获得指针变量指向的原变量的值。
2.2 使用指针修改值
通过指针不仅可以取值也可以修改值。
package mainfunc main(){// 利用指针修改值var num 10modifyFromPoint(num)fmt.Println(未使用指针方法外,num)var num2 22newModifyFromPoint(num2) // 传入指针fmt.Println(使用指针 方法外,num2)
}func modifyFromPoint(num int) {// 未使用指针num 10000fmt.Println(未使用指针方法内:,num)
}func newModifyFromPoint(ptr *int) {// 使用指针*ptr 1000 // 修改指针地址指向的值fmt.Println(使用指针方法内:,*ptr)
}2.3 创建指针的另一种方法
Go语言还提供了另外一种方法来创建指针变量格式如下
new(类型)str : new(string)
*str ms的go教程Go语言教程
fmt.Println(*str)new() 函数可以创建一个对应类型的指针创建过程会分配内存被创建的指针指向默认值。
2.4 指针小案例 获取命令行的输入信息 Go语言内置的 flag 包实现了对命令行参数的解析flag 包使得开发命令行工具更为简单。
package main
// 导入系统包
import (flagfmt
)
// 定义命令行参数
var mode flag.String(mode, , fast模式能让程序运行的更快)func main() {// 解析命令行参数flag.Parse()fmt.Println(*mode)
}
3. 变量的生命周期 变量的生命周期指的是在程序运行期间变量有效存在的时间间隔。 变量的生命周期与变量的作用域有不可分割的联系
全局变量它的生命周期和整个程序的运行周期是一致的局部变量它的生命周期则是动态的从创建这个变量的声明语句开始到这个变量不再被引用为止形式参数和函数返回值它们都属于局部变量在函数被调用的时候创建函数调用结束后被销毁。
go的内存中应用了两种数据结构用于存放变量
堆heap堆是用于存放进程执行中被动态分配的内存段。它的大小并不固定可动态扩张或缩减。当进程调用 malloc 等函数分配内存时新分配的内存就被动态加入到堆上堆被扩张。当利用 free 等函数释放内存时被释放的内存从堆中被剔除堆被缩减栈(stack)栈又称堆栈 用来存放程序暂时创建的局部变量也就是我们函数的大括号{ }中定义的局部变量。
栈是先进后出往栈中放元素的过程称为入栈取元素的过程称为出栈。
栈可用于内存分配栈的分配和回收速度非常快
在程序的编译阶段编译器会根据实际情况自动选择在栈或者堆上分配局部变量的存储空间不论使用 var 还是 new 关键字声明变量都不会影响编译器的选择。
var global *int
func f() {var x intx 1global x
}
func g() {y : new(int)*y 1
}上述代码中函数 f 里的变量 x 必须在堆上分配因为它在函数退出后依然可以通过包一级的 global 变量找到虽然它是在函数内部定义的。
用Go语言的术语说这个局部变量 x 从函数 f 中逃逸了。
相反当函数 g 返回时变量 y 不再被使用也就是说可以马上被回收的。因此y 并没有从函数 g 中逃逸编译器可以选择在栈上分配 *y 的存储空间也可以选择在堆上分配然后由Go语言的 GC垃圾回收机制回收这个变量的内存空间。
4. 类型别名 类型别名是 Go 1.9 版本添加的新功能主要用于解决代码升级、迁移中存在的类型兼容性问题。 格式
//TypeAlias 只是 Type 的别名本质上 TypeAlias 与 Type 是同一个类型就像一个孩子小时候有小名、乳名上学后用学名英语老师又会给他起英文名但这些名字都指的是他本人。
type TypeAlias Type还有一种是类型定义
//定义Name为Type类型 ,定义之后 Name为一种新的类型
type Name Type类型别名与类型定义表面上看只有一个等号的差异那么它们之间实际的区别有哪些呢
package main
import (fmt
)
// 将NewInt定义为int类型
type NewInt int
// 将int取一个别名叫IntAlias
type IntAlias int
func main() {// 将a声明为NewInt类型var a NewInt// 查看a的类型名 main.NewIntfmt.Printf(a type: %T\n, a)// 将a2声明为IntAlias类型var a2 IntAlias// 查看a2的类型名 int //IntAlias 类型只会在代码中存在编译完成时不会有 IntAlias 类型。fmt.Printf(a2 type: %T\n, a2)
}5. 注释
Go语言的注释主要分成两类分别是单行注释和多行注释。
单行注释简称行注释是最常见的注释形式可以在任何地方使用以//开头的单行注释多行注释简称块注释以/*开头并以*/结尾且不可以嵌套使用多行注释一般用于包的文档描述或注释成块的代码片段。
单行注释的格式如下所示
//单行注释多行注释的格式如下所示
/*
第一行注释
第二行注释
...
*/每一个包都应该有相关注释在使用 package 语句声明包名之前添加相应的注释用来对包的功能及作用进行简要说明。
同时在 package 语句之前的注释内容将被默认认为是这个包的文档说明。一个包可以分散在多个文件中但是只需要对其中一个进行注释说明即可。
6. 关键字和标识符
关键字
关键字即是被Go语言赋予了特殊含义的单词也可以称为保留字。
Go语言中的关键字一共有 25 个
breakdefaultfuncinterfaceselectcasedefergomapstructchanelsegotopackageswitchconstfallthroughifrangetypecontinueforimportreturnvar
之所以刻意地将Go语言中的关键字保持的这么少是为了简化在编译过程中的代码解析。
和其它语言一样关键字不能够作标识符使用。
标识符
标识符是指Go语言对各种变量、方法、函数等命名时使用的字符序列标识符由若干个字母、下划线_、和数字组成且第一个字符必须是字母。
下划线_是一个特殊的标识符称为空白标识符
标识符的命名需要遵守以下规则
由 26 个英文字母、0~9、_组成不能以数字开头例如 var 1num int 是错误的Go语言中严格区分大小写标识符不能包含空格不能以系统保留关键字作为标识符比如 breakif 等等。
命名标识符时还需要注意以下几点
标识符的命名要尽量采取简短且有意义不能和标准库中的包名重复为变量、函数、常量命名时采用驼峰命名法例如 stuName、getVal
在Go语言中还存在着一些特殊的标识符叫做预定义标识符如下表所示
appendboolbytecapclosecomplexcomplex64complex128uint16copyfalsefloat32float64imagintint8int16uint32int32int64iotalenmakenewnilpanicuint64printprintlnrealrecoverstringtrueuintuint8uintptr
预定义标识符一共有 36 个主要包含Go语言中的基础数据类型和内置函数这些预定义标识符也不可以当做标识符来使用。
7. 运算符优先级 所谓优先级就是当多个运算符出现在同一个表达式中时先执行哪个运算符。 Go语言有几十种运算符被分成十几个级别有的运算符优先级不同有的运算符优先级相同请看下表。
优先级分类运算符结合性1逗号运算符,从左到右2赋值运算符、、-、*、/、 %、 、 、、^、|从右到左3逻辑或||从左到右4逻辑与从左到右5按位或|从左到右6按位异或^从左到右7按位与从左到右8相等/不等、!从左到右9关系运算符、、、从左到右10位移运算符、从左到右11加法/减法、-从左到右12乘法/除法/取余*乘号、/、%从左到右13单目运算符!、*指针、 、、–、正号、-负号从右到左14后缀运算符( )、[ ]、-从左到右
注意优先级值越大表示优先级越高。
一下子记住所有运算符的优先级并不容易还好Go语言中大部分运算符的优先级和数学中是一样的大家在以后的编程过程中也会逐渐熟悉起来。如果实在搞不清可以加括号就像下面这样
d : a (b * c)括号的优先级是最高的括号中的表达式会优先执行这样各个运算符的执行顺序就一目了然了。
8. 字符串与其他数据类型的转换
整数 与 字符串 // 字符串与其他类型的转换// str 转 intnewStr1 : 1intValue, _ : strconv.Atoi(newStr1)fmt.Printf(%T,%d\n, intValue, intValue) // int,1// int 转 strintValue2 : 1strValue : strconv.Itoa(intValue2)fmt.Printf(%T, %s\n, strValue, strValue)浮点数 与字符串
// str 转 floatstring3 : 3.1415926f,_ : strconv.ParseFloat(string3, 32)fmt.Printf(%T, %f\n, f, f) // float64, 3.141593//float 转 string
floatValue : 3.1415926
//4个参数1要转换的浮点数 2. 格式标记b、e、E、f、g、G
//3. 精度 4. 指定浮点类型32:float32、64:float64
// 格式标记
// ‘b’ (-ddddp±ddd二进制指数)
// ‘e’ (-d.dddde±dd十进制指数)
// ‘E’ (-d.ddddE±dd十进制指数)
// ‘f’ (-ddd.dddd没有指数)
// ‘g’ (‘e’:大指数‘f’:其它情况)
// ‘G’ (‘E’:大指数‘f’:其它情况)
//
// 如果格式标记为 ‘e’‘E’和’f’则 prec 表示小数点后的数字位数
// 如果格式标记为 ‘g’‘G’则 prec 表示总的数字位数整数部分小数部分
formatFloat : strconv.FormatFloat(floatValue, f, 2, 64)
fmt.Printf(%T,%s,formatFloat,formatFloat)