返回C语言字符串数组指针

3

我希望能够返回从文件中获取的字符串数组。文件的格式如下(第一行是单词数量,每一行都是一个单词)。在函数完成后,主要部分输出了一些奇怪的内容。我想要返回指向字符串数组的指针。请注意,使用打印的代码部分是用来检查我的程序。

以下是分配内存的函数:

char *generisiProstor(int n) {
    return (char*)malloc(n*sizeof(char[20])); 
}

这是一个从rijeci.txt获取单词并应返回指向包含这些单词的字符串数组的指针的函数:

char* ucitajRijeci(int n) {

    char  i;
    char *rijeci;

    static const char filename[] = "rijeci.txt";
    FILE *file;
    file = fopen(filename, "r");
    if (file != NULL)
    {
        char line[20];
        int n;
        fscanf(file, "%d", &n);
        rijeci = generisiProstor(n);
        if (rijeci == NULL) { 
            return NULL;
        }
        int i = -1;

        fgets(line, 20, file);        //skipping first line witch is integer and not needed
        while (fgets(line, 20, file) != NULL) 
        {
            printf("%s\n", line);            //normal output
            i++;
            strcpy(rijeci + i, line);
            printf("%s\n", rijeci + i);     //normal expected output
        }
        for (i = 0; i < n; i++) {
            printf("%s\n", rijeci + i);   //wrong output
        }
    }
    return rijeci;
}

主要

int main()
{
    static const char filename[] = "rijeci.txt";
    FILE *file;
    file = fopen(filename, "r");
    char *rijeci;
    int i;
    if (file != NULL)
    {
        char line[20];
        int n;
        fscanf(file, "%d", &n);
        rijeci = ucitajRijeci(n);
        printf("Here is the array: ");
        for (i = 0; i < n; i++) {
            printf("%s ", rijeci+i);  //wrong output
        }
    }
    return 0;
}

2
你只返回了一个长的字符数组,而不是一个字符数组的数组。 - Christian Gibbons
你正在覆盖最后一个字符串,并带有偏移量,返回了一个乱码字符串而不是一个字符串数组。 - dustinroepsch
为什么你要打开同一个文件两次?你可以在主函数中打开它,并将指向该文件的指针传递进去。 - Nick S
5个回答

2

在这里,您需要使用二维数组(char **而不是char *)。由于您正在返回2-d数组,因此必须将rijeci声明为char **rijeci;

  1. 两个函数的返回类型也应为char **
  2. rijeci + i更改为rijeci[i]
  3. 适当的代码缩进。

尝试使用此修改后的代码。 这将起作用:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* generisiProstor */

char **generisiProstor(int n)
{
    char **c; // making 2-d array
    c = (char **)malloc(n * sizeof(char *));
    for (int i = 0; i < n; i++)
    {
        c[i] = (char *)malloc(20 * sizeof(char));
    }
    return c;
}

/*  ucitajRijeci  */

char **ucitajRijeci(int n)
{

    char **rijeci; // change to char **

    static const char filename[] = "rijeci.txt";
    FILE *file;
    file = fopen(filename, "r");
    if (file != NULL)
    {
        char line[20];
        int n;
        fscanf(file, "%d", &n);
        rijeci = generisiProstor(n);
        if (rijeci == NULL)
        {
            return NULL;
        }
        int i = -1;

        fgets(line, 20, file); //skipping first line witch is integer and not needed
        while (fgets(line, 20, file) != NULL)
        {
            printf("%s\n", line); //normal output
            i++;
            strcpy(rijeci[i], line);
            printf("%s\n", rijeci[i]); //changed to rijeci[i]
        }
        for (i = 0; i < n; i++)
        {
            printf("%s\n", rijeci[i]); //changed to rijeci[i]
        }
    }
    return rijeci;
}

/*  main()  */

int main()

{

    static const char filename[] = "rijeci.txt";
    FILE *file;
    file = fopen(filename, "r");
    char **rijeci; // change to char **
    int i;
    if (file != NULL)
    {
        char line[20];
        int n;
        fscanf(file, "%d", &n);
        rijeci = ucitajRijeci(n);
        printf("Here is the array: ");
        for (i = 0; i < n; i++)
        {
            printf("%s ", rijeci[i]); //changed to rijeci[i]
        }
    }
    return 0;
}

1
如果你想返回一个大小为20的字符数组指针,你需要按照以下方式声明函数:
char (*generisiProstor(int n))[20]
{
    return malloc(n*sizeof(char[20]));
}

保存指向数组的指针的变量声明如下:

char (*rijeci)[20];

rijeci[i]char[20] 类型的,你可以在那里写入你的字符串。


1
你遇到的第一个问题在这里:


char *generisiProstor(int n) {

return (char*)malloc(n*sizeof(char[20])); 
}

你想要一个字符指针数组,但你返回了一个字符指针或者一个字符数组。
这部分应该是:
char **generisiProstor(int n) {

return (char**)malloc(n*sizeof(char[20])); 
}

同样的问题也出现在char *rijeci中,你声明它为字符串或字符指针。
你应该这样声明:char **rijeci(在这种情况下,你可能希望它是char *(rigeci[20])),这将是一个字符串数组。

如果我理解你的代码正确,另一个问题可能来自于这部分:

while (fgets(line, 20, file) != NULL) 
    {
        printf("%s\n", line);            //normal output
        i++;
        strcpy(rijeci + i, line);
        printf("%s\n", rijeci + i);     //normal expected output
    }

在代码的前面,你为n个单词分配了内存。在这里,你正在读取一行,并将其放入line中。因此,当你读取第一行时,i是0,但在复制之前你会将其递增,因此你的数组的第一个出现未设置,你正在将最后一个单词写入未分配的内存中。
这部分应该是:
while (fgets(line, 20, file) != NULL) 
    {
        printf("%s\n", line);            //normal output
        strcpy(rijeci + i, line);
        i++
        printf("%s\n", rijeci + i);     //normal expected output
    }

1
你知道数组字符串的定义吗?
我将给出它们在2011 C标准中的定义:

数组类型描述了一组具有特定成员对象类型(称为元素类型)的连续分配的非空对象。[...]

字符串是一个由字符序列组成的连续序列,以及包括第一个空字符。[...]

因此,数组是从完整对象类型派生的类型,但字符串不是一种类型,而是一种数据结构。
你很喜欢强制类型转换。你确定让编译器无缘由地相信你是一个好习惯吗?在重构时,建议优先使用sizeof expr而不是sizeof (TYPE),因为这样最初更难出错或失步。
建议阅读"Do I cast the result of malloc?"。

0

你已经分配了正确数量的内存,但需要更改使用方式。malloc()只能返回一个“平坦”的字符数组,因此generisiProstor()的返回值是整个数组中第一个字符的简单指针。

最初它能够工作的原因是每个字符串都覆盖了前一个字符串的尾部,因此在读取循环期间进行打印输出时,它们显示正确。但即使如此,当你完成读取时,rijeci数组的有效负载也完全损坏了。

一种可能的解决方案是使用结构体来保存你的单词:

struct Rijec
{
    char rijec[20];
};

然后将 generisiProstor(int n) 更改为以下内容:

struct Rijeci *generisiProstor(int n)
{
    return malloc(n * sizeof(struct Rijec)); 
}

请注意,在 C 语言中不需要使用 cast,实际上应该避免使用
然后,您需要将 ucitajRijeci() 的顶部更改为以下内容:
struct Rijec *ucitajRijeci(int n)
{
    struct Rijec *rijeci;
    ...

无论何时您使用 rijeci + i 的情况,都请将其更改为 rijeci[i].rijec
这样做的最终结果是,当您使用 i 索引 rijeci 数组中的单词时,偏移量将会正确。

我知道如何使用结构体来完成,但我需要返回指向数组的指针。谢谢你的解释。 - bakero98
啊。在这种情况下,你有两个选择。继续现状,并明确地将访问rijeci数组的所有索引乘以20:例如strcpy(rijeci + i * 20, line);或使用anoopknr修改后的解决方案。这两种方法都有其优点和缺点。 - dgnuff

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接