2016-11-02 11 views
1

main()関数のout[0] = '\0';の理由は何ですか?K&R - 再帰的降下パーサ - strcat

それはそれなしで働いているようです。

コード

#include <stdio.h> 
#include <string.h> 
#include <ctype.h> 

#define MAXTOKEN 100 
enum { NAME, PARENS, BRACKETS }; 

int tokentype; 
char token[MAXTOKEN]; /*last token string */ 
char name[MAXTOKEN]; /*identifier name */ 
char datatype[MAXTOKEN]; /*data type = char, int, etc. */ 
char out[1000]; 

void dcl(void); 
void dirdcl(void); 
int gettoken(void); 

/* 
Grammar: 

    dcl:   optional * direct-dcl 
    direct-dcl:  name 
        (dcl) 
        direct-dcl() 
        direct-dcl[optional size] 
*/ 

int main() /* convert declaration to words */ 
{ 
    while (gettoken() != EOF) { /* 1st token on line */ 

     /* 1. gettoken() gets the datatype from the token */ 
     strcpy(datatype, token); 

     /* 2. Init out to end of the line? */ 
     /* out[0] = '\0'; */ 

     /* parse rest of line */ 
     dcl(); 

     if (tokentype != '\n') 
      printf("syntax error\n"); 

     printf("%s: %s %s\n", name, out, datatype); 
    } 

    return 0; 
} 

int gettoken(void) /* return next token */ 
{ 
    int c, getch(void); 
    void ungetch(int); 
    char *p = token; 

    /* Skip blank spaces and tabs */ 
    while ((c = getch()) == ' ' || c == '\t') 
     ; 

    if (c == '(') { 

     if ((c = getch()) == ')') { 

      strcpy(token, "()"); 
      return tokentype = PARENS; 

     } else { 
      ungetch(c); 
      return tokentype = '('; 
     } 

    } else if (c == '[') { 

     for (*p++ = c; (*p++ = getch()) != ']';) 
      ; 

     *p = '\0'; 
     return tokentype = BRACKETS; 

    } else if (isalpha(c)) { 

     /* Reads the next character of input */ 
     for (*p++ = c; isalnum(c = getch());) { 
      *p++ = c; 
     } 

     *p = '\0'; 
     ungetch(c); /* Get back the space, tab */ 

     return tokentype = NAME; 

    } else 
     return tokentype = c; 
} 

/* dcl: parse a declarator */ 
void dcl(void) 
{ 
    int ns; 

    for (ns = 0; gettoken() == '*';) /* count *'s */ 
     ns++; 

    dirdcl(); 

    while (ns-- > 0) 
     strcat(out, " pointer to"); 
} 

/* dirdcl: parse a direct declarator */ 
void dirdcl(void) 
{ 
    int type; 

    if (tokentype == '(') { 

     dcl(); 

     if (tokentype != ')') 
      printf("error: missing)\n"); 

    } 
    else if (tokentype == NAME) /* variable name */ { 
     strcpy(name, token); 
     printf("token: %s\n", token); 
    } 
    else 
     printf("error: expected name or (dcl)\n"); 

    while ((type = gettoken()) == PARENS || type == BRACKETS) { 

     if (type == PARENS) 
      strcat(out, " function returning"); 
     else { 
      strcat(out, " array"); 
      strcat(out, token); 
      strcat(out, " of"); 
     } 

    } 
} 
+1

'strcat'はNULで終了する文字列を必要としますが、この場合(グローバル変数として){0}に初期化されます。 –

+0

2行の入力を試みてください。 –

+1

@Keineそれは静的な記憶期間を持っているので、自動的にゼロ初期化されます。 –

答えて

2

あなたはstrcatが動作するためにゼロにするout[0]を必要としています。

このライン

out[0] = '\0'; 

は、従来の静的初期化ルールの導入に必要であったが、このようなout[]ような静的アレイは、すべてゼロに初期化されるので、それはもはや、必要とされません。それが算術型を持つ場合initialization rules of C99によれば

  • ...
  • は、それが(正または符号なし)がゼロに初期化されます。
  • 集合体の場合、すべてのメンバーはこれらの規則に従って(再帰的に)初期化されます。
+0

私は見ていますが、最初の文字を '\ 0'に初期化するのはなぜでしょうか?' strcat(out、 "abc"); 'out = {'\ 0' 'a'、 'b'、 'c'} '? – dud3

+0

@ dud3いいえ、 '' \ 0 ''はヌルターミネータ*と見なされるので、 ''\ 0''を先頭文字とする文字列は' '\ 0' 'の後にどんな文字が来ても空文字列のように振る舞います。 – dasblinkenlight

+0

ああ、私たちは単純に 'out [0] = '''に初期化することはできませんでしたか? – dud3

1

char配列(別名文字列)を空の配列にリセットしています。そのためジャンク値が0インデックスに

をだから '\ 0' を追加しないでください

i += 1; 

:ような何かを行う前に

int i = 0; 

:我々が使用 のように(ジャンク値を削除します)配列の配列は完全に空であり、strcat関数は配列の他のインデックスに迷惑値を書き込む以上、0のインデックスから値を追加し始めます。

プログラムが配列をリセットせずに動作している場合は、IDEツールがそれを実行していることを意味しますが、リセットすることをお勧めします。

1

要するに、この特定のケースでは、厳密には必要ではありませんが、他の多くのケースでは疑わしいほど似ています。だから、ほとんどの人はそれを「良いスタイル」とします。それでなぜ必要なのでしょうか?

「空」のメモリはありません。 「長さ」というものはありません。あなたが明示的にそれを追跡したり、あなた自身のものを定義しない限り。

メモリは0から255の数字です。0は255と同じくらい有効な数字なので、バイトが使用されているかどうかを判断する方法はありません。より大きい数値が必要な場合はいくつかのバイトを「追加」できますが、すべてがバイトで構築されます。テキストは単に番号にマップされます。数十年前、どの数字がどの文字を表すかが決定されました。だから、値32のバイトを見ると、それは32であるかもしれません。あるいは、コンピュータのアルファベットの32番目の文字(スペース文字です)かもしれません。

文字列を受け取ったときに処理するテキストの量がわからない場合は、大量のバイトブロックを予約することが通常です。これは上記のchar out[1000];です。しかし、テキストがどこで終わるかをどのように教えていますか?あなたがすでに使用している1000バイトのどれくらい?

まあ、昔は、別の変数、たとえばint length;を宣言し、それまでに使用したバイト数を記録している人もいます。 Cのデザイナーは、別のルートを行った。彼らは非常にまれなキャラクターを選んでマーカーとして使用することに決めました。彼らは0の文字を選びました(それは文字 '0'ではありません。実際には '0'はコンピュータのアルファベットの48番目の文字です)。

文字列内のすべてのバイトを最初から見ることができます。文字が0より大きい場合は、その文字が使用されていることがわかります。 0文字に達すると、これがあなたの文字列の最後であることがわかります。どちらのアプローチにも様々な利点があります。 intは4バイトを使用し、追加の0文字のみを使用します。一方、intを使用する場合、文字列に0文字を含めることもできます。これはまったく別の文字です。

あなたがCで"foo"を書くたびに、何Cが実際には'f'ための4バイトのための予備室、'o''o'で、0は終わりを示すため。 ""をC言語で記述すると、1バイトの予約領域(0)が実行されます。文字列が空であることがわかります。

スタートアップ時に何かを入れる前にいっぱいになっているのは何ですか?まあ、ほとんどの場合、それはちょうどゴミです。最後の使用時にそのメモリにあったものは何でも(結局のところ、RAMが限られているため、コンピュータ上で1つのアプリケーションを終了すると、その後に起動する次のアプリケーションでそのメモリを再利用することができます)。これらは一般的な文字の範囲外の乱数になります。

strcatに空の文字列としてoutが表示されるようにするには、値の文字である0で始まるメモリブロックを指定する必要があります。あなたがちょうどそのような記憶を残しておけば、その中にいくつかのランダムな文字があるかもしれません。あなたのバッファには、 "jbhasugaudq7e1723876123798dbkda 0skno§§^^%$# - 9H 0 HWDZmwus 0/usr/local/bin" またはそれ以前にそのメモリにあったものが含まれている可能性があります。ここにテキストを追加すると、最初の0(これはランダムにこの場所にある)の前のものが有効な文字列であり、それをに追加すると考えられます。開始時に0を置くと、この文字列が空であることがわかります。

なぜ「厳密には必要ではない」と言われましたか?あなたのケースでは、outはグローバル変数で、グローバル変数は特別なものです。なぜなら、アプリケーションの起動時に自動的に0にクリアされる(または宣言時に割り当てた値が割り当てられる)からです。

ただし、これはグローバル変数(通常のグローバルとstaticグローバル)にのみ該当します。多くのプログラマは、バイトのブロックを常に初期化することを習慣にしています。そうすれば、後で誰かがグローバル変数をローカル変数に変更するか、ローカル変数で使用するコードを別の場所にコピーアンドペーストしても、このステートメントを追加するのを忘れる心配はありません。

ランダムメモリには、しばしば0文字が含まれているため、これは特に便利です。だからあなたが以前に使ったプログラムによっては、最初に0があったので、あなたはそれを忘れてしまったかもしれません。後で、あなたのユーザーの1人がこのアプリケーションを実行すると、文字列の先頭にガベージが表示されます。

これは少し物事を明確にしていますか?

+0

はい、それは多くを明確にしました。 – dud3

+0

strcat()関数はsrc文字列をdest文字列に追加し、 はdestの最後に終端のヌルバイト( '\ 0')を上書きします。 はそれから、私たちのためにすでにこれを処理していると思っていた終端のヌルバイトを追加します。 – dud3

+0

「Home \ 0」と「work \ 0」という2つの文字列がある場合(ここで\ 0は前にお話した「0」です)、一方をもう一方に追加すれば、 "Home \ 0work " Cは文字列が\ 0で終了すると考えるので、\ 0と同じになります。だからそれはstrcatがうまくいくかどうかではない。代わりに最初の\ 0を削除して残りを追加することで、期待される "宿題"を得ることができます。それから別の "\ 0"を追加するので、 "work"の後に文字列が本当に終了することがわかります。しかし、最初の\ 0はstrcatが "Home"の長さを知るためにはまだそこにある必要があります。 – uliwitness

関連する問題