0

我正在尝试执行一个函数,该函数将在 char 数组中存储一些要在其上打印的信息:

int offset = 0;
size_t size = 1;
char *data = NULL;
data = malloc(sizeof(char));

void create(t_var *var){
    size_t sizeLine = sizeof(char)*(strlen(var->nombre)+2)+sizeof(int);
    size = size + sizeLine;

    realloc(data, size);

    sprintf(data+offset,"%s=%d\n",var->name,var->value);
    offset=strlen(data);
}

list_iterate(aList, (void *)create);

t_var 是一个具有两个字段的结构:名称 (char*) 和值 (int)。

这段代码有什么问题?在 Valgrind 上运行它时,它会抱怨 realloc 和 sprintf。

4

2 回答 2

4

在不知道具体的 valgrind 错误的情况下,突出的是:

realloc(data, size);应该data = realloc(data, size);

于 2012-11-01T02:22:36.070 回答
0

我很抱歉这么说,但你的代码几乎所有东西都是错误的。

首先,不完整的代码。

你说你的t_var类型有两个成员,name并且value.

但是您的代码引用了一个nombre成员。发布代码的时候是忘记提还是忘记重命名了?

二是误用sizeof

你使用一个sizeof(int)表达式。你知道你在这里实际做什么吗?!

显然您尝试计算打印int值的长度。唉,运算符sizeof检索有关参数在内存中占用的字节数的信息。因此,例如,对于 32 位整数,其结果sizeof(int)为 4(32 位适合 4 个字节),但最大有符号 32 位整数值为 power(2,31)-1,即2147483647十进制。十位数,而不是四位数。

您可以使用(int)(2.41 * sizeof(any_unsigned_int_type)+1)来确定打印 的值可能需要的字符数any_unsigned_int_type。在有符号整数类型的情况下,为前面的减号添加一个。

魔术常数2.41256(在第 3 位十进制数字处四舍五入)的十进制对数,因此它将以字节为单位的长度缩放为以十进制数字为单位的长度。
如果您希望避免浮点运算,您可以使用另一个近似值 29/12=2.41666...,然后计算(sizeof(any_unsigned_int_type)*29/12+1).

第三,sizeof(char)

您将结果strlen乘以sizeof(char)

实际上,这不是一个错误,但完全没用,就像定义上的sizeof(char)平等一样1

第四,realloc

正如其他人已经解释的那样,您必须存储返回值:

    data = realloc(data, size);

否则,您可能会丢失重新分配的数据并继续在先前的位置写入,这可能会导致覆盖(因此破坏)堆上的其他一些数据。

第五,offset

您使用该值来确定要到达的位置sprintf()。但是,在打印之后,您 offset最后打印输出的长度代替而不是增加它。结果连续sprintf的 s 将覆盖以前的输出!

做:

    offset += strlen(data);

第六:strlensprintf

您根本不需要strlen在这里调用,因为 family 的所有函数都会printf返回打印的字符数。你可以使用它:

    int outputlen = sprintf(data+offset, "%s=%d\n", var->name, var->value);
    offset += outputlen;

第七:realloc。严重地。

这是相当昂贵的功能。它可能需要对malloc新大小的数据进行内部操作,将数据复制到新位置和free旧块中。你为什么要强迫它?如果有一天它需要打印五千个字符串,它会对你的程序产生什么影响......?

这也是相当危险的。真的。假设您需要打印 5,000 个字符串,但只有 2,000 个空间。你会NULLrealloc(). 打印到该点的所有数据都还在当前data指针处,但是接下来要做什么呢?
你怎么能告诉list_iterate停止迭代......?
您如何通知上面的例程list_iterate该字符串不完整...?

没有好的答案。幸运的是,您不需要解决问题——您可以避免解决问题!

解决方案。

首先遍历您的列表并计算您需要的缓冲区大小。然后分配缓冲区——只需一次!- 并继续填充它。只有一个地方分配可能会失败,如果发生这种情况,您根本无法解决问题:

int totaloutputlength = 0;
char *outputbuffer    = NULL;
char *currentposition = NULL;

void add_var_length(t_var *var){
    const int numberlength = sizeof(var->value)*29/12 + 1;
    totaloutputlength += strlen(var->name) + 2 + numberlength;
}

void calculate_all_vars_length(t_list *aList){
    totaloutputlength = 0;
    list_iterate(aList, (void *)add_var_length);
}

void sprint_var_value(t_var *var){
    int outputlen = sprintf(currentposition, "%s=%d\n", var->name, var->value);
    currentposition += outputlen;  // advance the printing position
}

int sprint_all_vars(t_list *aList){
    calculate_all_vars_length(aList);
    outputbuffer = malloc(totaloutputlength + 1);  // +1 for terminating NUL char

    // did allocation succeed?
    if(outputbuffer == NULL) {                     // NO
        // possibly print some error message...
        // possibly terminate the program...
        // or just return -1 to inform a caller something went wrong

        return -1;
    }
    else {                                         // YES
        // set the initial printing position
        currentposition = outputbuffer;

        // go print all variables into the buffer
        list_iterate(aList, (void *)sprint_var_value);

        // return a 'success' status
        return 0;
    }
}
于 2017-11-11T17:03:38.040 回答