Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Надежно ли обнулять массив с помощью memset()?


Автор: Royan 25.2.2006, 07:42
Допустим есть такой код

Код

double **mx = new double*[col - 1];
        for(int i = 0; i < col - 1; i++) {
            mx[i] = new double[row];
            sizeOfMx += sizeof(double) * row;
        } 

memset(mx, 0, sizeOfMx);



Надежно ли обнулять память массива таким образом? Могут ли быть какие-то побочные эффекты. Меня интересуют 32 и 64 битные платформы, используя g++, gcc, icl, cl(msvc). Насколько я знаю на cl особых проблем нет, но все-таки хочется знать точку зрения профессионала

Автор: Daevaorn 25.2.2006, 10:43
Royan,
нет таким образом нельзя вообще! Получишь утечку памяти. У тебя же двухмерный массив! Вот если бы это был обычный массив POD типа, то использование memset вполне легально и надежно. Но в твоём случае это преступление. Откуда ты решил что так можно писать?

Автор: MAKCim 25.2.2006, 15:12
если тебе надо занулить все строки матрицы пиши
Код

double** mx=new double* [col-1];
for (int i=0; i<col-1; i++) mx[i]=new double [row]();
...

Автор: Royan 25.2.2006, 17:56
MAKCim, Так не могу очень долго. Массив используется в части, где время критично, поэтому необходимо его очень быстро обнулять к тому же твоим способом я тоже получу утечку, а чтобы этого не случилось, придется еще его и удалять.

Daevaorn, Мне казалось, что в любом массиве элементы располагаются друг за другом. А можешь объяснить, в чем отличие динамического массива от статического с физической точки зрения, откуда там возникает утечка?

Автор: MAKCim 25.2.2006, 18:36
Цитата

MAKCim, Так не могу очень долго. Массив используется в части, где время критично, поэтому необходимо его очень быстро обнулять

что значит долго? Обнуляется каждая строка в процессе создания
Цитата

к тому же твоим способом я тоже получу утечку, а чтобы этого не случилось, придется еще его и удалять.

А в твоем примере нет утечки? Сделай класс, в деструкторе удаление...
Цитата

А можешь объяснить, в чем отличие динамического массива от статического с физической точки зрения, откуда там возникает утечка?

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

Автор: Fin 25.2.2006, 18:40
Цитата(Royan @ 25.2.2006, 16:56 Найти цитируемый пост)
Daevaorn, Мне казалось, что в любом массиве элементы располагаются друг за другом. А можешь объяснить, в чем отличие динамического массива от статического с физической точки зрения, откуда там возникает утечка?

В двухмерном массиве, созданом не динамически, члены массива идут один за другим. Пример:
Код

  char ch[2][2]={"c","a"};

Вот вырезка, как этот массив разместился у меня в памяти:
Цитата

0012FF7C  63 00 61 00

Теперь расмотрим динамический массив. В первую размерность записываются не данные, а адреса на вторую размерность. Во второй размерности уже записываются сами данные.
Пример:
Код

    char **ch=new char *[2];
    for (int i=0; i<2; i++) ch[i]=new char[2];
    ch[0][0]='c';
    ch[0][1]=0;
    ch[1][0]='a';
    ch[1][1]=0;

После выполнения в самой переменной ch был записан адрес на выделенную память.
Цитата

00322908  50 29 32 00 98 29 32 00

Это так выгледела первая размерность.
Так выгледит вторая размерность
Цитата

00322950  63 00
00322998  61 00

Если ты даеш memset для двухмерного динамического массива в твоем случае, то ты просто напросто затираеш ссылки на вторую размерность. И последуюшие попытки обрашения ко второй размерности должны привести к вылету программы.
Добавлено @ 18:48
Если ты хочеш обнулять вторую размерность, нужно делать
Код

for(int i = 0; i < col - 1; i++) {
            memset(mx[i],0, sizeof(double)*row);
}

Автор: Royan 25.2.2006, 19:16
MAKCim, Дело в том, что создание/удаление элементов априорно медленнее, чем простая запись нулей. Хотя бы, поэтому для меня предложенный тобой вариант не годится. Статический массив мне не подойдет, у меня там алгоритм работы на динамике построен.


Fin, +1 Теперь все кристально ясно. Честное слово я бы проставился за такой ответ ;) Это надо пометить в F.A.Q!

Но вопрос о наиболее оптимальном способе обнуления я бы закрывать не хотел. Несомненно, вариант, предложенный Fin, значительно лучше, чем просто присвоение каждому элементу массива нуля, но может кто-то знает еще более изящный способ?

Автор: Daevaorn 25.2.2006, 19:24
Цитата(Royan @ 25.2.2006, 20:16 Найти цитируемый пост)
Но вопрос о наиболее оптимальном способе обнуления я бы закрывать не хотел. Несомненно, вариант, предложенный Fin, значительно лучше, чем просто присвоение каждому элементу массива нуля, но может кто-то знает еще более изящный способ?

Изящнее не существует. Добавление "()" к твоему коду - это предел изящности

Автор: maxim1000 25.2.2006, 20:20
можно сделать одномерный массив A размером M*N
и к нему еще сделать доп. массив указателей на начала строк B[i]=A+RowLength*i
тогда и создавать, и удалять нужно будет с помощью одного new и одного delete
а это, как мне кажется, и будут основные затраты при создании/удалении
а для обнуления можно будет использовать тот факт, что все элементы идут друг за другом и вызывать memset для A...

Автор: MAKCim 25.2.2006, 20:30
Цитата

Дело в том, что создание/удаление элементов априорно медленнее, чем простая запись нулей

и у тебя и у меня идет создание double-ов
Код

ms[i]=new double[row];

выражение
Код

ms[i]=new double[row]();

обнуляет каждый double
чем эта запись медленнее memset, и каким образом устроен memset в таком случае
получается, что
Код

int* p=new int(0);

медленнее чем
Код

int* p=new int;
memset(p,0,sizeof(int))


Автор: Royan 25.2.2006, 22:17
MAKCim, У меня создание массива только в оном месте, далее могут произойти десятки тысяч обнулений. Просто в примере эти опреации я написал друг за другом.

Автор: ДобренькийПапаша 25.2.2006, 22:25
for(i=0; i<n; i++)
{
for(j=0;j<n;j++)
a[i][j]=0;
}

А что, так нельзя?

Автор: YonasBriginas 26.2.2006, 01:09
можно только цикл ускорить, а с памятью самое оптимальное решение было предложено.
Код

int k = sizeof(double)*row;
// префиксная операция инкремента выполняется без создания временой 
// переменной в отличии от постфиксной операции, которая в свою очередь использует префиксную
for(int i = 0; i < col - 1; ++i)    
{
    memset(mx[i],0, k);
}

Автор: Royan 26.2.2006, 05:15
maxim1000, Только сейчас понял твою мысль smile ты вот это имел ввиду?
Код

template < typename _Ty >
_Ty **AllocateMatrix( int nRows, int nCols) {
        _Ty **ppi = new _Ty*[nRows];        
        _Ty *curPtr = new _Ty [nRows * nCols]();
        for(int i = 0; i < nRows; i++) {
                *(ppi + i) = curPtr;
                curPtr += nCols;
        }
        return ppi;
}

template < typename _Ty >
void FreeMatrix(_Ty** mx) {
        delete [] *mx;
        delete [] mx;
}


2YonasBriginas, Спасибо за внимательность smile, правда, насколько мне известно, постфиксные операции на базовых типах оптимизируются компилятором (во всяком случае, это одна из первых вещей, которую может и оптимизирует любой достойный компилятор)

Автор: MAKCim 26.2.2006, 10:08
Цитата

У меня создание массива только в оном месте, далее могут произойти десятки тысяч обнулений. Просто в примере эти опреации я написал друг за другом.

тогда ладно, хотя memset тоже скорее всего на циклах основана
я думаю что-то типа
Код

void memset(void* ptr,char zn, unsigned int size) 
{
    char* pointer=static_cast<char*>(ptr);
    while (size--) *pointer--=zn;
}

Автор: maxim1000 26.2.2006, 13:37
Цитата(Royan @ 26.2.2006, 04:15 Найти цитируемый пост)
ты вот это имел ввиду?

да...
тогда обнуление - просто
Код

memset(m[0],N*M);

Автор: YonasBriginas 26.2.2006, 16:18
2Royan,
да. для фундаментальных типов компилятор может устранить дополнительную копию. но лучше не надеятся на него. smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)