概述

事情简单来讲就是发现同一段代码,其返回值的逻辑似乎在Linux和Windows上有不同的表现。因为一些环境配置和时间的原因暂时没来得及深入研究,在此记录下,如果有懂这个问题的大佬可以留言下,感谢。

详细描述

具体而言,在开发tensorflow.net这个仓库的时候,遇到了一种情况,需要我自定义一个tensorflow的C API,当时只顾得上抄代码了,犯了一个致命的问题,代码如下:

const char *TFC_GetHandleShapeAndType(TF_Graph *graph, TF_Output output) {
  Node *node = &output.oper->node;
  tensorflow::CppShapeInferenceResult::HandleData handle_data;
  handle_data.set_is_set(true);
  {
    // do somethings...
  }
  string result;
  handle_data.SerializeToString(&result);

  return result.c_str();

错误还蛮低级的,就是不应该返回result.c_str(),因为函数返回之后会析构掉字符串,自然也就把对应内存释放了,所以调用方拿到的是一个已经失效的指针。

为什么这么低级的错误一直没发现呢?这是因为开发时候都是在Windows上,所以当初也只测试了Windows。实际上大部分时候Windows和Linux都是完全表现一致的,但这次出岔子了,Windows上一切正常,而Linux上接收到的返回数据是异常的。

当然,这个写法肯定是有错的,根据常识而言,一个函数返回的时候是先依次pop掉局部变量进行析构,最后用一个ret指令跳转到函数调用前的地址,然后这时候寄存器里面的值就是返回值。所以,如果是返回了局部string变量的char*数据,外部肯定是失效了。

但为什么Windows上一切都正常呢?我的猜测有两个:

  1. MSVC和GCC之间的差异,具体怎么个差异法还不知道,毕竟实在想不出怎么会实现成先赋值再跳出栈帧。
  2. .NET中P/Invoke在Windows和Linux上的差异,这个同样感觉不是很站得住脚,毕竟.NET是跨平台的。