C언어 함수내에서 배열 설정시..

dgsquare의 이미지

오늘 하루의 절반을 잡아먹었던 문제가 있습니다 ^^; 지금은 해결했는데 갑자기 궁금해져서......
일단 간단히 설명드리면, mini shell을 만들고 있거든요.
문자열을 받아서 파싱하는 함수가 있는데, 이것은 공백이나 특정 문자들을 받으면 각 토큰으로 나누어서 인자로 보내진 배열에 저장시켜줍니다.

대충 함수를 보자면

int parse(char *string, token a_token[])
{
    char word[MAX_WORD_LEN];
    char *temp;
    int word_offset;
    temp = string;
    
    ......
    어쩌구 저쩌구
    ......
    return 0;
}
이런 식이구요.
중간 부분은 temp를 하나씩 이동하면서 공백이나 기타 분리자가 나타날때까지 word에 저장시키는 내용입니다. word[word_offset]에 한 문자열씩 저장되고, 구분자가 나오면, word의 마지막에 '\0'을 첨가후 token에 strcpy로 옮깁니다. (소스를 가져다 설명하려면 너무 복잡해서리 ^^ 필요하면 이 함수만 올려드릴께요)

각설하구, 질문은요.
이 함수를 사용한 프로그램이 컴파일은 이상이 없고, 실행도 되는데, 실행때마다 결과값이 다르다는 것 입니다.
예를 들어 string에 'ls -al'이 들어가면 token[0].word에는 ls, token[1].word에는 -al이 들어가게 됩니다. 근데, 이 프로그램을 실행해, 결과값을 출력시키면 word[0]에 5번중 3번은 ls 가 들어가지만, 어느때는 'lsa'가 들어가고, 또 어느때는 'lsQ'가 들어가고, 대충 ls뒤에 한글자(깨진문자포함)가 들어가서 출력이 됩니다.
즉 ./a.out을 할때마다, 결과값이 예상대로 나올때도, 아닐때도 있다는 것입니다.
뭐, 안에서 랜덤함수를 부른것도 아니고 이게 뭔 조화인지-_-;;
해결은 word배열의 요소를 모두 '\0'으로 할당 시켜주니 정상적으로 돌아가네요.

쓰다보니 잡설이 됬네요. 암튼 궁금한 것은, 지역함수내에서 배열을 생성뒤 초기화 안하면 사용시 크게 문제가 생기는지 알고 싶고요.
두번째로는 왜 이함수가 실행때마다 다른 결과를 나타내는지 궁금합니다.
마지막으로 함수내에서 배열이 설정되면 어떻게 저장되는지도 간단히 부탁합니다.

맨날 신세만 지네여^^.. 프로그래밍을 시작한뒤 얼마안되서 아직은 모르는것만 늘어갑니다- 여기까지 읽어주시느냐 감사합니다 :wink:

추가)
한가지 더 설명하자면, parse함수를 한번 호출시에는 이상이 없습니다. 문자열을 변경한후 2번 3번 호출할때부터 저런 문제가 생기는 군요. 그러니까
parse("cd /root", token); --> 이상없음
parse("vi foo.txt", token); --> 여기부터 문제발생
parse("ls -al", token);

File attachments: 
첨부파일 크기
Binary Data otshell.tar.gz28.29 KB
kihongss의 이미지

dgsquare wrote:
오늘 하루의 절반을 잡아먹었던 문제가 있습니다 ^^; 지금은 해결했는데 갑자기 궁금해져서......
일단 간단히 설명드리면, mini shell을 만들고 있거든요.
문자열을 받아서 파싱하는 함수가 있는데, 이것은 공백이나 특정 문자들을 받으면 각 토큰으로 나누어서 인자로 보내진 배열에 저장시켜줍니다.

대충 함수를 보자면

int parse(char *string, token a_token[])
{
    char word[MAX_WORD_LEN];
    char *temp;
    int word_offset;
    temp = string;
    
    ......
    어쩌구 저쩌구
    ......
    return 0;
}
이런 식이구요.
중간 부분은 temp를 하나씩 이동하면서 공백이나 기타 분리자가 나타날때까지 word에 저장시키는 내용입니다. word[word_offset]에 한 문자열씩 저장되고, 구분자가 나오면, word의 마지막에 '\0'을 첨가후 token에 strcpy로 옮깁니다. (소스를 가져다 설명하려면 너무 복잡해서리 ^^ 필요하면 이 함수만 올려드릴께요)

각설하구, 질문은요.
이 함수를 사용한 프로그램이 컴파일은 이상이 없고, 실행도 되는데, 실행때마다 결과값이 다르다는 것 입니다.
예를 들어 string에 'ls -al'이 들어가면 token[0].word에는 ls, token[1].word에는 -al이 들어가게 됩니다. 근데, 이 프로그램을 실행해, 결과값을 출력시키면 word[0]에 5번중 3번은 ls 가 들어가지만, 어느때는 'lsa'가 들어가고, 또 어느때는 'lsQ'가 들어가고, 대충 ls뒤에 한글자(깨진문자포함)가 들어가서 출력이 됩니다.
즉 ./a.out을 할때마다, 결과값이 예상대로 나올때도, 아닐때도 있다는 것입니다.
뭐, 안에서 랜덤함수를 부른것도 아니고 이게 뭔 조화인지-_-;;
해결은 word배열의 요소를 모두 '\0'으로 할당 시켜주니 정상적으로 돌아가네요.

쓰다보니 잡설이 됬네요. 암튼 궁금한 것은, 지역함수내에서 배열을 생성뒤 초기화 안하면 사용시 크게 문제가 생기는지 알고 싶고요.
두번째로는 왜 이함수가 실행때마다 다른 결과를 나타내는지 궁금합니다.
마지막으로 함수내에서 배열이 설정되면 어떻게 저장되는지도 간단히 부탁합니다.

맨날 신세만 지네여^^.. 프로그래밍을 시작한뒤 얼마안되서 아직은 모르는것만 늘어갑니다- 여기까지 읽어주시느냐 감사합니다 :wink:

추가)
한가지 더 설명하자면, parse함수를 한번 호출시에는 이상이 없습니다. 문자열을 변경한후 2번 3번 호출할때부터 저런 문제가 생기는 군요. 그러니까
parse("cd /root", token); --> 이상없음
parse("vi foo.txt", token); --> 여기부터 문제발생
parse("ls -al", token);

parser 함수를 어떻게 구현하셨는지 코드라도 보여주셨으면 좋았을텐데, char word[MAX_WORD_LEN]; 는 함수가 불려질때마다 스택에 새롭게 생성됩니다. 자동으로 0으로 초기화가 되지 않기 때문에 보통 쓰레기 값이 채워집니다. 그래서 결과값이 실행할때마다
엉뚱하게 나오는것 같습니다.

char word[MAX_WORD_LEN] = {0,}; 하시거나 memset(word,0,MAX_WORD_LEN) 으로 해주시기 바랍니다.

doldori의 이미지

증상만으로 원인을 알아내기는 힘들고 역시 소스가 있어야겠네요.
함수가 하는 일을 보니 strtok()과 많이 겹치는데 굳이 직접 만드시는 이유라도...?

dgsquare의 이미지

=== 해결되었습니다. (아랫댓글) 이것은 그 전에 올린글입니다. ===

일단 답변 감사드리구요. :D
리펙토링이 제대로 안된상황이라서 넘 복잡하네여. 함튼 함수 소스이구요.
Token은 symbol과 word로 이루어진 구조체입니다.
지금은 앞에서 말한 증상이 나타 나는 상태이구요. word배열을 '\0'으로 초기화 시켜주면 이상없이 작동합니다.

   typedef struct token
   {
       SYMBOL symbol;
       char word[MAX_WORD];
   } Token;

   // func: parseCommand
   // 인자: *s_command(전체 입력 명령), a_token(token별로 분류된 배열)
   // 기능: 문자열을 입력받아 종류별로 token에 넣습니다.
   // 반환값: 성공시 tokenSize, 실패시 SYNTEX_ERROR
   int parseCommand(char *s_command, Token a_token[])
   {
       int idx_token;
       char *c_char;
       char word[MAX_WORD];
       int word_offset;
       STATUS status;
       int i;

       idx_token = 0;
       word_offset = 0;
       status = INIT;

   //    for(i = 0; i < MAX_WORD; i++)
   //        word[i] = '\0';

       c_char = s_command;

       while (status != END)
       {
           if(*(c_char) == ';' || *(c_char) == '&' || *(c_char) == '|' || *(c_char) == '<' || *(c_char) == '>' || *(c_ch   ar) == ' ' || *(c_char) == '\t' || *(c_char) == '\0') {
               if(status == WORD) {
                   word[word_offset+1] = '\0';
                   strcpy(a_token[idx_token].word, word);
                   a_token[idx_token].symbol = getSymbol(a_token[idx_token].word);
                   word_offset = 0;
                   idx_token++;
               }
           }

           if(*(c_char) == ';' || *(c_char) == '&' || *(c_char) == '|' || *(c_char) == '<' || *(c_char) == '>') {
               if (status == S_COMMAND && *(c_char-1) == '>') {
                   status = M_COMMAND;
                   idx_token--;
                   strcpy(word, ">>");
               } else if (status == S_COMMAND || status == M_COMMAND) {
                   return SYNTEX_ERROR;
               } else {
                   status = S_COMMAND;
                   word[0] = *(c_char);
                   word[1] = '\0';
               }
               strcpy(a_token[idx_token].word, word);
               a_token[idx_token].symbol = getSymbol(a_token[idx_token].word);
               idx_token++;
           }
           else if(*(c_char) == ' ' || *(c_char) == '\t') {
               status = BLANK;
           }
           else if(*(c_char) == '\0')
               status = END;
           else {
               word[word_offset] = *(c_char);
               word_offset ++;
               status = WORD;
           }
           c_char ++;
       }
       return idx_token;
   }

Test 하는 함수는 다음과 같습니다.

   void test()
   {
       char s_command[MAX_COMMAND_LEN+1];
       Token a_token[MAX_TOKEN];
       int tokenSize, tokenOffset;
       int argSize;
       int i;
       char *args[MAX_ARG];

       for(i = 0; i < MAX_ARG; i++)
           args[i] = (char *)malloc(sizeof(char)*MAX_ARG_LEN+1);

       tokenOffset = 0;
       tokenSize = parseCommand("ls -al",a_token);
       printf("%s\n", a_token[0].word);


       for(i = 0; i < MAX_ARG; i++)
           free(args[i]);

   }

실행 결과

com071@apple:~/operatingSystem/otshell/job$ ./otshell
lsC
com071@apple:~/operatingSystem/otshell/job$ ./otshell
lsR
com071@apple:~/operatingSystem/otshell/job$ ./otshell
lsu
com071@apple:~/operatingSystem/otshell/job$ ./otshell
ls?
com071@apple:~/operatingSystem/otshell/job$ ./otshell
lsh
com071@apple:~/operatingSystem/otshell/job$ ./otshell
ls+
com071@apple:~/operatingSystem/otshell/job$ ./otshell
ls?
com071@apple:~/operatingSystem/otshell/job$

원래는 TDD로 하나 씩 확인하면서 했구요. 엉뚱하게 다른 Test를 하는데, 그때까지 ok하던넘이 fail이 되더군요-_-;;

그리고, 해결책은 알구요. ^^; 이런문제가 일어나는 이유에 대해서 알고 싶네요.

Quote:
자동으로 0으로 초기화가 되지 않기 때문에 보통 쓰레기 값이 채워집니다. 그래서 결과값이 실행할때마다
엉뚱하게 나오는것 같습니다

쓰레기 값이 들어가는 것은 알고 있습니다- 하지만 함수에서 하나씩 덮어씌우고, 마지막에 '\0'로 종결한후, 그 부분까지를 매게변수로 들어온 type에 strcpy로 복사하면 상관없지 않나요? 널문자 이후의 쓰레기 문자열이 c언어 string인식에 영향을 미치지는 않는다고 생각하는데...

Quote:
함수가 하는 일을 보니 strtok()과 많이 겹치는데 굳이 직접 만드시는 이유라도...?

두가지 이유입니다. 1.학교 실습이구요^^; 2. 추가기능이 많이 필요해서 직접해보는것이 좋을것 같아서입니다.
댓글 첨부 파일: 
첨부파일 크기
Binary Data 0바이트
dgsquare의 이미지

찾았습니다. o-O
역시나 단순했네여 -_-;

Quote:
if(status == WORD) {
word[word_offset+1] = '\0';
strcpy(a_token[idx_token].word, word);
a_token[idx_token].symbol = getSymbol(a_token[idx_token].word);
word_offset = 0;
idx_token++;
}

이부분에서 word[word_offset]으로 바꾸니 되네여 -_-;
kihongss님 말씀이 맞구요.

자세히 알아보지 못하고 질문을 올려서 미안합니다 :oops:

한가지 신기한거는, 분명이 저 부분을 예전에 확인했었는데, 그때는 고쳐도 안되었던 기억이 ㅠ.ㅠ... (둘중하나겠져.. 꿈에서 고쳤거나, make를 재대로 안돌리고 실행했거나...)

게리롱푸리롱 송이나 들으며 마음을 달래야 겠습니다 :roll:

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.