Как обнулить новую память после realloc - PullRequest
8 голосов
/ 26 января 2010

Каков наилучший способ обнуления новой памяти после вызова realloc при сохранении изначально выделенной памяти без изменений?

#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>

size_t COLORCOUNT = 4;

typedef struct rgb_t {
    int r;
    int g;
    int b;
} rgb_t;

rgb_t** colors;

void addColor(size_t i, int r, int g, int b) {
    rgb_t* color;
    if (i >= COLORCOUNT) {
        // new memory wont be NULL
        colors = realloc(colors, sizeof(rgb_t*) * i);
       //something messy like this...
        //memset(colors[COLORCOUNT-1],0 ,sizeof(rgb_t*) * (i - COLORCOUNT - 1));

         // ...or just do this (EDIT)
        for (j=COLORCOUNT; j<i; j++) {
            colors[j] = NULL;
        }

        COLORCOUNT = i;
    }

    color = malloc(sizeof(rgb_t));
    color->r = r;
    color->g = g;
    color->b = b;

    colors[i] = color;
}

void freeColors() {
    size_t i;
    for (i=0; i<COLORCOUNT; i++) {
        printf("%x\n", colors[i]);
        // can't do this if memory isn't NULL
       // if (colors[i])
         //   free(colors[i]);

    }
}


int main() {
    colors = malloc(sizeof(rgb_t*) * COLORCOUNT);
    memset(colors,0,sizeof(rgb_t*) * COLORCOUNT);
    addColor(0, 255, 0, 0);
    addColor(3, 255, 255, 0);
    addColor(7, 0, 255, 0);


    freeColors();
    getchar();
}

Ответы [ 4 ]

7 голосов
/ 26 января 2010

Нет способа решить эту проблему как общий шаблон. Причина в том, что для того, чтобы узнать, какая часть буфера является новой, вам нужно знать, как долго был старый буфер. Это невозможно определить в C и, следовательно, мешает общему решению.

Тем не менее, вы могли бы написать обертку, как это

void* realloc_zero(void* pBuffer, size_t oldSize, size_t newSize) {
  void* pNew = realloc(pBuffer, newSize);
  if ( newSize > oldSize && pNew ) {
    size_t diff = newSize - oldSize;
    void* pStart = ((char*)pNew) + oldSize;
    memset(pStart, 0, diff);
  }
  return pNew;
}
4 голосов
/ 26 января 2010

Вероятно, нет необходимости делать memset: возможно, вы не используете colors[k] перед тем, как установить его с чем-то действительным позже. Например, ваш код устанавливает colors[i] для недавно выделенного указателя color, поэтому вам не нужно устанавливать colors[i] в NULL.

Но, даже если вы хотите «обнулить его, чтобы все было хорошо», или действительно хотите, чтобы новые указатели были NULL: стандарт C не гарантирует, что ноль всех битов является константой нулевого указателя (то есть NULL), поэтому memset() в любом случае не является правильным решением.

Единственная переносимая вещь, которую вы можете сделать, это установить каждый указатель на NULL в цикле:

size_t k;
for (k=COLORCOUNT; k < i+1; ++k) /* see below for why i+1 */
    colors[k] = NULL;

Ваша основная проблема в том, что ваш realloc() звонок неправильный. realloc() возвращает указатель на измененный размер памяти, он (не обязательно) изменяет размер на месте.

Итак, вы должны сделать:

/* i+1 because you later assign to colors[i] */
rgb_t **tmp = realloc(colors, (i+1) * sizeof *tmp);
if (tmp != NULL) {
    /* realloc succeeded, can't use colors anymore */
    colors = tmp;
} else {
    /* realloc failed, colors is still valid */
}

Если вы действительно хотите знать, каким должен быть вызов memset(), вам нужно обнулить память, начиная с colors+COLORCOUNT, и установить i+1-COLORCOUNT членов на ноль:

memset(colors+COLORCOUNT, 0, (i+1-COLORCOUNT) * sizeof *colors);

Но, как я сказал выше, ноль всех байтов не обязательно является указателем NULL, поэтому ваш memset() в любом случае бесполезен. Вы должны использовать цикл, если вы хотите NULL указатели.

4 голосов
/ 26 января 2010

Прежде всего, realloc может потерпеть неудачу, поэтому вам нужно проверить NULL. Во-вторых, нет лучшего способа обнуления памяти: просто memset от конца старого буфера до конца большего буфера.

1 голос
/ 26 января 2010

Напишите свою собственную функцию, скажем, reallocz, которая принимает текущий размер в качестве параметра и вызывает для вас realloc и memset. Это действительно не станет намного лучше, чем то, что у вас уже есть ... в конце концов, это C.

...