【问题标题】:Segmentation Fault while using wrefresh function of ncurses in C在C中使用ncurses的wrefresh函数时出现分段错误
【发布时间】:2019-11-10 22:26:52
【问题描述】:

首先,我用 ncurses 创建了两个窗口:一个用于发送,一个用于接收。基本上,一个用于编写命令,另一个用于打印命令。为了在接收窗口中写入输入,我做了一个小函数来连接已经在接收窗口中的字符串和用户编写的字符串(我认为问题就在那里)。

所以,当我运行代码时,程序失败并显示在wrefresh(winReception);行转储的分段错误代码

但奇怪的是,如果输入是 7 个或更少的字符,它可以工作,如果它是 8 个或更多,它会中断。

我正在使用 Code::Blocks

截图在这里:https://imgur.com/a/zEwBs1k

这里是一些代码:

//Global variables
WINDOW * winReception;
WINDOW * winTransmission;
char * command;
char mesg[] = "Enter a command";
//variable to stock the input in reception window
char *textinwindow = "";

//main
int main(int argc, char* argv[])
{
    initscr();

    /* WINDOW RECEPTION */
    winReception = newwin(15, 0, 0, 0);
    wrefresh(winReception);


    /* WINDOW TRANSMISSION*/
    winTransmission= newwin(8, 0, 15, 0);
    wrefresh(winTransmission);
    mvwprintw(winTransmission, 1, 2, mesg);

    wgetstr(winTransmission, &command);
    verifInput(&command);

    free(textinwindow);

    exit(0);
}

//concat function (where I think the bug is)
char* concat(char *s1, char *s2)
{
    char *result = (char *) malloc(strlen(s1) + strlen(s2) + 1);
    strcpy(result, s1);
    strcat(result, "\n ");
    strcat(result, s2);

    return result;
}

//verifInput (where the program fails)
void verifInput (char* cmd)
{
    /* WINDOW RECEPTION */
    textinwindow = concat(textinwindow, cmd);
    mvwprintw(winReception, 1, 2, textinwindow);
    wrefresh(winReception);

    /* WINDOW TRANSMISSION*/
    touchwin(winTransmission);
    wclear(winTransmission);
    wrefresh(winTransmission); //Program fails here
    mvwprintw(winTransmission, 1, 2, mesg);

    wgetstr(winTransmission, &command);
    verifInput(&command);
}

【问题讨论】:

  • 在您的 concat() 函数中,代码没有分配足够的内存。它需要两个字符串的大小(您可以这样做),再加上一个用于结尾的 NUL 字节(您可以这样做),但对于 "\n " 字符需要 另外两个。这是在分配数组的末尾写入,可能会导致问题。
  • 还建议将void verifInput(char *cmd); 放在main 上方的顶部,以便代码看到正确的原型。您是否尝试过打开最大编译器警告以查看这是否给您任何提示?
  • @SteveFriedl 我尝试了你提到的一切,但它仍然给我同样的错误。但是,我不知道如何打开最大编译器警告。
  • 啊哈。 wgetstr() 电话可能是这里的罪魁祸首。您将指针的地址传递给缓冲区(而不是缓冲区本身),但那是随机内存。我很惊讶它没有死在那里。
  • @SteveFriedl 而不是wgetstr(winTransmission, &command); 我应该有wgetstr(winTransmission, command);

标签: c codeblocks ncurses


【解决方案1】:

这里有许多问题,如 cmets 中所述,但主要问题是 wgetstr() 的使用完全不正确,因为它正在接受来自用户的输入并将其填充到缓冲区中,但您尚未分配缓冲区.我们不知道command的值是多少,所以它把数据存储到随机内存中。

解决这个问题的错误方法是:

char command[256];
wgetstr(winTransmission, command); // NO

因为尽管您提供了一个存储用户输入的位置,wgetstr() 不知道缓冲区有多大,如果用户输入太多,它也会覆盖那里的内存。不好。

相反,我们将使用一个有界版本wgetnstr(),它接受一个缓冲区和一个计数

char command[256];
wgetnstr(winTransmission, command, sizeof command); // YES

现在它永远不会覆盖缓冲区!

其他问题:虽然您在代码末尾释放了 textinwindow 内存,但每次调用 concat() 时,textinwindowold 值 - 这也是分配的内存- 被扔掉。这肯定是内存泄漏。

最后,一个微妙的问题。由于 mvwprintw() 采用 printf 样式的格式字符串,因此您传递给它的值来自用户,并且可能包含 %s 令牌。没有好处。而是:

    mvwprintw(winReception, 1, 2, "%s", textinwindow);

这意味着用户输入字符串中任何时髦的% 都不会造成破坏。

这是我想出的:

#include <stdio.h>
#include <ncurses.h>
#include <string.h>
#include <stdlib.h>

//Global variables
WINDOW * winReception;
WINDOW * winTransmission;

char command[256];

const char mesg[] = "Enter a command";
//variable to stock the input in reception window
char *textinwindow = 0;

void verifInput (const char* cmd);

//main
int main(int argc, char* argv[])
{
    initscr();

    /* WINDOW RECEPTION */
    winReception = newwin(15, 0, 0, 0);
    wrefresh(winReception);


    /* WINDOW TRANSMISSION*/
    winTransmission= newwin(8, 0, 15, 0);
    wrefresh(winTransmission);
    mvwprintw(winTransmission, 1, 2, mesg);

    wgetnstr(winTransmission, command, sizeof command);
    verifInput(command);

    free(textinwindow);

    exit(0);
}

//concat function (where I think the bug is)
char* concat(const char *s1, const char *s2)
{
    // 2 = newline + space
    // 1 = final NUL byte
    char *result = (char *) malloc(strlen(s1) + 2 + strlen(s2) + 1);
    strcpy(result, s1);
    strcat(result, "\n ");
    strcat(result, s2);

    return result;
}

//verifInput (where the program fails)
void verifInput (const char* cmd)
{

    // free up old memory except for the first time
    if (textinwindow == 0)
        textinwindow = concat("", cmd);
    else
    {
        char *save = textinwindow;
        textinwindow = concat(textinwindow, cmd);
        free(save);
    }

    /* WINDOW RECEPTION */
    mvwprintw(winReception, 1, 2, "%s", textinwindow);
    wrefresh(winReception);

    /* WINDOW TRANSMISSION*/
    touchwin(winTransmission);
    wclear(winTransmission);
    wrefresh(winTransmission); //Program fails here
    mvwprintw(winTransmission, 1, 2, mesg);

    wgetnstr(winTransmission, command,  sizeof command);
    verifInput(command);
}

【讨论】:

  • 我还制作了一些函数参数const char * 而不仅仅是char * 来表示函数期望永远不会写入它们。这不是绝对必要的,但习惯 const 意识是一个好习惯。
  • 哇,它工作得很好。非常感谢!现在回到编码。
  • 似乎我应该更多地关注% 问题:将用户输入传递给任何接受 printf 样式格式列表的东西都是一个安全风险 .
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-04
  • 1970-01-01
  • 2020-12-26
  • 2020-04-20
  • 1970-01-01
  • 2016-07-23
相关资源
最近更新 更多