【问题标题】:Go cgo ldap_init could not determine kind of name for C.ldap_initGo cgo ldap_init 无法确定 C.ldap_init 的名称类型
【发布时间】:2016-02-08 06:22:17
【问题描述】:

我试图学习和理解 cgo。 我正在用 Go 编写一个程序,我连接到 AD 并解析输出。我在 C 中测试了代码,它按预期工作正常。 C中的相关部分是

char *ldap_host = "x.x.x.x";
int   ldap_port     = 389;
ldap = ldap_init(ldap_host, ldap_port)) 

现在我正在尝试在 go 中进行相同的工作

//#cgo CFLAGS: -lldap 
//#include <ldap.h>
import "C"
func main() {
    var hostname *string
    hostname = &os.Args[1]
    ldap_port := 389
    ldap := C.ldap_init(*hostname, ldap_port)
}

但我收到以下错误

could not determine kind of name for C.ldap_init

我在这里做错了什么?

【问题讨论】:

    标签: c go ldap cgo


    【解决方案1】:

    在添加像 ldap 这样的库的个别特性之前,我会先了解 Go 和 cgo 的基础知识。一些很好的起点,尤其是官方的 cgo 文档页面。

    从顶部开始:

    这里不需要CFLAGS,但链接器需要LDFLAGS,运行它需要liblber

    #cgo LDFLAGS: -lldap -llber
    

    您不能将指向 Go 字符串的指针作为 C *char 传递,它们是不同的类型。 Go 中的字符串实际上根本无法寻址,所以如果构建过程到了那么远,这将是一个编译错误。如果需要创建 C 字符串,请使用 C.CStringC.free。您还需要将stdlib.h 包含在C.free 中。

        url := C.CString(os.Args[1])
        defer C.free(unsafe.Pointer(url))
    

    Go 的 int 大小因架构而异,而不是 C 的 int 类型。您的示例中的ldap_port 被转换为默认类型int,您需要C.int

    最后,您问题中的原始错误与Go或cgo无关。 ldap_init 函数已弃用,如果不设置 LDAP_DEPRECATED 则不存在。您应该使用ldap_initialize 函数。这是一个编译的最小示例:

    package main
    
    import (
        "log"
        "unsafe"
    )
    
    /*
    #include <stdlib.h>
    #include <ldap.h>
    
    #cgo LDFLAGS: -lldap -llber
    */
    import "C"
    
    func main() {
        url := C.CString("ldap://127.0.0.1:389/")
        defer C.free(unsafe.Pointer(url))
    
        var ldap *C.LDAP
    
        rv := C.ldap_initialize(&ldap, url)
        if rv != 0 {
            log.Fatalf("ldap_initialize() error %d", rv)
        }
        log.Println("initialized")
    }
    

    【讨论】:

    • 谢谢@JimB。感谢有关在哪里学习的指针。
    • 我想知道这是否 100% 安全。根据我对#12416#8310 的理解,当您不知道Go 指针是否会被C 端保留时,将Go 指针传递给C 函数是不正确的。我不希望 ldap 这样做,但是对于 C.malloc 那个指针(*LDAP,不是 LDAP)不是更安全吗?
    • @TomaszKłak:这很好。 #8310 无关紧要,已被 #12416 取代。这里ldap的内存是在传递**C.LDAP时在C中分配的,其中指针本身由C代码填充,指向C内存。 go1.6 cgo 检查默认启用,会导致程序崩溃而不是允许潜在的不安全行为。
    • 哦,你确实在 1.6 下检查过这个。我知道ldap_initialize 分配的内存在这里不是问题。我担心的是ldap_initialize 可能会将Go 变量ldap 的地址存储在某个全局C 值的某个位置。这将导致 C 在 C 调用结束后拥有指向 Go 值的指针。
    • @TomaszKłak:我的本地 go build 距离 master 仅几天时间。即使ldap 的值存储在 C 中,它也不是指向 Go 内存的指针。它以nil 开头,并由初始化函数填充。所以你拥有的是 Go 堆上的一个指针值,指向我们认为安全的 C 内存。就好像ldap_initialize 返回了一个*LDAP,我们将其分配给ldap,但由于C 没有多个返回值或异常,因此通常将** 作为参数传递。
    猜你喜欢
    • 2021-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多