我写这个函数是从文件中读取一行:
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输出胡言乱语。为什么?
这个例子有一些错误:
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.
如果你的任务不是发明逐行读取函数,而只是逐行读取文件,你可以使用一个典型的代码片段,包括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);
}
提供一个可移植的通用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;
}