如何在不泄漏内存的情况下将 fgets() 读取的字符串存储在变量中?

How might I store in a variable the string read by fgets() without leaking memory?

我想要下面的代码来完成将它读取的文件的内容存储在一个变量中 content 没有泄漏内存:

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

char* concat(const char *s1, const char *s2)
{
    const size_t len1 = strlen(s1);
    const size_t len2 = strlen(s2);
    char *result = malloc(len1 + len2 + 1); 
    memcpy(result, s1, len1);
    memcpy(result + len1, s2, len2 + 1); 
    return result;
}

int main() {

char path[] = "/home/jim/trackers_best.txt";
FILE *archivo = fopen(path,"r");
char line[200];
char * content = "";


if (archivo == NULL)
        exit(1);
else {

fseek(archivo, 0L, SEEK_END);
  int sz = ftell(archivo);
  fseek(archivo, 0L, SEEK_SET);
  if (sz < pow(10, 7) ) {
        printf("\nThe content of %s is:\n", path);
        while (feof(archivo) == 0) {
        fgets(line, 200, archivo); //reads one line at a time - comment out the while loop to find out
        content = concat(content, line);
       //free(content);//No leaks but no content either       
            }
          }
  else {
      puts("File take up more than 10 MBs, I refuse to read it.");
      fclose(archivo);
      exit(0);
        }
      }
fclose(archivo);
puts(content); 
free(content);
return 0;   
}

与使用 fread() 的代码非常相似:

#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include <sys/types.h> 

int main() {
 
int count;  
char path[] = "/home/jim/trackers_best.txt";
FILE *archivo = fopen(path,"rb");
    
 if (archivo == NULL) {
     puts("Does not exist.");
     exit(1); 
       }
 else {
  
  fseek(archivo, 0L, SEEK_END);
  int sz = ftell(archivo);
  char contenido[sz];
  fseek(archivo, 0L, SEEK_SET);
  if (sz < pow(10, 7) ) {
    count = fread(&contenido, 10 * sizeof(char), sz, archivo);//reads 10 characters at a time.
    }
  else {
      puts("File take up more than 10 MBs, I refuse to read it.");
        }
  fclose(archivo);
  // Printing data to check validity
  printf("Data read from file: %s \n", contenido);
  printf("Elements read: %i, read %li byte at a time.\n", count, 10 * sizeof(char));
  
 }
return 0;   
}

第一个代码片段泄漏了内存,valgrind 证明了这一点:

...
total heap usage: 44 allocs, 4 frees, 23,614 bytes allocated
...
==404853== LEAK SUMMARY:
==404853==    definitely lost: 17,198 bytes in 40 blocks
...

当我试图通过在 while 循环中执行 free() 来防止此类泄漏时,我没有在 content 中存储任何值。如何在不泄漏内存的情况下存储 fgets 读取的字符串?

您知道自己需要做什么,但没有做对

    content = concat(content, line);
   //free(content);//No leaks but no content either 

您需要保留旧内容指针,以便在创建新内容指针后将其删除

    char *oldc = content;
    content = concat(content, line);
    free(oldc);

当您在 second 次调用 concat 时,first 调用的值被 malloc 打电话。那就是内存泄漏。

我假设您希望 content 是一个长字符串,其中包含来自 所有 行的数据。

您想在 concat 中使用 realloc(对比 malloc),只需将 s2 附加到放大的 s1.

使用此修改后的方案,而不是 char *content = "";,您需要:char *content = NULL;

此外,不要使用 feof

而且,ftell returns long 不是 int。而且,最好不要使用pow与极限进行比较。

这里是重构后的代码(我已经编译了但是没有测试过):

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

char *
concat(char *s1, const char *s2)
{
    size_t len1;
    size_t len2;

    if (s1 != NULL)
        len1 = strlen(s1);
    else
        len1 = 0;

    len2 = strlen(s2);

    s1 = realloc(s1,len1 + len2 + 1);
    if (s1 == NULL) {
        perror("realloc");
        exit(1);
    }

    memcpy(s1 + len1, s2, len2 + 1);

    return s1;
}

int
main(void)
{

    char path[] = "/home/jim/trackers_best.txt";
    FILE *archivo = fopen(path, "r");
    char line[200];
#if 0
    char *content = "";
#else
    char *content = NULL;
#endif

    if (archivo == NULL)
        exit(1);

    fseek(archivo, 0L, SEEK_END);
    long sz = ftell(archivo);
    fseek(archivo, 0L, SEEK_SET);

    if (sz < (10 * 1024 * 1024)) {
        printf("\nThe content of %s is:\n", path);

        while (fgets(line, sizeof(line), archivo) != NULL)
            content = concat(content, line);
    }
    else {
        puts("File take up more than 10 MBs, I refuse to read it.");
        fclose(archivo);
        exit(0);
    }

    fclose(archivo);

    if (content != NULL)
        puts(content);

    free(content);

    return 0;
}