C++ char 数组 - 相同的输入给出不同的输出

Posted

技术标签:

【中文标题】C++ char 数组 - 相同的输入给出不同的输出【英文标题】:C++ char arrays - same input giving different outputs 【发布时间】:2016-05-26 15:45:05 【问题描述】:

我有一个实现 Netezza 用户定义函数的 cpp 类(文档here)。它接受一个参数,该参数将是某种日期格式的字符串,并将其转换为 YYYYMMDD 格式。如果它不是一个有效的日期,它将返回“99991231”。每当我在某些表上运行代码时,每次相同的输入都会得到不同的输出。我认为有一些我没有看到的内存问题。

从逻辑上讲,我们将 char 数组 retval 设置为等于 date 命令的输出。如果它给出一个空输出,我们设置为“99991231”。然后我们将一个临时字符数组设置为 retval 的前 9 个字节(最后一个是空终止符)。然后我们 memcpy 进入 ret->data (我们必须返回的结构的一个字符指针)。

#include <stdarg.h>
#include <string.h>
#include "udxinc.h"
#include "udxhelpers.h"

using namespace nz::udx_ver2;

class Dateconvert: public Udf

public:

    Dateconvert(UdxInit *pInit) : Udf(pInit)
    ~Dateconvert()

    static Udf* instantiate(UdxInit *pInit);

    virtual ReturnValue evaluate()
    

      StringReturn* ret = stringReturnInfo();
      StringArg *str;
      str = stringArg(0);

      int lengths = str->length;
      char *datas = str->data;

      string tempData = datas;

      string shell_arg = tempData;
      shell_arg = "'" + shell_arg + "'";
      string cmd="date -d " + shell_arg +  " +%Y%m%d 2>/dev/null";

      FILE *ls = popen(cmd.c_str(), "r");
      char retval[100];
      retval[0]='n';
      fgets(retval, sizeof(retval), ls);

      if(!isdigit(retval[0]))
      
          strcpy(retval,"99991231");
      

      pclose(ls);

      char temp1[9];
      memcpy(temp1, retval, 8);
      temp1[8]='\0';

      ret->size = 9;
      memcpy(ret->data, temp1, 9);

      NZ_UDX_RETURN_STRING(ret);
    
;


Udf* Dateconvert::instantiate(UdxInit *pInit)

    return new Dateconvert(pInit);

当我在 Netezza 中对一个不同的值运行 UDF 时,它给了我预期的输出。但是,当我在多列上运行它时,输出有时是正确的,有时是错误的,似乎是随机的。我认为这必须是内部内存问题。例子:

   input          output
1) 8/11/2014      20140811

2) 8/11/2014      20140811
   Fri 10/17/14   20141017

3) 8/11/2014      99991231
   Fri 10/17/14   20141017

4) 8/11/2014      20140811
   Fri 10/17/14   20141017

5) 8/11/2014      20140811
   Fri 10/17/14   20141017
   9-Nov-12       20121109

6) 8/11/2014      20140811
   Fri 10/17/14   20141017
   9-Nov-12       01241109 (what?)

7) 8/11/2014      99991231
   Fri 10/17/14   20141017
   9-Nov-12       20121109

只要函数只有一次调用,它就会返回正确的答案。多次调用时会出现问题,我不明白。为什么会有东西被带走?在评估函数结束时将返回值大小从 9 更改为 8 并不能解决问题。

这是调用函数的格式:

select a.val1, DATECONVERT(a.val1)
from
(
 select '8/11/2014' as val1 from calendar
 union
 select 'Fri 10/17/14' as val1 from calendar
 union
 select '9-Nov-12' as val1 from calendar
) a

并为 UDF 编译命令:

nzudxcompile /export/home/nz/dateconvert.cpp -o dateconvert.o --sig "Dateconvert(VARCHAR(200))" --version 2 --return "VARCHAR(200)" --class Dateconvert --user user1 --pw mypw  --db mydb

【问题讨论】:

请提供minimal reproducible example。 我已经添加了查询示例和编译命令。 @巴里 应该没什么区别,但是你可以用strcpy()而不是memcpy(),那么它会自动放空终止符。 为什么要先复制到temp1再复制到ret-&gt;data 我还没有浏览UDF文档,你需要做一些事情来为ret-&gt;data分配空间吗? 【参考方案1】:

切入正题,这里的问题在于如何分配 tempData。

  StringReturn* ret = stringReturnInfo();
  StringArg *str;
  str = stringArg(0);

  int lengths = str->length;
  char *datas = str->data;

  string tempData = datas;

StringArg 不存储以 NUL 结尾的字符串,而是提供长度并希望您自己管理。

select a.val1, ADMIN.DATECONVERT(a.val1)
from
(
 select '09-Nov-12'::varchar(20) as val1 
 union all
 select '9-Nov-12'::varchar(20) as val1 
) a;

   VAL1    | DATECONVERT 
-----------+-------------
 09-Nov-12 | 20121109
 9-Nov-12  | 01221109
(2 rows)

在这个例子中,当第二个较短的字符串分配给 tempData 时,较长的第一个字符串仍然有一个字符在内存中徘徊。挂在末尾的“2”会像这样添加:

09-Nov-12
9-Nov-122

这些都是 date 的有效输入,这很好地解释了您所看到的输出。

$ date -d 09-Nov-12 +%Y%m%d
20121109
$ date -d 09-Nov-122 +%Y%m%d
01221109 

更改分配以使用该长度,您将避免该问题。

  //string tempData = datas;
  string tempData(datas, datas+lengths);

然后你得到预期的输出:

select a.val1, ADMIN.DATECONVERT(a.val1)
from
(
 select '09-Nov-12'::varchar(20) as val1 
 union all
 select '9-Nov-12'::varchar(20) as val1 
) a;

   VAL1    | DATECONVERT 
-----------+-------------
 09-Nov-12 | 20121109
 9-Nov-12  | 20121109
(2 rows)

话虽如此,我不知道您在此 UDF 中采用的整体方法是否可行。当我在上面运行它时,这些行是在主机上生成的,因为它们在 SQL 中是硬编码的,并且 date 在主机上肯定是可用的。但是,您不能期望在 MPP 后端(我们通常称为 SPU)上运行的代码具有与主机上相同的 linux 实用程序可用性,或者如果它们存在,它们具有相同的功能.

如果我将日期移动到实际表中,UDF 将在 SPU 上对其进行操作,并且它会给我错误的输出,因为 SPU 图像上的 date 命令与该命令明显不同主机的,根本不懂这种输入格式。

select a.col1, admin.DATECONVERT(a.col1) from calendar a;

   COL1    | DATECONVERT 
-----------+-------------
 09-Nov-12 | 99991231
 9-Nov-12  | 99991231
(2 rows)

【讨论】:

有趣。当我在实际表上运行命令时,它会像您所说的那样给出 99991231(偶尔会抛出 20170812?)。当我在查询末尾添加“限制 N”时,它按预期工作。我认为这是因为 limit 关键字导致它在主机而不是 SPU 上运行? @ScottMcG 这似乎是一个可能的解释。可能有一种方法可以欺骗它总是这样做,但这会将 MPP 系统变成 SMP 系统,这会损害性能。但如果它只是一百万行左右,没什么大不了的

以上是关于C++ char 数组 - 相同的输入给出不同的输出的主要内容,如果未能解决你的问题,请参考以下文章

将 C# char 数组传递给 C++ 与字节数组不同

翻转字符串(C++)

在 C++ 中切片 char 数组(python 到 c++)

一个c++有关问题,当定义一个char 型数组时,用cin循环输入为啥会出错,怎么解决,代码如下

相同算法的 C++ 和 Python 版本给出不同的结果

UOJ #56. WC2014非确定机