重新分配一个字符*

时间:2012-11-01 02:19:06

标签: c printf realloc

我正在尝试执行一个函数,它将在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是一个包含两个字段的结构:name(char *)和value(int)。

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

2 个答案:

答案 0 :(得分:4)

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

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

答案 1 :(得分:0)

我很抱歉地说,但你的代码几乎一切都有问题。

首先,代码不完整。

您说t_var类型有两个成员,namevalue

但您的代码是指nombre成员。您是否忘记提及或者在发布代码时忘记重命名?

其次,误用了sizeof

您使用sizeof(int)表达式。你知道你在这里做了什么吗?!

显然,您尝试计算打印的int值的长度。唉,运算符sizeof检索有关参数在内存中占用的字节数的信息。因此,例如,对于32位整数,sizeof(int)的结果是4(32位适合4个字节),但最大有符号32位整数值是幂(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);

第六:strlen sprintf

您根本无需在此处致电strlen,因为printf系列的所有功能都会返回打印的字符数。你可以使用它:

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

第七名:realloc。严重。

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

这也很危险。真。假设您需要打印5,000个字符串,但只有2,000个空间。您将从NULL获得realloc()指针。打印到该点的所有数据仍然是当前的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;
    }
}