【问题标题】:Freeing allocated memory conditionally within a macro in C在 C 中的宏内有条件地释放分配的内存
【发布时间】:2013-04-28 07:18:12
【问题描述】:

我有一个函数可以读取一个 csv 文件,然后用它的参数更新一个 struct。 我希望能够循环遍历结构元素,所以我转向宏。解析 csv 文件的输出是文件行和列的二维字符串数组。为了将字符串转换为它们各自的数据类型(当前结构只有intchar*),我在用于循环遍历结构的宏中使用了一个转换宏。

CVT_INT atoi(str)
CVT_STR str

但是,在释放通过解析 csv 文件分配的内存时,如果文件中的字符串在开始或结束时没有组合在一起,就会变得很棘手。

csv[row][col]
string|string|int|string|int
string|string|int|string|int
...

for(int row = 0; row < number_of_rows; row++)
    for(int col = 2; col < number_of_cols; col++)
        free(csv[row][col]) // frees string when row[i][3]

我可以确保所有字符串都位于结构的开头,但我希望它是动态的,并且我不想考虑确保数据类型被分组。

我可以释放由 CVT_INT 转换的分配字符串,但释放 CVT_STR 使用的字符串会导致结构的字符串被释放。我可以想到一种解决方法: 1.分配新空间 2.复制旧字符串 3. 释放旧字符串。

CVT_STR strcpy((char*)malloc(sizeof(char)*(1+strlen(str))), str)

但是,在执行上述操作时,无论何时调用它都会导致崩溃,我不明白为什么。谁能给我一个解释和解决它的方法/做同样工作的不同路线?我知道这不是很有效,因此也欢迎提出改进这方面的建议。

另一种可能性,我只能释放int。但是,我不知道如何在宏中执行此操作,因为它必须返回一个 int。

下面是我调用循环结构宏的示例,其中包含转换宏。

#define STRUCT(type, name, converter) \
        obj->struct.name = converter(csv[row][col++]);
STRUCT_FIELDS
#undef PLAYER

感谢您的帮助

更新:

更换

CVT_STR strcpy((char*)malloc(sizeof(char)*(1+strlen(str))), str)

CVT_STR strdup(str)

工作,但我不明白为什么。也许有人可以启发我?

【问题讨论】:

  • 你为什么要使用宏,而不是内联函数[至少在最后一步,你仍然可以使用宏来做转换器的扩展,但传递一个函数而不是宏作为“转换器”。这样,您就可以调试代码(而且很可能不会降低效率,因为编译器会内联函数!)
  • 您的系统是否缺少strdup()
  • @user315052:由于对现有答案的评论说 strcpy() 不起作用,我怀疑一个函数做同样的事情[或者你认为strdup 做与strcpy(malloc(strlen(str)+1), str) 不同的东西 - 当然,它会检查 malloc 是否为空,等等,但除此之外它是相同的。
  • 谢谢user315052,我以前没见过这个功能,它已经解决了这个问题!从阅读手册页来看,它似乎与我用strcpy 硬编码的方式完全相同,但由于某种原因,它在我的代码没有的地方工作。也许宏不喜欢malloc 的类型转换。内联函数 - 我不熟悉它们,但建议我使用宏来循环浏览 struct。我猜测使用宏来执行此操作表明我不能使用内联函数?不过,我会对此进行研究,因为缺乏调试功能已经令人沮丧。
  • 我猜你没有#include &lt;stdlib.h&gt;,所以你的演员隐藏了丢失的原型。由于缺少原型,并且如果int 小于指针,您的malloc()s 返回值将被截断,从而导致指针无效。

标签: c memory-management macros


【解决方案1】:

所以,我们有一个可以转换事物的宏:

#define STRUCT(type, name, converter) \
    obj->type.name = converter(csv[row][col++]);    // Guessing `struct` meant `type`?

它会被这样调用:

STRUCT(foo, bar, CVT_STR)

现在,如果 CVT_STR 是一个宏,则该宏将直接展开为 STRUCT 宏展开,如下所示:

obj->foo.bar = strdup(csv[row][col++]); 

现在,如果您想单步执行此操作,您会遇到困难,因为它只是将宏作为一个步骤执行。

如果相反,我们编写一个函数:

inline char *cvt_str(const char *str)
{
    char *tmp = strdup(str); 
    return tmp;
}

并像这样使用这个函数:

STRUCT(foo, bar, cvt_str)

现在我们可以使用调试器在 cvt_str 中设置断点,并单步执行它以查看哪里出错了。

【讨论】:

  • 谢谢,我明天会尝试这个,但我不确定它是否符合我的其他宏乍一看 - 我可能也需要用内联函数替换它!
  • 您不必这样做,只要宏扩展导致有效的 C,您可以将一个作为宏执行,另一个作为函数执行。但是inline int cvt_int(const char *str) { return atoi(str); } 应该差不多。
  • 我将结构元素设置为:#define STRUCT_FIELDS \ STRUCT(char*, s1, cvt_string) \ STRUCT(int, i1, cvt_integer) 使用内联函数:inline int cvt_integer(const char* str) {return atoi(str);} inline char* cvt_string(const char* str) (return strdup(str);} 并像以前一样调用:#define STRUCT(type, name, converter) \ obj-&gt;struct.name = converter(csv[row][col++]); STRUCT_FIELDS #undef STRUCT 但这甚至不会编译,说内联有多个定义功能。
【解决方案2】:

您需要在strlen 中添加一个,其中不包括尾随 NUL 字节。

CVT_STR strcpy((char*)malloc((sizeof(char) * strlen(str)) + 1), str)

或者干脆

CVT_STR strcpy((char*)malloc(strlen(str) + 1), str)

【讨论】:

  • sizeof(char)定义为1,不用写出来。
  • 不幸的是这不起作用,问题一定出在其他地方。因为它在一个宏中,所以我也不能使用任何调试工具。我被告知我应该始终使用 sizeof(char) 因为有时它大于 1(我认为在某些亚洲字母中它们有更多的符号)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-28
  • 1970-01-01
  • 2016-11-16
  • 2018-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多