In Go, slices and slices of pointers (i.e. each element in a slice is a pointer to a certain data type) are two different concepts, each with specific uses and advantages.
Slice
A slice is a reference to a continuous fragment of an array, which provides an abstract representation of a collection of array elements. The underlying data structure of slices is an array, which contains three key parts: a pointer to the starting element of the array, a slice length, and a slice capacity. The slice length refers to the number of elements currently contained in the slice, and the slice capacity refers to the number of elements from the starting element of the slice to the last element of the bottom array.
An important feature of slices is that it provides a dynamic view of the underlying array. This means that you can access, modify, and manipulate some or all elements of an array through slices without copying the entire array. In addition, the slice can be easily expanded and reduced in size to meet the needs of different scenarios.
Example:
package main import "fmt" func main() { // Define an array array := [5]int{1, 2, 3, 4, 5} // Create a slice that references the first three elements of the array slice := array[:3] // Print slice elements (slice) // Output: [1 2 3] // Modifying the slice element will also modify the elements corresponding to the underlying array slice[1] = 100 (array) // Output: [1 100 3 4 5]}
Slice of pointer
A slice of a pointer is a slice, but its element is a pointer pointing to an instance of a certain data type. This means that each element is an address through which the actual data can be accessed and manipulated indirectly. Slices of pointers are often used in scenarios where large amounts of data are required to store and want to avoid data replication, or when it is necessary to store variable-sized objects in slices.
Using slicing of pointers saves memory space because only pointers are stored instead of actual data. At the same time, the original data can be easily modified through pointers. However, this also brings additional complexity and risks, as the dereference and memory management of pointers need to be handled with care.
Example:
package main import "fmt" type Person struct { Name string Age int } func main() { // Create pointers to several Person instances person1 := &Person{Name: "Alice", Age: 30} person2 := &Person{Name: "Bob", Age: 25} // Create a slice of pointers to store these pointers people := []*Person{person1, person2} // Modify the properties of the Person instance through a pointer people[0].Age = 31 // Print the modified Person instance (people[0].Name, people[0].Age) // Output: Alice 31}
The difference between structure slice and structure pointer slice
-
Memory usage:
Structure slice: Each element is a complete copy of the structure, so it consumes a lot of memory.
Structure pointer slice: Each element is just a pointer to the structure, and the memory consumption is small. However, this does not mean that the overall memory footprint will be small, because the memory occupied by the actual structure object also needs to be considered.
-
Modify elements:
Structure slice: Modifying an element in the slice will directly modify the value of the element and will not affect other slices or original structure objects.
Structure pointer slice: Modify the element pointed to by a pointer in the slice, which will affect all pointers to that element. If multiple slices or variables point to the same structure object, modifying the content of that object will affect all references.
-
Initialization and assignment:
Structure slice: can be initialized and assigned directly.
Structure pointer slice: You need to initialize the structure object first, and then assign the address of the object to the slice.
-
nil with empty slices:
For structure pointer slices, nil slices and empty slices (slices of length 0) are different. The nil slice does not allocate the underlying array, while the empty slices allocate the underlying array but have a length of 0.
For structure slices, nil slices are not usually discussed because slices are always associated with the underlying array.
Can you directly traverse the structure slice and assign the value to the structure pointer slice
Cannot directly traverse the structure slice and assign value to the structure pointer slice. Because the elements in the structure slice are the values of the structure, and the elements in the structure pointer slice are pointers to the structure. You need to traverse the struct slices and create pointers for each element separately, and then add these pointers to the struct pointer slice.
Example description
Suppose we have onePerson
Structure:
type Person struct { Name string Age int }
Use of structure slices
// Create and initialize a Person structure slicepeopleSlice := []Person{ {"Alice", 30}, {"Bob", 25}, } // Modify an element in the slicepeopleSlice[0].Age = 31 // traverse and print elements in the slicefor _, person := range peopleSlice { (, ) }
Use of structure pointer slices
// Create and initialize some Person structure objectsalice := Person{"Alice", 30} bob := Person{"Bob", 25} // Create a Person pointer slice and add the address of the structure object to the slicepeoplePtrSlice := []*Person{&alice, &bob} // Modify the element pointed to by a pointer in the slicepeoplePtrSlice[0].Age = 31 // This will change the age of alice, because peoplePtrSlice[0] points to alice// Iterate over and print elements in the slice (by dereference pointer)for _, personPtr := range peoplePtrSlice { (, ) } // If you want to traverse the structure slice and assign the value to the structure pointer slice, you need to do this:peopleSlice := []Person{ {"Charlie", 28}, {"David", 35}, } // Initialize an empty Person pointer slice with the same length as peopleSlicepeoplePtrSlice = make([]*Person, len(peopleSlice)) // traverse peopleSlice and assign a new pointer to peoplePtrSlicefor i, person := range peopleSlice { // Create a copy of the person, get its address, and assign it to peoplePtrSlice peoplePtrSlice[i] = &Person{Name: , Age: } } // NowpeoplePtrSliceIncludes pointingpeopleSlicePointer to the element copy
In the example above,peoplePtrSlice
It is through traversalpeopleSlice
and create the address of each element's copy to initialize. If you wantpeoplePtrSlice
Pointer in pointer topeopleSlice
The same objects (not copies) you need to make sure that these objects are passednew
Function or through&
Operators are allocated on the heap, and their addresses are added topeoplePtrSlice
middle. However, in most cases, you may not want to do this, as this will result in sharing the same object between slices, which can cause unexpected side effects.
Can you directly traverse the structure pointer slice and assign value to the structure slice
You cannot directly traverse the structure pointer slice and assign value to the structure slice. The element in the structure pointer slice is a pointer to the structure, while the element in the structure slice is the value of the structure. So if you try to assign a pointer in the pointer slice directly to the structure slice, you will get the value of the pointer (i.e. the memory address), not the value of the structure object itself.
To traverse the structure pointer slice and assign the structure object pointed to by the pointer to the structure slice, you need to dereference each pointer, get the structure object it points to, and then assign the object to the corresponding position in the structure slice.
Here is a detailed example and analysis:
package main import ( "fmt" ) // Define Person structuretype Person struct { Name string Age int } func main() { // Initialize some Person structure objects and get their addresses alice := &Person{"Alice", 30} bob := &Person{"Bob", 25} // Create a Person pointer slice peoplePtrSlice := []*Person{alice, bob} // Create a Person structure slice with the same length as peoplePtrSlice peopleSlice := make([]Person, len(peoplePtrSlice)) // traverse peoplePtrSlice, dereference pointer and assign structure objects to peopleSlice for i, personPtr := range peoplePtrSlice { peopleSlice[i] = *personPtr // Dereference pointer to get structure object } // Now peopleSlice contains a copy of the structure object pointed to by the pointer in peoplePtrSlice (peopleSlice) // Output: [{Alice 30} {Bob 25}] // Modifying elements in peopleSlice will not affect peoplePtrSlice or original structure objects peopleSlice[0].Age = 31 (peopleSlice) // Output: [{Alice 31} {Bob 25}] (peoplePtrSlice) // Output: [&{Alice 30} &{Bob 25}], the elements in peoplePtrSlice have not changed}
In this example:
- We first created two
Person
Pointer to structure objectalice
andbob
。 - Then we create a containing both pointers
peoplePtrSlice
slice. - Then we created a
peoplePtrSlice
The same length is emptyPerson
Slice structurepeopleSlice
。 - On traversal
peoplePtrSlice
When we use*personPtr
to dereference pointer and assign the resulting structure object topeopleSlice
corresponding position in . - Finally, we verified the modification
peopleSlice
The elements in it will not affectpeoplePtrSlice
or original structure object.
It should be noted that doing so creates a copy of the structure object. If you just want to reference the original objects instead of their copy, you should use pointer slices directly instead of creating structure slices. If you really need structure slices,And want to keep a reference to the original object,Then you need to rethink your data structure design,Because the structure slice itself does not save references to the original object。
This is the end of this article about go slices and pointer slices. For more related go slices and pointer slices, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!