Go language interview questions (1): basic grammar

Q1 What is the difference between = and :=?

:= declaration + assignment

= assignment only

var foo int
foo = 10
// 等价于
foo := 10

Q2 What is the function of the pointer?

A pointer is used to store the address of a variable.

For example

var x =  5
var p *int = &x
fmt.Printf("x = %d",  *p) // x 可以用 *p 访问
  • operator, also known as the dereference operator, is used to access the value at an address.
    The & operator, also known as the address operator, is used to return the address of a variable.

Q3 Does Go allow multiple return values?

allow

func swap(x, y string) (string, string) {
    
    
   return y, x
}

func main() {
    
    
   a, b := swap("A", "B")
   fmt.Println(a, b) // B A
}

Q4 Does Go have exception types?

Go has no exception type, only error type (Error), and the return value is usually used to represent the abnormal state.

f, err := os.Open("test.txt")
if err != nil {
    
    
    log.Fatal(err)
}

Q5 What is a coroutine (Goroutine)

Goroutines are functions or methods that run concurrently with other functions or methods. Goroutines can be thought of as lightweight threads. The overhead of creating Goroutines is small compared to threads. It is very common practice for Go applications to run thousands of Goroutines concurrently.

Q6 How to splice strings efficiently

In the Go language, strings are read-only, which means that each modification operation will create a new string. If you need to splice multiple times, you should use strings.Builder to minimize the number of memory copies.

var str strings.Builder
for i := 0; i < 1000; i++ {
    
    
    str.WriteString("a")
}
fmt.Println(str.String())

Q7 What is the type of rune

The ASCII code only needs 7 bits to be fully represented, but it can only represent 128 characters including English letters. In order to represent most of the writing systems in the world, Unicode was invented. It is a superset of ASCII, including writing in the world. All characters existing in the system, and each code is assigned a standard number (called Unicode CodePoint), which is called rune in Go language, which is an alias of int32 type.

In the Go language, the underlying representation of a string is a byte (8 bit) sequence, not a rune (32 bit) sequence. For example, in the following example, language and language use UTF-8 encoding and each occupies 3 bytes, so len("Go language") is equal to 8. Of course, we can also convert the string into a rune sequence.

fmt.Println(len("Go语言")) // 8
fmt.Println(len([]rune("Go语言"))) // 4

Q8 How to judge whether a certain key is included in the map?

if val, ok := dict["foo"]; ok {
    
    
    //do something here
}

dict[“foo”] has two return values, val and ok, if ok is equal to true, it means that dict contains key “foo”, and val will be assigned the corresponding value of “foo”.
insert image description here

Q9 Does Go support default parameters or optional parameters?

The Go language does not support optional parameters (python support), nor does it support method overloading (java support).

Execution order of Q10 defer

Multiple defer statements follow the Last In First Out (LIFO) principle, and the defer statement declared last is executed first.
defer is executed after the return statement, but before the function exits, defer can modify the return value.
For example:

func test() int {
    
    
	i := 0
	defer func() {
    
    
		fmt.Println("defer1")
	}()
	defer func() {
    
    
		i += 1
		fmt.Println("defer2")
	}()
	return i
}

func main() {
    
    
	fmt.Println("return", test())
}
// defer2
// defer1
// return 0

In this example, you can see the execution order of defer: last in first out. However, the return value has not been modified, which is determined by Go's return mechanism. After executing the return statement, Go will create a temporary variable to store the return value. Therefore, the defer statement modifies the local variable i, but does not modify the return value. What if it is a famous return value?

func test() (i int) {
    
    
	i = 0
	defer func() {
    
    
		i += 1
		fmt.Println("defer2")
	}()
	return i
}

func main() {
    
    
	fmt.Println("return", test())
}
// defer2
// return 1

In this example, the return value is modified. For a function with a named return value, when the return statement is executed, no temporary variable will be created to save it. Therefore, the defer statement modifies i, which affects the return value.

Q11 How to exchange the values ​​of 2 variables?

a, b := "A", "B"
a, b = b, a
fmt.Println(a, b) // B A

Q12 What is the use of Go language tag?

tag can be understood as the annotation of the struct field, which can be used to define one or more attributes of the field. Frameworks/tools can obtain the attributes defined by a certain field through reflection, and take corresponding processing methods. tag enriches the semantics of the code and enhances flexibility.

For example:

package main

import "fmt"
import "encoding/json"

type Stu struct {
    
    
	Name string `json:"stu_name"`
	ID   string `json:"stu_id"`
	Age  int    `json:"-"`
}

func main() {
    
    
	buf, _ := json.Marshal(Stu{
    
    "Tom", "t001", 18})
	fmt.Printf("%s\n", buf)
}

This example uses tag to define the conversion relationship between the structure field and the json field, Name -> stu_name, ID -> stu_id, ignoring the Age field. It is very convenient to realize the conversion between the Go structure and the json text of different specifications.

Q13 How to judge that two string slices (slice) are equal?

In the go language, you can use reflection reflect.DeepEqual(a, b) to judge whether the two slices a and b are equal, but it is generally not recommended to do so, and using reflection will greatly affect performance.

The usual method is as follows, traversing each element in the comparison slice (pay attention to handling the out-of-bounds situation).

func StringSliceEqualBCE(a, b []string) bool {
    
    
    if len(a) != len(b) {
    
    
        return false
    }

    if (a == nil) != (b == nil) {
    
    
        return false
    }

    b = b[:len(a)]
    for i, v := range a {
    
    
        if v != b[i] {
    
    
            return false
        }
    }

    return true
}

Q14 When printing strings, the difference between %v and %+v

Both %v and %+v can be used to print the value of struct, the difference is that %v only prints the value of each field, and %+v also prints the name of each field.

type Stu struct {
    
    
	Name string
}

func main() {
    
    
	fmt.Printf("%v\n", Stu{
    
    "Tom"}) // {Tom}
	fmt.Printf("%+v\n", Stu{
    
    "Tom"}) // {Name:Tom}
}

But if the structure defines a String() method, both %v and %+v will call String() to override the default value.

Q15 How to represent enumeration values ​​(enums) in Go language

Often constants (const) are used to represent enumeration values.

type StuType int32

const (
	Type1 StuType = iota
	Type2
	Type3
	Type4
)

func main() {
    
    
	fmt.Println(Type1, Type2, Type3, Type4) // 0, 1, 2, 3
}

参考 What is an idiomatic way of representing enums in Go? - StackOverflow

Q16 The use of empty struct{}

Using an empty structure struct{} can save memory, and is generally used as a placeholder, indicating that a value is not needed here.

fmt.Println(unsafe.Sizeof(struct{
    
    }{
    
    })) // 0

For example, when using a map to represent a collection, only focus on the key, and the value can use struct{} as a placeholder. If you use other types as placeholders, such as int and bool, it not only wastes memory, but also easily causes ambiguity.

type Set map[string]struct{
    
    }

func main() {
    
    
	set := make(Set)

	for _, item := range []string{
    
    "A", "A", "B", "C"} {
    
    
		set[item] = struct{
    
    }{
    
    }
	}
	fmt.Println(len(set)) // 3
	if _, ok := set["A"]; ok {
    
    
		fmt.Println("A exists") // A exists
	}
}

For another example, when using a channel to control concurrency, we only need a signal, but do not need to pass a value. At this time, we can also use struct{} instead.

func main() {
    
    
	ch := make(chan struct{
    
    }, 1)
	go func() {
    
    
		<-ch
		// do something
	}()
	ch <- struct{
    
    }{
    
    }
	// ...
}

Another example is declaring a struct that contains only methods.

type Lamp struct{
    
    }

func (l Lamp) On() {
    
    
        println("On")

}
func (l Lamp) Off() {
    
    
        println("Off")
}

Guess you like

Origin blog.csdn.net/weixin_44816664/article/details/132143563