空指针解引用

huangapple go评论91阅读模式
英文:

Nil Pointer Dereference

问题

所以我今天尝试了一些新的东西,但是这段代码给了我一个空指针解引用错误。我知道为什么会出现这个错误。然而,我想要做的是在解引用之前检查指针是否为空,由于我使用了unsafe.Pointer,我无法弄清楚如何做到这一点。

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	test := readByte(0x34534)
	fmt.Println(test)
}

func readByte(address uintptr) byte {
	byteAddr := (*byte)(unsafe.Pointer(address))
	if byteAddr == nil {
		return 0
	}
	return *byteAddr
}
英文:

So I'm trying something new today, and this code gives me a nil pointer dereference. I know why I'm getting one. However what I want to do is see if the pointer is nil before I dereference it and due to my use of unsafe.pointer I can't figure out how to do that.

package main

import (
	"fmt"
	"unsafe"
)

func main() {
	test := readByte(0x34534)
	fmt.Println(test)
}

func readByte(address uintptr) byte {
	byteAddr := (*byte)(unsafe.Pointer(address))
	if byteAddr == nil {
		return 0
	}
	return *byteAddr
}

答案1

得分: 2

这不是一个空指针,并且不会引发空指针解引用错误。它会给出一个更通用的错误。在Go语言中,没有一种通用的方法来验证将整数视为指针,实际上,有时候也没有机器特定的方法。在非常低层次的操作中,比如汇编代码,往往需要使用非常专门的技巧。例如,某些指令集有一个"测试地址可访问性"的指令:

probe (r1),#read,r2

这个指令会根据存储在r1中的地址是否可读,将r2设置为true/false值。其他一些指令集甚至不允许这样做,而是要求你捕获访问时发生的错误,这导致了下面这种形式的代码,我将其转换为伪Go代码:

p = sysmagic.GetMagic()
didfault := false
p.trapfault = &didfault // 告诉错误处理程序设置didfault并跳过加载
p.allowedpc = getPC()   // 假设程序计数器在这附近
v := *((*byte)unsafe.Pointer(testaddr))
p.trapfault = nil
if didfault {
    // 地址无效
} else {
    // v保存了被访问的字节
}

(尽管这种代码更常见地使用C或汇编语言编写)。操作系统的错误处理代码会检查是否处于特殊的"捕获并跳过错误"模式,并将程序计数器前进到失败的加载指令之后,这个加载指令必须在注释的程序计数器附近(否则,任何失败的加载指令,例如在NMI处理程序中,都可能将didfault设置为true)。

(这里还使用了另一种方法,即在一个表中标记"无错误加载"指令的地址,如果发生错误,则检查是否尝试执行了其中一个标记指令,并在这种情况下,将"恢复"程序计数器设置为表中存储的地址。这在具有可变长度指令的机器上非常有用,可以编写特殊的"将字符串复制到内核"函数等。)

简而言之,你不能以可移植的方式做到这一点。在任何给定的机器上,通常都有一些方法可以实现,但可能会受到限制。你必须查阅你的机器和/或操作系统的硬件和软件文档,看看是否可以恢复此类错误。鉴于(a)操作系统的作者通常已经为你做了这方面的工作,无论在多大程度上是可能的,以及(b)Go语言倾向于在这里使用panic,你可能可以捕获Go的panic并进行恢复,但在某些机器上,各种总线错误可能会使机器本身变得相当不稳定,导致你的程序变得不稳定。

英文:

That isn't a nil pointer and doesn't give a nil-pointer-deref-error. (It gives you a more generic error.)

There is no general method in Go to validate an arbitrary integer-treated-as-pointer, and in fact, sometimes there's no machine-specific method either. To do this sort of thing at a very low level—e.g., in assembly code—tends to require extremely specialized tricks. Some instruction sets have a "test accessibility of address" instruction, for instance:

probe (r1),#read,r2

sets r2 to a true/false value depending on whether the address stored in r1 is readable. Others don't even allow this, and instead require that you catch the fault that occurs when you attempt the access, which leads to code of this form, which I've translated into pseudo-Go:

p = sysmagic.GetMagic()
didfault := false
p.trapfault = &didfault // tell fault handler to set didfault and skip the load
p.allowedpc = getPC()   // provided the program counter is near here
v := *((*byte)unsafe.Pointer(testaddr))
p.trapfault = nil
if didfault {
    // address was bad
} else {
    // v holds the byte that was accessed
}

(although this sort of thing is more commonly written in C or assembly these days). The fault-catching code, part of the OS, then checks to see if we're in the special "catch and skip a fault" mode and advances the program counter to move past the failing load instruction, which must be within close range of the annotated program counter (otherwise any load instruction that fails, e.g., in an NMI handler, might set didfault to true).

(Another method that is used here is to mark, in a table, the address of "no fault load" instructions, and if a fault occurs, to check to see if we've tried to execute one of those marked instructions and if so, set the "resume" PC to the address stored as a companion in the table. This is useful on machines that have variable-length instructions, and lets us write special "copy string into kernel" functions and the like.)

The short version, though, is that you cannot do this portably. There usually is some method for doing it, on any given machine, but it may be restricted. You must consult the hardware and software documentation for your machine and/or OS and see if such faults are recoverable. Given that (a) OS authors generally have already done this for you to whatever extent is possible and (b) Go tends to use this to panic here, it's possible that you can catch the Go panic and recover—but on some machines, bus faults of various kinds can make the machine itself rather erratic, so that your program becomes unstable.

答案2

得分: -2

你可以使用if语句并返回true或false的值。

package main

import (
    "fmt"
)

type Temp struct {
}

func main() {
    var pnt *Temp // 指针
    var x = "123"
    var pnt1 *string = &x

    if pnt == nil {
        fmt.Println("True")
    } else if pnt1 == nil {
        fmt.Println("False")
    }

    fmt.Printf("pnt是一个空指针:%v\n", pnt == nil)
    fmt.Printf("pnt1是一个空指针:%v\n", pnt1 == nil)
}

希望对你有帮助!

英文:

You can use the if statement and return the true or false value.

package main
  
import (
    "fmt"
)
  
type Temp struct {
}
  
func main() {
    var pnt *Temp // pointer
    var x = "123"
    var pnt1 *string = &x
  
    if pnt == nil {
	fmt.Println("True")
    } else if pnt1 == nil {
        fmt.Println("False")
    }

    fmt.Printf("pnt is a nil pointer: %v\n", pnt == nil)
    fmt.Printf("pnt1 is a nil pointer: %v\n", pnt1 == nil)
}

huangapple
  • 本文由 发表于 2021年9月16日 03:50:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/69199029.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定