字符串数组:删除原始数组并返回副本

3
我刚开始学C语言,现在想了解如何分配字符串。
我正在尝试创建一个名为adding_string的函数。它接受零个或多个字符串的数组,并在最后一个位置包含空值。然后,它会复制该数组,使其大小增加1,然后将字符串str的副本附加到该数组中。最后,它会删除原始数组并返回新的副本。
这是我目前的代码:
char **adding_string(char **array, const char *str)
{

    size_t num = strlen(str) + 1;
    char *final= (char *)malloc(num);
    strncpy(final, str, num);
    free(array);
    //The above code would create a copy of the string "str".
    //Then it puts that into the array.
    //Not sure if free(array); would be the right method
    //Having issues with returning final too

    return final;
}

main函数中,你会有类似以下的内容:
char **array = NULL;
char **lines;

array = (char **)calloc(1, sizeof(char *));

array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");

for (lines = array; *lines; lines++)
{
    printf("%s\n", *lines);
}

我不确定是否应该使用free(array)方法来删除原始数组,而且我在返回新副本时遇到了问题。
当我尝试返回新副本时,出现以下情况:
warning: return from incompatible pointer type

由于以下原因:
return final;

你并不是复制数组,而只是str。并且final的类型是char*,这就是为什么它会说“不兼容的指针类型” - 函数应该返回char** - rain city
@rain city,这很有道理,谢谢。如果我错了,请纠正我,但要修复这个问题,我必须将str更改为数组,然后使用strncat将两者相加。接下来,我会返回char **,这将是数组。 - An Unsullied
为什么*lines应该变成null? - Quassel Kasper
@QuasselKasper 是一种模拟 cstrings 如何通过使用 '\0' 终止字节的技术。在这里,您使用了一个 NULL 指针。在 main 中的 argv 就是以这种方式工作的,最后一个元素是 NULL。例如,execv 也要求这样做。 - Pablo
2个回答

2
你的 adding_string 没有意义,你复制了一个 str,释放了来自 array 的内存并返回新副本。该函数应该返回指向 char 的双指针,而你传递了指向单个 char 的指针。所有其他值都会丢失,你的内存泄漏非常严重。
我会这样重新编写你的 adding_string
char **adding_string(char **array, const char *str)
{
    char **tmp;
    if(str == NULL)
        return NULL;

    // first make copy
    size_t len = strlen(str);
    char *strcopy = malloc(len+1);
    if(strcopy == NULL)
        return NULL;

    // you've allocated enough memory for the copy
    // no need of strncpy here
    strcpy(strcopy, str);

    // get the number of strings saved
    size_t size = 0; // number of strings saved
    if(array)
    {
        tmp = array;
        while(*(tmp++))
            size++;
    }

    // reallocate memory for array of strings
    tmp = realloc(array, (size+2) * sizeof *tmp);

    if(tmp == NULL)
    {
        // something went wrong, free the copy
        free(strcopy);
        return NULL;
    }

    tmp[size] = strcopy;
    tmp[size+1] = NULL;

    return tmp;
}

注意,在这个版本中,如果arrayNULL,函数将分配字符串数组的内存。这只是一个设计选择,你也可以检查array不是NULL,并传递预先分配的字符串数组给adding_string。我认为(这只是我的观点),更优雅的做法是adding_string将创建第一个数组。通过这种方式,分配内存的代码只存在于一个地方。
现在在你的主函数中:
char **array = NULL;
char **lines;

// adding_string will allocate the memory for array when it's NULL
array = adding_string(array, "help");
array = adding_string(array, "plz");
array = adding_string(array, "thanks");

for (lines = array; *lines; lines++)
{
    printf("%s\n", *lines);
}

请注意,我需要
tmp = realloc(array, (size+2) * sizeof *tmp);

size 表示保存的字符串数量,这意味着 arraysize+1 个空间,因为最后一个指向 NULL。您正在追加一个字符串,因此必须重新分配 size+1+1 个空间,即 size+2

完成操作后,请不要忘记释放内存。


非常感谢!这让我很明白了! - An Unsullied

1
以下程序严格遵循您的需求和意图。
每次添加新字符串时,数组array都会被重新调整大小。程序结束时,对所有已分配内存进行适当的清理。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char ** adding_string(char **array, const char *str)
{
    size_t num = strlen(str) + 1;
    char *final = (char *)malloc(num); // allocate memory for the string `str`
    strncpy(final, str, num);   // create the copy of the `str`  

    int i=0;
    for(i=0; array[i] !=NULL; i++) {}  // find how many elements do we have in the array

    array[i] = final; // add final to the first empty spot in the `array`
    i++;

    char ** new_array = calloc(1+i, sizeof(char *));  // allocate a new array 1 size bigger
    memcpy(new_array, array, sizeof(char*)*i);        // copy all the pointers

    free (array); // no need for the old array 

    return new_array; // return a pointer to the new bigger array
}

int main(void)
{
    char **array = NULL;
    char **lines;

    array = (char **)calloc(1, sizeof(char *)); // allocate array for 4 poiters if type (char *)

    array = adding_string(array, "help");
    array = adding_string(array, "plz");
    array = adding_string(array, "thanks");

    for (lines = array; *lines; lines++)
    {
       printf("%s\n", *lines);
       free(*lines);
    }

    free (array);

    return 0;
}

输出:

help
plz
thanks

这是一种不同的方法,其中保留了HTML标记。
char *adding_string(const char *str)

返回一个指针(char *),指向该字符串的副本。 数组已经预先分配了内存来容纳所有字符串指针。

一个演示此概念的小程序:

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

char *adding_string(const char *str)
{

    size_t num = strlen(str) + 1;
    char *final= (char *)malloc(num); // allocate memory for the string str
    strncpy(final, str, num);   // crreate the copy 

    return final; // return a pointer to created copy
}


int main(void)
{
    char **array = NULL;

    array = (char **)calloc(4, sizeof(char *)); // allocate array for 4 pointers if type (char *)

    array[0] = adding_string("help");
    array[1] = adding_string("plz");
    array[2] = adding_string("thanks");

    for (int i=0; i<3; i++ )
    {
        printf("%s\n", array[i]);
        free(array[i]);
    }

    free (array);

    return 0;
}

输出:

help
plz
thanks

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