用C和go语言分别打印int类型地址的,进行对比。
C版本:
#include <stdio.h>
int main()
{
int a=0;
printf("%p\n", &a);
return 0;
}
输出:0x7fff5510b888
可以看到输出地址为12位16进制格式。
go版本:
package main
import (
"fmt"
)
func main() {
a := 1
fmt.Printf("%p\n", &a)
}
输出:0x8201c82d0
可以看到输出的地址为8位16进制格式。
问题来了:
为什么地址空间长度不一致?
是不是意味着go的地址空间范围比c小,
哦,go不支持指针运算。
这2个地址没啥可比性,一个是真实的地址(C&C++),一个是跑在GC上的地址。
package main
/*
#include <stdio.h>
void MyPrint(int arg){
printf("calling from C %p\n", &arg);
}
*/
import "C"
import "fmt"
func main() {
var a int = 0
fmt.Printf("calling from go %p\n", &a)
C.MyPrint(C.int(a))
}
输出
root@ubuntu:/test# ./main
calling from go 0xc82000a300
calling from C 0x7ffc3cf58fdc
PS: 上面的例子其实并不是很适合,但能看出来同样的数据类型,在go和c里面地址空间是不同的。
之所以说这个例子不是很合适是因为:
1) c.int(a) 将a类型从go的int转换成c的int时, 中间可能是数据的拷贝,而非数据的引用。
2) MyPrint的参数是int,而非int*(go不支持对C.int取地址),中间在C的层面上肯定有数据拷贝。
所以在MyPrint里的参数和外面的参数不是引用到同样的一个内存空间。但该例子应该能说明同样的int类型,在go和C里面的地址格式是有区别的。
你的C语言估计编译的是64位程序,在x64位程序中,地址的长度为8个字节。
go语言不支持指针操作,只能用unsafe.pointer封装。因此你看到的地址实际上并不存在于内存中,而是go语言封装后的结果。
刚看到题主更新了答案,似乎对我提出的go语言不支持指针运算有异议。
那么请题主谷歌后回复:
windows系统下内存有大于0x7ffffff的吗?
go语言指针在不启用unsafe.point封装的情况下支持算术运算吗?
《go源码剖析》中说,因为go的运行时使用mmap申请内存储存动态对象,并非使用系统提供的堆区,所以地址有点奇怪。
这段地址是 VA 还是 GC 的地址,要直接看反汇编。由于我电脑上没有 Go 的环境,我从书里面找了一段(26~27):
func test() *int {
x := new(int)
*x = 0xAABB
return x
}
反汇编过来是:
TEXT main.test(SB) test.go
test.go:5 SUBQ $0x10, SP
test.go:6 LEAQ 0x56d46(IP), BX
test.go:6 MOVQ BX, 0(SP)
; x := new(int)
test.go:6 CALL runtime.newobject(SB)
test.go:6 MOVQ 0x8(SP), AX
; *x = 0xAABB
test.go:7 MOVQ $0xaabb, 0(AX)
; return x
test.go:8 MOVQ AX, 0x18(SP)
test.go:8 ADDQ $0x10, SP
test.go:8 RET
大家可以看到,指针x
直接扔给了 CPU,没有经过 runtime 的转化,所以是 VA 而不是 GC 虚拟出来的地址。
go源码剖析