2

私は C 文字列とストリームをいじって、それらをよりよく理解しています。入力ファイルから固定サイズのデータ​​ ブロックをバッファーに読み取り、バッファーの内容を中間ストレージに格納するこのテスト プログラムがあります (この場合、ストレージに 3 つの異なる "読み取り" を格納できるようにします)。次に、読み取った文字列と中間ストレージ内の文字列の 1 つを出力ファイルに書き込みます。

これに関する注意: 各反復では、中間ストレージの最初の 2 つの位置を使用し、2 番目の「格納された文字列」をファイルに書き込みます。

コード:

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

#define SIZE 3
#define BUFFER_SIZE 5

int main(int argc, char** argv) {
  FILE* local_stream_test = fopen("LOCAL_INPUT_FILE","r");
  FILE* local_output_test = fopen("LOCAL_OUTPUT_TEST","w");

  if(!local_stream_test) {
    puts("!INPUT FILE");
    return EXIT_FAILURE;
  }
  if(!local_output_test) {
    puts("!OUTPUT FILE");
    return EXIT_FAILURE;
  }
  char my_buffer[BUFFER_SIZE];
  char test[SIZE];
  char* test2[SIZE];
  memset(my_buffer,0,sizeof(my_buffer));
  memset(test,0,sizeof(test));
  memset(test2,0,sizeof(test2));

  int read = fread( my_buffer, sizeof(my_buffer[0]), sizeof(my_buffer)/sizeof(my_buffer[0]), local_stream_test );

   printf("FIRST READ TEST: %d\n",read);
   printf("\tMY_BUFFER, SIZEOF: %lu, STRLEN: %lu\n",sizeof(my_buffer),strlen(my_buffer));

   fwrite(my_buffer,sizeof(my_buffer[0]),/*strlen(aux)*/ read,local_output_test);
   char* aux_test = strdup(my_buffer);
   printf("\tAUX_TEST STRLEN: %lu, ## %s\n",strlen(aux_test), aux_test);
   free(aux_test);
   aux_test = NULL;

   while(read > 0) {
     if(feof(local_stream)) {
       puts("BYE");
       break;
     }
     read = fread( my_buffer, sizeof(my_buffer[0]), sizeof(my_buffer)/sizeof(my_buffer[0]), local_stream_test );
     aux_test = strdup(my_buffer);

     if(!aux_test) {
       puts("!AUX_TEST");
       break;
     }


     printf("READ TEST: %d\n",read);
     printf("\tMY_BUFFER, SIZEOF: %lu, STRLEN: %lu\n",sizeof(my_buffer),strlen(my_buffer));
     printf("\tAUX_TEST, SIZEOF: %lu, STRLEN: %lu ** SIZEOF *AUX_TEST: %lu, SIZEOF AUX_TEST[0]: %lu\n",sizeof(aux_test),strlen(aux_test),sizeof(*aux_test),sizeof(aux_test[0]));

     fwrite(aux_test,sizeof(aux[0]),/*strlen(aux)*/ read,local_output_test);

     printf("** AUX_TEST: %s\n",aux_test);
     test2[0] = aux_test;
     test2[1] = aux_test;
     test2[1][3] = toupper(test2[1][3]);

     fwrite(test2[1],sizeof(test2[1][0]),read,local_output_test);

     printf("\n** TEST2[0] SIZEOF: %lu, STRLEN: %lu, TEST2[0]: %s\n",sizeof(test2[0]),strlen(test2[0]),test2[0]);
     printf("\n** TEST2[1] SIZEOF: %lu, STRLEN: %lu, TEST2[1]: %s\n",sizeof(test2[1]),strlen(test2[1]),test2[1]);

     strcpy(test2[1],aux_test);
     printf("** COPIED TEST2[1]: %s\n",test2[1]);
     free(aux_test);
     aux_test = NULL;
     puts("*******************************************");
  }
  return EXIT_SUCCESS;
}

入力ファイル:

converts a byte string to a floating point value
converts a byte string to an integer value
converts a byte string to an integer value

文字列を出力すると、2 回目の読み取り後に末尾に余分なジャンク値が表示されます。stdoutファイルからの 1 回目、2 回目、3 回目の読み取りの出力は次のとおりです。

FIRST READ TEST: 5
    MY_BUFFER, SIZEOF: 5, STRLEN: 5
    AUX_TEST STRLEN: 5, ## conve
READ TEST: 5
    MY_BUFFER, SIZEOF: 5, STRLEN: 5
    AUX_TEST, SIZEOF: 4, STRLEN: 5 ** SIZEOF *AUX_TEST: 1, SIZEOF AUX_TEST[0]: 1

** AUX_TEST: rts a

** TEST2[0] SIZEOF: 4, STRLEN: 5, TEST2[0]: rts a

** TEST2[1] SIZEOF: 4, STRLEN: 5, TEST2[1]: rts a
** COPIED TEST2[1]: rts a

*******************************************
READ TEST: 5
    MY_BUFFER, SIZEOF: 5, STRLEN: 13
    AUX_TEST, SIZEOF: 4, STRLEN: 13 ** SIZEOF *AUX_TEST: 1, SIZEOF AUX_TEST[0]: 1

** AUX_TEST:  byte▒▒▒▒

** TEST2[0] SIZEOF: 4, STRLEN: 13, TEST2[0]:  byTe▒▒▒▒


** TEST2[1] SIZEOF: 4, STRLEN: 13, TEST2[1]:  byTe▒▒▒▒

** COPIED TEST2[1]:  byTe▒▒▒▒

私を悩ませているのは、ジャンク値が表示され始めると、文字列の長さがファイルから読み取られたバイトよりも大きくなるという事実です:135. で遊んでみましたが、一度にファイルを読み取るのに十分なサイズでない限り、BUFFER_SIZE印刷するときに常にジャンク値を取得します。stdout

たとえば、BUFFER_SIZEequals to500の場合、これは の出力ですstdout

FIRST READ TEST: 135
    MY_BUFFER, SIZEOF: 300, STRLEN: 135
    AUX_TEST STRLEN: 135, ## converts a byte string to a floating point value
       converts a byte string to an integer value
        converts a byte string to an integer value

 BYE

そして生成された出力ファイル:

BUFFER_SIZE = 5

converts arts a byte byTe stri stRing tong To a fl a FloatinoatIng poig pOint vant Value
clue
converonvErts a ts A byte bytE strinstrIng to g tO an inan IntegertegEr valu vaLue
cone
cOnvertsverTs a by a Byte stte String rinG to anto An inte inTeger vger value
aluE

BUFFER_SIZE = 500:入力ファイルと同じ。

それで、私は範囲外のメモリにアクセスしていますよね?しかしここで?この問題の原因がわかりません (C 文字列の操作方法を誤解している可能性が高いです)。

PS:

おそらく私の問題は、文字列の最後に NULL マークを追加するのを忘れたことですやっている:

 test2[0] = aux_test;
 test2[0][ strlen(aux_test)+1 ] = '\0';

 /* OR THIS */
 test2[0][read+1] = '\0';

同じ結果になります。

4

2 に答える 2

4

あなたの問題の一部は、配列の境界外を読んでいて、fread()確かに何も null で終了しないことです。

例えば:

printf("\tMY_BUFFER, SIZEOF: %lu, STRLEN: %lu\n",sizeof(my_buffer),strlen(my_buffer));

5 バイトのデータをサイズ 5 バイトの配列に読み込みます。レポートstrlen()5; 配列の末尾を超えた最初のバイトがたまたまゼロバイトだったのは幸運でしたが、配列の外側にあったため、その時点で未定義の動作を呼び出しました (期待していた答えが得られたとしても)。

ループでは、最初の繰り返しでtoupper()大文字と小文字が変換されますが、空白は変更されません。 test2[0]両方ともtest2[1]同じ文字列を指しているため、toupper()が何かを行うと、両方のポインターが指す値に影響します。

ジャンク値が「現れる」場合、 の末尾の後にゼロ以外のバイトをデータに挿入し、 はゼロ バイトに達するまでそれらのゼロ以外のバイトを読み取りますmy_bufferstrlen()したがって、問題はすべて、割り当てられた長さの範囲内で文字バッファーが null で終了することを保証しないことが原因です。未定義の動作を呼び出すと、奇妙なことが起こる可能性があります。

printf("<<%.*s>>\n", read, my_buffer);を使用すると、読み取られたデータのバイトのみが出力されることに注意してください。


あなたは次のことを尋ねます:

test2[0] = aux_test;
test2[0][ strlen(aux_test)+1 ] = '\0';
/* OR THIS */
test2[0][read+1] = '\0';

提供されたものの終わりを超えて 1 バイトにアクセスしています。定義により、となる最初の数値をstrlen(str)返します。したがって、書き込むときは、文字列の最初の null の末尾を超えて 1 バイトを書き込みます。5バイトを読み取ったと仮定すると、割り当ては を上書きしますが、読み取られたデータの最後のバイトは にあるため、変更されていません(許可されているかどうかは明確ではありません)。lenstr[len] == '\0'test2[0][[strlen(aux_test)+1] = '\0';test2[0][read+1] = '\0';test2[0][6]test2[0][4]test2[0][5]

test2[0][strlen(aux_test)] = '\0';  // No-op, but safe
test2[0][read] = '\0';              // If you left enough space, null terminates the input
于 2013-11-04T15:30:44.320 に答える
1

どの場合でも、ガベージは 5 番目のビットの後に始まります#define BUFFER_SIZE 5。値を読み取った後、次のように、'\0' を使用して文字列の有効な長さ (5) を null で終了した場合:

my_buffer[strlen(my_buffer)-1]=0;//または、その長さがわかっているため、my_buffer[4]=0;

これにより、バッファの内容が正当な文字列になります。実際に問題を解決するには、最初に my_buffer をより多くのスペースで作成し、常に '\0' で終了します。

于 2013-11-04T15:37:41.537 に答える