Нужна помощь в ошибке сегментации в моем коде C

Я безуспешно пытался определить, где моя программа генерирует сегментацию. Мне нужна помощь в определении того, какая из строковых операций или указателей символов вызывает ошибку сегментации во время выполнения. Программа успешно компилируется, но во время выполнения выдает ошибку сегментации.

#include<curses.h>
#include<strings.h>
#include<unistd.h> 
#include<stdlib.h>

/*Implements a Scrolling headband that takes a string argument and continously scrolls*/               
int main(int argc, char* argv[])
{
    /*A Substring function to return substring of a string*/
    char *substr(const char *src, int start, int len);
    /*Appends a character to the Given string*/
    void append(char* s, char c);
    /***Check if the argument is invalid***/
    if(argc!=2)
    {
        puts("Invalid number of arguments: Usage:headband <String>");
    }
    /**Get headtext from the string argument argv[1]**/
    char *headtext = argv[1];

    /*headband(headtext);*/
    /*temporary variable to store strings as execution progresses*/
    char temp[100];
    /*Counter for streaming and scolling headband text*/
    int count = 0;
    /*Placeholder for temporary headband text*/
    char hold[100];
    int i;
    /*maximum x and y co-ordinates of the Screen*/
    int max_x,max_y;
    /*Initialize screen for ncurses*/
    initscr();
    /*Don't show cursor*/
    curs_set(0);
    /*Get terminal console dimensions*/
    getmaxyx(stdscr, max_y, max_x);
    /*Get console width set to default console screen width=80*/
    int consolewidth = max_x;
    /*Clear the screen*/
    clear();
    /*Set the first value as end of String for the momment*/
    temp[0] = '\0';
    /*Run this loop continuously to keep scrolling*/
    for (;;)
    {
        for(i=0; i < strlen(headtext); i++)
        {
            count++;
            /*Append headband text character by character to string hold*/
            append(temp, headtext[i]);
            move(0,consolewidth - count);
            if (consolewidth - count > 0)
            {
                mvaddstr(0,console_width-count,temp);
                refresh();
            }
            else if (consolewidth - count == 0)
            {
                strcpy(hold, temp);
                char q;
                int com = i;
                for(;;)
                {
                    /*Scroll text by triming one character at a time*/ 
                    /*from the left, then adding that character to the*/
                    /*right side of the text*/
                    com = com + 1;
                    if (com < strlen(headtext))
                    {
                        q = headtext[com];
                    }
                    else
                    {
                        com = 0;
                        q = headtext[com];
                        //q = hold[0];
                    }
                    strcpy(hold, substr(hold, 1, strlen(hold) - 1));
                    append(hold, q);
                    move(0,0);
                    clear();
                    mvaddstr(0,0,hold);
                    refresh();
                    usleep(50);
                }
            }
            usleep(50);
        }
    }
    return 0;
}

/*A Substring function to return substring of a string*/
char * substr(const char *src, int start, int len) 
{   
    char *dest = malloc(len+1);   
    if (dest) 
    {
        memcpy(dest, src+start, len);     
        dest[len] = '\0';   
    }   
    return dest;
}

/*Appends a character to the Given string*/
void append(char s[], char c)
{
    int len = strlen(s);
    s[len] = c;
    s[len+1] = '\0';
}

person realsaid    schedule 18.02.2011    source источник
comment
Вам придется отлаживать его с помощью GDB, если вы работаете в среде Linux. Он остановится именно на той строке, где происходит сбой, поэтому вы можете проверить значения переменных и почти все, что захотите.   -  person SirDarius    schedule 18.02.2011
comment
Вероятно, проблема не в этом, но вы не удалили первый комментарий. Если это запутает компилятор, может случиться что угодно.   -  person Dave    schedule 18.02.2011
comment
@Dave - Поскольку в ОП указано, что программа компилируется, я взял на себя смелость исправить это.   -  person jschmier    schedule 18.02.2011


Ответы (3)


Я не знаю, какой компилятор вы используете, но вот краткое руководство по отладке с помощью GCC и GDB:

$ gcc -g file.c -o myexec   # the -g flag enables debug info

$ gdb ./myexec

Теперь вы находитесь в командной строке GDB. Если вам нужно установить аргументы командной строки, используйте:

set args <arg1> <arg2> ...

Затем запустите свою программу

run

После того, как он выйдет из строя, вы сможете делать все что угодно. Он также показывает, в какой точке программы произошла ошибка. Вероятно, вы захотите использовать эти команды:

bt            # prints the backtrace / call stack
print <expr>  # print value of an expression

В Интернете есть несколько памяток, так что вы может получить быстрое представление о том, какие типы команд доступны.

person nominolo    schedule 18.02.2011
comment
Спасибо @nominolo, я отладил программу и обнаружил, что проблема связана с stdscr, затем я протестировал консоль терминала Unix по умолчанию (т. Е. Не развернута, открыта в Gnome), и она отлично работала без ошибки сегментации. Однако при максимизации консоли программа печатала с середины экрана и прокручивала влево. Через несколько секунд он выдал ошибку сегментации. Я обнаружил, что окно stdscr, в которое я печатал с помощью библиотеки ncurses, имело собственную максимальную ширину экрана, которая не соответствует ширине консоли, поэтому оно не будет печатать за пределами своего окна. Спасибо. - person realsaid; 19.02.2011
comment
Есть ли способ получить размеры экрана консоли Unix, напечатать строки/символы в определенном месте на экране или переместить курсор в определенное положение экрана без использования библиотеки ncurses в c. Я искал, чтобы понять это, но безрезультатно. - person realsaid; 19.02.2011
comment
Извините, я никогда не использовал проклятия, поэтому я не могу помочь вам с этим конкретным вопросом. Существуют определенные экранные коды терминала, которые позволяют перемещать курсор, но вы не могу получить размеры экрана с ними. Я предлагаю вам открыть отдельный вопрос для этого. - person nominolo; 19.02.2011

Используйте метод ручной отладки

#define DEBUG(A) fprintf(stderr, "%d step\n", (A))
#define PRINT(A) fprintf(stderr, "%s\n", (A))

#include<curses.h>
#include <strings.h>
#include<unistd.h> 
#include<stdlib.h>

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

  char *substr(const char *src, int start, int len);
  PRINT("at top of the main" ) ; /***/
  DEBUG(1);
  void append(char* s, char c);

  if(argc!=2)
  {
    puts("Invalid number of arguments: Usage:headband <String>");
  }
  char *headtext = argv[1];
  DEBUG (2); 
  char temp[100];

  int count = 0;

  char hold[100];
  int i;

  int max_x,max_y;

  initscr();
  DEBUG (3);
  curs_set(0);

  getmaxyx(stdscr, max_y, max_x);
  DEBUG (4);
  int consolewidth = max_x;

  clear();
  DEBUG(5);
  temp[0] = '\0';

  for (;;)
  {
    for(i=0; i < strlen(headtext); i++)
    {
      count++;

      append(temp, headtext[i]);
      DEBUG(6);
      move(0,consolewidth - count);
      DEBUG(7);
      if (consolewidth - count > 0)
      {
        mvaddstr(0,console_width-count,temp);
        refresh();
      }
      else if (consolewidth - count == 0)
      {                
        char q;
        int com = i;
        strcpy(hold, temp);
        for(;;)
        {
          com = com + 1;
          if (com < strlen(headtext)){
            q = headtext[com];
            DEBUG (10);
          }else{
            com = 0;
            q = headtext[com];
            //q = hold[0];
          }
          strcpy(hold, substr(hold, 1, strlen(hold) - 1));
          append(hold, q);
          move(0,0);
          clear();
          mvaddstr(0,0,hold);
          refresh();
          usleep(50);
        }
      }
      usleep(50);
    }
  }
  return 0;
}

char * 
   substr(const char *src, int start, int len) 
{   
  char *dest = malloc(len+1);   
  PRINT ( "in substr" );
  if (dest) 
  {
    memcpy(dest, src+start, len);     
    dest[len] = '\0';   
  }   
  PRINT ("at the end of the sub);
  return dest;
}

void append(char s[], char c)
{       
  int len = strlen(s);
  PRINT( "in append function" );
  s[len] = c;
  s[len+1] = '\0';
  PRINT ( "at the end of the append function" );
}

скомпилируйте его, тогда вы можете легко увидеть, где вы ошибаетесь в сегментации

person Community    schedule 18.02.2011
comment
Замените printf в макросах на fprintf(stderr, ...) иначе вы рискуете не увидеть последние сообщения перед сбоем - person Laurynas Biveinis; 18.02.2011
comment
Спасибо @gcc, @Laurynas Biveinis, я выполнил отладку и обнаружил, что ошибка сегментации исходит от DEBUG(7). Развернутая консоль Unix находится за пределами ширины консоли окна stdscr, используемого библиотекой ncurses. Таким образом, возникает ошибка сегментации. Когда я использую окно консоли по умолчанию в Linux GNOME, оно не генерирует ошибку сегментации. Есть ли способ получить размеры экрана терминальной консоли Unix, напечатать строки/символы в определенном месте на экране или переместить курсор в определенную позицию на экране без использования библиотеки ncurses в c. - person realsaid; 19.02.2011

Я вижу наиболее очевидную проблему, которая может привести к ошибке сегментации, в следующем коде:

if(argc!=2)
{
    puts("Invalid number of arguments: Usage:headband <String>");
}
char *headtext = argv[1];

После определения того, что было предоставлено недопустимое количество аргументов, вы переходите к назначению указателя на argv[1]. Если пользователь не предоставил аргумент , результатом будет передача NULL в strlen(). Это приводит к неопределенному поведению, и strlen(NULL) обычно аварийно завершает работу в Linux\Unix. В моем кратком тестировании предоставление аргумента действительно приводило к прокрутке заставки без каких-либо сбоев.

Вы можете подумать об изменении своего кода на что-то похожее на следующее:

if(argc<2)
{
    puts("Invalid number of arguments: Usage:headband <String>");
    exit(EXIT_FAILURE);
}
char *headtext = argv[1];

Обновить
Кажется, ваш текущий код излишне сложен (например, вложенные бесконечные циклы) для того, что, как я считаю, является его предполагаемой целью. Есть также несколько проблем с кодом в его текущей форме, которые следует решить. Произошла утечка памяти из-за вызова malloc() в функции substr() и последующей потери указателя на выделенная память. Если введенный пользователем текст больше, чем размер вашего буфера temp[100], вы переполните буфер в своей функции append(). Также нет необходимости вычислять длину headtext более одного раза, так как ее длина не изменится.

Я утверждаю, что curses следует использовать поверх необработанных escape-кодов терминала. Библиотека curses фактически представляет собой оболочку необработанных кодов терминала и предоставляет гибкий и эффективный API, так что прикладным программам не нужно беспокоиться о базовых возможностях терминала. Возможно, NCURSES Programming HOWTO или X/Open Curses, справочные страницы могут оказаться полезными.

Я сам не эксперт по curses, однако в следующем примере показан другой подход, основанный на curses, который частично основан на исходном коде и, по-видимому, дает тот же результат*:

#include<curses.h>
#include<signal.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h> 

/* maximum x and y co-ordinates of the window */
int max_x, max_y;
/* pointer to headband buffer */
char * headband = NULL;
/* counter for streaming and scolling headband text */
unsigned int count;

/* Handler for window resize */
void handle_winch(int sig)
{
    /* Function for initializing curses & headband buffer */
    void my_init();

    /* Disable signal handler during reinitialization */
    signal(SIGWINCH, SIG_IGN);

    /* Reinitialize the window to update data structures */
    endwin();
    my_init();
}

/* Function for initializing curses & headband buffer */
void my_init()
{
    /* Initialize / Reinitialize screen for ncurses */
    initscr();
    curs_set(0);
    clear();
    refresh();
    getmaxyx(stdscr, max_y, max_x);

    /* Allocate / Reallocate and initialize scrolling headband buffer */
    free(headband);
    headband = (char *)malloc(max_x+1);
    memset(headband, ' ', max_x);
    headband[max_x] = '\0';
    count = 0;

    /* Setup signal handler for window resizing */
    signal(SIGWINCH, handle_winch);
}

/* Implements a scrolling headband that takes a 
 * string argument and scrolls continously */

int main(int argc, char* argv[])
{
    char * headtext;
    int headtext_len;
    int size;

    if(argc<2)
    {
        puts("Invalid number of arguments: Usage:headband <String>");
        exit(EXIT_FAILURE);
    }
    /* Get headtext from the argument argv[1] and compute length */
    headtext = argv[1];
    headtext_len = strlen(headtext);

    /* Call common initialization / reinitialization routine */
    my_init();

    /* Run this loop continuously to keep scrolling */
    for(;;)
    {
        /* Store & use copy of max_x in case original max_x is 
         * modified in signal handler by window resize event */
        size = max_x;

        /* Rotate headband by shifting entire buffer and then
         * appending next character from headtext buffer */
        memmove(&headband[0], &headband[1], size-1);
        headband[size-1] = headtext[count++ % headtext_len];

        /* Update window */
        move(0,0);
        mvaddstr(0,0,headband);
        refresh();
        usleep(50000);
    }

    /* Unreachable, but included for completeness */
    endwin();
    free(headband);
    exit(EXIT_SUCCESS);
}

* Включает обработку изменения размера окна

person jschmier    schedule 18.02.2011
comment
Спасибо, я исправил эту часть программы, отладил ее и обнаружил, что ошибка сегментации исходит от DEBUG(7). Развернутая консоль Unix находится за пределами максимальной ширины консоли окна stdscr, используемого библиотекой ncurses. Таким образом, возникает ошибка сегментации. Когда я использую окно консоли по умолчанию в Linux GNOME, оно не генерирует ошибку сегментации. Есть ли способ получить размеры экрана терминальной консоли Unix, напечатать строки/символы в определенном месте на экране или переместить курсор в определенную позицию на экране без использования библиотеки ncurses в c. - person realsaid; 19.02.2011
comment
@realsaid - Хотя это возможно сделать без ncurses, вы окажете себе и всем, кто придет за вами, огромную услугу, если научитесь делать это с ncurses, а не пытаетесь работать с необработанными escape-последовательностями. Как вы могли бы сделать это без ncurses, должно быть предметом другого вопроса. - person jschmier; 22.02.2011
comment
@realsaid - Возможно, этот вопрос о получении ширины терминала в C может быть полезен. - person jschmier; 25.02.2011