Go有四种数据类型:1、基础类型,包括整数、浮点数、复数、布尔值、字符串、常量;2、聚合类型,包括数组、结构体(一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员);3、引用类型,包括指针、slice、map、函数、通道;4、接口类型,是对其它类型行为的抽象和概括,是一种抽象的类型。
本教程操作环境:windows7系统、GO 1.18版本、Dell G3电脑。
Go的数据类型一共分为四大类:基础类型、聚合类型、引用类型和接口类型。
- 基础类型分为:整数、浮点数、复数、布尔值、字符串、常量
- 聚合类型包括:数组、结构体
- 引用类型包括:指针、slice、map、函数、通道
- 接口类型
基础类型
// int8 is the set of all signed 8-bit integers. // Range: -128 through 127. type int8 int8 // int16 is the set of all signed 16-bit integers. // Range: -32768 through 32767. type int16 int16 // int32 is the set of all signed 32-bit integers. // Range: -2147483648 through 2147483647. type int32 int32 // int64 is the set of all signed 64-bit integers. // Range: -9223372036854775808 through 9223372036854775807. type int64 int64 // uint8 is the set of all unsigned 8-bit integers. // Range: 0 through 255. type uint8 uint8 // uint16 is the set of all unsigned 16-bit integers. // Range: 0 through 65535. type uint16 uint16 // uint32 is the set of all unsigned 32-bit integers. // Range: 0 through 4294967295. type uint32 uint32 // uint64 is the set of all unsigned 64-bit integers. // Range: 0 through 18446744073709551615. type uint64 uint64 // byte is an alias for uint8 and is equivalent to uint8 in all ways. It is // used, by convention, to distinguish byte values from 8-bit unsigned // integer values. type byte = uint8 // rune is an alias for int32 and is equivalent to int32 in all ways. It is // used, by convention, to distinguish character values from integer values. type rune = int32 // int is a signed integer type that is at least 32 bits in size. It is a // distinct type, however, and not an alias for, say, int32. type int int // uint is an unsigned integer type that is at least 32 bits in size. It is a // distinct type, however, and not an alias for, say, uint32. type uint uint
// float32 is the set of all IEEE-754 32-bit floating-point numbers. type float32 float32 // float64 is the set of all IEEE-754 64-bit floating-point numbers. type float64 float64
// complex64 is the set of all complex numbers with float32 real and // imaginary parts. type complex64 complex64 // complex128 is the set of all complex numbers with float64 real and // imaginary parts. type complex128 complex128 // The complex built-in function constructs a complex value from two // The complex built-in function constructs a complex value from two // floating-point values. The real and imaginary parts must be of the same // size, either float32 or float64 (or assignable to them), and the return // value will be the corresponding complex type (complex64 for float32, // complex128 for float64). func complex(r, i FloatType) ComplexType // The real built-in function returns the real part of the complex number c. // The return value will be floating point type corresponding to the type of c. func real(c ComplexType) FloatType // The imag built-in function returns the imaginary part of the complex // number c. The return value will be floating point type corresponding to // the type of c. func imag(c ComplexType) FloatType
// bool is the set of boolean values, true and false. type bool bool // true and false are the two untyped boolean values. const ( true = 0 == 0 // Untyped bool. false = 0 != 0 // Untyped bool. )
// string is the set of all strings of 8-bit bytes, conventionally but not // necessarily representing UTF-8-encoded text. A string may be empty, but // not nil. Values of string type are immutable. type string string
// iota is a predeclared identifier representing the untyped integer ordinal // number of the current const specification in a (usually parenthesized) // const declaration. It is zero-indexed. const iota = 0 // Untyped int.
聚合类型
- 数组和结构体是聚合类型;
- 它们的值由许多元素或成员字段的值组成。
- 数组是由同构的元素组成——每个数组元素都是完全相同的类型——结构体则是由异构的元素组成的。
- 数组和结构体都是有固定内存大小的数据结构。
- 相比之下,slice和map则是动态的数据结构,它们将根据需要动态增长。
a := [2]int{1, 2} b := [...]int{1, 2} c := [2]int{1, 3} fmt.Println(a == b, a == c, b == c) // "true false false" d := [3]int{1, 2} fmt.Println(a == d) // compile error: cannot compare [2]int == [3]int
- 如果一个数组的元素类型是可以相互比较的,那么数组类型也是可以相互比较的,这时候我们可以直接通过==比较运算符来比较两个数组,只有当两个数组的所有元素都是相等的时候数组才是相等的。不相等比较运算符!=遵循同样的规则。
- 当调用一个函数的时候,函数的每个调用参数将会被赋值给函数内部的参数变量,所以函数参数变量接收的是一个复制的副本,并不是原始调用的变量。因为函数参数传递的机制导致传递大的数组类型将是低效的,并且对数组参数的任何的修改都是发生在复制的数组上,并不能直接修改调用时原始的数组变量。Call by value值传递
- 我们可以显式地传入一个数组指针,那样的话函数通过指针对数组的任何修改都可以直接反馈到调用者
- 结构体是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体。每个值称为结构体的成员。
- 如果结构体成员名字是以大写字母开头的,那么该成员就是导出的;
1, 结构体值也可以用结构体面值表示,结构体面值可以指定每个成员的值。 type Point struct{ X, Y int } p := Point{1, 2} 2, 以成员名字和相应的值来初始化,可以包含部分或全部的成员, anim := gif.GIF{LoopCount: nframes} 在这种形式的结构体面值写法中,如果成员被忽略的话将默认用零值。 3, 因为结构体通常通过指针处理,可以用下面的写法来创建并初始化一个结构体变量,并返回结构体的地址: pp := &Point{1, 2} 它是下面的语句是等价的 pp := new(Point) *pp = Point{1, 2} 不过&Point{1, 2}写法可以直接在表达式中使用,比如一个函数调用。
- 如果结构体的全部成员都是可以比较的,那么结构体也是可以比较的,那样的话两个结构体将可以使用或!=运算符进行比较。相等比较运算符将比较两个结构体的每个成员,因此下面两个比较的表达式是等价的:
type Point struct{ X, Y int } p := Point{1, 2} q := Point{2, 1} fmt.Println(p.X == q.X && p.Y == q.Y) // "false" fmt.Println(p == q) // "false"
引用类型
一个指针变量指向了一个值的内存地址。
var ip *int /* 指向整型*/ ip是一个指向int类型对象的 指针 var fp *float32 /* 指向浮点型 */ fp是一个指向float32类型对象的 指针
指针使用流程:
- 定义指针变量。
- 为指针变量赋值。
- 访问指针变量中指向地址的值。
在指针类型前面加上 * 号(前缀)来获取指针所指向的内容。
var a int= 20 /* 声明实际变量 */ var ip *int /* 声明指针变量 */ ip = &a /* 指针变量的存储地址 */ fmt.Printf("a 变量的地址是: %xn", &a ) /* 指针变量的存储地址 */ fmt.Printf("ip 变量储存的指针地址: %xn", ip ) /* 使用指针访问值 */ fmt.Printf("*ip 变量的值: %dn", *ip )
- Slice(切片)代表变长的序列,序列中每个元素都有相同的类型。
- 一个slice类型一般写作[]T,其中T代表slice中元素的类型;
- slice的语法和数组很像,只是没有固定长度而已
type slice struct { array unsafe.Pointer len int cap int }
-
多个slice可以复用同一个底层数组
// The len built-in function returns the length of v, according to its type: // Array: the number of elements in v. // Pointer to array: the number of elements in *v (even if v is nil). // Slice, or map: the number of elements in v; if v is nil, len(v) is zero. // String: the number of bytes in v. // Channel: the number of elements queued (unread) in the channel buffer; // if v is nil, len(v) is zero. // For some arguments, such as a string literal or a simple array expression, the // result can be a constant. See the Go language specification's "Length and // capacity" section for details. func len(v Type) int // The cap built-in function returns the capacity of v, according to its type: // Array: the number of elements in v (same as len(v)). // Pointer to array: the number of elements in *v (same as len(v)). // Slice: the maximum length the slice can reach when resliced; // if v is nil, cap(v) is zero. // Channel: the channel buffer capacity, in units of elements; // if v is nil, cap(v) is zero. // For some arguments, such as a simple array expression, the result can be a // constant. See the Go language specification's "Length and capacity" section for // details. func cap(v Type) int // The append built-in function appends elements to the end of a slice. If // it has sufficient capacity, the destination is resliced to accommodate the // new elements. If it does not, a new underlying array will be allocated. // Append returns the updated slice. It is therefore necessary to store the // result of append, often in the variable holding the slice itself: // slice = append(slice, elem1, elem2) // slice = append(slice, anotherSlice...) // As a special case, it is legal to append a string to a byte slice, like this: // slice = append([]byte("hello "), "world"...) func append(slice []Type, elems ...Type) []Type // The make built-in function allocates and initializes an object of type // slice, map, or chan (only). Like new, the first argument is a type, not a // value. Unlike new, make's return type is the same as the type of its // argument, not a pointer to it. The specification of the result depends on // the type: // Slice: The size specifies the length. The capacity of the slice is // equal to its length. A second integer argument may be provided to // specify a different capacity; it must be no smaller than the // length. For example, make([]int, 0, 10) allocates an underlying array // of size 10 and returns a slice of length 0 and capacity 10 that is // backed by this underlying array. // Map: An empty map is allocated with enough space to hold the // specified number of elements. The size may be omitted, in which case // a small starting size is allocated. // Channel: The channel's buffer is initialized with the specified // buffer capacity. If zero, or the size is omitted, the channel is // unbuffered. func make(t Type, size ...IntegerType) Type // The new built-in function allocates memory. The first argument is a type, // not a value, and the value returned is a pointer to a newly // allocated zero value of that type. func new(Type) *Type // The copy built-in function copies elements from a source slice into a // destination slice. (As a special case, it also will copy bytes from a // string to a slice of bytes.) The source and destination may overlap. Copy // returns the number of elements copied, which will be the minimum of // len(src) and len(dst). func copy(dst, src []Type) int // The delete built-in function deletes the element with the specified key // (m[key]) from the map. If m is nil or there is no such element, delete // is a no-op. func delete(m map[Type]Type1, key Type)
- 在Go语言中,一个map就是一个哈希表的引用,map类型可以写为map[K]V,其中K和V分别对应key和value。map中所有的key都有相同的类型,所有的value也有着相同的类型。
- 其中K对应的key必须是支持==比较运算符的数据类型,所以map可以通过测试key是否相等来判断是否已经存在。虽然浮点数类型也是支持相等运算符比较的,但是将浮点数用做key类型则是一个坏的想法。
- 对于V对应的value数据类型则没有任何的限制。
创建map: 1, 内置的make函数可以创建一个map: ages := make(map[string]int) // mapping from strings to ints 2, 我们也可以用map字面值的语法创建map,同时还可以指定一些最初的key/value: ages := map[string]int{ "alice": 31, "charlie": 34, } 这相当于 ages := make(map[string]int) ages["alice"] = 31 ages["charlie"] = 34 因此,另一种创建空的map的表达式是map[string]int{}。 Map中的元素通过key对应的下标语法访问: ages["alice"] = 32 delete(ages, "alice") // remove element ages["alice"] 所有这些操作是安全的,即使这些元素不在map中也没有关系; 如果一个查找失败将返回value类型对应的零值,例如, 即使map中不存在“bob”下面的代码也可以正常工作,因为ages["bob"]失败时将返回0。 ages["bob"] = ages["bob"] + 1 // happy birthday! 遍历map for name, age := range ages { fmt.Printf("%st%dn", name, age) }
函数声明包括函数名、形式参数列表、返回值列表(可省略)以及函数体。
func name(parameter-list) (result-list) { body }
- 如果说goroutine是Go语音程序的并发体的话,那么channels它们之间的通信机制。
- 一个channels是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息。
- 每个channel都有一个特殊的类型,也就是channels可发送数据的类型。一个可以发送int类型数据的channel一般写为chan int。
使用内置的make函数,我们可以创建一个channel: 使用内置的make函数,我们可以创建一个channel: ch := make(chan int) // ch has type 'chan int'
-
和map类似,channel也一个对应make创建的底层数据结构的引用。
-
当我们复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者何被调用者将引用同一个channel对象。和其它的引用类型一样,channel的零值也是nil。
-
两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相通的对象,那么比较的结果为真。一个channel也可以和nil进行比较。
接口类型
-
接口类型是对其它类型行为的抽象和概括;因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力。
-
很多面向对象的语言都有相似的接口概念,但Go语言中接口类型的独特之处在于它是满足隐式实现的。
-
也就是说,我们没有必要对于给定的具体类型定义所有满足的接口类型;简单地拥有一些必需的方法就足够了。
-
这种设计可以让你创建一个新的接口类型满足已经存在的具体类型却不会去改变这些类型的定义;当我们使用的类型来自于不受我们控制的包时这种设计尤其有用。
-
接口类型是一种抽象的类型。它不会暴露出它所代表的对象的内部值的结构和这个对象支持的基础操作的集合;它们只会展示出它们自己的方法。也就是说当你有看到一个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的方法来做什么。
【