2

我正在使用 C 字符串和流来更好地理解它们。我有这个测试程序从输入文件读取固定大小的数据块到缓冲区,将缓冲区内容存储在中间存储中(在这种情况下,我希望存储能够存储三个不同的“读取”)和然后将读取的字符串和中间存储中的字符串之一写入输出文件。

关于这一点的说明:在每次迭代中,我只使用中间存储的两个第一个位置,并将第二个“存储的字符串”写入文件。

编码:

#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

打印字符串时,我在第二次读取后会在结尾处得到额外的垃圾值。这是stdout文件中第一次、第二次和第三次读取的输出:

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_SIZE等于 时500,这是 中的输出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

并且生成的输出文件:

缓冲区大小 = 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()case-convert 一个空白,这不会改变它。 test2[0]并且test2[1]都指向同一个字符串,所以如果toupper()做任何事情,它会影响这两个指针指向的值。

当垃圾值“出现”时,您已在 结束后将非零字节放入数据中my_buffer,并strlen()读取这些非零字节,直到达到零字节。所以,问题都是由于没有确保您的字符缓冲区在分配的长度内以空结尾。当您调用未定义的行为时,可能会发生奇怪的事情。

请注意,如果您使用printf("<<%.*s>>\n", read, my_buffer);,您将只打印读取的数据字节。


你问:

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

您正在访问超出所提供内容末尾的一个字节。根据定义,strlen(str)返回第一个len这样的数字str[len] == '\0'。因此,当您写入时test2[0][[strlen(aux_test)+1] = '\0';,您正在写入超出字符串中第一个 null 结尾的一个字节。test2[0][read+1] = '\0';假设您刚刚读取了 5 个字节,则分配会覆盖,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) 的合法长度,如下所示:

my_buffer[strlen(my_buffer)-1]=0;//或者因为你知道它的长度,my_buffer[4]=0;

这将使我的缓冲区的内容成为合法字符串。要真正解决问题,首先创建具有更多空间的 my_buffer,然后始终以 '\0' 终止。

于 2013-11-04T15:37:41.537 回答