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

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)
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • 转载自:https://halfrost.com/go_map_chapter_one/ https://half...
    HuJay阅读 6,060评论 1 5
  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 3,387评论 3 44
  • 1.C和C++的区别?C++的特性?面向对象编程的好处? 答:c++在c的基础上增添类,C是一个结构化语言,它的重...
    杰伦哎呦哎呦阅读 9,293评论 0 45
  • 《见识》第三章学习 孩子考试失利了,升职失败了,投资被骗了,父母生病了,老公出轨了……,人到中年,上有老,下有小,...
    遇见小白杨阅读 1,335评论 0 0
  • 鲁迅吾师: 冒昧给您写信,必会惹人以鼻嗤之。我称你为师,也定会令人耻笑。然中山先生自称洪秀全第二,也丝毫没有忌...
    UncleLemon阅读 179评论 0 0