变量使用注意事项
- 变量表示内存中的一个存储区域
 - 该区域有自己的名称(变量名)和类型(数据类型)
 - golang使用变量的3种方式:
- 指定变量类型,声明后若不赋值,则使用默认值
 
func main() { // int的默认值是0,其他数据类型的默认值在后面介绍 var i int fmt.Println("i=",i) }- 根据值自行判断变量类型(类型推导)
 
func main() { var i = 10.12 fmt.Println("i=",i) }- 省略var,
:=左边的变量不应该是已经声明过的,否则会导致编译错误 
func main() { i := "tom" fmt.Println("i=",i) } - 多变量声明
- 一次性声明多个局部变量
 
func main() { // 方式1: // var x, y, z int // fmt.Println("x=",x, "y=",y, "z=",z) // 方式2: // var x, y, z = 100, "tom", 888 // fmt.Println("x=",x, "y=",y, "z=",z) // 方法3: x, y, z := 100, "tom", 888 fmt.Println("x=",x, "y=",y, "z=",z) }- 一次性声明多个全局变量
 
// 方法1: //var x = 100 //var y = "tom" //var z = 888 // 方法2: var( x = 100 y = "tom" z = 888 ) func main() { fmt.Println("x=",x, "y=",y, "z=",z) } - 该区域的数据值可以在同一类型范围内不断变化
 
package main
import "fmt"
func main () {
    var i int =10
    i = 20
    i = 30
    fmt.Printf("i=",i)
    i = 1.2  // i的类型变为float,不能改变数据类型
}
- 变量在同一作用域(在一个函数或者代码块)内不能重名
 
func main () {
	var i int = 10
	i := 30 // 变量名称重复
}
- 变量=变量名+数据类型+值(自定义值或者默认值)
 - golang的变量如果没有赋初始值,编译器会使用默认值,比如int默认值为0、string默认值为空串、float默认值为0
 
变量的声明、初始化和赋值
- 声明变量
基本语法var 变量名 数据类型或者var 变量名 := 变量初始值(自动推导出变量类型)
例如var a int声明int类型的变量a,var num1 float32声明一个单精度类型的变量num1 - 初始化变量
即在声明变量的时候就赋值
例如var a int = 45,如果声明时就赋值,可以省略数据类型var a = 45 - 变量赋值
声明变量时不初始化var a int,然后再赋值a = 45,即变量赋值 
程序中“+”使用
- 当左右两边都是数值型时,则做加法运算
 - 当左右两边都是字符串时,则做字符串拼接
 
数据类型基本介绍
数据类型分为两大类,基本数据类型和派生/复杂数据类型:
1. 基本数据类型
分类
- 数值型
- 整数类型
- int(有符号)
- int8,有符号,占1字节,范围
-2^7~2^7-1 - int16,有符号,占2字节,范围
-2^15~2^15-1 - int32,有符号,占4字节,范围
-2^31~2^31-1 - int64,有符号,占8字节,范围
-2^63~2^63-1 
 - int8,有符号,占1字节,范围
 - uint(无符号)
- uint8,无符号,占1字节,范围
0~2^8-1 - uint16,无符号,占2字节,范围
0~2^16-1 - uint32,无符号,占4字节,范围
0~2^32-1 - uint64,无符号,占8字节,范围
0~2^64-1 
 - uint8,无符号,占1字节,范围
 - 常用类型说明
- int,有符号
- 32位系统占4字节,范围
-2^31~2^31-1 - 64位系统占8字节,范围
-2^63~2^63-1 
 - 32位系统占4字节,范围
 - uint,无符号
- 32位系统占4字节,范围
0~2^32-1 - 64位系统占8字节,范围
0~2^64-1 
 - 32位系统占4字节,范围
 - rune,等价int32,表示一个Unicode码
 - byte,等价uint8,但需要存储字符时选用byte
 
 - int,有符号
 
 - int(有符号)
 - 浮点类型
- float32,单精度,占4字节
 - float64,双精度,占8字节
 
 
 - 整数类型
 - 字符型(没有专门的字符型,使用byte来保存的那个字母字符)
 - 布尔型bool,只允许取值true或者false,占1字节,适用于逻辑运算,一般用于流程控制
 - 字符串string
 
整型使用细节
- 整数类型分有符号和无符号,int和uint的大小和系统有关
 - 整型默认声明为int型(即推导类型时,默认为int),例如
var n1 = 100,中的变量n1的数据类型默认为int - 查看某个变量的字节大小和数据类型
 
func main () {
	var n2 int64 = 10
    // %T查看变量的类型;unsafe.Sizeof()是unsafe包的一个函数,返回变量的占用字节数
	fmt.Printf("n2 的类型 %T , n2 占用的字节数 %d",n2,unsafe.Sizeof(n2))
}
- 整型变量在使用时,原则是保小不保大,即在保证程序正确运行下,尽量使用占用空间小的数据类型,例如年龄可以使用byte类型
 - bit计算机中最小的存储单位,byte是计算机中存储的基本单元
 
浮点型使用细节
- 浮点数在机器中存放形式:浮点数=符号位+指数位+尾数位
浮点数都是有符号的 - 尾数部分可能丢失,造成精度损失
float64的精度比float32的要准确,如果要保存精度高的数据,则首选float64。通常情况下也建议使用float64 - 浮点类型有固定的范围和字段长度,不受具体OS的影响
 - 浮点型默认声明位float64类型
 - 浮点型常量有两种表示形式:十进制例如5.12和科学计数法例如5.12e2
 
字符型使用细节
golang中没有专门的字符串类型,如果要存储单个字符(字母),一般使用byte来保存。
字符串就是一串固定长度的字符串连接起来的字符序列。go的字符产是由单个字节连接起来的,也就是说对于传统的字符串是由字符组成的,而go的字符串不同,它是由字节组成的。
byte类型的值使用的是单引号,而不是双引号。单引号代表byte,双引号是字符串string
func main () {
	var c1 byte = 'a'
	var c2 byte = '1'
	// 当直接输出byte值,输出的是对应字符的ASCII码值
	fmt.Printf("c1=",c1,"c2=",c2)
	// 如果希望输出对应的字符,则需要使用格式化输出
	fmt.Printf("\nc1=%c c2=%c\n",c1,c2)
	// var c3 byte = '我' //overflow溢出,“我”类型为rune(等价int32)
	var c3 int = '我'
	fmt.Printf("c3=%c c3对应的码值%d",c3,c3)
}
- 如果保存的字符在ASCII码表中,比如0-9、a-z、A-Z直接可以保存为byte;如果对应的码值大于255,则需要使用int类型保存;如果需要输出字符而不是码值,则需要进行格式化输出,即
fmt.Printf("%c",c1...) - 字符常量是用单引号
''括起来的单个字符,例如var c1 byte = 'a'、var c2 int = '中'、var c3 byte = '9' - golang使用utf-8编码,查询字符对应的utf-8编码http://www.mytju.com/classcode/tools/encode_utf8.asp
英文字符占1字节,中文字符占3字节 - 字符的本质是一个整数,所以直接输出时,输出的是该字符对应的utf-8编码的码值
 - 可以直接给某个变量赋一个数字,然后按格式化输出
%c,会输出该数字对应的unicode字符 - 字符类型是可以进行运算的,相当于一个整数,因为它对应有Unicode
 - 字符型本质:
- 字符型存储到计算机中,需要将字符对应的码值(整数)找出来
- 存储:字符--对应码值--二进制--存储
 - 读取:二进制--码值--字符--读取
 
 - 字符和码值的对应关系是通过字符编码表决定的
 - go语言的编码都统一成utf8,非常方便
 
 - 字符型存储到计算机中,需要将字符对应的码值(整数)找出来
 
字符串使用细节
- golang的字符串的字节使用utf-8编码表示Unicode文本,不会再有中文乱码的问题
 - 字符串一旦赋值,就无法修改了,即在golang中字符串时不可变的
 
func main () {
	var c1 string = "abcdef"
	c1[0] = 'x'  //无法修改字符串内容,提示报错
	fmt.Printf("c1= %s",c1)
}
- 字符串的两种表示形式
- 双引号,会识别转义字符,例如
"abc\n123" - 反引号,以字符串的原生形式输出,包括换行和特殊字符,可以实现防止攻击、输出源代码等效果,类似linux的shell中的单引号
 
 - 双引号,会识别转义字符,例如
 - 字符串拼接方式
 
func main () {
	var c1 = "hello " + "world"
	c1 += " haha!"
	fmt.Printf("c1= %s",c1)
}
- 当一行字符产太长时,需要使用到多行字符串,可以分行写,但是
+号必须保留在上一行 
func main () {
	var c1 = "hello " + "world" +
	" haha!"
	fmt.Printf("c1= %s",c1)
}
2. 派生/复杂数据类型
分类
- 指针pointer
 - 数组
 - 结构体struct
 - 管道channel
 - 函数
 - 切片slice
 - 接口interface
 - map
 
基本数据类型的默认值
| 数据类型 | 默认值 | 
|---|---|
| 整型int | 0 | 
| 浮点型float | 0.0 | 
| 字符串string | "" | 
| 布尔型bool | false | 
func main () {
	var a int
	var b float32
	var c float64
	var d bool
	var e string
	// %v 表示按照变量的值输出
	fmt.Printf("默认值 int=%d float32=%v float64=%v bool=%v string=%v",a,b,c,d,e)
}
// 默认值 int=0 float32=0 float64=0 bool=false string=
基本数据类型的相互转换
golang在不同类型的变量之间赋值时需要显式转换,即golang中数据类型不能自动转换
转换方式:T(v),表示将值v转换成类型T
func main () {
	var a int32 =100
	var b float32 = float32(a)
	var c int8 = int8(a)
	var d int64 = int64(a)
	var e string = string(a)
	// %v 表示按照变量的值输出
	fmt.Printf("数据类型转换 int32=%v float32=%v int8=%v int64=%v string=%v",a,b,c,d,e)
}
//数据类型转换 int32=100 float32=100 int8=100 int64=100 string=d
由上可见:
- 数据类型转换,可以从精度高<-->精度低 或者 范围大<-->范围小进行双向转换。但是在转换时需要考虑数据类型所能表示的范围,否则由范围大转向范围小的类型例如int64转换成int8,在编译时不会报错,只是转换的结果按照溢出处理
 - 被转换的是变量存储的数据,变量本身的数据类型并没有变化
原因是同一个变量无法进行第2次声明,即第一次声明之后其类型固定 - 不同类型的数据不能相加,需要先转成同一类型之后才能进行相加操作
 
// 溢出处理:
func main () {
	var a int64 = 9999
	var b int8 = int8(a)
	// %v 表示按照变量的值输出
	fmt.Printf("b= %v",b)
}
//b= 15
//--------------------------------------
// 先转换类型再操作:
func main () {
	var a int32 = 10
	var b int64
	var c int8
	b = int64(a) + 1  // b=a+1会报错,因为b和a数据不同数据类型
	c = int8(b) + 2
	// %v 表示按照变量的值输出
	fmt.Printf("b=%v c=%v",b,c)
}
// b=11 c=13
基本数据类型和string之间转换
基本数据类型-->string
- 方法1(更通用更灵活)
fmt.Sprintf("%参数",表达式),Sprintf根据format参数生成格式化的字符串并返回该字符串 - 方法2
使用strconv包的函数,大致的格式为strconv.FormatXXX() 
import (
	"fmt"
	"strconv"
)
func main () {
	// 第一种方式
	var num1 int = 99
	var num2 float64 = 23.456
	var b bool = true
	var myChar byte = 'h'
	var str string //空的str
	//使用第一种方式来转换 fmt.Sprintf方法
	str = fmt.Sprintf("%d", num1)
	fmt.Printf("str type %T \tstr=%q\n", str, str)	// str type string 	str="99"
	str = fmt.Sprintf("%f", num2)
	fmt.Printf("str type %T \tstr=%q\n", str, str)	// str type string 	str="23.456000"
	str = fmt.Sprintf("%t", b)
	fmt.Printf("str type %T \tstr=%q\n", str, str)	// str type string 	str="true"
	str = fmt.Sprintf("%c", myChar)
	fmt.Printf("str type %T \tstr=%q\n", str, str)	// str type string 	str="h"
	
	//第二种方式 strconv 函数
	var num3 int = 99
	var num4 float64 = 23.456
	var num5 int64 = 4567
	var b2 bool = true
	str = strconv.FormatInt(int64(num3), 10)
	fmt.Printf("str type %T \tstr=%q\n", str, str)	// str type string 	str="99"
	// strconv.FormatFloat(num4, 'f', 10, 64)
	// 说明: 'f' 格式 10:表示小数位保留10位 64 :表示这个小数是float64
	str = strconv.FormatFloat(num4, 'f', 10, 64)
	fmt.Printf("str type %T \tstr=%q\n", str, str)	// str type string 	str="23.4560000000"
	str = strconv.FormatBool(b2)
	fmt.Printf("str type %T \tstr=%q\n", str, str)	// str type string 	str="true"
	//strconv包中有一个函数Itoa
	str = strconv.Itoa(int(num5))
	fmt.Printf("str type %T \tstr=%q\n", str, str)	// str type string 	str="4567"
}
string-->基本数据类型
使用strconv包的函数,大致的格式为strconv.ParseXXX()
注意事项:
- 默认返回的数据类型为int64或者float64,想转成其他数据类型则需要进一步转换
 - 将string转换成基本数据类型时,需要确保string类型能够转换成有效的数据,比如可以把“123”转换成整数,但是无法将“hello”转换成整数,此时golang直接将其转换成0,其他类型类似,float为0、bool为false
 
import (
	"fmt"
	"strconv"
)
func main () {
	var str string = "true"
	var b bool
	// b, _ = strconv.ParseBool(str)
	// 说明
	// 1. strconv.ParseBool(str) 函数会返回两个值 (value bool, err error)
	// 2. 只想获取到 value bool ,不想获取 err ,所以使用_忽略
	b , _ = strconv.ParseBool(str)
	fmt.Printf("b type %T  b=%v\n", b, b)	// b type bool  b=true
	var str2 string = "1234590"
	var n1 int64
	var n2 int
	n1, _ = strconv.ParseInt(str2, 10, 64)
	n2 = int(n1)
	fmt.Printf("n1 type %T  n1=%v\n", n1, n1)	// n1 type int64  n1=1234590
	fmt.Printf("n2 type %T n2=%v\n", n2, n2)	// n2 type int n2=1234590
	var str3 string = "123.456"
	var f1 float64
	f1, _ = strconv.ParseFloat(str3, 64)
	fmt.Printf("f1 type %T f1=%v\n", f1, f1)  // f1 type float64 f1=123.456
    // 因为返回的只能是int64或者float64,如果希望得到int16、int32、float32等基本数据类型的数据,则需要进一步转换处理:
    // var f2 float32
    // f2 = float32(f1)
	//注意:
	var str4 string = "hello"
	var n3 int64 = 11
	n3, _ = strconv.ParseInt(str4, 10, 64)
	fmt.Printf("n3 type %T n3=%v\n", n3, n3)  // n3 type int64 n3=0
}
指针
基本介绍
- 基本数据类型,变量存的就是值,也叫值类型
 - 值类型都有对应的指针类型,形式为
*数据类型,比如int对应的指针类型就是*int - 使用
&获取变量的地址,比如var num int,获取变量num的地址方式为&num 
func main () {
	var str int = 100
	fmt.Printf("str地址=",&str)	// str地址=%!(EXTRA *int=0xc000012078)
}
- 指针类型,指针变量存的是一个地址,这个地址指向的空间存的才是值,例如
var ptr *int = &num - 获取指针类型变量所指向的值,使用
*,例如*ptr获取ptr指向的值。下面的代码详细说明了指针在内存中的布局情况 
func main () {
	//基本数据类型在内存布局
	var i int = 10
	// i 的地址是什么,&i
	fmt.Println("i的地址=", &i)	//i的地址= 0xc0000ac058
	//下面的 var ptr *int = &i
	//1. ptr 是一个指针变量
	//2. ptr 的类型 *int
	//3. ptr 本身的值&i
	var ptr *int = &i
	fmt.Printf("ptr=%v\n", ptr)	//ptr=0xc0000ac058
	fmt.Printf("ptr 的地址=%v\n", &ptr)	//ptr 的地址=0xc0000d8020
	fmt.Printf("ptr 指向的值=%v\n", *ptr)	//ptr 指向的值=10
	// 修改i在内存中的值
	*ptr = 99
	fmt.Printf("i 修改之后内存中的值=%v", *ptr)	//i 修改之后内存中的值=99
}

为什么要存在指针?
因为变量的值在声明过程中或者赋值过程中已经确定,之后无法进行修改。如果想要修改该变量的值,则需要直接进行内存级别的操作,修改内存中的值,首先需要获取这个变量存储的值在内存中的地址,然后才能进行内存操作,这个内存中的地址就是指针。
简而言之,指针存在的意义就是为了对值类型变量进行操作
值类型和引用类型
值类型
- 包含基本数据类型(int系列、float系列、bool、string)、数组、结构体struct
 - 变量直接存储值,内存通常在
栈中分配

 
引用类型
包含指针、切片slice、map、管道chan、interface等
变量存储的是一个地址,这个地址对应的空间才真正存储数据值,内存通常在堆上分配,当没有任何变量引用这个地址时,该地址对应的数据空间就成为一个垃圾,由GC来回收


标识符
标识符命名规则
- 由0-9、a-z、A-Z、_组成,且开头不可以是数字
 - golang中严格区分大小写,且标识符中不能包含空格
 - 下划线
_本身在go中是一个特殊的标识符,称为空标识符,可以代表任何其他标识符,但是它对应的值会被忽略(例如,忽略某个返回值时可以使用_),因此,仅能作为占位符使用,无法作为标识符使用 - 不能以系统保留关键字(25个)和预定义标识符(36个)作为自定义标识符,系统保留关键字和预定义标识符如下:
- 系统保留关键字
简化代码编译过程中对代码的解析- break:强制退出循环或switch语句块。跳出当前循环、switch、select,switchselect默认break,如果不希望break,使用fallthrough,以上这些结构可以用标签命名,break+标签名,可以跳出多层结构
 - case:用于switch和select语句块中,后跟一个表达式,表示一个待匹配项
 - chan:用于定义channel的关键字,,要用于不同协程直接通信,类似linux管道
 - const:常量关键字
 - continue:跳过本轮循环,进入下一轮循环迭代。continue也可以使用标签,忽略当前循环结构,直接继续执行标签指定的循环
 - default:switch和switch语句块中定义的默认项
 - defer:延迟调用关键字。在方法结束(包括异常)时调用,同一个方法内可以定义多个defer,调用顺序和定义相反。注意,defer后跟的表达式是定义时计算的,不是调用时计算的:
 
func Defer() { i := 1 defer println("first", i+1) i += 1 defer println("second", i) } //输出 //second 2 //first 2- else:选择判断语句关键字
 - fallthrough:switch语句块中使一个case子块执行完毕后,继续执行下一个case子块的代码
 - for:循环体语句块关键字
 - func: 定义函数和方法的关键字
 - go:开启goroutine协程,后面直接跟方法调用
 - goto:流程控制关键字,可以讲程序执行流程跳转到一个精确的位置
 - if:选择判断语句关键字
 - import:导入包,golang目前是不支持循环引用的,例如A import B、B 不能再引用A,A import B、B import C、C import A 也是不可以的
 - interface:接口类型
 - map:内置map类型,哈希字典
 - package:定义包名
 - range:定义迭代范围,常用在循环语句块中
 - return:结束函数指定,使函数返回
 - select: 用于定义通信控制结构的关键字
 - struct: 结构体结构关键字
 - switch:多重选择判断结构语句块关键字
 - type:自定义类型关键字,即声明结构体、接口、类型和类型别名。声明一个类型,是作为一个新的类型使用,虽然本质上和原类型没有区别,但是无法直接当作原类型使用,而别名和原类型使用起来完全相同
 
type A = int //int的别名 type B int //新类型b var a A = 1 var b B = 1 func GetSize(i int) { println(unsafe.Sizeof(i)) } //64位机 GetSize(a) //输出8 GetSize(b) //无法通过编译,不能直接把B类型当作int使用 GetSize(int(b)) //输出8,可以强制转换 GetSize(*(*int)(unsafe.Pointer(&b)))//输出8,Pointer当然也是可以的- var:变量定义关键字
 
 - 预定义标识符
包括基础数据类型和系统内嵌函数- 内建常量: true false iota nil
 - 内建类型: int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr float32 float64 complex64 complex128 bool byte rune string error
 - 内建函数: make len cap new append copy close delete complex real imag panic recover
 
 
 - 系统保留关键字
 
fmt(格式化IO)
fmt包实现了类似C语言printf和scanf的格式化I/O,格式化动作('verb')源自C语言但更简单。
type user struct {
    name string
}
func main() {
    u := user{"tang"}
    //Printf 格式化输出
    fmt.Printf("% + v\n", u)     //格式化输出结构
    fmt.Printf("%#v\n", u)       //输出值的 Go 语言表示方法
    fmt.Printf("%T\n", u)        //输出值的类型的 Go 语言表示
    fmt.Printf("%t\n", true)     //输出值的 true 或 false
    fmt.Printf("%b\n", 1024)     //二进制表示
    fmt.Printf("%c\n", 11111111) //数值对应的 Unicode 编码字符
    fmt.Printf("%d\n", 10)       //十进制表示
    fmt.Printf("%o\n", 8)        //八进制表示
    fmt.Printf("%q\n", 22)       //转化为十六进制并附上单引号
    fmt.Printf("%x\n", 1223)     //十六进制表示,用a-f表示
    fmt.Printf("%X\n", 1223)     //十六进制表示,用A-F表示
    fmt.Printf("%U\n", 1233)     //Unicode表示
    fmt.Printf("%b\n", 12.34)    //无小数部分,两位指数的科学计数法6946802425218990p-49
    fmt.Printf("%e\n", 12.345)   //科学计数法,e表示
    fmt.Printf("%E\n", 12.34455) //科学计数法,E表示
    fmt.Printf("%f\n", 12.3456)  //有小数部分,无指数部分
    fmt.Printf("%g\n", 12.3456)  //根据实际情况采用%e或%f输出
    fmt.Printf("%G\n", 12.3456)  //根据实际情况采用%E或%f输出
    fmt.Printf("%s\n", "wqdew")  //直接输出字符串或者[]byte
    fmt.Printf("%q\n", "dedede") //双引号括起来的字符串
    fmt.Printf("%x\n", "abczxc") //每个字节用两字节十六进制表示,a-f表示
    fmt.Printf("%X\n", "asdzxc") //每个字节用两字节十六进制表示,A-F表示
    fmt.Printf("%p\n", 0x123)    //0x开头的十六进制数表示
}
格式化动作分类:
- 通用
 
%v	值的默认格式表示
%+v	类似%v,但输出结构体时会添加字段名
%#v	值的Go语法表示
%T	值的类型的Go语法表示
%%	百分号
- 布尔值
 
%t	单词true或false
- 整数
 
%b	表示为二进制
%c	该值对应的unicode码值
%d	表示为十进制
%o	表示为八进制
%q	该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
%x	表示为十六进制,使用a-f
%X	表示为十六进制,使用A-F
%U	表示为Unicode格式:U+1234,等价于"U+%04X"
没有%u。整数如果是无符号类型自然输出也是无符号的
类似的,也没有必要指定操作数的尺寸(int8,int64)
宽度通过一个紧跟在百分号后面的十进制数指定,如果未指定宽度,则表示值时除必需之外不作填充。
精度通过(可选的)宽度后跟点号后跟的十进制数指定。
如果未指定精度,会使用默认精度;如果点号后没有跟数字,表示精度为0。例如:
%f:    默认宽度,默认精度
%9f    宽度9,默认精度
%.2f   默认宽度,精度2
%9.2f  宽度9,精度2
%9.f   宽度9,精度0    
宽度和精度格式化控制的是Unicode码值的数量(不同于C的printf,它的这两个因数指的是字节的数量)。
两者任一个或两个都可以使用'*'号取代,此时它们的值将被对应的参数(按'*'号和verb出现的顺序,即控制其值的参数会出现在要表示的值前面)控制,这个操作数必须是int类型。
对于大多数类型的值,宽度是输出字符数目的最小数量,如果必要会用空格填充。
对于字符串,精度是输出字符数目的最大数量,如果必要会截断字符串。
对于整数,宽度和精度都设置输出总长度。采用精度时表示右对齐并用0填充,而宽度默认表示用空格填充。
对于浮点数,宽度设置输出总长度;精度设置小数部分长度(如果有的话),除了%g和%G,此时精度设置总的数字个数。例如,对数字123.45,格式%6.2f 输出123.45;格式%.4g输出123.5。%e和%f的默认精度是6,%g的默认精度是可以将该值区分出来需要的最小数字个数。
对复数,宽度和精度会分别用于实部和虚部,结果用小括号包裹。因此%f用于1.2+3.4i输出(1.200000+3.400000i)。
- 浮点数与复数
 
%b	无小数部分、二进制指数的科学计数法,如-123456p-78;参见strconv.FormatFloat
%e	科学计数法,如-1234.456e+78
%E	科学计数法,如-1234.456E+78
%f	有小数部分但无指数部分,如123.456
%F	等价于%f
%g	根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
%G	根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)
- 字符串和字符
 
%s	直接输出字符串或者[]byte
%q	该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
%x	每个字节用两字符十六进制数表示(使用a-f)
%X	每个字节用两字符十六进制数表示(使用A-F) 
- 指针
 
%p	表示为十六进制,并加上前导的0x    
- 其他flag
 
'+'	总是输出数值的正负号;对%q(%+q)会生成全部是ASCII字符的输出(通过转义);
' '	对数值,正数前加空格而负数前加负号;
'-'	在输出右边填充空白而不是默认的左边(即从默认的右对齐切换为左对齐);
'#'	切换格式:
  	八进制数前加0(%#o),十六进制数前加0x(%#x)或0X(%#X),指针去掉前面的0x(%#p);
 	对%q(%#q),如果strconv.CanBackquote返回真会输出反引号括起来的未转义字符串;
 	对%U(%#U),输出Unicode格式后,如字符可打印,还会输出空格和单引号括起来的go字面值;
  	对字符串采用%x或%X时(% x或% X)会给各打印的字节之间加空格;
'0'	使用0而不是空格填充,对于数值类型会把填充的0放在正负号后面;