【问题标题】:`ncurses` function `wgetstr` is modifying my variables`ncurses` 函数 `wgetstr` 正在修改我的变量
【发布时间】:2016-03-05 16:24:15
【问题描述】:

解决方案显然,wgetstr 函数不会创建一个新缓冲区。如果第二个参数被称为data 并且大小为n 并且您输入的输入超过n 字符,它将访问并覆盖内存中不属于data 的部分,例如在存储cursorY 的内存。为了使一切正常,我用char data[] = " ";(八个空格)声明了data,并写了wgetnstr(inputWin, data, 8);

----------------------------------------------- -------------------------------------------------- -------------

看来ncurses 函数wgetstr 确实在改变我的变量的值。在一个名为playGame 的函数中,我有一个名为cursorYint 类型)的变量,每当我按下键盘上的向上或向下箭头时都会调整它(这工作正常)。

请看一下这段代码(inputWin 的类型是WINDOW*):

mvprintw(0, 0, (to_string(cursorY)).c_str());
refresh();
usleep(500000);

wgetstr(inputWin, data);

mvprintw(0, 0, (to_string(cursorY)).c_str());
refresh();
usleep(500000);

假设我将光标移动到第 6 行,然后按 Enter(这会导致这段代码被执行)。我可以做两件事:

  • 只需输入 1 个字符。在两次refresh 调用之后,值 6 将显示在屏幕上(位置 (0, 0))。
  • 输入 2 个或更多字符。在这种情况下,在第一次refresh 调用后,我只得到了 6,但在第二次之后,我神奇地得到了 0。

上面代码后面的前两行是

noecho();
_theView -> _theActualSheet -> putData(cursorY-1, cursorX/9 - 1, data);

(不要担心实际参数:关于它们的数学检查)。当我在putData 时,我得到一个Segmentation fault,而gdbputData 的第一个参数是-1,所以cursorY 必须是0(@987654346 的前两个参数@ 用于使用SheetCells[row][column] 访问二维数组,其中rowcolumn 分别是putData 的第一个和第二个形参。

很明显,wgetstr 修改了cursorY 的值。后一个变量的名称无关紧要:将其更改为 cursorrY 或类似 monkeyBusiness 的奇怪名称(是的,我已经尝试过了)不起作用。 某种的作用是将上面的代码替换为

mvprintw(0, 0, (to_string(cursorY)).c_str());
refresh();
usleep(500000);

int a = cursorY;
wgetstr(inputWin, data);
cursorY = a;

mvprintw(0, 0, (to_string(cursorY)).c_str());
refresh();
usleep(500000);

在这两种情况下,我都会在屏幕的左上角看到 6。但是,知道字符串的行为很奇怪:当我输入asdf 作为我的字符串,然后向右移动(即,我按键盘上的右键),然后再次输入asdf,我得到@ 987654358@.

所以基本上,我想知道两件事:

  • 为什么 wgetstr 会更改我的变量?
  • 为什么只有输入超过 1 个字符时才会出现这种情况?
  • 一般来说wgetstr 似乎有什么问题?处理输入似乎很糟糕。

我可以尝试其他事情(例如手动读取字符,然后将 data 与它们连接),但 wgetstr 似乎非常适合我想做的事情,我没有理由切换到这里。

非常感谢任何帮助。 (请记住:我特别想知道为什么 cursorY 的值被更改。如果您建议不要使用 wgetstr 并有一个好的替代方案,请告诉我,但我最想知道为什么 @ 987654365@ 正在修改中。)

EDIT 变量data 的类型为char[],声明如下:char data[] = ""。我没有“清除”这个变量(即,删除所有“字母”),但我认为这没有任何区别,因为我认为 wgetstr 只是覆盖了整个变量(或者我在这里大错特错了吗?) .

【问题讨论】:

  • 什么是data,它是如何定义的?我敢打赌,你没有正确初始化它,导致cursorY 被意外覆盖。
  • 我已经编辑了我的问题,请看一下新的部分。
  • 看起来@H.Guijt 有正确的想法。 wgetstr 期望写入一个已分配且长度足以容纳输入字符串的缓冲区。您还没有使用data 完成此操作,因此wgetstr 正在写入缓冲区的末尾并写入接下来定义的任何变量。如果您的输入字符串足够长,我预计会发生更多损坏。
  • 您宣称,几十年来没有问题使用的库有一个以前没有人注意到的严重错误。您自己的描述留下了足够的空间,这是您的错误。 向我们提供一个小测试用例来演示该错误,我们会看看是否可以帮助您解决问题。
  • @Dúthomhas wgetstr 函数可能已经有几十年的历史了,但我一眼就能看出一个主要的安全问题:它会让你通过无人看管的缓冲区。一旦引入 wgetnstr,它就应该被弃用。

标签: c++ string segmentation-fault ncurses


【解决方案1】:

您为数据提供的缓冲区data 被定义为单个字符长(只有空终止符会在那​​里)。这意味着如果您输入一个或多个字符的任何输入,您将在数据提供的空间之外写入,从而覆盖其他内容。看起来 cursorY 是被击中的幸运变量。

您需要确保数据至少足够大以处理所有输入。最好,您应该切换到一些输入函数(如wgetnstr),让您传递缓冲区的大小,否则总是有可能通过输入足够的字符来崩溃您的应用程序。

【讨论】:

  • 还有没有办法提供一个总是足够大的缓冲区?就像一个不断增长的缓冲区?不幸的是,strings 不起作用,因为wgetstr 想要一个char*,而使用data.c_str() 也不起作用,因为从const char*char* 的转换显然也太多了。
  • 这似乎太麻烦了。您希望能够处理任何长度的输入,并在需要时扩展缓冲区大小。我没有什么可以做的吗?无论如何,第 8 位之后的所有字符最终都会被“切掉”,但我想在不同的函数中处理它(事实上,我已经这样做了)。
  • 这就是总是使用 C API 而不是 C++ API 的诅咒:固定缓冲区无处不在,而且由于它们没有有意义的抽象,因此动态重新分配它们基本上是不可能的。但至少使用 wgetnstr 可以避免因输入过长而崩溃。
【解决方案2】:

wgetstr 期望将接收到的字符写入预分配的缓冲区,该缓冲区至少应与预期的输入字符串一样长。它不会为您分配新的缓冲区!

您所做的是为它提供一个单字节缓冲区,并向其写入多个字节。这将覆盖您在函数中在data 之后定义的其他变量,例如cursorY不管它叫什么。对变量的任何更改都会反过来更改读取的字符串:

int a = cursorY;
wgetstr(inputWin, data);
cursorY = a;

会将一个 int 值写入您的字符串,这就是它明显被损坏的原因。

您实际上应该做的是使data 实际上足够长以用于预期输入,并且理想情况下使用wgetnstr 之类的东西以确保您不会离开缓冲区的末端并造成损坏。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-15
    • 2012-01-15
    • 1970-01-01
    • 1970-01-01
    • 2015-11-11
    • 2014-03-25
    • 1970-01-01
    相关资源
    最近更新 更多