对于我的作业,我必须使用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代码,以及一些解释,如果你有时间的话。
答案 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";
这是无效的,因为inventory
是char *
,因此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)
我可以提供一些提示:
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")