我写这个函数是从文件中读取一行:

const char *readLine(FILE *file) {

    if (file == NULL) {
        printf("Error: file pointer is null.");
        exit(1);
    }

    int maximumLineLength = 128;
    char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);

    if (lineBuffer == NULL) {
        printf("Error allocating memory for line buffer.");
        exit(1);
    }

    char ch = getc(file);
    int count = 0;

    while ((ch != '\n') && (ch != EOF)) {
        if (count == maximumLineLength) {
            maximumLineLength += 128;
            lineBuffer = realloc(lineBuffer, maximumLineLength);
            if (lineBuffer == NULL) {
                printf("Error reallocating space for line buffer.");
                exit(1);
            }
        }
        lineBuffer[count] = ch;
        count++;

        ch = getc(file);
    }

    lineBuffer[count] = '\0';
    char line[count + 1];
    strncpy(line, lineBuffer, (count + 1));
    free(lineBuffer);
    const char *constLine = line;
    return constLine;
}

该函数正确地读取文件,使用printf我看到constLine字符串也被正确读取。

然而,如果我像这样使用函数:

while (!feof(myFile)) {
    const char *line = readLine(myFile);
    printf("%s\n", line);
}

Printf输出胡言乱语。为什么?


当前回答

readLine()返回指向局部变量的指针,这会导致未定义的行为。

你可以:

在调用函数中创建变量,并将其地址传递给readLine() 使用malloc()为行分配内存——在这种情况下,行将是持久的 使用全局变量,尽管这通常是一个不好的做法

其他回答

这个例子有一些错误:

you forgot to add \n to your printfs. Also error messages should go to stderr i.e. fprintf(stderr, .... (not a biggy but) consider using fgetc() rather than getc(). getc() is a macro, fgetc() is a proper function getc() returns an int so ch should be declared as an int. This is important since the comparison with EOF will be handled correctly. Some 8 bit character sets use 0xFF as a valid character (ISO-LATIN-1 would be an example) and EOF which is -1, will be 0xFF if assigned to a char. There is a potential buffer overflow at the line lineBuffer[count] = '\0'; If the line is exactly 128 characters long, count is 128 at the point that gets executed. As others have pointed out, line is a locally declared array. You can't return a pointer to it. strncpy(count + 1) will copy at most count + 1 characters but will terminate if it hits '\0' Because you set lineBuffer[count] to '\0' you know it will never get to count + 1. However, if it did, it would not put a terminating '\0' on, so you need to do it. You often see something like the following: char buffer [BUFFER_SIZE]; strncpy(buffer, sourceString, BUFFER_SIZE - 1); buffer[BUFFER_SIZE - 1] = '\0'; if you malloc() a line to return (in place of your local char array), your return type should be char* - drop the const.

FILE* filePointer;
int bufferLength = 255;
char buffer[bufferLength]; /* not ISO 90 compatible */

filePointer = fopen("file.txt", "r");

while(fgets(buffer, bufferLength, filePointer)) {
    printf("%s\n", buffer);
}

fclose(filePointer);

如果你的任务不是发明逐行读取函数,而只是逐行读取文件,你可以使用一个典型的代码片段,包括getline()函数(参见手册页):

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE * fp;
    char * line = NULL;
    size_t len = 0;
    ssize_t read;

    fp = fopen("/etc/motd", "r");
    if (fp == NULL)
        exit(EXIT_FAILURE);

    while ((read = getline(&line, &len, fp)) != -1) {
        printf("Retrieved line of length %zu:\n", read);
        printf("%s", line);
    }

    fclose(fp);
    if (line)
        free(line);
    exit(EXIT_SUCCESS);
}
//open and get the file handle
FILE* fh;
fopen_s(&fh, filename, "r");

//check if file exists
if (fh == NULL){
    printf("file does not exists %s", filename);
    return 0;
}


//read line by line
const size_t line_size = 300;
char* line = malloc(line_size);
while (fgets(line, line_size, fh) != NULL)  {
    printf(line);
}
free(line);    // dont forget to free heap memory

提供一个可移植的通用getdelim函数,通过msvc, clang, gcc测试。

/*
 * An implementation conform IEEE Std 1003.1-2017:
 * https://pubs.opengroup.org/onlinepubs/9699919799/functions/getdelim.html
 *
 * <nio.h>:
 * https://github.com/junjiemars/c/blob/c425bd0e49df35a2649327664d3f6cd610791996/src/posix/nio.h
 * <nio.c>:
 * https://github.com/junjiemars/c/blob/c425bd0e49df35a2649327664d3f6cd610791996/src/posix/nio.c
 *
*/

#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>


/*
 * LINE_MAX dependents on OS' implementations so check it first.
 * https://github.com/junjiemars/c/blob/c425bd0e49df35a2649327664d3f6cd610791996/src/posix/nlim_auto_check
 */
#define NM_LINE_MAX  4096       /* Linux */

#if (MSVC)
typedef SSIZE_T  ssize_t;
#  if !defined(SSIZE_MAX)
#    define SSIZE_MAX  ((ssize_t)((size_t)((ssize_t)-1) >> 1))
#  endif
#endif


ssize_t getdelim(char **restrict lineptr, size_t *restrict n, int delimiter,
                 FILE *restrict stream);

#if defined(getline)
#  undef getline
#endif
#define getline(lp, n, f)  getdelim((lp), (n), 0x0a, (f))


ssize_t
getdelim(char **restrict lineptr, size_t *restrict n, int delimiter,
         FILE *restrict stream)
{
    int       c;
    char     *p, *p1;
    ssize_t   len;

  if (NULL == lineptr || NULL == n || NULL == stream
      || (UCHAR_MAX < delimiter || delimiter < 0))
    {
      errno = EINVAL;
      return EOF;
    }

  if (feof(stream) || ferror(stream))
    {
      return EOF;
    }

    if (0 == *lineptr)
    {
      if (0 == *n)
        {
          *n = NM_LINE_MAX;
        }

      *lineptr = malloc(*n);
      if (0 == *lineptr)
        {
          return EOF;
        }
    }

  p = *lineptr;
  len = 0;

    while (EOF != (c = fgetc(stream)))
    {
      if (SSIZE_MAX == (ssize_t) len)
        {
          errno = EOVERFLOW;
          return EOF;
        }

      if ((size_t) len == (*n - 1))
        {
          *n <<= 1;
          p1 = realloc(*lineptr, *n);
          if (0 == p1)
            {
              return EOF;
            }
          *lineptr = p1;
          p = p1 + len;
        }
      *p++ = (char) c;
      len++;

      if (c == delimiter)
        {
          break;
        }
    }

  if (ferror(stream))
    {
      return EOF;
    }

  *p = 0;
    return len;
}



int
main(void)
{
  FILE     *fp;
  char     *line  =  NULL;
  size_t    len   =  0;
  ssize_t   read;

  fp = fopen("/some-file", "r");
  if (fp == NULL)
    exit(1);
  while ((read = getline(&line, &len, fp)) != -1) {
    printf("Retrieved line of length %zu :\n", read);
    printf("%s", line);
  }
  if (ferror(fp)) {
    /* handle error */
  }
  free(line);
  fclose(fp);

  return 0;

}