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

栏目: C · 发布时间: 7年前

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.GoString(str)
    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)
}
  • 结构体类型
    结构体变量.属性名称
    
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)
    }
}

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learn Python the Hard Way

Learn Python the Hard Way

Zed A. Shaw / Addison-Wesley Professional / 2013-10-11 / USD 39.99

Master Python and become a programmer-even if you never thought you could! This breakthrough book and CD can help practically anyone get started in programming. It's called "The Hard Way," but it's re......一起来看看 《Learn Python the Hard Way》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

在线进制转换器
在线进制转换器

各进制数互转换器

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具