使用 cgo,为什么 C 输出不是 'survive' 管道,而 golang 是?

Using cgo, why does C output not 'survive' piping when golang's does?

我正在尝试使用 cgo 来使用来自 golang 的 C 代码,但是在我的 hello-world 小测试中,我 运行 遇到了一些我无法理解或找不到更多信息的东西。

我从类似于 examples I've found

的简单测试开始
    package main

    import (
        "fmt"
        "unsafe"
    )

    /*
    #import <stdio.h>
    #import <stdlib.h>
    */
    import "C"

    func main() {
        go2c := "Printed from C.puts"
        var cstr *C.char = C.CString(go2c)
        defer C.free(unsafe.Pointer(cstr))
        C.puts(cstr)
        fmt.Printf("Printed from golang fmt\n")
    }

这个简单的示例只是通过基本的 cgo 绑定将 golang(使用 fmt.Printf)和原始 C(使用 C.puts)中的字符串回显到标准输出。

当我直接在终端中 运行 时,我看到两行:

    $ ./main
    Printed from C.puts
    Printed from golang fmt

当我 运行 this but redirect 以任何方式输出 – 管道到 less,shell 重定向到文件等 –我只看到golang的输出:

    ./main | cat
    Printed from golang fmt

管道/重定向时 C.puts 内容会怎样?

次要问题:这是 cgo 怪癖,还是我不知道的 c 标准库怪癖?这种行为是否记录在案?我将如何自己调试它(例如,是否有 good/plausible 方法让我 'inspect' 每个块中的 FD1 到底是什么?)

更新:如果相关,我正在使用 go version go1.6.2 darwin/amd64

C 库缓冲是按行进行的,因此第一行可以在正确刷新之前留在缓冲区中(在 C 程序退出时完成)。您可以尝试刷新标准输出,或尝试在第一个字符串中添加尾随 \n。添加\n是否有效?

这是您看到的 C 行为。

Go 没有缓冲 stdout,而在 C 中通常有缓冲。当 C 库检测到 stdout 是一个 tty 时,它可能会使用行缓冲,因此由 puts 插入的附加 \n 将导致显示输出。

您需要刷新 stdout 以确保获得所有输出:

go2c := "Printed from C.puts"
var cstr *C.char = C.CString(go2c)
defer C.free(unsafe.Pointer(cstr))
C.puts(cstr)
C.fflush(C.stdout)
fmt.Printf("Printed from golang fmt\n")

另见

Why does printf not flush after the call unless a newline is in the format string?

Is stdout line buffered, unbuffered or indeterminate by default?