0

次のコードでは、char 配列は位置 1 と 2 で最大 100 文字を出力しますが、位置 3 では 22 文字しか出力しません。この動作の理由は何ですか?

#include<stdio.h>
/* print the longest input line */
/*Exercise 1-16. Revise the main routine of the longest-line program so it will correctly print the length of arbitrary long input lines, and as much as possible of the text.*/

#define MAXLENGTH 100

int mygetline(char s[], int limit);
void charcopy(char to[], char from[]);

int main(){
  char current[MAXLENGTH];
  char longest[MAXLENGTH];
  int curlen;
  int maxlen;

  maxlen = 0;
  while( (curlen = mygetline(current, MAXLENGTH)) > 0 ){
    if (curlen > 80)
      printf("\nvery long:%d; %s\n", curlen, current);//#1# prints 100 digits
    if(curlen>maxlen){
      maxlen=curlen;
      charcopy(longest, current);
      printf("\nlonger:%d; %s\n", maxlen, longest);//#2# prints 100 digits
    }
  }
  if (maxlen)//char array seems to truncates itself at scope boundry.
    printf("\nlongest:%d; %s\n", maxlen, longest);//#3# prints 22 digits
  printf("\nall done!\n");
  return 0;
}

int mygetline(char s[], int limit){
  int i, c;
  for(i=0; i < limit-1 && ((c=getchar()) != EOF) && c != '\n'; ++i)
    s[i]=c;
  if(c=='\n'){
    s[i]=c;
    ++i;}
  else
    if(i >= limit-1)
      while (((c=getchar()) != EOF) && c != '\n')
    ++i;
  s[i]='\0';
  return i-1;
}


void charcopy(char to[], char from[]){
  int i;
  i=0;
  while( (to[i] = from[i])  != '\0'){
    ++i;}
}

コメントで 3 とマークされた場所は、100 文字ではなく 22 文字しか出力されません。非常に奇妙です。

編集: Scotts の回答に従って、mygetline を次のように変更しました。

int mygetline(char s[], int limit){
  int i, c, k;
  for(i=0; i < limit-1 && ((c=getchar()) != EOF) && c != '\n'; ++i)
    s[i]=c;
  if((c=='\n') && (i < limit -1)){
    s[i]=c;
    ++i;}
  else{//if we are over the limit, just store the num of char entered without storing chars
    k = 0;
    while (((c=getchar()) != EOF) && c != '\n')
      ++k;}
  s[i]='\0';
  return i+k;
}

ご覧のとおり、入力が制限を超えた場合、入力された文字の数は、配列に触れないまったく新しい変数 k に格納されます。最後に印刷された行が切り捨てられ、行の長さとして奇妙な 32770 が表示されます。なぜですか? ご覧のとおり、アレイはベビーシッターで甘やかされ、正確な量のイワナだけを与えられ、それ以上は与えられていません。

編集:最初のリストの問題は、スコットが指摘したように、配列をオーバーシュートしていたことです。2 番目の mygetline の問題はk=0;、if else ネストの内部で初期化されていたことです。初期化を上に移動し、関数全体に対してグローバルにすることで、2 番目の問題が解決されるようです。

次のように mygetline を使用します。

int mygetline(char s[], int limit){
  int i, c, k;
  k=0;
  for(i=0; i < limit-1 && ((c=getchar()) != EOF) && c != '\n'; ++i)
    s[i]=c;
  if((c=='\n') && (i < limit -1)){
    s[i]=c;
    ++i;}
  else{//if we are over the limit, just add the num of char entered without storing chars
    while (((c=getchar()) != EOF) && c != '\n')
      ++k;}
  s[i]='\0';
  return i+k;
}
4

1 に答える 1

1

わかりました。C について知っておくべきことは、C はまったく子守をしないということです。として宣言された配列があり、char foo[4]に書き込もうとしてもfoo[20]、C はまったく文句を言いません。(通常、NULL などの制限されたメモリに書き込むと、セグメンテーション違反がスローされますが、メモリにアクセスできる場合は、好きなことを行うことができます。)

では、必要以上に配列に書き込むとどうなるでしょうか? 公式の回答は「未定義の動作」です。これは完全に一般的な包括的な回答であり、「コンパイラ次第です」と述べています。ただし、ほとんどの C コンパイラでは、スタックの破損と呼ばれる処理が行われます。

メインを含む任意の関数で要求するメモリはすべて、適切な単一の一貫したブロックに割り当てられます。したがって、メイン関数では、 に 100 バイトcurrent、 に 100 バイト、longestに 4 バイト、curlenおよび に 4 バイトがあります (32 ビット整数を想定しています。64maxlenの場合もありますが、コンパイラによって異なります)。current[123]はあなたにそれをさせます - そしてそれはあなたが書いたものを の代わりに置きますlongest[23]. (通常、繰り返しますが、これは技術的に未定義の動作であるため、これが発生する保証はありません。)

あなたの問題は、mygetlineあなたが設定した行ですs[i] = '\0';。問題は、i配列よりも大きくなったことです。そのprintf("i = %d\n", i);行の直前に、i = 123 が表示されます。最後の行は最大の行ほど大きくないため、上書きしlongestたくないデータを上書きしています。

これを修正する方法はたくさんあります。つまり、何かを '\0' に設定するときは、i <= limit - 1. (これを行うには、行を行の上に移動し、s[i] = '\0'行を超えないようにするwhile !EOFためだけに設定しs[limit - 1]ます。また、if ステートメントに {} を追加する必要があります。一般的に、追加するのは良いポリシーです。 if または while ステートメントにそれらを追加します. これらは 1 行を占めますが、適切な場所にコーディングしていることを確認してください.) 命令は「最大文字列ではなく、最大長を取得する」ことです.

最初の 2 行で実際に 100 文字が表示されているとしたら、私は驚かれることでしょう。私の知る限りでは、122 が表示されているはずです。

于 2013-10-03T20:57:59.373 に答える