2011-02-18 3 views
0

내 프로그램에서 세그먼트 화를 생성하는 곳을 식별하지 못했습니다. 문자열 작업 중 또는 문자열 포인터가 런타임 중에 세그먼트 오류를 ​​일으키는 것을 가리키는 데 도움이 필요합니다. 프로그램이 성공적으로 컴파일되지만 런타임에 세그먼트 화 오류가 발생합니다.내 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'; 
} 
+0

Linux 환경에 있다고 가정 할 때 GDB를 사용하여 디버깅해야합니다. 충돌이 발생하는 정확한 행에서 중지되므로 변수 값을 검사 할 수 있습니다. – SirDarius

+0

아마도 디버거가 맞습니까? –

+0

아마도 문제는 아니지만 첫 번째 댓글을 종료하지 않았습니다. 컴파일러가 혼란 스러우면 아무 일도 일어나지 않을 것입니다. – Dave

답변

1

핸드 디버그 방법을 사용하십시오.

#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"); 
} 

컴파일, 당신은 쉽게 당신이 세그먼트 오류를 ​​

+0

매크로에서 printf를 fprintf (stderr, ...)로 바꾸거나 충돌 이전에 마지막 메시지가 나타나지 않을 위험이 있습니다. –

+0

@Laurynas Biveinis, 감사합니다. –

+0

감사합니다. @ gcc, @ Laurynas Biveinis, 디버그를 꺼내서 알아 냈습니다. 세그먼트 화 오류는 DEBUG (7)에서 유래합니다. 최대화 된 Unix 콘솔은 ncurses 라이브러리가 사용하는 "stdscr"창의 콘솔 너비 밖에 있습니다. 그래서 세분화 오류가 발생합니다. Linux GNOME에서 기본 콘솔 창을 사용할 때 세그먼트 화 오류가 생성되지 않습니다. 유닉스 터미널 콘솔의 화면 크기를 얻거나, 문자열/문자를 화면의 특정 위치에 인쇄하거나, c의 ncurses 라이브러리를 사용하지 않고 커서를 특정 화면 위치로 옮기는 방법이 있습니까? – realsaid

1

을 어디 당신이 사용하고있는 컴파일러 모른다 볼 수 있지만, 여기 GCC와 GDB와 함께이 문제를 디버깅하는 방법을 빠른 가이드 수 :

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

$ gdb ./myexec 

이제 GDB 프롬프트가 나타납니다. 사용자가 설정해야하는 경우 명령 줄 인수를 사용 : 당신이 모든 일을 할 수있는 충돌하면

set args <arg1> <arg2> ... 

그런 다음 프로그램

run 

를 실행합니다. 또한 프로그램에서 오류가 발생한 지점을 보여줍니다. cheat sheets on the web의 몇 가지가 있습니다

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

, 그래서 당신은 명령의 종류를 사용할 수 있습니다 무엇 빠른 아이디어를 얻을 수 있습니다 당신은 아마 이러한 명령을 사용하려고합니다.

내가 분할 오류가 다음 코드에 해당이 이어질 수있는 볼 수있는 가장 확실한 문제
+0

@nominolo 감사합니다. 프로그램을 디버깅하여 문제가 stdscr에서 발견 된 후 기본 유닉스 터미널 콘솔 (예 : 최대화되지 않음, 그놈 내에서 열림)에서 테스트 한 결과 세분화 오류없이 완벽하게 작동했습니다. 그러나 콘솔을 최대화 할 때 프로그램은 화면 가운데에서 인쇄하고 왼쪽으로 스크롤합니다. 몇 초 후에 세그먼트 화 오류가 발생했습니다. ncurses 라이브러리를 사용하여 stdscr 창에 인쇄하는 것이 콘솔의 최대 화면 너비이므로 콘솔 외부에 인쇄하지 않는다는 것을 알았습니다. 감사합니다. – realsaid

+0

유닉스 콘솔의 화면 크기를 얻거나, 문자열/문자를 화면의 특정 위치에 인쇄하거나, c의 ncurses 라이브러리를 사용하지 않고 커서를 특정 화면 위치로 옮기는 방법이 있습니까? 나는 이것을 알아 내기 위해 조사했지만 아무 소용이 없다. – realsaid

+0

죄송 합니다만 저주를 사용한 적이 없으므로이 특정 질문에 대해 도움을 드릴 수 없습니다. 커서를 움직일 수있는 특정 [터미널 이스케이프 코드] (http://www.termsys.demon.co.uk/vtansi.htm)가 있지만 이것으로 화면 크기를 얻을 수는 없습니다. 이 질문에 대해 별도의 질문을하는 것이 좋습니다. – nominolo

0

: 잘못된 개수의 인수가 제공되었음을 확인한 후

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() 함수에있는 memory leak이 있으며 할당 된 메모리에 대한 포인터가 손실됩니다. 사용자가 제공 한 텍스트가 temp[100] 버퍼의 크기보다 큰 경우 append() 함수에서 버퍼 오버플로가 발생합니다. 또한 길이가 변경되지 않으므로 headtext 길이를 두 번 이상 계산할 필요가 없습니다.

저주raw terminal escape codes 이상으로 사용해야합니다. curses 라이브러리는 실제로 원시 터미널 코드를 감싸는 래퍼이며 유연하고 효율적인 API를 제공하므로 응용 프로그램이 기본 터미널 기능에 대해 걱정할 필요가 없습니다. 아마도 NCURSES Programming HOWTO 또는 X/Open Curses, Reference Pages 일 수 있습니다.

내가 더 저주 전문가에게 자신을 생각하지, 그러나, 다음과 같은 예를 느슨하게 원래의 코드를 기반으로 또 다른 저주 기반의 접근 방식을 보여주고 동일한 출력 * 가지고 나타납니다

#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); 
} 

* 윈도우 크기 조정 처리 포함

+0

감사합니다. 프로그램의이 부분을 수정하여 디버깅하여 세그먼테이션 오류가 DEBUG (7)에서 발생한다는 것을 알았습니다. 최대화 된 유닉스 콘솔은 ncurses 라이브러리가 사용하는 "stdscr"윈도우의 최대 콘솔 너비 밖에 있습니다. 그래서 세분화 오류가 발생합니다. Linux GNOME에서 기본 콘솔 창을 사용할 때 세그먼트 화 오류가 생성되지 않습니다. 유닉스 터미널 콘솔의 화면 크기를 얻거나, 문자열/문자를 화면의 특정 위치에 인쇄하거나, c의 ncurses 라이브러리를 사용하지 않고 커서를 특정 화면 위치로 옮기는 방법이 있습니까? – realsaid

+0

@realsaid - ncurses없이이 작업을 수행 할 수는 있지만 원시 탈출 시퀀스로 작업하기보다는 ncurses로 배우는 경우 큰 도움이 될 것입니다. ncurses없이 이것을 할 수있는 방법은 다른 질문의 주제가되어야합니다. – jschmier

+0

@realsaid - 어쩌면이 질문에 [C에서 터미널 폭을 얻는] (http://stackoverflow.com/q/1022957/203667) 몇 가지 사용할 수 있습니다. – jschmier

관련 문제