new
new(type)返回的是类型type对象的指针
和直接通过var声明变量一样,new会根据不同的类型,赋予相应的零值(未初始化)
例如new([]int)得到的是一个nil切片的头指针
make
make函数只能用于创建切片、map和通道channel变量,并且会对这些变量进行初始化,不再是零值,可以直接进行使用,例如make 生成的切片可以直接赋值使用。
make返回的是变量而不是指针,由于make的变量都是些数据结构,所以必须要初始化,保证后续代码对这些变量的正确使用。
new和make的区别
var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful
var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints
打印函数
Go的标准库fmt中包含了许多用于打印的函数
例如Print,Println,Sprintf,Fprintf等等,其中Sprintf返回的是一个字符串,而不是像C++那样将格式化后的字符串写入缓冲区中。
下面的几种打印方式,结果是一样的。
fmt.Printf("Hello %d\n", 23)
fmt.Fprint(os.Stdout, "Hello ", 23, "\n")
fmt.Println("Hello", 23)
fmt.Println(fmt.Sprint("Hello ", 23))
注意到Fprintf的第一个参数是一个实现了io.Writer接口的类型。
格式化字符:
| **格式 ** | 含义 |
|---|---|
| %% | 一个%字面量 |
| %b | 一个二进制整数值(基数为2),或者是一个(高级的)用科学计数法表示的指数为2的浮点数 |
| %c | 字符型。可以把输入的数字按照ASCII码相应转换为对应的字符 |
| %d | 一个十进制数值(基数为10) |
| %e | 以科学记数法e表示的浮点数或者复数值 |
| %E | 以科学记数法E表示的浮点数或者复数值 |
| %f | 以标准记数法表示的浮点数或者复数值 |
| %g | 以%e或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出 |
| %G | 以%E或者%f表示的浮点数或者复数,任何一个都以最为紧凑的方式输出 |
| %o | 一个以八进制表示的数字(基数为8) |
| %p | 以十六进制(基数为16)表示的一个值的地址,前缀为0x,字母使用小写的a-f表示 |
| %q | 使用Go语法以及必须时使用转义,以双引号括起来的字符串或者字节切片[]byte,或者是以单引号括起来的数字 |
| %s | 字符串。输出字符串中的字符直至字符串中的空字符(字符串以'\0‘结尾,这个'\0'即空字符) |
| %t | 以true或者false输出的布尔值 |
| %T | 使用Go语法输出的值的类型 |
| %U | 一个用Unicode表示法表示的整型码点,默认值为4个数字字符 |
| %v | 使用默认格式输出的内置或者自定义类型的值,或者是使用其类型的String()方式输出的自定义值,如果该方法存在的话 |
| %x | 以十六进制表示的整型值(基数为十六),数字a-f使用小写表示 |
| %X | 以十六进制表示的整型值(基数为十六),数字A-F使用小写表示 |
o := 0666
fmt.Printf("%d %[1]o %#[1]o\n", o) // "438 666 0666"
x := int64(0xdeadbeef)
fmt.Printf("%d %[1]x %#[1]x %#[1]X\n", x)
// Output:
// 3735928559 deadbeef 0xdeadbeef 0XDEADBEEF
请注意fmt的两个使用技巧。通常Printf格式化字符串包含多个%参数时将会包含对应相同数量的额外操作数,但是%之后的[1]副词告诉Printf函数再次使用第一个操作数。第二,%后的#副词告诉Printf在用%o、%x或%X输出时生成0、0x或0X前缀。
当打印一个结构体时,使用%+v同时打印结构体的字段名和该字段对应的值,使用%#v则能够打印出Go中完整的符号:
m2:= map[int]string{2:"world"}
fmt.Printf("%+v\n", m2)
fmt.Printf("%#v\n", m2)
//output
map[2:world]
map[int]string{2:"world"}
如果想通过%v按照自己想要的输出内容打印自己定义的类型,可以为自己的类型添加String() string 方法,fmt包打印时会调用这个函数。
type newStruct struct {
a, b int
}
func (ns *newStruct) String() string {
return fmt.Sprintf("In newStruct a is %d b is %d", ns.a, ns.b)
}
func main(){
ns := new(newStruct)
ns.a = 123
ns.b = 456
fmt.Printf("%v\n", ns)
}
//output
In newStruct a is 123 b is 456
如果说想通过%v既可以自定义打印对象指针,也可以打印对象,那么String方法的接收者必须为该类型的值类型,而不是指针类型:
type newStruct struct {
a, b int
}
func (ns newStruct) String() string {
return fmt.Sprintf("In newStruct a is %d b is %d", ns.a, ns.b)
}
func main(){
ns := new(newStruct)
ns.a = 123
ns.b = 456
nns := newStruct{a: 777, b: 888}
fmt.Printf("%v\n", ns)
fmt.Printf("%v\n", nns)
}
//output
In newStruct a is 123 b is 456
In newStruct a is 777 b is 888
Sprintf函数以%s字符串的形式打印一个类型对象时,也会去调用该类型的实现的String方法,因为它想要一个字符串结果,所以,要注意不能在实现类型A的String函数的内部调用Sprintf去打印A,因为会一直递归调用String。如果真要在String函数内部打印类型A对象本身,一个简单的方法就是强制转换成没有实现String函数的类型,例如基本的string类型就没有实现该方法:
type MyString string
func (m MyString) String() string {
return fmt.Sprintf("MyString=%s", string(m)) // OK: note conversion.
}
fmt包的打印函数都可以接收多个参数,打印多个变量的内容,因为空接口类型可以接收任何类型的对象,所以这些函数原型大致如下:
func Printf(format string, v ...interface{})
Within the function Printf, v acts like a variable of type []interface{} but if it is passed to another variadic function, it acts like a regular list of arguments.
其中...代表这个变量其实是一个包,作为参数时可以理解成v是一个包,而在函数内部使用时,可以看成是一个列表:
// Println prints to the standard logger in the manner of fmt.Println.
func Println(v ...interface{}) {
std.Output(2, fmt.Sprintln(v...)) // Output takes parameters (int, string)
}
在这个参数的函数内部,这个变量如同一个切片一样,而如果把他传给另外一个支持可变参数的函数,那么这个就可以看成是一个参数列表,所以上面的Sprintln中的v后缀必须要加...,不然传给这个函数的v是一个值,而不是一个列表(包)。
其实对于这种...的参数,可以看成是一个切片:
func listParams(a ...int) {
for _, val := range a {
fmt.Println(val)
}
}
func main(){
listParams(1,2,3)
}
//output
1
2
3