This is a creation in Article, where the information may have evolved or changed.
Although Golang is implemented in C and is called the next generation of C, the difference between Golang and C is still very large. It defines a rich set of data types and data structures that are either directly mapped to the C data type or are implemented using C struct. Understanding the Golang data type and the underlying implementation of the structure will help us better understand Golang and write better-quality code.
Base type
SOURCE in: $GOROOT/src/pkg/runtime/runtime.h. Let's take a look at the underlying type:
/* * basic types */typedef signed char int8;typedef unsigned char uint8;typedef signed short int16;typedef unsigned short uint16;typedef signed int int32;typedef unsigned int uint32;typedef signed long long int int64;typedef unsigned long long int uint64;typedef float float32; typedef double float64; #ifdef _64BITtypedef uint64 uintptr; Typedef int64 intptr;typedef int64 intgo; // go ' S inttypedef uint64 uintgo; // go ' s uint# Elsetypedef uint32 uintptr;typedef int32 intptr;typedef int32 intgo; // go ' s inttypedef uint32 uintgo; // go ' s uint#endif/* * Defined types */tyPedefuint8bool;typedefuint8byte;
int8, Uint8, Int16, UInt16, Int32, UInt32, Int64, UInt64, float32, and float64 correspond to the type of C, which is easy to see as long as there is a C basis. UIntPtr and IntPtr are unsigned and signed pointer types, and ensure that 64-bit platforms are 8 bytes, 4 bytes on 32-bit platforms, and UINTPTR is primarily used for pointer operations in Golang. The reason why Intgo and Uintgo are not named int and UINT is because int is a type name in C, presumably Uintgo is to correspond to the naming of Intgo. Intgo and Uintgo correspond to int and uint in Golang. As you can see from the definition, int and UINT are variable size types, 8 bytes on 64-bit platforms and 4 bytes on 32-bit platforms. So if there is a clear request, you should choose Int32, Int64 or UInt32, UInt64. The underlying type of the byte type is uint8. You can look at the test:
Package Mainimport ("FMT" "reflect") func main () {var b byte = ' D ' FMT. Printf ("Output:%v\n", reflect. TypeOf (b). Kind ())}
$ cd $GOPATH/src/basictype_test$ go build$./basictype_testoutput:uint8
The data type is divided into static and underlying types, and byte is its static type, relative to the variable B in the above code, and uint8 is its underlying type. This is important, and this concept is often used in the future.
Rune Type
Rune is an alias for Int32, which is used to represent Unicode characters. It is usually used when dealing with Chinese, but it can also be used with the range keyword.
String type
The bottom of the string type is a C struct.
struct string{byte* str; Intgo Len;};
The member Str is an array of characters and Len is the length of the character array. The Golang string is an immutable type, and initialization of a variable of type string means that the underlying structure is initialized. As for why STR uses the byte type instead of the rune type, this is because the Golang for loop is byte-based and, if necessary, can be turned into a rune slice or used to iterate through a range. Let's look at an example:
$GOPATH/SRC
----Basictype_test
--------Main.go
Package Mainimport ("FMT" "unsafe") Func main () {var str string = "Hi, chen Back ~" P: = (*struct {str Uintptrlen int}) (unsafe. Pointer (&STR)) fmt. Printf ("%+v\n", p)}
$ cd $GOPATH/src/basictype_test$ go build$./basictype_testoutput: &{str:135100456 len:14}
The built-in function Len takes the Len value directly from the underlying structure, without requiring additional action, and, of course, initializing the Len value at initialization time, for the string type operation.
Slice type
The bottom layer of the slice type is also a C struct.
structslice{//must not move anythingbyte*array;//actual datauintgolen;//number of elementsuintgocap;//allocated Numbe R of elements};
Consists of three members. Array is the underlying array, Len is the actual number of stores, and the CAP is the total capacity. Use the built-in function make to initialize the slice, or it can be initialized in a manner similar to an array. When using the Make function to initialize the slice, the first parameter is the slice type, the second parameter is Len, the third parameter is optional, and if not passed in, the cap equals Len. The CAP parameter is typically passed in to pre-allocate the size of the slice, avoiding frequent reallocation of memory.
Package Mainimport ("FMT" "unsafe") Func main () {var slice []int32 = make ([]int32, 5, p): = (*struct {array Uintptrlen i Ntcap int}) (unsafe. Pointer (&slice)) fmt. Printf ("Output:%+v\n", p)}
$ cd $GOPATH/src/basictype_test$ go build$./basictype_testoutput: &{array:406958176 Len:5 Cap:10}
Because slices point to an underlying array, and you can generate slices directly from the array through the slice syntax, you need to understand the relationship between slices and arrays, or you might unknowingly write the code with the bug. For example, there is the following code:
Package Mainimport ("FMT") func main () {var array = [...] Int32{1, 2, 3, 4, 5}var slice = array[2:4]fmt. Printf ("Before changing slice: Array=%+v, slice=%+v\n", array, slice) slice[0] = 234fmt. Printf ("After changing slice: Array=%+v, slice=%+v\n", Array, slice)}
$ cd $GOPATH/src/basictype_test$ go build$/basictype_test change slice before: Array=[1 2 3 4 5], slice=[3 4] After change slice: array=[1 2 234 4 5], slice=[234 4]
As you can see clearly, after changing the slice, the array is changed. This is because the slice created by the array points to the array, which means that the slice is the array. So it is clear that the change in slice is actually changing its underlying array. Of course, if you delete or add elements, Len will change and the cap may change.
So how does this slice point to an array? The underlying array pointer to slice points to the element indexed as 2 in the array (because the slices are generated by array[2:4],Len records the number of elements, and the cap equals Len.
The cap may change because the CAP represents total capacity, and adding or removing does not necessarily cause the total capacity to change. Let's look at another example:
Package Mainimport ("FMT") func main () {var array = [...] Int32{1, 2, 3, 4, 5}var slice = Array[2:4]slice = Append (Slice, 6, 7, 8) FMT. Printf ("Before changing slice: Array=%+v, slice=%+v\n", array, slice) slice[0] = 234fmt. Printf ("After changing slice: Array=%+v, slice=%+v\n", Array, slice)}
$ cd $GOPATH/src/basictype_test$ go build$/basictype_test change slice before: Array=[1 2 3 4 5], slice=[3 4 6 7 8] After changing slice: array =[1 2 3 4 5], slice=[234 4 6 7 8]
After append operation, the modification to the slice does not affect the array. The reason is that the append operation makes slice reassign the underlying array, so at this point the underlying array of slice no longer points to the previously defined array.
But obviously, this rule is the same for slices generated from slices, see the code:
Package Mainimport ("FMT") func main () {var slice1 = []int32{1, 2, 3, 4, 5}var slice2 = slice1[2:4]fmt. Printf ("Before changing Slice2: Slice1=%+v, slice2=%+v\n", Slice1, Slice2) slice2[0] = 234fmt. Printf ("After changing Slice2: Slice1=%+v, slice2=%+v\n", Slice1, Slice2)}
$ cd $GOPATH/src/basictype_test$ go build$/basictype_test change slice2 before: Slice1=[1 2 3 4 5], slice2=[3 4] After change Slice2: slice1= [1 2 234 4 5], slice2=[234 4]
Slice1 and Slice2 share a bottom-level array, and modifying slice2 elements causes Slice1 to change as well.
Package Mainimport ("FMT") func main () {var slice1 = []int32{1, 2, 3, 4, 5}var slice2 = slice1[2:4]fmt. Printf ("Before changing Slice2: Slice1=%+v, slice2=%+v\n", Slice1, slice2) Slice2 = Append (Slice2, 6, 7, 8) FMT. Printf ("After changing Slice2: Slice1=%+v, slice2=%+v\n", Slice1, Slice2)}
$ cd $GOPATH/src/basictype_test$ go build$/basictype_test change slice2 before: Slice1=[1 2 3 4 5], slice2=[3 4] After change Slice2: slice1= [1 2 3 4 5], slice2=[3 4 6 7 8]
The append operation allows Slice1 or SLICE2 to reassign the underlying array, so SLICE2 operations on Slice1 or append do not affect each other.
Interface type
The implementation of the interface in Golang is quite complex, and in the $GOROOT/src/pkg/runtime/type.h defines:
struct type{uintptr size;uint32 hash;uint8 _unused;uint8 align;uint8 fieldalign;uint8 kind; ALG *alg;void *GC; String *string; Uncommontype *x; Type *ptrto;};
in the the $GOROOT/src/pkg/runtime/runtime.h defines:
struct iface{itab*tab;void*data;}; struct eface{type*type;void*data;}; Structitab{interfacetype*inter; Type*type;itab*link;int32bad;int32unused;void (*fun[]) (void);};
Interface is actually a struct, consisting of two members, one pointer to the data, and one containing the type information for the member. Eface is the data structure used at the bottom of interface{}. Because the type information is saved in the interface, reflection can be implemented. Reflection is actually the metadata of finding the underlying data structure. Complete implementation in:$GOROOT/src/pkg/runtime/iface.c.
Package Mainimport ("FMT" "unsafe") Func main () {var str interface{} = "Hello world!" P: = (*struct {tab Uintptrdata uintptr}) (unsafe. Pointer (&STR)) fmt. Printf ("%+v\n", p)}
$ cd $GOPATH/src/basictype_test$ go build$./basictype_testoutput: &{tab:134966528 data:406847688}
Map type
Golang Map Implementation is hashtable, source code in: $GOROOT/src/pkg/runtime/hashmap.c.
struct Hmap{uintgo count;uint32 flags;uint32 hash0;uint8 b;uint8 keysize;uint8 valuesize;uint16 bucketsize;byte *buckets;byte *oldbuckets;uintptr nevacuate;};
The test code is as follows:
Package Mainimport ("FMT" "unsafe") Func main () {var m = make (Map[string]int32, ten) m["Hello"] = 123p: = (*struct {count Intflags uint32hash0 uint32b uint8keysize uint8valuesize uint8bucketsize uint16buckets Uintptro Ldbuckets uintptrnevacuate UIntPtr}) (unsafe. Pointer (&m)) fmt. Printf ("Output:%+v\n", p)}
$ cd $GOPATH/src/basictype_test$ go build$./basictype_testoutput: &{count:407032064 flags:0 hash0:134958144 B:192 keysize:0 valuesize:64 bucketsize:30063 buckets:540701813 oldbuckets:0 nevacuate:0}
Golang pits or more, need to study the bottom, otherwise it is easy to fall into the pit.