Complex Data Types/Derived Data Types [Pointers, Arrays, Structures, Pipes, Functions, Slices, Interfaces, Maps]#
Pointers#
What is a pointer#
package main
import "fmt"
func main() {
var age int = 18
//*int represents the pointer type corresponding to ptr
//&age represents the address of the age variable in memory
var ptr *int = &age
fmt.Println(ptr)
//&ptr represents the address of the memory space where the ptr pointer variable is located
fmt.Println(&ptr)
//*ptr represents the data pointed to by the ptr pointer
fmt.Println(*ptr)
}
Summary:
& symbol indicates taking the memory address
* symbol indicates taking the value based on the address
Four details about pointers#
-
You can change the pointed value through a pointer.
-
The pointer variable must receive an address value.
-
The address of the pointer variable must match.
-
Each basic data type has a corresponding pointer type.
Functions#
Introduction to functions#
Definition of a function: A collection of program instructions to accomplish a certain function is called a function.
Purpose: To improve code reuse, reduce code redundancy, and enhance code maintainability.
Basic Syntax#
Func functionName (parameter list) (return value type list) {
execution statements...
Return + return value list
}
package main
import "fmt"
// Custom cal function, functionality: adding two numbers
func cal(sum1 int, num2 int) int { // The return value type list can omit parentheses if there is only one
var sum int = 0
sum += sum1
sum += num2
return sum
}
func main() {
var n int = 10
var m int = 20
s := cal(m, n)
fmt.Println(s)
}
Details#
-
Function name
- Follows identifier naming conventions
- The first letter cannot be a number
- A capitalized first letter can be called by this package and other packages (similar to public)
- A lowercase first letter can only be called by this package (similar to private)
-
Parameter list
- Count: Parameters can be 0, 1, or n
- Purpose: To receive external data
-
Return value type list
- If there are 0 return value types, this item can be omitted.
- If there is only one return value type, parentheses can be omitted.
- If there are return values that you do not want to receive, you can use _ to ignore them.
-
Overloading is not supported.
-
Variable parameters are supported.
package main
import "fmt"
func test(args ...int) {
for j := 0; j < len(args); j++ {
fmt.Println(args[j])
}
}
func main() {
test()
fmt.Println("________________________")
test(3)
fmt.Println("________________________")
test(2, 5, 6, 9)
}
Running result:
________________________
3
________________________
2
5
6
9
- Basic data types and arrays are passed by value by default, and modifications within the function will not affect the original value.
package main
import "fmt"
func ex(num int) {
num = 30
}
func main() {
var num int = 10
ex(num)
fmt.Println(num)
}
Running result:
10
- For data types passed by value, if you want the variable inside the function to modify the variable outside the function, you can pass the address of the variable using & and operate on the variable in pointer mode within the function.
package main
import "fmt"
func ex(num *int) {
*num = 30
}
func main() {
var num int = 10
ex(&num)
fmt.Println(num)
}
Running result:
30
- Functions are also a type of data and can be assigned to a variable, which can be used to call the function, and functions can also be passed as parameters.
package main
import "fmt"
// Define a function
func re(num int) {
fmt.Println(num)
}
// Define a function that takes another function as a parameter
func te(num1 int, num2 float32, testFunc func(int)) {
fmt.Println("*************")
}
func main() {
a := re
fmt.Printf("The type of a is: %T, the type of re function is: %T", a, re) //The type of a is: func(int), the type of re function is: func(int)
a(10) //Equivalent to test(10)
te(1, 2.5, a) //Equivalent to te(1, 2.5, re)
}
- Go supports custom data types (equivalent to giving existing data types an alias)
Basic syntax: type customDataTypeName dataType
For example: type myInt int 【At this point, myInt is equivalent to int】
For example: type mySum func (int, int) int 【At this point, mySum is equivalent to func (int, int) int】 - Supports naming return values of functions
package main
func test1(num1 int, num2 int) (int, int) {
result1 := num1 + num2
result2 := num1 - num2
return result1, result2
}
// Naming the return values of the function can be written as
func test2(num1 int, num2 int) (sum int, sub int) {
sum = num1 + num2
sub = num1 - num2
return
}
Introduction to packages#
Reasons for using packages#
- Functions can be categorized and placed in different source files.
- Solve naming conflicts; functions in different packages can have the same name, distinguished by the package.
Details#
- It is recommended that the declared package has the same name as the folder it is in.
- The main package is the entry point of the program, and the main function is usually in this package.
- Package syntax: package packageName
- Importing package syntax: import "package path", multiple packages can be imported at once using {}
- When calling functions from different packages, you must first locate the package.
- Function names and variable names must start with a capital letter to be accessible from other packages.
- There cannot be duplicate functions in the same directory.
- Package names and folder names can be different.
- Files at the same level in a directory belong to the same package.
- You can give a package an alias; once an alias is given, the original package name cannot be used.
Init function#
The initialization function can be used for some initialization operations.
Anonymous functions#
If a function is only intended to be used once, consider using an anonymous function.
Usage of anonymous functions#
- Directly call the anonymous function when defining it; this method can only be called once.
package main
import "fmt"
func main() {
// Define an anonymous function: define and call at the same time
sum := func(num1 int, num2 int) int {
return num1 + num2
}(10, 20)
fmt.Println(sum)
}
- Assign the anonymous function to a variable (i.e., function variable) and call it through this variable.
package main
import "fmt"
func main() {
// Define an anonymous function and assign it to a variable
sub := func(num1 int, num2 int) int {
return num1 - num2
}
result := sub(20, 10)
fmt.Println(result)
}
Closure#
What is a closure#
A closure is a combination of a function and its related reference environment as a whole.
package main
import "fmt"
// Define the fucSum function, with no parameters
// The fucSum function returns a function that takes an int type parameter and returns an int type value
func fucSum() func(int) int {
var sum int = 0
return func(p int) int {
sum += p
return sum
}
}
// Closure is like the function returned by fucSum combined with the variable sum
func main() {
f := fucSum()
fmt.Println(f(1)) //1
fmt.Println(f(2)) //3
fmt.Println(f(3)) //6
fmt.Println(f(4)) //10
}
> The variable referenced by the anonymous function will be kept in memory and can be used continuously, which can consume a lot of memory, so it should not be used excessively.
The essence of closure#
Closure = Anonymous function + Referenced variable/parameter
Defer keyword#
To release resources promptly after the function execution is complete.
package main
import "fmt"
func add(m int, n int) int {
// In golang, when encountering the defer keyword, the statements after defer are not executed immediately, but are pushed onto a stack, and then the function continues to execute the subsequent statements.
// The characteristics of the stack: last in, first out
// After the function execution is complete, the statements are taken from the stack and executed according to the last in, first out rule.
defer fmt.Println(m)
defer fmt.Println(n)
fmt.Println(m + n)
return m + n
}
func main() {
fmt.Println("The result is:", add(10, 20))
}
Running result:
30
20
10
The result is: 30
Defer application scenarios#
Defer has a delayed execution mechanism (executing the statements pushed onto the stack after the function execution is complete), so when resources need to be closed, just use defer for convenience.
System functions#
String-related functions#
Count string length
len(str)
String traversal
r := []rune(str)
String to integer conversion
n, err := strconv.Atoi("66")
Integer to string conversion
str := strconv.Itoa(88)
Check if a string contains another string
strings.Contains("javaandgolang", "go")
Count occurrences of a substring in another string
strings.Count("javaandgolang", "a")
Case-insensitive comparison of two strings
fmt.Println(strings.EqualFold("go", "Go"))
Return the index of the first occurrence of a substring in another string
strings.Index("javaandgolang", "a")
If not found, returns -1
String replacement
strings.Replace("goandjavagogo", "go", "golang", n)
N indicates how many replacements to make; if n is -1, it means replace all.
Split a string into an array of strings based on a specified delimiter
strings.Split("go-java-python", "-")
Change letter case in a string
strings.ToLower("GO")
strings.ToUpper("go")
Trim whitespace from both ends of a string
strings.TrimSpace(" go and java ")
Trim specified characters from both ends of a string
strings.Trim("~go~", "~")
Trim specified characters from the left end of a string
strings.TrimLeft("~go", "~")
Trim specified characters from the right end of a string
strings.TrimRight("go~", "~")
Check if a string starts with a specified substring
strings.HasPrefix("https://libilibi.eu.org", "https")
Check if a string ends with a specified substring
strings.HasSuffix("https://libilibi.eu.org", "org")
Date and time-related functions#
package main
import (
"fmt"
"time"
)
func main() {
// Get the current local time Now(), the return value is a structure of type time.Time
now := time.Now()
fmt.Println(now) //2023-06-27 23:11:41.8027164 +0800 CST m=+0.001038201
fmt.Printf("%T \n", now) //time.Time
// Call methods from the structure:
fmt.Printf("Year: %v \n", now.Year()) //Year: 2023
fmt.Printf("Month: %v \n", now.Month()) //Month: June
fmt.Printf("Month: %v \n", int(now.Month())) //Month: 6
fmt.Printf("Day: %v \n", now.Day()) //Day: 27
fmt.Printf("Hour: %v \n", now.Hour()) //Hour: 23
fmt.Printf("Minute: %v \n", now.Minute()) //Minute: 20
fmt.Printf("Second: %v \n", now.Second()) //Second: 56
fmt.Println("_____________________________________________")
// Printf directly outputs the string
fmt.Printf("Current date: %d-%d-%d Time: %d:%d:%d \n", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
// Sprintf can get a string for further use
dateStr := fmt.Sprintf("Current date: %d-%d-%d Time: %d:%d:%d \n", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second())
fmt.Println(dateStr) //Current date: 2023-6-27 Time: 23:25:37
fmt.Println("_____________________________________________")
dateStr2 := now.Format("2006/01/02 15/04/05") // The numbers in each position must be fixed; they can be omitted but not changed
fmt.Println(dateStr2) //2023/06/27 23/38/24
}
Built-in functions#
Built-in functions are under the builtin package and can be used directly without importing.
Common built-in functions:
- Len function: counts the length of a string in bytes.
- New function: allocates memory, primarily for value types.
package main
import "fmt"
func main() {
// new allocates memory; the argument of the new function is a type, not a value; the return value of the new function is a pointer of the corresponding type.
num := new(int)
fmt.Printf("The type of num is: %T, the value of num is: %v, the address of num is: %v, the value pointed to by num is: %v", num, num, &num, *num) //The type of num is: *int, the value of num is: 0xc0000a6058, the address of num is: 0xc0000ca018, the value pointed to by num is: 0
}
- Make function: allocates memory, primarily for reference types.
Arrays#
Array declaration#
var variableName [indexValue]dataType
package main
import (
"fmt"
)
func main() {
// Declare a variable named scores, an int type array with an index value of 5 (i.e., can store 5 variables)
var scores [5]int
scores[0] = 89
scores[1] = 79
scores[2] = 92
scores[3] = 95
scores[4] = 88
var sum int
// Calculate the sum
for i := 0; i < len(scores); i++ {
sum += scores[i]
}
// Calculate the average
avg := sum / len(scores)
fmt.Println(sum) //443
fmt.Println(avg) //88
}
Review for range
package main
import "fmt"
func main() {
var scores [5]int
// Manually input the value of each variable in the array
for i := 0; i < len(scores); i++ {
fmt.Printf("Please enter the value for the %dth variable------", i+1)
fmt.Scanln(&scores[i])
}
for m, n := range scores {
fmt.Printf("The value for the %dth variable is: %d\n", m+1, n)
}
}
Array initialization#
package main
import "fmt"
func main() {
// First method
var a [3]int = [3]int{1, 2, 3}
fmt.Println(a)
// Second method
var b = [3]float32{2.5, 3.6, 5.5}
fmt.Println(b)
// Third method
var c = [...]byte{4, 5, 6}
fmt.Println(c)
// Fourth method
var d = [...]string{1: "jia", 2: "ming", 0: "wang"}
fmt.Println(d)
}
Running result:
[1 2 3]
[2.5 3.6 5.5]
[4 5 6]
[wang jia ming]
Array considerations#
- The length of the array is part of the array type.
package main
import "fmt"
func main() {
// First method
var a [3]int = [3]int{1, 2, 3}
fmt.Printf("Array type is: %T\n", a)
// Second method
var b = [2]float32{2.5, 3.6}
fmt.Printf("Array type is: %T\n", b)
// Third method
var c = [...]byte{4, 5, 6, 7}
fmt.Printf("Array type is: %T\n", c)
// Fourth method
var d = [...]string{1: "jia", 2: "ming", 0: "wang"}
fmt.Printf("Array type is: %T\n", d)
}
Running result:
Array type is: [3]int
Array type is: [2]float32
Array type is: [4]uint8
Array type is: [3]string
- In Go, arrays are value types and are passed by value by default, so they undergo value copying. Therefore, to modify an array in another function, you need to use reference passing (i.e., pointer method).
package main
import "fmt"
func main() {
var arr = [3]int{7, 8, 9}
test1(arr)
fmt.Println(arr) //[7,8,9] The array does not change after assigning arr[0] in the test function.
test2(&arr) // Pass the address of the array
fmt.Println(arr) //[5,8,9] The array has changed now.
}
func test1(arr [3]int) { // Extract the values of the array for assignment
arr[0] = 5
}
func test2(arr *[3]int) { // Extract the values of the array for assignment
arr[0] = 5
}
Two-dimensional array declaration#
var variableName [indexValue][indexValue]dataType
package main
import "fmt"
func main() {
var a [2][3]int
fmt.Println(a) //[[0 0 0] [0 0 0]] Indicates that this array consists of 2 one-dimensional arrays of length 3.
}
Two-dimensional array initialization#
package main
import "fmt"
func main() {
var arr = [2][3]string{{"a", "b", "c"}, {"a", "b", "c"}}
fmt.Println(arr)
}
Two-dimensional array traversal#
package main
import "fmt"
func main() {
var arr = [3][3]string{{"a", "b", "c"}, {"d", "e", "f"}, {"g", "h", "i"}}
// Double for loop
for i := 0; i < len(arr); i++ {
for j := 0; j < 3; j++ {
fmt.Println(arr[i][j])
}
}
println("______________________________________________________")
// Double for range; if the index is not needed, it can be ignored with an underscore.
for _, m := range arr {
for _, n := range m {
fmt.Println(n)
}
}
}
Running result:
a
b
c
d
e
f
g
h
i
______________________________________________________
a
b
c
d
e
f
g
h
i
Slices#
What is a slice#
A slice is a structure where len indicates the length of the underlying array, and cap is the total length of the allocated memory space (i.e., the total length of the underlying array and the pre-allocated memory). The length of the pre-allocated memory is variable; once exceeded, it will continue to increase in units of the total allocated memory length.
Slice declaration and initialization#
var variableName []dataType
package main
import "fmt"
func main() {
var arr []int
arr = make([]int, 3, 5)
arr[0], arr[1], arr[2] = 7, 8, 9
// Print the length of the underlying array and the total length of the allocated memory space
fmt.Printf("%d %d\n", len(arr), cap(arr)) //3 5
// Print the slice
fmt.Println(arr) //[7 8 9]
}
Slice details#
package main
import "fmt"
func main() {
var arr = make([]int, 3, 4)
arr[0], arr[1], arr[2] = 7, 8, 9
// Print the length of the underlying array and the total length of the allocated memory space
fmt.Printf("%d %d\n", len(arr), cap(arr)) //3 4
// Print the slice
fmt.Println(arr) //[7 8 9]
// Assign the slice arr to slice brr
brr := arr
fmt.Printf("%p %p\n", arr, brr) //0xc00000c330 0xc00000c330 Both slices have the same memory address, so they share the same memory.
// Append 10 to the slice arr and assign it to slice brr
brr = append(arr, 10)
fmt.Printf("%d %d %d\n", len(brr), cap(brr), brr) //4 4 [7 8 9 10] The underlying array has changed, and the pre-allocated memory is now full.
// Append 11 to the original slice brr; since the pre-allocated memory is insufficient, it needs to increase. The previous total length was 4, and now it allocates an additional length of 4.
brr = append(brr, 11)
fmt.Printf("%d %d %d\n", len(brr), cap(brr), brr) //5 8 [7 8 9 10 11] The underlying array has changed, and the total allocated memory length has also changed.
fmt.Printf("%p %p\n", arr, brr) //0xc000012180 0xc00000e3c0 The memory addresses have changed, so they no longer share the same memory.
}
Slice traversal#
package main
import "fmt"
func main() {
var arr = make([]int, 3, 4)
arr[0], arr[1], arr[2] = 7, 8, 9
for i, value := range arr {
fmt.Printf("%p %p %d %d\n", &value, &arr[i], value, arr[i])
}
}
Running result:
0xc0000a6058 0xc0000d2020 7 7
0xc0000a6058 0xc0000d2028 8 8
0xc0000a6058 0xc0000d2030 9 9
During the traversal, the memory address of value remains unchanged.
Maps#
Map declaration and initialization#
var variableName map[key data type]value data type
package main
import "fmt"
func main() {
var m map[string]int
fmt.Println(m)
}
Map details#
package main
import "fmt"
func main() {
var m map[string]int
m = map[string]int{"a": 7, "b": 8, "c": 9}
m["a"] = 6
fmt.Println(m["a"])
// Delete the value mapped by a in m; although deleted, it returns the default value 0.
delete(m, "a")
fmt.Println(m["a"])
// Check if the value mapped by a in m has been deleted.
if v, exists := m["a"]; exists { // Initialize v and exists, equivalent to m["a"], check if exists is true.
fmt.Println(v)
} else {
fmt.Println("m[\"a\"] has been deleted")
}
}
Running result:
6
0
m["a"] has been deleted
Map traversal#
package main
import (
"fmt"
)
func main() {
var m = map[string]int{"a": 1, "b": 2, "c": 3}
for key, value := range m {
fmt.Println(key, value)
}
}
Running result:
a 1
b 2
c 3
Structures#
Structure definition and usage#
package main
import "fmt"
type Teacher struct {
Name string
Age int
School string
}
type User struct {
Name string
Age int
School string
}
func main() {
t := Teacher{"Zhang San", 18, "Tsinghua"}
fmt.Println(t) //{Zhang San 18 Tsinghua}
u := *new(User)
u.Name = "Li Si"
u.Age = 19
u.School = "Peking University"
fmt.Println(u) //{Li Si 19 Peking University}
}
Interfaces#
Interface definition and usage#
An interface is a collection of behavioral specifications.
package main
import "fmt"
// The Math interface can define some rules.
type Math interface {
Formula(int, int) int
}
func test(math Math) {
m := math.Formula(6, 3)
fmt.Println(m)
}
func main() {
// Add and Sub are concrete implementations of the Math interface.
m := Add{}
n := Sub{}
test(m) //9
test(n) //3
}
type Add struct {
}
// Install a method for the structure.
func (Add) Formula(a int, b int) int {
return a + b
}
type Sub struct {
}
// Install a method for the structure.
func (Sub) Formula(a int, b int) int {
return a - b
}
Empty interface#
An empty interface can be written as interface{}, which accepts all behavioral specifications (i.e., encompasses everything) and can be assigned freely.
package main
import "fmt"
func main() {
var inter interface{}
var str = "golang"
inter = str
fmt.Println(inter) //golang
var a = 0
inter = a
fmt.Println(inter) //0
var f = true
inter = f
fmt.Println(inter) //true
}