输入Safe Vs松散键入 - GO Vs C.

时间:2017-02-12 19:30:06

标签: c go casting

在C中,以下是嗅探器代码,

void Handle_IP(char *buf)
{
    struct iphdr *ip_hdr; // declaring pointer of type ip header 

    struct in_addr in; // declaring a structure which holds ip address 

    FILE *fp;
    int ctl, len;

    /* In the following statement we're adjusting the offset so that
       ip pointer can point to correct location */

    ip_hdr = (struct iphdr *)(buf + 14);
    ....
}

行,

ip_hdr = (struct iphdr *)(buf + 14)的{​​{1}}类型已投放到char *

GO是一种类型安全的语言,不允许这种类型的转换。除了结构类型

之后的struct iphdr *,Go没有void *

如何在GO中处理此类代码?

2 个答案:

答案 0 :(得分:6)

让我们从不同的角度来看待这个问题:类型转换在C中做了什么?

嗯,从某种意义上说,它没有做任何事情!它只需要在内存中的某个点上采用一些位模式,并盲目地假设这个特定的位模式实际上代表了某种类型的值。但是没有对此进行验证,没有任何解析。这就是问题所在。

但是,我已经使用了上述问题的解决方案:解析。而不是盲目地假设内存中的某些随机位模式是C编译器为某种类型的值生成的相同位模式,我们实际上查看位模式,检查它是否符合如何构造该数据类型的位模式的规则,并根据这些规则将位模式解构为其组成部分。

换句话说:我们写一个解析器。

请注意,这不是Go特有的。即使在C中也是一个好主意。实际上,您发布的代码至少有三种,也许是四种不同的方式:

  • char不是8位宽的平台
  • 对准
  • 填充
  • 端序

AFAIK,已有现有的IP,UDP和TCP解析器写在Go那里,你可以使用或学习。此外,来自Alan Kay的Viewpoints Research Institute的当前系统有一个非常酷的TCP / IP堆栈,只需120行左右,非常容易阅读。 (事实上​​,解析器的源代码实际上只包含从RFC中剪切和粘贴的ASCII图。)

答案 1 :(得分:3)

前言:尽管您想要的是可能的,但请尽量避免使用包unsafe。这不是"转到第一个"解决方案,而是将其视为最后的手段(或之后的事情)。

Go会在unsafe包中为您提供支持。

即使是规范也有专门的部分。 Spec: Package unsafe:

  

编译器已知的内置包unsafe为低级编程提供了工具,包括违反类型系统的操作。使用unsafe的包必须手动审查以确保类型安全,并且可能无法移植。

该软件包被故意命名为unsafe,为您提供正确的事先警告,如果出现问题,请不要责怪编译器或语言。

您需要的是unsafe.Pointer类型。 Pointer是一个特殊的pointer type,可能不是dereferenced,但任何指针类型都可以转换为PointerPointer可以转换为任何({1}} )指针类型。所以这是你的网关"不同类型之间。

例如,如果您的值为float64(内存中为8个字节),则可以将这8个字节解释为int64(内存中也是8个字节),如这样:

var f float64 = 1
var i int64

ip := (*int64)(unsafe.Pointer(&f))
i = *ip

fmt.Println(i)

输出(在Go Playground上尝试):

4607182418800017408

关键是这一行:

(*int64)(unsafe.Pointer(&f))

表示取f的地址(类型为*float64),将其转换为unsafe.Pointer(任何指针类型都可以转换为Pointer),然后将此unsafe.Pointer值转换为另一个指针类型*int64。如果我们取消引用此指针,则会得到类型int64的值。

在您的示例中,您希望"放置"应用了偏移量的地址上的变量。 Go没有指针算术。你可以通过两种方式解决这个问题:

  1. 使用可能包含地址的uintptr,但您可以将其视为int并为其添加值

  2. 或使用指向&#34;缓冲区的指针&#34;类型,例如*[1<<31]byte;您可以将地址转换为此指针,并且可以通过在给定偏移处索引缓冲区来应用偏移量,并获取该元素的地址,例如&buf[14]

  3. 方法#1看起来像这样:

    type X [16]byte
    var x = X{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
    
    xx := (uintptr)(unsafe.Pointer(&x[0])) + 14 // APPLY OFFSET
    
    var x2 X = *(*X)(unsafe.Pointer(xx))
    fmt.Println(x2[0], x2[1])
    

    输出(在Go Playground上尝试):

    14 15
    

    方法#2可能如下所示:

    type X [16]byte
    var x = X{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
    
    xx := (*X)(unsafe.Pointer(&x[0]))
    xx = (*X)(unsafe.Pointer(&xx[14])) // APPLY OFFSET
    
    var x2 X = *(*X)(unsafe.Pointer(xx))
    fmt.Println(x2[0], x2[1])
    

    输出是一样的。在Go Playground上尝试。