Coding a getline() implementation - Valgrind errors

对着背影说爱祢 提交于 2019-12-18 09:42:05

问题


I have to recode an implementation of the getline() function, but using the file descriptor of the file and not a FILE *. I am only allowed to use malloc() and free(), along with 5 functions being 25 lines long at most. I think I've done correctly the project although I am a beginner in C and my code isn't probably good.

When I run it, it works fine, but valgrind shows that I definetely lost x bytes, x depending of the file length and the READ_SIZE (macro defined in the header).

According to valgrind's --leak-check=full, I have a memory leak in the str_realloc_cat function, when I malloc dest. I tried but couldn't find where should I free / do something else?

Here below is my code:

char *get_next_line(const int fd)
{
  static char   *remaining = "";
  char          *buffer;
  ssize_t       cread;
  size_t       i;

  i = 0;
  if (remaining == NULL)
    return (NULL);
  if ((buffer = malloc(SOF(char) * READ_SIZE + 1)) == NULL ||
      (cread = read(fd, buffer, READ_SIZE)) < 0)
        return (NULL);
  buffer[cread] = 0;
  remaining = str_realloc_cat(remaining, buffer);
  while (remaining[i])
    {
      if (remaining[i] == 10)
        {
          remaining[i] = 0;
          buffer = str_create_cpy(remaining);
          remaining = remaining + i + 1;
          return (buffer);
        }
      i++;
    }
  return (check_eof(fd, buffer, remaining, cread));
}

char *str_realloc_cat(char *rem, char *buf)
{
  size_t i;
  size_t dest_i;
  char   *dest;

  i = (dest_i = 0);
  if ((dest = malloc(SOF(char) * (str_len(rem) + str_len(buf) + 1))) == NULL)
    return (NULL);
  while (rem[i])
    {
      dest[dest_i] = rem[i];
      dest_i++;
      i++;
    }
  i = 0;
  while (buf[i])
    {
      dest[dest_i] = buf[i];
      dest_i++;
      i++;
    }
  dest[dest_i] = 0;
  free(buf);
  return (dest);
}

char    *check_eof(const int fd, char *buffer, char *remaining, ssize_t cread)
{
  if (cread == 0)
    return (NULL);
  if (cread < READ_SIZE)
    {
      buffer = remaining;
      remaining = NULL;
      return (buffer);
    }
  return (get_next_line(fd));
}

char *str_create_cpy(const char *src)
{
  char *dest;
  size_t i;

  i = 0;
  if ((dest = malloc(sizeof(char) * str_len(src) + 1)) == NULL)
    return (NULL);
  while (src[i])
    {
      dest[i] = src[i];
      i++;
    }
  dest[i] = 0;
  return (dest);
}

int str_len(const char *str)
{
  size_t i;

  i = 0;
  while (str[i])
    i++;
  return (i);
}

And a main functon if you would like to test:

#define SOF(x) sizeof(x) // Why in the comments

int main(int ac, char **av)
{
  int  fd;
  char *s;

  UNUSED(ac);
  if (!av[1])
    return 1;
  fd = open(av[1], O_RDONLY);
  while ((s = get_next_line(fd)))
    {
      printf("%s\n", s);
      free(s);
    }
  close(fd);
}

回答1:


Your algorithm is bad:

  1. You keep the buffer in a allocate memory
  2. You don't use a structure to regroup your variable
  3. You use magic number remaining[i] == 10
  4. You use recursive you can stack overflow return get_next_line(fd). Never mind, I didn't read well you have a tail recursive, just be sure to have the optimization on your compile for it.
  5. You have Spaghetti code.
  6. etc.

You should rewrite your whole function with a better logic first use this structure:

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

#define GNL_SIZE 4096

struct gnl_context {
  char buffer[GNL_SIZE];
  size_t i;
  size_t read;
};

char *get_next_line_r(int fd, struct gnl_context *gnl_context);
char *get_next_line(int fd);

static char *read_buffer(struct gnl_context *gnl_context, char *str,
                         size_t *size) {
  size_t i = gnl_context->i;
  while (i < gnl_context->read && gnl_context->buffer[i] != '\n') {
    i++;
  }
  size_t j = i - gnl_context->i;

  char *ret = realloc(str, *size + j + 1);
  if (ret == NULL) {
    return NULL;
  }
  memcpy(ret + *size, gnl_context->buffer + gnl_context->i, j);
  *size += j;
  ret[*size] = '\0';
  gnl_context->i = i;

  return ret;
}

char *get_next_line_r(int fd, struct gnl_context *gnl_context) {
  char *str = NULL;
  size_t size = 0;
loop:
  if (gnl_context->i == gnl_context->read) {
    ssize_t ret = read(fd, gnl_context->buffer, GNL_SIZE);
    if (ret <= 0) {
      return str;
    }
    gnl_context->read = (size_t)ret;
    gnl_context->i = 0;
  }

  char *tmp = read_buffer(gnl_context, str, &size);
  if (tmp == NULL) {
    return str;
  }
  if (gnl_context->i != gnl_context->read) {
    gnl_context->i++;
    return tmp;
  }
  str = tmp;
  goto loop;
}

char *get_next_line(int fd) {
  static struct gnl_context gnl_context;
  return get_next_line_r(fd, &gnl_context);
}

int main(void) {
  char *str;
  while ((str = get_next_line(0)) != NULL) {
    printf("%s\n", str);
    free(str);
  }
}



回答2:


I am concerned about this line:

remaining = remaining + i + 1;

remaining is a pointer to the allocated buffer. On this line, you destroy it, which means that you cannot free() it anymore.



来源:https://stackoverflow.com/questions/41655671/coding-a-getline-implementation-valgrind-errors

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!