Keywords: Go language | pointers | parameter passing
Abstract: This article provides an in-depth analysis of pointer values in Go, including their meaning, printing methods, and behavior during function parameter passing. Through detailed code examples, it explains why printing the address of the same pointer variable in different scopes yields different values, clarifying Go's pass-by-value nature. The article thoroughly examines the relationship between pointer variables and the objects they point to, offering practical recommendations for using the fmt package to correctly print pointer information and helping developers build accurate mental models of memory management.
Fundamental Concepts of Pointer Values
In Go, a pointer is a special data type that stores the memory address of another variable. Understanding the meaning of pointer values is crucial for mastering Go's memory management mechanisms. A pointer value essentially serves as an identifier for a memory location, through which data stored at that location can be accessed.
Function Parameter Passing Mechanism
Go strictly adheres to the principle of pass-by-value. This means that when a function is called, the values of all arguments are copied into the function's parameter variables. For pointer-type parameters, what gets copied is the pointer value (i.e., the memory address), not the data pointed to by the pointer.
Consider the following simplified example:
package main
import "fmt"
func byval(q *int) {
fmt.Printf("3. byval -- q %T: &q=%p q=&i=%p *q=i=%v\n", q, &q, q, *q)
*q = 4143
fmt.Printf("4. byval -- q %T: &q=%p q=&i=%p *q=i=%v\n", q, &q, q, *q)
q = nil
}
func main() {
i := int(42)
fmt.Printf("1. main -- i %T: &i=%p i=%v\n", i, &i, i)
p := &i
fmt.Printf("2. main -- p %T: &p=%p p=&i=%p *p=i=%v\n", p, &p, p, *p)
byval(p)
fmt.Printf("5. main -- p %T: &p=%p p=&i=%p *p=i=%v\n", p, &p, p, *p)
fmt.Printf("6. main -- i %T: &i=%p i=%v\n", i, &i, i)
}
The program output clearly demonstrates the pointer passing process:
1. main -- i int: &i=0xf840000040 i=42
2. main -- p *int: &p=0xf8400000f0 p=&i=0xf840000040 *p=i=42
3. byval -- q *int: &q=0xf8400000d8 q=&i=0xf840000040 *q=i=42
4. byval -- q *int: &q=0xf8400000d8 q=&i=0xf840000040 *q=i=4143
5. main -- p *int: &p=0xf8400000f0 p=&i=0xf840000040 *p=i=4143
6. main -- i int: &i=0xf840000040 i=4143
Reason for Pointer Address Differences
In the original question, the user observed that printing &s in the main function and in the goroutine yielded different address values: 0x4930d4 and 0x4974d8. This occurs because &s prints the memory address of the pointer variable s itself, not the address of the object that s points to.
In the main function, s is a variable of type *Something, stored at address 0x4930d4. When this variable is passed as an argument to the gotest function, Go creates a new parameter variable (also named s) stored at address 0x4974d8. Although these two variables reside at different memory locations, they contain the same pointer value—both point to the same Something object.
Correct Methods for Printing Pointers
Using println(&s) to print pointer information has limitations, as it prints the address of the pointer variable rather than the address of the object it points to. It is recommended to use the formatting capabilities provided by the fmt package:
fmt.Printf("%v %p %v\n", &s, s, *s)
Where:
%vprints the address of the pointer variable (&s)%pprints the pointer value (s, i.e., the address of the pointed-to object)%vprints the dereferenced value of the pointer (*s)
Pointers and Object Identity
Go does not provide an object ID concept similar to Java. The unique identifier of an object is its memory address. When two pointer variables contain the same memory address value, they point to the same object. It is important to note that pointer values may change during program execution due to garbage collector relocation of objects, but this is an internal behavior of the runtime system and is transparent to programmers.
Practical Recommendations
- Clearly distinguish between the address of a pointer variable and the address of the object it points to
- Understand Go's strict pass-by-value mechanism, including the copying of pointer values
- Use the
fmtpackage instead ofprintlnfor debugging output - Avoid using printed pointer values to determine object identity; instead, use object content or business logic for such determinations
By deeply understanding pointer passing mechanisms, developers can better master Go's memory model and write more efficient, reliable concurrent programs.