美文网首页Go知识库golang
18-Go语言和C语言交叉访问

18-Go语言和C语言交叉访问

作者: 极客江南 | 来源:发表于2018-09-27 17:18 被阅读304次

    Go语言中调用C语言函数

    • 在Go语言开篇中我们已经知道, Go语言与C语言之间有着千丝万缕的关系, 甚至被称之为21世纪的C语言
    • 所以在Go与C语言互操作方面,Go更是提供了强大的支持。尤其是在Go中使用C,你甚至可以直接在Go源文件中编写C代码,这是其他语言所无法望其项背的
    • 格式:
      • 在import "C"之前通过单行注释或者通过多行注释编写C语言代码
      • 在import "C"之后编写Go语言代码
      • 在Go语言代码中通过C.函数名称() 调用C语言代码即可
      • 注意: import "C"和前面的注释之间不能出现空行或其它内容, 必须紧紧相连
    package main
    //#include <stdio.h>
    //void say(){
    // printf("Hello World\n");
    //}
    import "C"
    
    func main()  {
        C.say()
    }
    
    package main
    /*
    #include <stdio.h>
    void say(){
        printf("Hello World\n");
    }
    */
    import "C"
    
    func main()  {
        C.say()
    }
    
    • Go语言中没有包名是C的包, 但是这个导入会促使Go编译器利用cgo工具预处理文件
    • 在预处理过程中,cgo会产生一个临时包, 这个包里包含了所有C函数和类型对应的Go语言声明
    • 最终使得cgo工具可以通过一种特殊的方式来调用import "C"之前的C语言代码

    C语言中调用Go语言函数(很少使用)

    • 在Go代码中通过 //export Go函数名称导出Go的函数名称
    • 在C代码中通过 extern 返回值类型 Go函数名称(形参列表); 声明Go中导出的函数名称
    • 注意: //export Go函数名称extern 返回值类型 Go函数名称(形参列表);不能在同一个文件中
    package main
    
    import "C"
    import "fmt"
    // 导出Go函数声明, 给C使用
    //export GoFunction
    func GoFunction() {
        fmt.Println("GoFunction!!!")
    }
    
    package main
    /*
    #include <stdio.h>
    // 声明Go中的函数
    extern void GoFunction();
    
    void CFunction() {
            printf("CFunction!\n");
            GoFunction();
    }
    */
    import "C"
    
    func main()  {
        C.CFunction()
    }
    
    • 由于不在同一个文件, 所以需要通过go build或者go install同时编译多个文件

    • Go中使用C语言的类型
    • 基本数据类型
      • 在Go中可以用如下方式访问C原生的数值类型:
    C.char,
    C.schar (signed char),
    C.uchar (unsigned char),
    C.short,
    C.ushort (unsigned short),
    C.int, C.uint (unsigned int),
    C.long,
    C.ulong (unsigned long),
    C.longlong (long long),
    C.ulonglong (unsigned long long),
    C.float,
    C.double
    
    • Go的数值类型与C中的数值类型不是一一对应的。因此在使用对方类型变量时必须显式转型操作
    package main
    /*
    #include <stdio.h>
    int num = 123;
    float value = 3.14;
    char ch = 'N';
    */
    import "C"
    import "fmt"
    
    func main()  {
        var num1 C.int = C.num
        fmt.Println(num1)
        var num2 int
        //num2 = num1 // 报错
        num2 = int(num1)
        fmt.Println(num2)
    
    
        var value1 C.float = C.value
        fmt.Println(value1)
        var value2 float32 = float32(C.value)
        fmt.Println(value2)
    
    
        var ch1 C.char = C.ch
        fmt.Println(ch1)
        var ch2 byte = byte(C.ch)
        fmt.Println(ch2)
    }
    

    • 字符串类型
      • C语言中并不没有字符串类型,在C中用带结尾'\0'的字符数组来表示字符串;而在Go中string类型是原生类型,因此在两种语言互操作是必须要做字符串类型的转换
      • C字符串转换Go字符串: C.GoString(str)
      • Go字符串转换C字符串: C.CString(str)
    package main
    /*
    #include <stdio.h>
    char *str = "www.it666.com";
    void say(char *name){
        printf("my name is %s", name);
    }
    */
    import "C"
    import (
        "fmt"
        "unsafe"
    )
    
    func main()  {
        // 1.C语言字符串转换Go语言字符串
        str1 := C.str
        str2 := C.GoString(str1)
        fmt.Println(str2)
    
        // 2.Go语言字符串转换C语言字符串
        str := "lnj"
        cs := C.CString(str)
        C.say(cs)
        // 注意: 转换后所得到的C字符串cs并不能由Go的gc所管理,我们必须手动释放cs所占用的内存
        C.free(unsafe.Pointer(cs))
    }
    

    • 指针类型
      • 原生数值类型的指针类型可按Go语法在类型前面加上*,例如:var p *C.int。
      • 而void*比较特殊,用Go中的unsafe.Pointer表示。
        • unsafe.Pointer:通用指针类型,用于转换不同类型的指针,不能进行指针运算
      • uintptr:用于指针运算,GC 不把 uintptr 当指针,uintptr 无法持有对象。uintptr 类型的目标会被回收
      • 也就是说 unsafe.Pointer 是桥梁,可以让任意类型的指针实现相互转换,也可以将任意类型的指针转换为uintptr 进行指针运算
    package main
    /*
    #include <stdio.h>
    int num = 123;
    void *ptr = &num;
    */
    import "C"
    import (
        "fmt"
        "unsafe"
    )
    
    func main()  {
        // 这是一个C语言变量
        var num C.int = C.num
        // 这是一个C语言指针
        var p1 *C.int = &num
        fmt.Println(*p1)
    
        //var p2 *C.void = C.ptr // 报错
        // 利用unsafe.Pointer接收viod *
        var p2 unsafe.Pointer = C.ptr
        // 将unsafe.Pointer转换为具体类型
        var p3 *C.int = (*C.int)(p2)
        fmt.Println(*p3)
    }
    

    • 枚举类型
      • C语言中的枚举类型在Go语言中的表现形式为C.enum_XXX
      • 访问枚举和访问普通变量无异, 直接通过C.枚举值即可
    package main
    /*
    #include <stdio.h>
    enum Gender {
       GenderMale,
       GenderFemale,
       GenderYao
    };
    */
    import "C"
    import "fmt"
    
    func main()  {
        var sex C.enum_Gender = C.GenderMale
        fmt.Println(sex)
        sex = C.GenderFemale
        fmt.Println(sex)
        sex = C.GenderYao
        fmt.Println(sex)
    }
    

    • 结构体类型
      • C语言中的结构体类型在Go语言中的表现形式为C.struct_XXX
      • 访问结构体 直接通过结构体变量.属性名称即可
    package main
    /*
    #include <stdio.h>
    struct Point {
        float x;
        float y;
    };
    */
    import "C"
    import (
        "fmt"
    )
    
    func main()  {
        // 1.利用C的结构体类型创建结构体
        var cp C.struct_Point = C.struct_Point{6.6, 8.8}
        fmt.Println(cp)
        fmt.Printf("%T\n", cp)
    
        // 2.将C语言结构体转换为Go语言结构体
        type GoPoint struct {
            x float32
            y float32
        }
        var gp GoPoint
        gp.x = float32(cp.x)
        gp.y = float32(cp.y)
        fmt.Println(gp)
    }
    

    • 数组类型
      • C语言中的数组与Go语言中的数组差异较大, C中的数组是指针类型, Go中的数组是值类型
      • 目前似乎无法直接显式的在两者之间进行转型,官方文档也没有说明。
    package main
    /*
    #include <stdio.h>
    int cArray[5] = {1, 2, 3, 4, 5};
    */
    import "C"
    import "fmt"
    
    func main()  {
        var cArr [5]C.int = C.cArray
        fmt.Println(cArr)
        fmt.Printf("%T\n", cArr)
    }
    

    • 利用Go语言调用C语言函数, 实现无缓冲区输入
      • 请在终端运行
    package main
    /*
    #include <stdio.h>
    char lowerCase(char ch){
        // 1.判断当前是否是小写字母
        if(ch >= 'a' && ch <= 'z'){
            return ch;
        }
        // 注意点: 不能直接编写else, 因为执行到else不一定是一个大写字母
        else if(ch >= 'A' && ch <= 'Z'){
            return ch + ('a' - 'A');
        }
        return ' ';
    }
    char getCh(){
        // 1.接收用户输入的数据
        char ch;
        scanf("%c", &ch);
        setbuf(stdin, NULL);
        // 2.大小写转换
        ch = lowerCase(ch);
        // 3.返回转换好的字符
        return ch;
    }
     */
    import "C"
    import "fmt"
    
    func main()  {
        for{
            fmt.Println("请输入w a s d其中一个字符, 控制小人走出迷宫")
            var ch byte = byte(C.getCh())
            fmt.Printf("%c", ch)
        }
    }
    

    相关文章

      网友评论

        本文标题:18-Go语言和C语言交叉访问

        本文链接:https://www.haomeiwen.com/subject/eklimftx.html