【问题标题】:C: working with void pointer arraysC:使用 void 指针数组
【发布时间】:2023-03-04 05:31:01
【问题描述】:

我是 C 的新手,但在使用和理解指针方面仍然很薄弱——尤其是 void 指针。我正在尝试编写一个从文件加载数据并将这些数据存储在 void 指针数组中的函数,这样数组的每个元素(在本例中)都具有来自文件该行的字符串。我怀疑代码有几个问题:

  1. 我不确定我是否正确使用 *voidArray[] 作为函数的参数之一。
  2. 我不确定 strcpy() 是否是将行缓冲区的内容复制到相关数组元素的好方法。
  3. 我不知道 strcpy() 的目标(即 void 指针数组中的相关元素)的正确语法是什么。

可能还有其他错误,但这是我非常不确定的三个问题。

这是我的功能:

void *readData(void *voidArray[], const char *filename, int lines) {
    FILE *stream = fopen(filename, "r");
    if (stream == NULL) {
        perror("Error loading file");
    return 1;
    }
    char lineBuffer[BUFFER_SIZE];

int i = 0;
    while(!feof(stream)) {
        while(fgets(lineBuffer, sizeof(lineBuffer), stream)) {
            // *voidArray[i] = malloc(strlen(lineBuffer) + 1);     // probably not what I want
            strcpy(*voidArray[i], lineBuffer);
            i++;
        }
    }

    fclose(stream);
}

...这是 main() 的开头,其中(再次)我不确定声明(和初始化?)数组的正确语法是什么:

int main(void)
{
    int lines = 20;
    void *varray[lines];
    // varray = malloc(sizeof(char *) * lines);     // probably not what I want
    readData(varray[lines], FILENAME, lines);       // FILENAME declared earlier

非常感谢一些建议的代码修复(特别是如果我完全错过了更合适的方法),但我觉得我需要更多的是对为什么的一个很好的解释建议是正确的。如果我能解决这个问题,我想——在最坏的情况下——我会很好地了解我仍然需要自学什么:指针。提前感谢您提供的任何帮助或评论,以及您耐心阅读本文。


编辑:Joachim Pileborg 的回答有所帮助,但我仍然缺少一些基本的东西(而且可能很明显)。这是我修改后的函数:

void *loadData(void *voidArray, const char *filename, int lines) {
    FILE *stream = fopen(filename, "r");
    if (stream == NULL) {
        perror("Error loading file");
    }
    char lineBuffer[BUFFER_SIZE];

    int i = 0;
    while(fgets(lineBuffer, sizeof(lineBuffer), stream)) {
        strcpy((voidArray+i), lineBuffer);
        printf("voidArray: %s\n", (char *)(voidArray+i));
        i++;
    }

    for (i = 0; i < lines; i++) {
        printf("array element %d: %s\n", i, (char *)(voidArray+i));
    }

    fclose(stream);
}

这是我的两个测试的结果:

voidArray: Good
voidArray: morning
voidArray: everyone
array element 0: Gmeveryone
array element 1: meveryone
array element 2: everyone

同样,我可能遗漏了一些微不足道的东西。我想要做的是让 varray 由 void 指针组成,每个指针都指向从外部文件读取的一个对象(对于这个例子,一个字符串)。我知道我在使用“varray+i”时做错了什么,但我不知道我实际上应该做什么。

【问题讨论】:

  • 您不需要外部循环(while (!feof(...)) 循环)。实际上,如果在到达文件末尾之前读取文件有错误,它可能会导致无限循环。
  • 如果你存储字符,为什么不直接使用字符数组、字符指针呢?你不需要为此做空
  • 啊,我认为您对外部循环的冗余是正确的。谢谢!读取我为测试创建的任何文件都没有错误,将文件行读入缓冲区是我确信该功能按预期工作的少数几个方面之一。
  • Buella Gabor - 我还希望能够存储整数或双精度数,具体取决于文件(显然我需要相应地调整数据读取功能)。为了缩短我提供的代码量,我只使用了字符的大小写。
  • 在原版中,您需要voidArray[i] = malloc( strlen(X) + 1 ); strcpy( voidArray[i], X);。你用readData(varray, FILENAME, lines); 调用它。那很好。修改后的版本没有意义。因为您只传入一个缓冲区,但您似乎想在其中存储多个字符串。您看到的是在彼此顶部复制字符串的结果,每次在同一缓冲区中稍后从一个位置开始,这就是您的修订版本所做的。

标签: c arrays pointers void-pointers


【解决方案1】:

我一眼就看到了四个问题(除了我在评论中提到的那个):

  • 首先是您注释掉了分配,因此如果在调用函数时未分配指针,您将写入内存中看似随机的位置。

  • 第二个是您在调用strcpy 时使用了取消引用运算符。例如,如果您有一个 char 指针数组,则这将是单个 char 而不是指针。除此之外,您实际上无法取消引用 void 指针。

  • 第三个问题是您从不检查数组中的条目数,您只是不断循环读取并复制到数组中,而不考虑其大小。

  • 第四个问题是你调用这个函数的方式,你实际上并没有传递一个指针数组,而是一个指向索引lines的指针,这将超出数组。


作为不相关的旁注:请记住,如果 fgets 读取换行符,则该换行符将在缓冲区中。

【讨论】:

  • 非常感谢!如果可以的话,我有两个问题: 1. 我不确定我是否理解第三个问题。在 main 中,我为数组指定了特定数量的元素(即文件中的行数)。 2. 如何解决问题 4?
  • @jda 你将数组中元素的数量传递给函数(lines 参数),你必须让你永远不会读到更多(记住数组索引的范围从零开始)大小减一)。对于第二个问题,干脆不要使用数组索引。
  • 再次感谢您。我已经更新了我的问题,并希望稍微缩小它的范围。我的问题是我不知道在不使用数组索引的情况下应该使用哪种语法来访问数组中的各种 void 指针。我进行了尝试,但打印的测试表明我的索引不正确。我想如果我能理解这部分,我应该可以探索和调查而不会感到手足无措。
【解决方案2】:

您不能将内容存储在 void 指针中,也不能对它们进行索引。除非先强制转换它们,否则不能取消引用 void 指针。你不能用它们做指针算术。它们是后来添加的泛型的语法糖,因为人们为此目的使用 char*。

【讨论】:

  • void *x; *x = 5; 是非法的就是我的意思。相比之下,int *x; *x = 5; 是合法的
  • 谢谢你,v3ga!我实际上希望将其与快速排序联系起来。如果我废弃该函数并在使用(例如)char 指针时只在 main() 中编写所有内容,我可以使事情顺利进行。所以对我来说(我希望)下一步似乎是理解 void 指针及其正确的语法和逻辑。
  • @jda: void* 很简单,只有两条规则:任何数据指针都可以从void* 转换/返回,而不会强制转换或丢失信息。强制转换、复制和相等比较是void* 上唯一有用的操作。
  • @v3ga 你的回答太稀疏了,没有太多解释,尤其是对新手。
  • int *x; *x = 5; 不合法
猜你喜欢
  • 1970-01-01
  • 2015-02-07
  • 2012-02-07
  • 2020-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多