Golang指针使用问题求助
Golang指针使用问题求助 我之前从未在支持指针的语言中编写过程序。我写了这段代码,但不太确定它为什么能正常工作:
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"path"
)
type Gist struct {
AccessToken string
Files []string
}
type Conf struct {
Gist Gist
}
// 尝试读取用户主目录下的 .sinkerrc.json 文件
func ReadSinkerRc() ([]byte, error) {
homdir, err := os.UserHomeDir()
if err != nil {
return nil, err
}
data, err := ioutil.ReadFile(path.Join(homdir, ".sinkerrc.json"))
if err != nil {
return nil, err
}
return data, nil
}
// 解析配置中的json。
func ParseJsonConfg(data []byte) (Conf, error) {
var conf Conf
err := json.Unmarshal(data, &conf)
return conf, err
}
// 获取配置数据,负责读取并解析配置文件中的json
func Get() (*Conf, error) {
data, err := ReadSinkerRc()
if err != nil {
return nil, err
}
conf, err := ParseJsonConfg(data)
if err != nil {
return nil, err
}
return &conf, nil
}
func main() {
config, err := Get()
if err != nil {
log.Fatal(err.Error())
}
fmt.Println(config.Gist.AccessToken)
}
Get 函数返回一个指向 Conf 的指针和一个错误,因为否则我们无法表示一个为 nil 的 Conf,但我们可以表示一个 nil 指针,因为指针的默认零值是 nil。并且没有办法让一个结构体的值为 nil,对吗?如果是这样,为什么不行呢?
我不理解的是,如果 Get 返回一个 Conf 的内存地址和一个错误,为什么在 main 函数中我可以直接访问 Conf 的属性,而不需要获取它的底层值?为什么我不需要做 *conf 来获取底层值?
更多关于Golang指针使用问题求助的实战教程也可以访问 https://www.itying.com/category-94-b0.html
非常感谢。我现在明白了。那确实让我很困惑。感谢您抽出时间。
更多关于Golang指针使用问题求助的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
性能并不是使用指针的特定用例。使用指针的主要目的是为了实现值的共享可变性,以便所有引用该值的地方都能观察到其变化。当你需要这种可变性时,应该使用指针;如果不需要,则应使用非指针值。
根据程序的行为以及逃逸分析器的决定,使用指针可能会将对象分配到堆上而非栈上,从而导致更多的"Stop-the-World"垃圾回收,从而损害程序的性能。
skillian:
我不太确定你所说的在函数作用域之外修改那个字符串有什么用处。
我询问的是,通常我编写代码时都不使用显式指针,并且总是从函数作用域返回新值。在这篇文章中,关于“为什么使用指针”的一个答案是,这本身并不完全是为了性能,而是为了让我们能够观察系统范围内的状态变化并做出反应。我特别想知道,什么时候能有一个良好且常见的用例来说明这个观点。
另一个问题……指针的使用似乎违背了我在 JavaScript、Python 等语言中遵循多年的许多实践,在这些语言中,我尽量避免函数作用域之外的隐式更改。
从函数外部修改变量似乎很危险,因为这可能难以追踪是谁在更改什么。话虽如此,我想我理解指针的意义,正是为了允许这种情况,以避免将数据结构复制到函数的作用域中。对于大型数据结构,在更注重性能的语言中,这是有道理的。我的观察对吗?
除了性能原因之外,指针还有其他特定的使用场景吗?
你能帮我理解,除了性能之外,为什么在函数作用域之外改变那个字符串是有用的吗?
对于这个特定的例子,基于结构体字段中的 json 标签,看起来该字段是可变的,以便 json.Unmarshal(或其他兼容的实现)能够将数据反序列化到其中。在没有阅读他们代码的情况下,我不明白他们为什么使用 *string 而不是 string。这似乎暗示在 JSON 中这些字段可能为 null,并且在 Go 代码中,nil 的 *string 和 "" 的 string 之间存在有意义的区别。
我不太确定你所说的“在函数作用域之外改变那个字符串是有用的”是什么意思。
例如,在GitHub API的Go语言绑定中,一个Gist结构体包含一个嵌入的Files结构体,而该Files结构体又有一个Filename字段,这个字段不是字符串类型,而是一个指针。您能帮我理解,除了性能原因之外,为什么在函数作用域之外修改这个字符串是有用的吗?
type GistFile struct {
Size *int `json:"size,omitempty"`
Filename *string `json:"filename,omitempty"`
Language *string `json:"language,omitempty"`
Type *string `json:"type,omitempty"`
RawURL *string `json:"raw_url,omitempty"`
Content *string `json:"content,omitempty"`
}
为什么我不需要执行
*conf来获取底层的值?
我相信这个问题在某个地方被 Rus Cox 回答过,但我现在找不到了。Go 语言的设计者们认为,使用 . 来访问结构体值或结构体指针的字段都是可以的,因为这没有歧义。我觉得这有点像这样:
var a, b float32
a = 1.2
b = 3.4
// 我们应该将 * 操作编译为 CPU 整数乘法还是浮点乘法?这很明显!a 和 b 是浮点数,所以使用浮点乘法!
c := a * b
var d struct{ e float32 }
// 这个 . 是针对指针还是值?d 是一个结构体值,所以显然是后者!
d.e = c
var f *struct{ g float32 } = new(struct{ float32 })
// 同样,类型让这一点显而易见。
f.g = c
你可以写 fmt.Println((*config).Gist.AccessToken),但这是不必要的,因为对于 . 你还能合理地表示其他什么意思呢?
在Go语言中,当你通过指针访问结构体字段时,编译器会自动进行解引用。config.Gist.AccessToken 实际上等价于 (*config).Gist.AccessToken,这是Go语言提供的语法糖,让指针操作更加简洁。
示例说明:
package main
import "fmt"
type Person struct {
Name string
Age int
}
func getPerson() (*Person, error) {
p := Person{Name: "Alice", Age: 30}
return &p, nil
}
func main() {
// 方法1:直接访问(自动解引用)
p1, _ := getPerson()
fmt.Println(p1.Name) // 自动解引用,等价于 (*p1).Name
// 方法2:显式解引用
p2, _ := getPerson()
fmt.Println((*p2).Name) // 显式解引用
// 两种方式是等价的
fmt.Println(p1.Name == (*p1).Name) // true
}
关于结构体nil值的问题:你是正确的。结构体类型的零值是一个所有字段都为相应类型零值的结构体实例,而不是nil。只有指针、切片、映射、通道、函数和接口类型的零值才是nil。
示例对比:
package main
import "fmt"
type Config struct {
Value string
}
func getConfigPtr() (*Config, error) {
return nil, nil // 可以返回nil指针
}
func getConfigValue() (Config, error) {
var c Config
return c, nil // 返回的是零值Config,不是nil
}
func main() {
// 指针可以检查nil
c1, _ := getConfigPtr()
if c1 == nil {
fmt.Println("c1 is nil")
}
// 结构体值不能是nil
c2, _ := getConfigValue()
// if c2 == nil { // 编译错误:cannot convert nil to type Config
// }
// 结构体零值
fmt.Printf("c2: %+v\n", c2) // c2: {Value:}
}
在你的代码中,Get() 返回 &conf 是正确的做法,这样可以在出错时返回 nil, err,调用方可以通过检查指针是否为nil来判断是否获取到了有效配置。


