sizeof() и struct
От: Аноним  
Дата: 08.09.11 16:09
Оценка: -2
проверял один класс
и наткнулся на такую вещь


class MyInfo
{
    int    ptr,p2,p4;
    long long    p3;
};

sizeof возвращает 24
это из за выравнивания по-умолчанию?

кстати память выделенная new[] можно освобождать delete (без[])
т.е. работает, но не вызывается деструктор эл. массива?
Re: sizeof() и struct
От: _Ursus_  
Дата: 08.09.11 16:13
Оценка: -1
Здравствуйте, Аноним, Вы писали:

А>
А>class MyInfo
А>{
А>    int    ptr,p2,p4;
А>    long long    p3;
А>};
А>

А>sizeof возвращает 24
А>это из за выравнивания по-умолчанию?

А>кстати память выделенная new[] можно освобождать delete (без[])

А>т.е. работает, но не вызывается деструктор эл. массива?

Атвет на оба вапроса — "да".
Re: sizeof() и struct
От: LaptevVV Россия  
Дата: 08.09.11 16:14
Оценка:
Здравствуйте, Аноним, Вы писали:

А>проверял один класс

А>и наткнулся на такую вещь


А>
А>class MyInfo
А>{
А>    int    ptr,p2,p4;
А>    long long    p3;
А>};
А>

А>sizeof возвращает 24
А>это из за выравнивания по-умолчанию?

А>кстати память выделенная new[] можно освобождать delete (без[])

А>т.е. работает, но не вызывается деструктор эл. массива?
Выравнивание по умолчанию зависит от компилятора.
Возврат памяти массива без [] — ИМХО ляп разработчиков компилятора.
Не следует пользоваться во избежание проблем.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re: sizeof() и struct
От: szag  
Дата: 08.09.11 16:20
Оценка: +1
Здравствуйте, Аноним, Вы писали:

А>проверял один класс

А>и наткнулся на такую вещь


А>
А>class MyInfo
А>{
А>    int    ptr,p2,p4;
А>    long long    p3;
А>};
А>

А>sizeof возвращает 24
А>это из за выравнивания по-умолчанию?

А>кстати память выделенная new[] можно освобождать delete (без[])

А>т.е. работает, но не вызывается деструктор эл. массива?
1. выравнивание зависит от настроек компилятора
2. new [] и delete без [] это UB. Так делать крайне не рекомендую.
Re: бояны-бояны... (что вернёт sizeof + delete vs delete[])
От: Erop Россия  
Дата: 08.09.11 16:38
Оценка: 33 (2)
Здравствуйте, Аноним, Вы писали:

А>sizeof возвращает 24

А>это из за выравнивания по-умолчанию?

На такой вопрос я уже как-то твечал

А>кстати память выделенная new[] можно освобождать delete (без[])

А>т.е. работает, но не вызывается деструктор эл. массива?
На такой тоже, но не могу найти сразу.

Если коротко, то очень зависит от компилятора и типа, который ты по new создавал.
Общий смысл такой, что компиляторы, когда ты аллокируешь массив данных с нетривиальными деструкторами/конструкторами или просто не POD, перед началом вектора оставляют немного места, чтобы записать туда длину этого массива.

Ну, то есть, они так смотрят, ты хочешь создать вектор из shared_ptr<T>, например.
new shared_ptr<int>[5];
и понимают, что когда ты будешь это дело разрушать, то кроме того, что надо будет освободить память из-под самого вектора, надо будет ещё и вызвать деструкторы у пяти его элементов.

поэтому компилятор вызывает функцию ::operator new[]( sizeof( size_t) + sizeof(shared_ptr<int>) * 5), потом по адресу, который вернул ::operator new[] записывает 5, а по адресу + 4 создаёт пять идущих один за другим объектов, и возвращает адрес первого из них.

При вызове delete[] компилятор видит, что тип элемента вектора такой, что "в заначке" должен быть счётчик, поэтому вычитают из переданного адреса 4, смотрят на счётчик, и разрушают пять объектов. А потом вызывают от уменшенного на 4 адреса ::operator delete[]()

То есть отличие состоит не только в том, что не зовутся деструкторы, но и в том, что в ::operator delete[] прийдут РАЗНЫЕ АДРЕСА! А это вот уже может фатально повредить кучу...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: sizeof() и struct
От: gegMOPO4  
Дата: 08.09.11 16:55
Оценка:
Здравствуйте, Аноним, Вы писали:
А>кстати память выделенная new[] можно освобождать delete (без[])
А>т.е. работает, но не вызывается деструктор эл. массива?

Нет.

In the first alternative (delete object), the value of the operand of delete may be a null pointer
value, a pointer to a non-array object created by a previous new-expression, or a pointer to a subobject (1.8)
representing a base class of such an object (Clause 10). If not, the behavior is undefined.

Re: sizeof() и struct
От: locker  
Дата: 08.09.11 16:58
Оценка:
зачем минусовать?
я то просил потому как считал если везде пишут что нельзя так делать (new-delete разные)
то и выйдет ошибка при работе программы
а тут все срабатывает, вроде хорошо, но ведь поменяй что то в классе например,
который используется как эл. массива, то в какой то момент да рухнет все
Re[2]: бояны-бояны... (что вернёт sizeof + delete vs delete[
От: locker  
Дата: 08.09.11 17:19
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, Аноним, Вы писали:


А>>sizeof возвращает 24

А>>это из за выравнивания по-умолчанию?

E>На такой вопрос я уже как-то твечал


А>>кстати память выделенная new[] можно освобождать delete (без[])

А>>т.е. работает, но не вызывается деструктор эл. массива?
E>На такой тоже, но не могу найти сразу.

E>Если коротко, то очень зависит от компилятора и типа, который ты по new создавал.

E>Общий смысл такой, что компиляторы, когда ты аллокируешь массив данных с нетривиальными деструкторами/конструкторами или просто не POD, перед началом вектора оставляют немного места, чтобы записать туда длину этого массива.

E>Ну, то есть, они так смотрят, ты хочешь создать вектор из shared_ptr<T>, например.

E>
new shared_ptr<int>[5];
и понимают, что когда ты будешь это дело разрушать, то кроме того, что надо будет освободить память из-под самого вектора, надо будет ещё и вызвать деструкторы у пяти его элементов.


E>поэтому компилятор вызывает функцию ::operator new[]( sizeof( size_t) + sizeof(shared_ptr<int>) * 5), потом по адресу, который вернул ::operator new[] записывает 5, а по адресу + 4 создаёт пять идущих один за другим объектов, и возвращает адрес первого из них.


E>При вызове delete[] компилятор видит, что тип элемента вектора такой, что "в заначке" должен быть счётчик, поэтому вычитают из переданного адреса 4, смотрят на счётчик, и разрушают пять объектов. А потом вызывают от уменшенного на 4 адреса ::operator delete[]()


E>То есть отличие состоит не только в том, что не зовутся деструкторы, но и в том, что в ::operator delete[] прийдут РАЗНЫЕ АДРЕСА! А это вот уже может фатально повредить кучу...


последние 2 абзаца не очень понял
что значит А потом вызывают от уменшенного на 4 адреса ::operator delete[]()?
ведь уже работает delete[]
или это для new[][]... ?

но и в том, что в ::operator delete[] прийдут РАЗНЫЕ АДРЕСА!
всмысле будет вызываться delete() для каждого из 5 адресов например?
Re[3]: функция и консрукция new и delete
От: Erop Россия  
Дата: 08.09.11 19:30
Оценка: 6 (1) +1
Здравствуйте, locker, Вы писали:


L>но и в том, что в ::operator delete[] прийдут РАЗНЫЕ АДРЕСА!

L>всмысле будет вызываться delete() для каждого из 5 адресов например?

Пусть есть такой код:
#include <stdio.h>
#include <iostream>

template<typename TOStream> 
TOStream& operator << ( TOStream& dst, const std::string& src )
    { return dst << src.c_str(); }

std::string ptrToText( const void* p )
{
    if( p == 0 )
        return "(NULL)";
    char buff[128];
    sprintf_s( buff, "0x%X", p );
    return buff;
}


struct Item {
    char I;
    ~Item() 
    {
        std::cout << "Item::~Item : this = " << ptrToText( this ) << ", I = " << (int)I << std::endl;
    }

    void SetI( size_t i )
    {
        std::cout << "Item::SetI( " << i << " ): this = " << ptrToText( this ) << std::endl;
        I = (char)i;
    }

    void* operator new[]( size_t size )
    {
        void* res = ::operator new[]( size );
        std::cout << "Item::operator new[]( " << size << " ) = " << ptrToText( res ) << std::endl;
        return res;
    }
    void operator delete [] ( void* p )
    {
        std::cout << "Item::operator delete[]( " << ptrToText( p ) << " )" << std::endl;
        ::operator delete[]( p );
    }

    static Item* AllocateVector( size_t size )
    {
        std::cout << "Item::AllocateVector( " << size << " )" << std::endl;
        Item* res = new Item[size];
        std::cout << "Item::AllocateVector: ptr = " << ptrToText( res ) 
            << ", count = " << ((int*)res)[-1] << std::endl;
        for( size_t i = 0; i < size; i++ )
            res[i].SetI( i );
        
        return res;
    }
};



int _tmain(int argc, _TCHAR* argv[])
{
    delete [] Item::AllocateVector( 5 );
    return 1;
}
На VC он выдат, например, такой результат:
Item::AllocateVector( 5 )
Item::operator new[]( 9 ) = 0xFAA18
Item::AllocateVector: ptr = 0xFAA1C, count = 5
Item::SetI( 0 ): this = 0xFAA1C
Item::SetI( 1 ): this = 0xFAA1D
Item::SetI( 2 ): this = 0xFAA1E
Item::SetI( 3 ): this = 0xFAA1F
Item::SetI( 4 ): this = 0xFAA20
Item::~Item : this = 0xFAA20, I = 4
Item::~Item : this = 0xFAA1F, I = 3
Item::~Item : this = 0xFAA1E, I = 2
Item::~Item : this = 0xFAA1D, I = 1
Item::~Item : this = 0xFAA1C, I = 0
Item::operator delete[]( 0xFAA18 )


Что это значит? Что есть С++ конструкция new Item[5] и есть функция operator new[]( size_t size ). И, аналогично, есть С++ конструкция delete[] pItems и есть функция operator delete[]( void* )

Конструкция new Item[5] вычисляет размре Item'а, умножает его на 5, и прибавляет к этим 5 байтам ещё 4 байта на хранение счётчика! итого выходит 9 байт, их С++ и передаёт в operator new[], тот выделяет память по адресу 0xFAA18 и возвращает ее С++.
После чего С++ пишет по этому адресу счётчик элементов массива, а сами элементы размещает с адреса 0xFAA1C, то есть с 0xFAA18 + 4

После этого С++ конструкция new Item[5] возвращает адрес вевсе и не начала выделенного буфера в памяти (0xFAA18), а адрес первого элемента ветора (0xFAA1C).

Теперь мы работаем с этим вектором (вызываем у всех элемнтов SetI, который, кроме всего прочего, распечатывает нам this, каждого из них), потом зовём C++ сонструкцию delete[], передавая в неё адрес 0xFAA1C, а не 0xFAA18.
Она считывает 5 из заныканного счётчка, вызывает деструкторы, потом вычитает 4 и вызывает функцию operator delete[] от 0xFAA1C.

А если мы позовём просто delete, то она не будет искать счётчик, позовёт деструктор только у одного объекта и передаст в функцию operator delete() НЕ ТОТ АДРЕС блока. То есть 0xFAA1C, вместо 0xFAA18, который вырнул аллокатор!
Для большинства аллокаторов это обозначате разрушение внутренней слогической структуры и совершенно непредсказуемые последствия!
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: функция и консрукция new и delete
От: locker  
Дата: 08.09.11 20:43
Оценка:
Erop, спасибо за подробное разъяснение
я кстати заметил что если вместо настоящго класса использовать простую структуру (или new int[size] например)
то ptr=new MyStruct[size]
ptr[-1] не будет лежать кол-во элементов
наверное о гето в другом месте лежит
(оптимизацию я выключил компиляции)
Re[5]: Случай тривиальных деструкторв на VC++
От: Erop Россия  
Дата: 09.09.11 05:31
Оценка: +1
Здравствуйте, locker, Вы писали:

L>Erop, спасибо за подробное разъяснение

Пожалуйста, обращайся.
Для "спасибо" тут есть кнопки.

L>я кстати заметил что если вместо настоящго класса использовать простую структуру (или new int[size] например)

В С++ такие данные называются аббревиатурой POD (Plain Old Data).

L>то ptr=new MyStruct[size]

L>ptr[-1] не будет лежать кол-во элементов
L>наверное о где-то в другом месте лежит
L>(оптимизацию я выключил компиляции)


Нет, нигде не лежит. Тут вот какое дело, такие тонкие подробности реализации различаются у разных компиляторов. Чтобы разговор был предметным, то стоит оговоирить о каком компиляторе мы говорим.
Если мы о VC, то он смотрит, есть ли у типа элемента динамически аллокируемого вектора нетривиальный деструктор. Если есть, то при вызове delete[] надо будет позвать дестуркторы всех созданных в векторе объектов, а для этого надо знать, сколько их было. Вот он и делает "заначку" в памяти, под счётчик.
А если мы аллокируем вектор POD'ов, или других каких-то объектов, с тривиальным деструктором, то деструкторы звать не надо, соответственно и занать, сколько объектов должно быть разрушено не надо, соответственно и счётчик не нужен. В этом случае VC++ просто передаёт в operator new[]() чсло sizeof(Item) * countOfItems, и никаких начек не делает, и указатель не сдвигает.

Ну и delete[] в этом случае, тоже ничего не сдвигает, деструкторы не зовёт, а просто вызывает operator delete[] от переданного в конструкцию delete[] указателя и всё.
В частности, это значит, что на текущей реализации компилятора VC++ вектор POD-типов можно разрушать конструкцией delete БЕЗ квадтартных скобок. В смысле на текущей реализации VC++ это не будет приводить к каким-то наблюдаемым негативным эффектам. Но нет никаких гарантий, что такое поведение сохранится в будущем, и, тем более, нет гарантий, что другие компиляторы ведут себя так же. Так что на такие тонкие хаки лучше бы не закладываться...



Кстати, не поделишься, зачем тебе понадобилось заменить delete[] на delete без []?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Случай тривиальных деструкторв на VC++
От: locker  
Дата: 09.09.11 17:29
Оценка: 1 (1) :)
Здравствуйте, Erop, Вы писали:

E>Нет, нигде не лежит. Тут вот какое дело, такие тонкие подробности реализации различаются у разных компиляторов. Чтобы разговор был предметным, то стоит оговоирить о каком компиляторе мы говорим.

.....

да, речь идет о VS 2005, но я встречал new POD_Type[size]-delete (без []) в исходном коде, которая была откомпилированна в vs 2010,g++ (MiniGW),Intel Parallel Studio 2011
и они все успешно работают ))

я догадался что с ПОДами это не надо, но я думал что с смещением -1 хранится тогда размер выделенного блока (кстати тогда где он храниться,или есть какая та таблица ptr-size?)
(SuperREP (SREP) huge-dictionary LZ77 preprocessor, хорошо сжимает файлы с текстурами/данными, в репаках игр часто используется, сначала сжимают срепом, потом еще можно хорошо пожать lzma)

E>Кстати, не поделишься, зачем тебе понадобилось заменить delete[] на delete без []?

я как раз этого не хотел делать, но когда я встретил в исходном коде шаблон массива, то я заметил что там несоответствие new/delete
(вот так, нахожу всякие мелочи когда сам нечего толкового не могу написать)) как человек с комитета стандартизации))) )
написал автору, он написал чуть больше чем я в первом посте написал (под анонимом еще)

а вообще я исходник этой проги уже давно смотрю периодически, потмоу как смотреть исходник 7z/LZMA это ужас ))
проект FreeArc, архиватор с множеством алгоритмов сжатия и ручной настройкой методов сжатия (что очень круто), также есть возможжность использовать цепочку алгоритмов, например 4x4 алгоритм + lzma намультипроцессорных/многоядерных ПК дает хороший прирость скорости сжатия, првада память немлохо съест и немного ухудшится степень сжатия

имя автора кстати в благодарнастях WinRAR можно встретить (Булат Зиганшин)
Re[2]: sizeof() и struct
От: roman313  
Дата: 10.09.11 07:24
Оценка:
Ну это зависит от реализации new-delete.
К примеру, я сам реализую их, и в моей реализации delete px будет аналогом delete [] px;


S>2. new [] и delete без [] это UB. Так делать крайне не рекомендую.
Re[4]: функция и консрукция new и delete
От: slava_phirsov Россия  
Дата: 10.09.11 09:00
Оценка: -1
Здравствуйте, Erop, Вы писали:

E>и передаст в функцию operator delete() НЕ ТОТ АДРЕС блока. То есть 0xFAA1C, вместо 0xFAA18, который вырнул аллокатор!


Ну да и что? Вместо имени массива (как и положено) подставлен указатель на первый его элемент, т.е. 0xFAA1C. Можно (вообще-то, не надо) сделать еще и вот так:

Item* items = new Items[5];
//....
Item* p = &items[0];
delete p; // вызываем ::operator delete(0xFAA1C), и возвращаем в "кучу" кусок памяти, который занимал первый элемент массива items...
//...
delete [] items; // пытаемся вернуть в "кучу" все остальное, что при этом произойдет - вряд ли ответят даже разработчики компилятора...


ИМХО, проблема не в том, что

Для большинства аллокаторов это обозначает разрушение внутренней логической структуры

, а в том, что мы будем дважды возвращать в "кучу" один и тот же кусок памяти: в первый раз сам по себе, во второй раз — как часть массива. Интересный (чисто теоретически) вопрос, лечится ли это реализацией собственных версий operator new, operator new[], operator delete и operator delete[] — для конкретного класса или "в мировом масштабе"? Стандарт-то суров:

If not, the behavior is undefined

Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[7]: Случай тривиальных деструкторв на VC++
От: Erop Россия  
Дата: 13.09.11 12:52
Оценка:
Здравствуйте, locker, Вы писали:

L>я догадался что с ПОДами это не надо, но я думал что с смещением -1 хранится тогда размер выделенного блока (кстати тогда где он храниться,или есть какая та таблица ptr-size?)


Это отдано на ответственность аллокатора, и клиентам аллокатора не видно.
Например, аллокатор может гранулировать размер запрашиваемого болока и выделать память из разных диапазонов, соответствующих разным гранулам. Соотвественно, он по адресу сразу узнет размер...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: функция и консрукция new и delete
От: Erop Россия  
Дата: 13.09.11 14:06
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

E>>и передаст в функцию operator delete() НЕ ТОТ АДРЕС блока. То есть 0xFAA1C, вместо 0xFAA18, который вырнул аллокатор!


_>Ну да и что? Вместо имени массива (как и положено) подставлен указатель на первый его элемент, т.е. 0xFAA1C. Можно (вообще-то, не надо) сделать еще и вот так:


Это не так. И указатель на массив, и указатель на первый элемент в С++/С всегда совпадают, и в этом примере и то и то будет 0xFAA1C...
Адрес 0xFAA18 -- это уже кишки реализации, которые
1) Нестандартные
2) Не видны непосредственно

_>...а в том, что мы будем дважды возвращать в "кучу" один и тот же кусок памяти: в первый раз сам по себе, во второй раз — как часть массива. Интересный (чисто теоретически)


Это другая проблема. ТС вроде как не про это писал...

Стандарт-то суров:

If not, the behavior is undefined

Дык ясно, что так делать не надо, но всегда интересно знать. что будет, если сделаешь "как не надо"...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[5]: функция и консрукция new и delete
От: slava_phirsov Россия  
Дата: 13.09.11 14:47
Оценка:

лечится ли это реализацией собственных версий operator new, operator new[], operator delete и operator delete[]


Скорее нет, чем да. Насколько я понимаю, подумавши как следует, таким образом можно только изменить способ выделения/утилизации памяти, не более того.

P.S. В качестве источника сошлюсь на Майерса, 2-я книга, "Правило 8", где в доступной (во всяком случае, для меня), хотя и упрощенной форме, объясняется разница между оператором new и функцией operator new

P.P.S. Если я не прав — поправьте.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[6]: функция и консрукция new и delete
От: Erop Россия  
Дата: 13.09.11 14:52
Оценка:
Здравствуйте, slava_phirsov, Вы писали:

_>P.P.S. Если я не прав — поправьте.


Если честно, то не ясно, что именно ты хочешь личить? То, что аллокаторо может утилизировать только весь блок сразу? Ну так можешь написать свой аллокатор, который умеет и так и сяк, выделять на нём память, а объекты создавать по new размещения.
Завернуть всё это в набор шаблонных функций или методов, чтобы не зависеть от особенностей реализации new и delete и получить фабрику векторов обхектов, которые можно освобождать по частям...

Вопрос в том, на кой это могло бы быть надо...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: функция и консрукция new и delete
От: gegMOPO4  
Дата: 13.09.11 15:01
Оценка: :)
Здравствуйте, Erop, Вы писали:
E>Стандарт-то суров:

If not, the behavior is undefined

E>Дык ясно, что так делать не надо, но всегда интересно знать. что будет, если сделаешь "как не надо"...

«Ага…» — сказали суровые сибирские мужики и сунули под японскую пилу железный лом.
Re[7]: функция и консрукция new и delete
От: Erop Россия  
Дата: 13.09.11 15:36
Оценка:
Здравствуйте, gegMOPO4, Вы писали:

E>>Дык ясно, что так делать не надо, но всегда интересно знать. что будет, если сделаешь "как не надо"...

MOP>«Ага…» — сказали суровые сибирские мужики и сунули под японскую пилу железный лом.

1) Да, я родился и вырос в Сибири.
2) Понимать, как ломает программу то или иное UB, полезно, напирмер, для отладки всяких "гейзен-багов"...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.