在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
A uintptr is an integer, not a reference.
1.1.2 应用: equivalent to f := unsafe.Pointer(&s.f) equivalent to e := unsafe.Pointer(&x[i]) If p points into an allocated object, it can be advanced through the object 将指针转化为uintptr,再加上偏移量,最后转换回指针:可以用来访问结构体的字段或数组的元素 注意还有其他用法,详情请见 源码 src\unsafe\unsafe.go
unsafe\unsafe.go // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* Package unsafe contains operations that step around the type safety of Go programs. Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines. */ package unsafe // ArbitraryType is here for the purposes of documentation only and is not actually // part of the unsafe package. It represents the type of an arbitrary Go expression. type ArbitraryType int // IntegerType is here for the purposes of documentation only and is not actually // part of the unsafe package. It represents any arbitrary integer type. type IntegerType int // Pointer represents a pointer to an arbitrary type. There are four special operations // available for type Pointer that are not available for other types: // - A pointer value of any type can be converted to a Pointer. // - A Pointer can be converted to a pointer value of any type. // - A uintptr can be converted to a Pointer. // - A Pointer can be converted to a uintptr. // Pointer therefore allows a program to defeat the type system and read and write // arbitrary memory. It should be used with extreme care. // // The following patterns involving Pointer are valid. // Code not using these patterns is likely to be invalid today // or to become invalid in the future. // Even the valid patterns below come with important caveats. // // Running "go vet" can help find uses of Pointer that do not conform to these patterns, // but silence from "go vet" is not a guarantee that the code is valid. // // (1) Conversion of a *T1 to Pointer to *T2. // // Provided that T2 is no larger than T1 and that the two share an equivalent // memory layout, this conversion allows reinterpreting data of one type as // data of another type. An example is the implementation of // math.Float64bits: // // func Float64bits(f float64) uint64 { // return *(*uint64)(unsafe.Pointer(&f)) // } // // (2) Conversion of a Pointer to a uintptr (but not back to Pointer). // // Converting a Pointer to a uintptr produces the memory address of the value // pointed at, as an integer. The usual use for such a uintptr is to print it. // // Conversion of a uintptr back to Pointer is not valid in general. // // A uintptr is an integer, not a reference. // Converting a Pointer to a uintptr creates an integer value // with no pointer semantics. // Even if a uintptr holds the address of some object, // the garbage collector will not update that uintptr's value // if the object moves, nor will that uintptr keep the object // from being reclaimed. // // The remaining patterns enumerate the only valid conversions // from uintptr to Pointer. // // (3) Conversion of a Pointer to a uintptr and back, with arithmetic. // // If p points into an allocated object, it can be advanced through the object // by conversion to uintptr, addition of an offset, and conversion back to Pointer. // // p = unsafe.Pointer(uintptr(p) + offset) // // The most common use of this pattern is to access fields in a struct // or elements of an array: // // // equivalent to f := unsafe.Pointer(&s.f) // f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f)) // // // equivalent to e := unsafe.Pointer(&x[i]) // e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0])) // // It is valid both to add and to subtract offsets from a pointer in this way. // It is also valid to use &^ to round pointers, usually for alignment. // In all cases, the result must continue to point into the original allocated object. // // Unlike in C, it is not valid to advance a pointer just beyond the end of // its original allocation: // // // INVALID: end points outside allocated space. // var s thing // end = unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s)) // // // INVALID: end points outside allocated space. // b := make([]byte, n) // end = unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(n)) // // Note that both conversions must appear in the same expression, with only // the intervening arithmetic between them: // // // INVALID: uintptr cannot be stored in variable // // before conversion back to Pointer. // u := uintptr(p) // p = unsafe.Pointer(u + offset) // // Note that the pointer must point into an allocated object, so it may not be nil. // // // INVALID: conversion of nil pointer // u := unsafe.Pointer(nil) // p := unsafe.Pointer(uintptr(u) + offset) // // (4) Conversion of a Pointer to a uintptr when calling syscall.Syscall. // // The Syscall functions in package syscall pass their uintptr arguments directly // to the operating system, which then may, depending on the details of the call, // reinterpret some of them as pointers. // That is, the system call implementation is implicitly converting certain arguments // back from uintptr to pointer. // // If a pointer argument must be converted to uintptr for use as an argument, // that conversion must appear in the call expression itself: // // syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n)) // // The compiler handles a Pointer converted to a uintptr in the argument list of // a call to a function implemented in assembly by arranging that the referenced // allocated object, if any, is retained and not moved until the call completes, // even though from the types alone it would appear that the object is no longer // needed during the call. // // For the compiler to recognize this pattern, // the conversion must appear in the argument list: // // // INVALID: uintptr cannot be stored in variable // // before implicit conversion back to Pointer during system call. // u := uintptr(unsafe.Pointer(p)) // syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n)) // // (5) Conversion of the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr // from uintptr to Pointer. // // Package reflect's Value methods named Pointer and UnsafeAddr return type uintptr // instead of unsafe.Pointer to keep callers from changing the result to an arbitrary // type without first importing "unsafe". However, this means that the result is // fragile and must be converted to Pointer immediately after making the call, // in the same expression: // // p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer())) // // As in the cases above, it is invalid to store the result before the conversion: // // // INVALID: uintptr cannot be stored in variable // // before conversion back to Pointer. // u := reflect.ValueOf(new(int)).Pointer() // p := (*int)(unsafe.Pointer(u)) // // (6) Conversion of a reflect.SliceHeader or reflect.StringHeader Data field to or from Pointer. // // As in the previous case, the reflect data structures SliceHeader and StringHeader // declare the field Data as a uintptr to keep callers from changing the result to // an arbitrary type without first importing "unsafe". However, this means that // SliceHeader and StringHeader are only valid when interpreting the content // of an actual slice or string value. // // var s string // hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1 // hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case) // hdr.Len = n // // In this usage hdr.Data is really an alternate way to refer to the underlying // pointer in the string header, not a uintptr variable itself. // // In general, reflect.SliceHeader and reflect.StringHeader should be used // only as *reflect.SliceHeader and *reflect.StringHeader pointing at actual // slices or strings, never as plain structs. // A program should not declare or allocate variables of these struct types. // // // INVALID: a directly-declared header will not hold Data as a reference. // var hdr reflect.StringHeader // hdr.Data = uintptr(unsafe.Pointer(p)) // hdr.Len = n // s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost // type Pointer *ArbitraryType // Sizeof takes an expression x of any type and returns the size in bytes // of a hypothetical variable v as if v was declared via var v = x. // The size does not include any memory possibly referenced by x. // For instance, if x is a slice, Sizeof returns the size of the slice // descriptor, not the size of the memory referenced by the slice. // The return value of Sizeof is a Go constant. func Sizeof(x ArbitraryType) uintptr // Offsetof returns the offset within the struct of the field represented by x, // which must be of the form structValue.field. In other words, it returns the // number of bytes between the start of the struct and the start of the field. // The return value of Offsetof is a Go constant. func Offsetof(x ArbitraryType) uintptr // Alignof takes an expression x of any type and returns the required alignment // of a hypothetical variable v as if v was declared via var v = x. // It is the largest value m such that the address of v is always zero mod m. // It is the same as the value returned by reflect.TypeOf(x).Align(). // As a special case, if a variable s is of struct type and f is a field // within that struct, then Alignof(s.f) will return the required alignment // of a field of that type within a struct. This case is the same as the // value returned by reflect.TypeOf(s.f).FieldAlign(). // The return value of Alignof is a Go constant. func Alignof(x ArbitraryType) uintptr // The function Add adds len to ptr and returns the updated pointer // Pointer(uintptr(ptr) + uintptr(len)). // The len argument must be of integer type or an untyped constant. // A constant len argument must be representable by a value of type int; // if it is an untyped constant it is given type int. // The rules for valid uses of Pointer still apply. func Add(ptr Pointer, len IntegerType) Pointer // The function Slice returns a slice whose underlying array starts at ptr // and whose length and capacity are len. // Slice(ptr, len) is equivalent to // // (*[len]ArbitraryType)(unsafe.Pointer(ptr))[:] // // except that, as a special case, if ptr is nil and len is zero, // Slice returns nil. // // The len argument must be of integer type or an untyped constant. // A constant len argument must be non-negative and representable by a value of type int; // if it is an untyped constant it is given type int. // At run time, if len is negative, or if ptr is nil and len is not zero, // a run-time panic occurs. func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
math\unsafe.go // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package math import "unsafe" // Float32bits returns the IEEE 754 binary representation of f, // with the sign bit of f and the result in the same bit position. // Float32bits(Float32frombits(x)) == x. func Float32bits(f float32) uint32 { return *(*uint32)(unsafe.Pointer(&f)) } // Float32frombits returns the floating-point number corresponding // to the IEEE 754 binary representation b, with the sign bit of b // and the result in the same bit position. // Float32frombits(Float32bits(x)) == x. func Float32frombits(b uint32) float32 { return *(*float32)(unsafe.Pointer(&b)) } // Float64bits returns the IEEE 754 binary representation of f, // with the sign bit of f and the result in the same bit position, // and Float64bits(Float64frombits(x)) == x. func Float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) } // Float64frombits returns the floating-point number corresponding // to the IEEE 754 binary representation b, with the sign bit of b // and the result in the same bit position. // Float64frombits(Float64bits(x)) == x. func Float64frombits(b uint64) float64 { return *(*float64)(unsafe.Pointer(&b)) }
package main import ( "fmt" "unsafe" ) func f() { a := [16]int{3: 3, 9: 9, 11: 11} eleSize := int(unsafe.Sizeof(a[0])) p9 := &a[9] up9 := unsafe.Pointer(p9) p3 := (*int)(unsafe.Add(up9, -6*eleSize)) s := unsafe.Slice(p9, 5)[:3] t := unsafe.Slice((*int)(nil), 0) _ = unsafe.Add(up9, 7*eleSize) _ = unsafe.Slice(p9, 8) fmt.Println(a) fmt.Println(*p3) fmt.Println(s) fmt.Println(len(s), cap(s)) fmt.Println(t == nil) i := unsafe.Add(up9, 7*eleSize) j := unsafe.Add(up9, 16*eleSize) i1 := unsafe.Add(up9, 1*eleSize) j1 := unsafe.Add(up9, 2*eleSize) j3 := unsafe.Add(up9, 17*eleSize) fmt.Println(i, j) fmt.Println(i1, j1, j3) } func main() { f() }
[0 0 0 3 0 0 0 0 0 9 0 11 0 0 0 0] unsafe package - unsafe - pkg.go.dev https://pkg.go.dev/unsafe https://pkg.go.dev/unsafe 非类型安全指针 - Go语言101(通俗版Go白皮书) https://gfw.go101.org/article/unsafe.html
(1) Conversion of a *T1 to Pointer to *T2. Provided that T2 is no larger than T1 and that the two share an equivalent memory layout, this conversion allows reinterpreting data of one type as data of another type. An example is the implementation of math.Float64bits: func Float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) }
Understanding uintptr in Golang - Welcome To Golang By Example https://golangbyexample.com/understanding-uintptr-golang/ This is an unsigned integer type which is large enough to hold any pointer address. Therefore its size is platform dependent. It is just an integer representation of an address.
Properties
Purposeuintptr can be used for below purposes:
Be careful that the above steps should be atomic with respect to Garbage Collector, otherwise it could lead to issues. For eg after the first step 1, the referenced object is liable to be collection. If that happens then after step 3, the pointer will be an invalid Go pointer and can crash the program. Look at the unsafe package documentation. https://golang.org/pkg/unsafe/#Pointer It lists down when the above conversion can be safe. See the below code for the scenario mentioned above. In the below code we are doing arithmetic like below to get to address of field “b” in struct sample and then printing the value at that address. This below code is atomic with reference to the garbage collector.
Output:
See below code where we are converting an unsafe.Pointer to uintptr and printing it. Also, note as mentioned before too one the unsafe.Pointer is converted to uinptr, the reference is lost and the reference variable can be garbage collected.
Output: The output will be dependent upon the machine as it is an address.
atomic.LoadUintptr() Function in Golang With Examples - GeeksforGeeks https://www.geeksforgeeks.org/atomic-loaduintptr-function-in-golang-with-examples/
In Go language, atomic packages supply lower-level atomic memory that is helpful is implementing synchronization algorithms. The LoadUintptr() function in Go language is used to atomically load *addr. This function is defined under the atomic package. Here, you need to import “sync/atomic” package in order to use these functions. Syntax: func LoadUintptr(addr *uintptr) (val uintptr) Here, addr indicates address. Note: (*uintptr) is the pointer to a uintptr value. And uintptr is an integer type that is too large that it can contain the bit pattern of any pointer. Return value: It returns the value loaded to the *addr.
Example 1:
Output: 98 255 6576567667788 5 Example 2:
Output: 1818 // A random value is returned in each run In the above example, the new values are returned from AddUintptr() method in each call until the loop stops, LoadUintptr() method loads these new uintptr values. And these values are stored in different addresses which can be random one so, the output of the LoadUintptr() method here in each run is different. So, here a random value is returned in the output.
go - Uint and uintptr in golang - Stack Overflow https://stackoverflow.com/questions/69182402/uint-and-uintptr-in-golang 非类型安全指针 - Go语言101(通俗版Go白皮书) https://gfw.go101.org/article/unsafe.html unsafe - The Go Programming Language https://golang.google.cn/pkg/unsafe/#Pointer PointerPointer represents a pointer to an arbitrary type. There are four special operations available for type Pointer that are not available for other types: - A pointer value of any type can be converted to a Pointer. - A Pointer can be converted to a pointer value of any type. - A uintptr can be converted to a Pointer. - A Pointer can be converted to a uintptr. Pointer therefore allows a program to defeat the type system and read and write arbitrary memory. It should be used with extreme care. The following patterns involving Pointer are valid. Code not using these patterns is likely to be invalid today or to become invalid in the future. Even the valid patterns below come with important caveats. Running "go vet" can help find uses of Pointer that do not conform to these patterns, but silence from "go vet" is not a guarantee that the code is valid. (1) Conversion of a *T1 to Pointer to *T2. Provided that T2 is no larger than T1 and that the two share an equivalent memory layout, this conversion allows reinterpreting data of one type as data of another type. An example is the implementation of math.Float64bits: func Float64bits(f float64) uint64 { return *(*uint64)(unsafe.Pointer(&f)) } (2) Conversion of a Pointer to a uintptr (but not back to Pointer). Converting a Pointer to a uintptr produces the memory address of the value pointed at, as an integer. The usual use for such a uintptr is to print it. Conversion of a uintptr back to Pointer is not valid in general. A uintptr is an integer, not a reference. Converting a Pointer to a uintptr creates an integer value with no pointer semantics. Even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed. The remaining patterns enumerate the only valid conversions from uintptr to Pointer. (3) Conversion of a Pointer to a uintptr and back, with arithmetic. If p points into an allocated object, it can be advanced through the object by conversion to uintptr, addition of an offset, and conversion back to Pointer. p = unsafe.Pointer(uintptr(p) + offset) The most common use of this pattern is to access fields in a struct or elements of an array: // equivalent to f := unsafe.Pointer(&s.f) f := unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f)) // equivalent to e := unsafe.Pointer(&x[i]) e := unsafe.Pointer(uintptr(unsafe.Pointer(&x[0])) + i*unsafe.Sizeof(x[0])) It is valid both to add and to subtract offsets from a pointer in this way. It is also valid to use &^ to round pointers, usually for alignment. In all cases, the result must continue to point into the original allocated object. Unlike in C, it is not valid to advance a pointer just beyond the end of its original allocation: // INVALID: end points outside allocated space. var s thing end = unsafe.Pointer(uintptr(unsafe.Pointer(&s)) + unsafe.Sizeof(s)) // INVALID: end points outside allocated space. b := make([]byte, n) end = unsafe.Pointer(uintptr(unsafe.Pointer(&b[0])) + uintptr(n)) Note that both conversions must appear in the same expression, with only the intervening arithmetic between them: // INVALID: uintptr cannot be stored in variable // before conversion back to Pointer. u := uintptr(p) p = unsafe.Pointer(u + offset) Note that the pointer must point into an allocated object, so it may not be nil. // INVALID: conversion of nil pointer u := unsafe.Pointer(nil) p := unsafe.Pointer(uintptr(u) + offset) (4) Conversion of a Pointer to a uintptr when calling syscall.Syscall. The Syscall functions in package syscall pass their uintptr arguments directly to the operating system, which then may, depending on the details of the call, reinterpret some of them as pointers. That is, the system call implementation is implicitly converting certain arguments back from uintptr to pointer. If a pointer argument must be converted to uintptr for use as an argument, that conversion must appear in the call expression itself: syscall.Syscall(SYS_READ, uintptr(fd), uintptr(unsafe.Pointer(p)), uintptr(n)) The compiler handles a Pointer converted to a uintptr in the argument list of a call to a function implemented in assembly by arranging that the referenced allocated object, if any, is retained and not moved until the call completes, even though from the types alone it would appear that the object is no longer needed during the call. For the compiler to recognize this pattern, the conversion must appear in the argument list: // INVALID: uintptr cannot be stored in variable // before implicit conversion back to Pointer during system call. u := uintptr(unsafe.Pointer(p)) syscall.Syscall(SYS_READ, uintptr(fd), u, uintptr(n)) (5) Conversion of the result of reflect.Value.Pointer or reflect.Value.UnsafeAddr from uintptr to Pointer. Package reflect's Value methods named Pointer and UnsafeAddr return type uintptr instead of unsafe.Pointer to keep callers from changing the result to an arbitrary type without first importing "unsafe". However, this means that the result is fragile and must be converted to Pointer immediately after making the call, in the same expression: p := (*int)(unsafe.Pointer(reflect.ValueOf(new(int)).Pointer())) As in the cases above, it is invalid to store the result before the conversion: // INVALID: uintptr cannot be stored in variable // before conversion back to Pointer. u := reflect.ValueOf(new(int)).Pointer() p := (*int)(unsafe.Pointer(u)) (6) Conversion of a reflect.SliceHeader or reflect.StringHeader Data field to or from Pointer. As in the previous case, the reflect data structures SliceHeader and StringHeader declare the field Data as a uintptr to keep callers from changing the result to an arbitrary type without first importing "unsafe". However, this means that SliceHeader and StringHeader are only valid when interpreting the content of an actual slice or string value. var s string hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // case 1 hdr.Data = uintptr(unsafe.Pointer(p)) // case 6 (this case) hdr.Len = n In this usage hdr.Data is really an alternate way to refer to the underlying pointer in the string header, not a uintptr variable itself. In general, reflect.SliceHeader and reflect.StringHeader should be used only as *reflect.SliceHeader and *reflect.StringHeader pointing at actual slices or strings, never as plain structs. A program should not declare or allocate variables of these struct types. // INVALID: a directly-declared header will not hold Data as a reference. var hdr reflect.StringHeader hdr.Data = uintptr(unsafe.Pointer(p)) hdr.Len = n s := *(*string)(unsafe.Pointer(&hdr)) // p possibly already lost type Pointer *ArbitraryType Addfunc Add(ptr Pointer, len IntegerType) Pointer The function Add adds len to ptr and returns the updated pointer Pointer(uintptr(ptr) + uintptr(len)). The len argument must be of integer type or an untyped constant. A constant len argument must be representable by a value of type int; if it is an untyped constant it is given type int. The rules for valid uses of Pointer still apply.
非类型安全指针我们已经从Go中的指针一文中学习到关于指针的各种概念和规则。 从那篇文章中,我们得知,相对于C指针,Go指针有很多限制。 比如,Go指针不支持算术运算,并且对于任意两个指针值,很可能它们不能转换到对方的类型。 事实上,在那篇文章中解释的指针的完整称呼应该为类型安全指针。 虽然类型安全指针有助于我们轻松写出安全的代码,但是有时候施加在类型安全指针上的限制也确实导致我们不能写出最高效的代码。 实际上,Go也支持限制较少的非类型安全指针。 非类型安全指针和C指针类似,它们都很强大,但同时也都很危险。 在某些情形下,通过非类型安全指针的帮助,我们可以写出效率更高的代码; 但另一方面,使用非类型安全指针也导致我们可能轻易地写出潜在的不安全的代码,这些潜在的不安全点很难在它们产生危害之前被及时发现。 使用非类型安全指针的另外一个较大的风险是Go中目前提供的非类型安全指针机制并不受到Go 1 兼容性保证的保护。 使用了非类型安全指针的代码可能从今后的某个Go版本开始将不再能编译通过,或者运行行为发生了变化。 如果出于种种原因,你确实希望在你的代码中使用非类型安全指针,你不仅需要提防上述风险,你还需遵守Go官方文档中列出的非类型安全指针使用模式,并清楚地知晓使用非类型安全指针带来的效果。否则,你很难使用非类型安全指针写出安全的代码。 关于
|
请发表评论