Hello, everyone. Today I will share with you the basic grammar of Go language. Please give me more advice, thank you.
The basic grammar content of Go language is divided into three chapters, and this paper is the second chapter
- The basic grammar of Golang (1)
- The basic grammar of Golang (2)
- The basic grammar of Golang (3)
Contents of this chapter
- channel
- structural morphology
- Pointer
- Control statement
channel
introduce
Simply executing functions concurrently is meaningless. Functions need to exchange data between functions to reflect the significance of concurrent execution of functions. channel is the connection between them.
channel is a communication mechanism that allows one goroutine to send a specific value to another goroutine.
Channel in Go language is a special type. The channel is like a conveyor belt or queue. It always follows the First In First Out rule to ensure the order of sending and receiving data. Each channel is a conduit of a specific type, that is, when declaring a channel, you need to specify the element type for it.
Note: goroutine is a unique mechanism in go language, which can be understood as thread in go language. With goroutine, you can use the go keyword
The knowledge of go concurrency will be put into subsequent articles to share with you. Simply understand this knowledge point
use
channel declaration
channel is a type, a reference type. Syntax format:
var variable chan Element type
example
var ch1 chan int // Declare a channel that passes an integer var ch2 chan bool // Declare a channel that passes Booleans var ch3 chan []int // Declare a channel that passes int slices
Create channel
The channel is a reference type, and the null value of the channel type is nil.
The declared channel can only be used after being initialized with the make function.
make(chan Element type, Buffer size) // format
example
ch4 := make(chan int) ch5 := make(chan bool) ch6 := make(chan []int)
channel operation
The channel has three operations: send, receive and close.
Both sending and receiving use the < - symbol.
send out
Send a value to the channel
ch := make(chan int) ch <- 100 // Send 100 to ch
receive
Receive values from a channel
x := <- ch // Receive from ch channel and assign x <- ch // Receive from ch channel, ignore value
close
Call the built-in close function to close the channel
close(ch)
The closed channel has the following characteristics:
- Sending a value to a closed channel will result in panic
- Receiving a closed channel will get the value until the channel is empty
- Performing a receive operation on a closed channel with no value will result in a corresponding type of zero value
- Closing a closed channel will cause panic
Note: the channel needs to be closed only when the receiver is notified that all data of goroutine has been sent. The channel can be recycled by the garbage collection mechanism. It is different from closing the file. Closing the file after the operation is necessary, but closing the channel is not necessary.
Unbuffered channel
Unbuffered channels are also called blocked channels
Unbuffered channels can only send values when someone receives them. Just as there is no express cabinet and collection point in your community, the courier must send this item to your hand when calling you. In short, the unbuffered channel must be received before sending.
The channel created with CH: = make (Chan int) is unbuffered
func main() { ch := make(chan int) ch <- 10 fmt.Println("Sent successfully") }
The above code can be compiled, but there will be deadlock errors during execution
fatal error: all goroutines are asleep - deadlock!
One way is to enable a goroutine to receive values
func recv(c chan int) { ret := <-c fmt.Println("Received successfully", ret) } func main() { ch := make(chan int) go recv(ch) // Enable goroutine to receive values from the channel ch <- 10 fmt.Println("Sent successfully") }
The sending operation on the unbuffered channel will be blocked until another goroutine performs the receiving operation on the channel. At this time, the value can be sent successfully, and the two goroutines will continue to execute. Conversely, if the receive operation is performed first, the receiver's goroutine will block until another goroutine sends a value on the channel.
The use of unbuffered channels for communication will result in the synchronization of the goroutine sent and received. Therefore, the unbuffered channel is also called the synchronous channel.
Buffered channel
As long as the capacity of the channel is greater than zero, the channel is a buffered channel. The capacity of the channel indicates the number of elements that can be stored in the channel. Just like the express cabinet in your community has only so many grids. When the grid is full, it will not fit, and it will be blocked. When someone else takes one, the courier can put one in it.
Specify the capacity of the channel when initializing the channel with the make function
func main() { ch := make(chan int, 1) // Create a buffered channel with a capacity of 1 ch <- 10 fmt.Println("Sent successfully") }
Cycle value case from channel
func main() { ch1 := make(chan int) ch2 := make(chan int) // Turn on goroutine to send the number from 0 to 100 to ch1 go func() { for i := 0; i < 100; i++ { ch1 <- i } close(ch1) }() // Turn on goroutine to receive the value from ch1 and send the square of the value to ch2 go func() { for { i, ok := <-ch1 // After the channel is closed, the value is ok=false if !ok { break } ch2 <- i * i } close(ch2) }() // Receive print value from ch2 in main goroutine for i := range ch2 { // After the channel is closed, the for range loop will exit fmt.Println(i) } }
Unidirectional channel
Sometimes we will pass the channel as a parameter between multiple task functions. Many times, when we use the channel in different task functions, we will restrict it. For example, we can only send or receive the channel in the function.
One way channel case
func counter(out chan<- int) { for i := 0; i < 100; i++ { out <- i } close(out) } func squarer(out chan<- int, in <-chan int) { for i := range in { out <- i * i } close(out) } func printer(in <-chan int) { for i := range in { fmt.Println(i) } } func main() { ch1 := make(chan int) ch2 := make(chan int) go counter(ch1) go squarer(ch2, ch1) printer(ch2) }
Of which:
- Chan < - int is a channel that can only be sent, which can be sent but cannot be received;
- < - Chan int is a channel that can only receive, but cannot send.
It is possible to convert a two-way channel into a one-way channel in the function transfer parameter and any assignment operation, but the reverse is not possible.
Pointer
introduce
Different from the pointer in C/C + +, the pointer in Go language cannot be offset and operated. It is a safe pointer.
Pointer in Go language needs to know three concepts: pointer address, pointer type and pointer value.
Function parameters in Go language are value copies. When we want to modify a variable, we can create a pointer variable pointing to the address of the variable.
Passing data uses pointers instead of copying data.
The pointer operation in Go language is very simple. You only need to remember two symbols: & (take the address) and * (take the value according to the address).
Each variable has an address at runtime, which represents the location of the variable in memory. In Go language, the & character is placed in front of the variable to "take the address" the variable.
The value types in Go language (int, float, bool, string, array, struct) have corresponding pointer types, such as: * int, * int64, * string, etc.
When a pointer is defined and not assigned to any variable, its value is nil
use
The syntax for fetching variable pointers is as follows
var v int = 10 ptr := &v fmt.Printf("v:%d ptr:%p\n", v, ptr) c := *ptr // Pointer value (according to the pointer to the memory value) fmt.Printf("c:%d\n", c)
v: Represents the variable of the address taken. ptr: the variable used to receive the address. The type of ptr is called the pointer type of int* Represents a pointer.
The program defines the address of an int variable num and prints it
Assign the address of a to the pointer p, and modify the value of a through p
func main() { var a int fmt.Println(&a) // Pointer address var p *int p = &a // Equivalent to P: = & A *p = 20 fmt.Println(a) // Output 20 }
Judgment of null pointer
func main() { var p *string fmt.Printf("p The value of is%v\n", p) if p != nil { fmt.Println("Non empty") } else { fmt.Println("Null value") } }
structural morphology
introduce
A structure is an aggregated data type. It is an entity composed of zero or more values of any type. Each value is called a member of the structure.
Usually, a row corresponds to a structure member, with the member name before the type, but if the adjacent member types are the same, they can be merged into one row.
If the name of a structure member starts with a capital letter, the member is exported; This is determined by the Go language export rules. A structure may contain both exported and non exported members.
A struct type named S will no longer contain members of type S: because an aggregate value cannot contain itself. (the same restriction applies to arrays.)
The zero value of structure type is that each member is zero. Zero is usually the most reasonable default.
If the structure has no members, it is an empty structure. Write struct {}. It has a size of 0 and does not contain any information, but it is still valuable sometimes.
use
type Info struct { // Create structure ID int Name, Hobby string } var info Info // Declaration structure
Members of structure variables can be accessed through point operators
info.Name = "Gunmen in Maoershan"
Take the address of the member and access it through the pointer
name := &info.Name fmt.Println(*name)
If you want to modify the structure member inside the function, it is necessary to pass it in with a pointer; Because in Go language, all function parameters are passed in by value copy, the function parameters will no longer be the original variables when the function is called.
func UpdateInfo(i *info) { i.Hobby = "Travel" }
Create and initialize a structure variable through a pointer and return the address of the structure
pp := &Point{1, 2}
It is equivalent to the following statement
pp := new(Point) *pp = Point{1, 2}
Note: structures can also be compared. Two structures can use = = or= Operator. The equality comparison operator = = compares each member of two structs.
Control statement
if judgment
Single condition judgment
if condition { // do something }
Multi condition judgment
if condition { } else if condition { // do something } else { // do something }
if single condition is followed by a statement before making conditional judgment
if statement;condition{ //do something }
Multi conditional sentence judgment
if num := 78;num <= 50{ fmt.Println("Number is less then 50") } else if num >= 51 && num <= 100{ fmt.Println("The number is between 51 and1 100") } else{ fmt.Println("The number is greater than 100") }
for loop
There is only one way of loop in go language, for loop.
The first syntax format
for Loop variable initialization; Cycle conditions; Cyclic variable iteration { // Loop operation (statement) } for j := 1; j <= 10; j++ { // Circular execution statement }
The second syntax format
for Cyclic judgment condition { // Circular execution statement } j := 1 for j <= 10 { j++ }
The third syntax format
for { // The circular execution statement is an infinite loop, which is usually used in conjunction with the break statement }
Traversal syntax format for range
var names []string{"xiaoming", "xiaohong", "xiaojun"} for _, name := range names { fmt.Println(name) }
Use the goto statement to jump out of the for loop
func main() { for i := 0; i < 10; i++ { fmt.Println(i) if i == 5 { goto end } } end: fmt.Println("end") }
output
0 1 2 3 4 5 end
switch
The first syntax format
func main() { num := 1 switch num { case 1: fmt.Println("num=1") case 2: fmt.Println("num=2") case 3: fmt.Println("num=3") default: fmt.Println("Unmatched") } }
The second syntax format
func main() { num := 1 switch { case num == 1: fmt.Println("num=1") case num == 2: fmt.Println("num=2") case num == 3: fmt.Println("num=3") default: fmt.Println("Unmatched") } }
The third syntax format
func main() { switch num := 1; { case num == 1: fmt.Println("num=1") case num == 2: fmt.Println("num=2") case num == 3: fmt.Println("num=3") default: fmt.Println("Unmatched") } }
The fourth syntax format
Use the keyword fallthrough. By default, in the switch, each case will have a hidden break. If you want to remove the hidden break, we can use fallthrough to replace it
package main import ( "fmt" ) func main() { a := 2 switch a { case 1: fmt.Println("a=1") case 2: fmt.Println("a=2") fallthrough case 3: fmt.Println("a=3") case 4: fmt.Println("a=4") default: fmt.Println("default") } }
output
a=2 a=3
select
The select statement is used to handle I/O operations related to channel:
- Each case must be a communication;
- All channel expressions and sent expressions will be evaluated;
- If any channel can run, it will execute and others will be ignored;
- Multiple case s can be run, and one can be selected randomly for execution;
- Can not run. If there is a default, execute the default, and block if there is no default, until a communication can run and the expression will not be evaluated again;
- A select can execute the code in the case at most once. The case needs to be detected all the time, and a for loop is added to the outer layer;
- The break in case only exits the current select and has nothing to do with the for loop;
Random execution case usage
package main import ( "fmt" "time" ) func main() { ch1 := make(chan int) ch2 := make(chan int) go func() { time.Sleep(time.Second) ch1 <- 1 }() go func() { time.Sleep(time.Second) ch2 <- 2 }() time.Sleep(2 * time.Second) select { case i := <-ch1: fmt.Println("ch1 receive", i) case i := <-ch2: fmt.Println("ch2 receive", i) default: fmt.Println("no i/o opeartion") } }
The results are printed randomly: ch1 receive: 1 or ch2 receive: 2. Because the two channels are waiting to be written, both case s in the select meet the execution conditions and are executed randomly.
Set receive channel timeout usage
func main() { ch1 := make(chan int) select { case i := <-ch1: fmt.Println(i) case <-time.After(5 * time.Second): fmt.Println("ch receive timeout") } }
Check whether the channel is full of usage
func main() { ch1 := make(chan int, 5) ch1 <- 1 ch1 <- 2 ch1 <- 3 ch1 <- 4 ch1 <- 5 select { case ch1 <- 6: fmt.Println("send 6 to ch1") default: fmt.Println("channel is full") } }
Technical articles are constantly updated. Please pay more attention~~