C - 动态创建字符串数组(malloc)

时间:2016-11-15 12:03:11

标签: c arrays string dynamic malloc

对于我的作业,我必须使用malloc动态创建一个字符串数组。 我真的不能理解已有的解决方案,因为它使用它的方式与我想要的方式略有不同。

我的问题: 所以,简而言之:我正在制作基于文本的冒险游戏。我的教授说库存应该是“任意大小”。如果此人没有物品,那么库存的大小也应为零,1个物品应该只包含1个物品等等。

我试图让某些东西起作用,但我真的无法理解malloc实际上是如何工作的,所以这是我的代码(这显然是不正确的,但是如何做我想要它做的事情是正确的? ):

char* inventory;
int amount=0;
inventory=(char*) malloc(sizeof(char)*amount);
//NOW THERE SHOULD BE AN INVENTORY WITH SIZE ZERO SINCE AMOUNT=0
//NOW I WANT TO GIVE THE PLAYER AN ITEM:
amount++;
inventory=(char*) malloc(sizeof(char)*(amount+1);
inventory[0]="sword";
//I WANT TO WRITE OUT INVENTORY TO TEST IF IT WORKS:
printf("%s", inventory[0]);
//FREE THE BITS LOCKED WITH MALLOC:
free(inventory);

教授告诉我们,我们必须写入数量+ 1,因为字符串的最后一个字符必须是'\ 0'或类似的东西。

所以,这就是我如何理解malloc是如何工作的(但也许这不是它的工作方式,我理解错误): 通常,这是你如何创建一个字符串数组,例如:

char strings[10][200];

这意味着你有10个字符串,每个字符串可以是200个字符。 当我在我的代码中使用malloc时: 整数'amount'与我的例子中的数字10基本相同,而sizeof(char)与我的例子中的数字200基本相同,对吧? 如果没有,那么我完全迷失了。

尽管如此,我的代码显然不起作用,所以我非常感谢你们的帮助,使用malloc C代码,以及一些解释,如果你有时间的话。

3 个答案:

答案 0 :(得分:0)

我不清楚你想做什么,但让我们分析你的代码:

char* inventory;
int amount=0;
inventory=(char*) malloc(sizeof(char)*amount);

由于amount为零,您刚刚分配了零字节的内存。文档说“如果size为0,malloc在堆中分配一个零长度项并返回一个指向该项的有效指针”,这样你就有了一个有效的指针,但没有存储。

amount++;
inventory=(char*) malloc(sizeof(char)*(amount+1);

您现在已经分配了2个字节的存储空间。但是,您丢失了刚刚分配的存储空间,即使它是零字节,因为您将第二次调用的结果分配给malloc到保存第一次调用malloc结果的变量}。你之前应该free

inventory[0]="sword";

这是无效的,因为inventorychar *,因此inventory[0]是一个char。你的编译器应该警告你,所以打开警告。

printf("%s", inventory[0]);

此printf调用的结果是未定义的,因为您希望打印一个字符串,但只传递一个字符。

free(inventory);

这很好,虽然你已经失去了第一次分配(见上文)。

从这个分析中,我希望你可以修改你的代码来做你想做的事。

答案 1 :(得分:0)

您似乎想要一个字符串数组(即char s的数组数组),但您有一个指向char的指针。在C中,指针和数组通常是可以互换的(尽管有人说它们是相同的东西没有使用C) - 你可以想到这一点,因为你可以对指针进行算术来访问相邻的数据,而数组只是存储作为相邻数据。所以你需要一个指向字符指针的指针,或char **

您需要仔细考虑数据将如何存储在内存中。在这种情况下,您将需要一个存储指针数组的库存列表(指针是固定长度的,所以这很好)。每个指针都是指向包含清单中该项目的实际字符串的指针。

其次,库存本身需要malloc',但是如果要在其中存储的所有字符串都在编译时修复,则字符串本身不一定是。如果这让你感到困惑,可以这样想吧 - 当你运行程序时,它需要在某个地方加载到内存中。因此编译到程序中的字符串将存在于内存中的某个位置,而C将允许您在内存中获取指向此字符串的指针,就像您可以获取指向内存中变量的指针一样。因此,每次您需要向广告资源添加字符串时,首先需要通过增加广告尺寸,使用新版本创建新广告资源来调整广告资源的大小,使用memcpy或类似内容复制现有内容库存到新副本,然后在旧副本上调用free(非常重要,否则你会得到内存泄漏,这很难发现,但意味着如果你的程序运行的时间足够长,它最终会吃掉所有的RAM),最后用指向新副本的指针覆盖指向旧副本的指针。

然后,你想通过像inventory[amount-1] = "some string";那样把字符串放入清单中(当然,字符串也可以作为参数传递给函数,在这种情况下它应该是一个变量类型char *)。

如果您的字符串在编译时并非都是固定的(即,如果您想将某人的姓名放在他们在命令行中输入的清单中,或者您想拥有类似“事物1”的数字,在循环中产生的“东西2”等等,那么你也需要malloc空间来表示字符串。

很抱歉,如果这令人困惑,但这确实是你需要能够理解才能在C中声明任何技能的事情。这是一个非常复杂的主题的简短解释,所以如果你不明白它,你真的需要花更多的时间学习更多的基础培训材料来学习它。我不会因为那个原因而给你代码(而且因为它需要花费更多的时间来写我的价值)。您需要基本了解指针如何工作以及它们与C中数组的关系,并至少模糊地理解这种事物是如何存储在内存中的(包括堆栈和堆之间的差异),以便能够正确解决这个问题。祝你好运!

答案 2 :(得分:0)

我可以提供一些提示:

  • 使用结构组将变量组合在一起(如数组和数组长度)
  • 使用结构创建abstract data type(例如:'广告资源'数据类型)
  • 创建常用操作功能(初始化库存,打印库存,增加库存容量,添加到库存,免费库存......)
  • 除非绝对必要,否则不要使用字符串切片,ropes和2D数组。在这种情况下,字符指针数组的工作正常。 (不要以为你打算实施这个,但无论如何我都会提到它)
  • 但是,您可能希望将来创建inventory_item结构,而不是仅存储char指针。这样您就可以添加其他项目属性,如计数和质量。 ("You have %d %s, it is %s", item.count, item.name, item.quality

重构代码:

struct inventory {
    size_t num_items; // What you call `amount`.
    char **items; // What you call `inventory`. Note that it's a pointer to a _character pointer_, as the other answers indicated.
};

int main(void) {
    struct inventory inv;

    // Initialize the inventory.
    // You might want to create a function for this.
    // In C++, you would use constructors.
    inv.num_items = 0;
    inv.items = NULL; // Set `items` to NULL, see below at realloc

    // Once again, you might want to create a function for this,
    // since you'll probably end up doing this a lot
    void *old; // Store the old inv.items; in case we fail to allocate a new one.
    old = inv.items;
    inv.items = realloc(inv.items, sizeof(*inv.items) * inv.num_items+1);
    // Explaination: `ptr = realloc(ptr, size)` is the same as `new_ptr = malloc(size); memcpy(ptr, old_ptr, /* size of old_ptr */); free(old_ptr);`
    // realloc works the same as malloc when it's first argument is NULL.
    if (!inv.items) { // Failed to allocate memory.
        free(old); // Deinitialize the inventory. A `goto fail;` would be useful here.
        return 1;
    }
    // Maybe you'd want seperate functions for adding items and growing the array, then call the growth function here.
    inv.items[inv.num_items] = "sword";
    inv.items++;

    // Print the inventory
    for (size_t i=0; i < inv.num_items; i++) {
        printf("inventory[%lu] = %s\n", i, inv.items[i]); // %lu = unsigned long, `size_t` on my system.
    }

    // You might again want a function for this
    free(inv.items); // Deinitialize the inventory
     // Optional, but cleaner:
    inv.items = NULL;
    inv.num_items;

    return 0;
}

上述方法的缺点是你不能混合字符串文字和分配字符,因为在去初始化时你不知道free到哪一个。free。 (==对字符串文字是未定义的行为)。

如果您的所有项目都是字符串文字,那么应该没问题。这样,您还可以在这些字符串上安全地使用==运算符。请注意,如果您发现自己能够在字符串上安全地使用typedef struct inventory inv_t; typedef struct inventory_item inv_item_t; struct inventory { size_t num_items; inv_item_t items; }; struct inventory_item { int type; }; // Convenience macro for grouping enum data #define GENERATE_ITEM_TYPES(XX) \ XX(ITEM_NONE, "<unknown item>") \ XX(ITEM_SWORD, "sword") enum inventory_type { #define XX(enumname, ignore) enumname, GENERATE_ITEM_TYPES(XX) #undef XX NUM_ITEMS, // Automatically equals 2 }; static char *names[NUM_ITEMS] = { #define XX(ignore, strname) strname, GENERATE_ITEM_TYPES(XX) #undef XX }; static int inv_init(inv_t *self); static int inv_fini(inv_t *self); static int inv_print(inv_t *self); static int inv_reserve(inv_t *self, size_t new_size); static int inv_add(inv_t *self, inv_type_t type); static inv_item_t inv_item_from_type(int type) { inv_item_t item = { .type = type }; return item; } int main(void) { inv_t inv; if (inv_init(&inv) != 0) goto fail; if (inv_add(&inv, inv_item_from_type(ITEM_SWORD)) != 0) goto fail; if (inv_print(&inv) != 0) goto fail; inv_fini(&inv); return 0; fail: inv_fini(&inv); return 1; } static int inv_init(inv_t *self) { self->num_items = 0; self->items = NULL; return 0; } static int inv_fini(inv_t *self) { free(self->items); self->items = NULL; self->num_items; return 0; } static int inv_print(inv_t *self) { for (size_t i=0; i < inv.num_items; i++) { printf("inventory[%llu] = %s\n", (unsigned long long int)i, names[inv.items[i]]); // No format for size_t, so convert to unsigned long long and use format `%llu`. More portable. } } static int inv_reserve(inv_t *self, size_t new_size) { void *old; old = inv.items; inv.items = realloc(inv.items, sizeof(*inv.items) * new_size); if (!inv.items) goto fail; return 0; fail: free(old); return 1; } static int inv_add(inv_t *self, char *item) { if (inv_reserve(self, inv.num_items + 1) != 0) return 1; inv.items[inv.num_items++] = item; return 0; } 运算符,则可能需要定义枚举。

我将如何写它(无法深入解释):

ActiveRecord::Base.connection.send(:table_structure, "projects")