如何在 C 方法(字符数组)中使用指针

How to use pointers in C methods (char arrays)

我是 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]='[=10=]';
    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];.

这会导致第二个问题,即您要返回一个指向局部变量的指针。该指针在函数 returns 时失效,因此尝试使用它会触发 undefined behavior.

您应该改为动态分配内存:

char *text = malloc(1000);

所以函数returns时仍然有效。请记住,当您使用完内存后,您将需要 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;
}

问题的症结在于:存储这些字符的内存 space 在哪里?并且 - 谁可以在你的函数 returns 之后使用?具体来说,当你的函数再次被调用时会发生什么?

现在(忽略你有一个指针数组的事实),你正在 return 获取局部变量的地址,正如@dbush 指出的那样。这意味着:

  • 只有您的函数可以使用内存中的 space。
  • 离开该功能后将停止使用...
  • ...并且相同的地址可能会被编译器分配给程序中的其他用途。

@dbush 的建议是解决该问题的一种方法:您的函数可以在每次调用时分配内存,并返回指向该分配内存的指针。

另一种方法是更改​​函数的签名,以便由调用者提供内存 space,例如:

int read_entire_file(char* destination_buffer, const char *filename);

在这两种情况下,还有另一个问题,即 1000 个字符的限制(或者更确切地说,999 个字符和尾随的 '\0',它标志着字符串的结尾)。如果文件中有更多数据怎么办?也许您实际上也应该采用缓冲区的大小?:

int read_entire_file(char* destination_buffer, size_t buffer_size, const char *filename);

除了我建议的方法和@dbush 的建议之外,还有其他替代方法,但我的观点是,完成此任务的方法不止一种。


其他comments/issues:

  • 不要将文件名作为非成本指针(char*);让它 const char* filename 澄清你不能更改文件名。
  • fgetc() 是一种从文件中读取数据的相当慢的方法。考虑 fread()
    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    
  • 您必须确保您的 fopen() 文件成功;如果没有,return NULL(或终止程序)。您的 fgetc() 电话也是如此。
  • 您没有检查是否已到达缓冲区的边缘,因此可能会溢出缓冲区。
  • 将大量数据放在 stack 上不是一个好主意。
  • read() 是一个过于通用的名称;更加详细一些。此外,该名称可能与 Linux、MacOS 和其他类 Unix 操作系统上的 typical system call of the same name 冲突。