我是C的新手,对指针和字符数组感到困惑…
我写了:
char *read(char *filename) {
char *text[1000];
FILE *inputFile = fopen(filename, "r");
int i=0;
while (feof(inputFile)) {
text[i++] = fgetc(inputFile);
}
text[i]=' ';
fclose(inputFile);
return text;
}
我的目标是传入我想要打开的文件的名称,打开它,并将其中的所有单词分配到一个字符数组(char text[]
)中。我不断得到以下错误:
expression which evaluates to zero treated as a null pointer constant of type 'char *' [-Wnon-literal-null-conversion]
incompatible pointer types returning 'char *[1000]' from a function with result type 'char *' [-Wincompatible-pointer-types]
寻求建议
这里有两个问题。
首先,你的数组被声明为char *text[1000];
,即指针指向char的数组,因此每个text[i]
是一个指针,而不是一个字符。你想要的可能是char text[1000];
。
这会导致第二个问题,即返回指向局部变量的指针。当函数返回时,该指针将失效,因此尝试使用它将触发未定义行为。
你应该动态分配内存:
char *text = malloc(1000);
所以函数返回时仍将有效。请记住,当您完成使用内存时,您需要free
。
或者,您可以将要填充的缓冲区作为参数传递给函数:
void read(char *filename, char *text) {
您希望用从文件中读取的字符填充字符数组。如果是这样,那么至少需要声明一个像
这样的字符数组。char text[1000];
而不是写
时指向字符的指针数组char *text[1000];
然而,声明的数组具有自动存储持续时间,并且在退出函数后将不再存活。所以返回的指针是无效的。
应该动态分配数组,例如
char *text = malloc( 1000 );
您还需要检查文件是否已成功打开。
while循环中的条件
while (feof(inputFile)) {
text[i++] = fgetc(inputFile);
}
可以在调用fgets之后出现。因此,数组可以存储一个无效的字符。
你应该写
for ( int value; i + 1 < 1000 && ( value = fgetc( inputFile ) ) != EOF; i++ )
{
text[i] = value;
}
问题的关键是:存储这些字符的内存空间在哪里?和-谁可以使用后,你的函数返回?具体来说,当函数再次被调用时会发生什么?
现在(忽略你有一个指针数组的事实),你返回一个局部变量的地址,正如@dbush指出的。这意味着:
- 只有你的函数可以使用内存空间
- 当您离开该功能时,它将停止可用…
- …并且编译器可能会为程序中的其他用途分配相同的地址。
@dbush的建议是解决这个问题的一种方法:你的函数可以在每次调用时分配内存,并返回一个指向分配内存的指针。
另一种方法是更改函数的签名,以便由调用者提供内存空间,例如:
int read_entire_file(char* destination_buffer, const char *filename);
在这两种情况下,都有另一个问题,那就是1000个字符的限制(或者更确切地说,999个字符和后面的' ',它标志着字符串的结束)。如果文件中有更多的数据呢?也许你应该把缓冲区的大小也取下来?:
int read_entire_file(char* destination_buffer, size_t buffer_size, const char *filename);
除了我的建议和@dbush的建议,还有其他的选择,但我的观点是,有不止一种方法来完成这项任务。
其他评论/问题:
- 不要将文件名作为非开销指针(
char*
);让它成为const char* filename
来澄清你不允许改变文件名。 fgetc()
是从文件中读取数据的一种相当慢的方式。考虑fread()
:size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
- 你必须确保你的
fopen()
文件成功;如果没有,返回NULL
(或者终止程序)。你的fgetc()
呼叫也是如此。 - 你没有检查你是否已经到达缓冲区的边缘,因此可能溢出缓冲区。 将大量数据放在堆栈上不是一个好主意。
read()
是一个过于通用的名称;更具体一些。此外,该名称可能与Linux, MacOS和其他类unix操作系统上的典型同名系统调用冲突。