Понимаю, что вопрос уже навяз в зубах, но поиск ничего конкретного не дал.
Вот полное описание объекта:
struct point
{
int x,y;
point() {}
point(int x_, int y_) : x(x_), y(y_) {}
};
Вопрос первый — является ли данный объект POD-типом? Согласно стандарту — не является, поскольку имеет явные конструкторы.
Вопрос второй — допустимо ли копировать данный объект целиком при помощи memcpy (хранить его в специализированном контейнере для POD-типов)?
Вопрос третий. Если не допустимо, то каково обоснование этому? Класс не содержит и не может содержать никаких неявных данных, типа VMT. Так же не имеет права иметь явный деструктор. Он не может наследоваться от чего-либо и не может иметь производных классов. Имеется так же запрет на какие-либо исключения. В нем даже нет никаких указателей — только базовые типы (это все — начальные условия). И только лишь от наличия одного конструктора, имеющего чисто утилитарное назначение (как синтаксический сахар) объект нельзя копировать при помощи memcpy? Это же маразм!
Вопрос четвертый. Где-либо, когда-либо, кто-либо сталкивался с реальными проблемами на практике, при "безбашенном" копировании подобных объектов при помощи memcpy (или memmove)? По-моему, таких проблем никогда не было и не будет.
Вопрос пятый — как следствие из четвертого. Возможна ли в принципе такая ситуация (при оговоренных выше ограничениях), что добавление простого конструктора изменит sizeof объекта?
То есть, я не вижу ни малейшего rationale подобных запретов. Да, как только в конструкторе аллокируется какая-либо память (а в деструкторе освобождается), объект сразу перестает быть POD-типом. Это совершенно логично и не вызывает ни малейших возражений. Но просто запрет каких бы то ни было конструкторов — это уже какой-то параноидальный объектно-ориентированный шовинизм
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
> struct point
> {
> int x,y;
> point() {}
> point(int x_, int y_) : x(x_), y(y_) {}
> };
>
> Вопрос первый — является ли данный объект POD-типом? Согласно стандарту — не является, поскольку имеет явные конструкторы.
Да, не является.
> Вопрос второй — допустимо ли копировать данный объект целиком при помощи memcpy (хранить его в специализированном контейнере для POD-типов)?
С точки зрения стандарта — нет.
> Вопрос третий. Если не допустимо, то каково обоснование этому?
Как это ни забавно, экономическое: ресурсы комитета очень ограничены. Если бы стандарт говорил, что такой класс можно копировать с помощью memcpy, то в стандарте нужно было бы указать: 1) что такое "такой класс"; 2) что именно произойдет, если его скопировать при помощи memcpy. Уже даже ответы на эти вопросы не так тривиальны, как может показаться на первый взгляд. Дальше — хуже. Фактически, нужно вводить особую разновидность объектов для поддержания memcpy для них. Когда начинается и заканчивается lifetime такого объекта? Определение POD не подходит, определение "обычных" объектов с нетривиальным конструктором — тоже. И т.п.
Если хочешь поддержки стандартом, пользуйся чуть-чуть другой стратегией создания объектов:
struct point
{
int x,y;
};
inline
point make_point(int x_, int y_) { point pt = { x_, y_}; return pt; }
И стандарт будет счастлив, и твоя совесть спокойна
> <...> только лишь от наличия одного конструктора, имеющего чисто утилитарное назначение (как синтаксический сахар) объект нельзя копировать при помощи memcpy? Это же маразм!
Проблема в выработке точной спецификации, что такое "конструктор, имеющий чисто утилитарное назначение". А учитывая наличие простого workaround, игра просто-напросто не стоит свеч.
> Вопрос четвертый. Где-либо, когда-либо, кто-либо сталкивался с реальными проблемами на практике, при "безбашенном" копировании подобных объектов при помощи memcpy (или memmove)? По-моему, таких проблем никогда не было и не будет.
Они могут появиться, если, скажем, в отладочной версии, компилятор добавит проверки, что объект был сконструирован, прежде чем разрешать им пользоваться, если класс имеет нетривиальный конструктор.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
P.S.
> Как это ни забавно, экономическое: ресурсы комитета очень ограничены. <...>
На самом деле, все еще проще. Если ты считаешь, что эта функциональность нужна, ты можешь выработать formal proposal, включая точные изменения в тексте стандарта, и комитетчики обязаны будут его рассмотреть. Вопрос: тебе оно настолько нужно?
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
ПК>struct point
ПК>{
ПК> int x,y;
ПК>};
ПК>inline
ПК>point make_point(int x_, int y_) { point pt = { x_, y_}; return pt; }
ПК>И стандарт будет счастлив, и твоя совесть спокойна
Очень не хочется возвращать значение... Это у меня такая фобия
А так можно?
struct point
{
int x,y;
static point make(int x_, int y_) { point pt = { x_, y_}; return pt; }
};
А так?
struct point
{
int x,y;
point clone(int x_, int y_) { point pt; memcpy(pt, this, sizeof(pt)); return pt; }
};
Но вообще-то говоря, мне хочется иметь некий тип, свободно копируемый при помощи memcpy и имеющий некую инициализацию по умолчанию плюс возможность присвоить один-два значения при создании. Все! Никаких аллокаций, ни деструкторов, ни чего прочего — чисто сишная структура с инициализацией.
IMO, это тот самый случай, когда спецификация сама себе наступила на хвост...
ПК>Они могут появиться, если, скажем, в отладочной версии, компилятор добавит проверки, что объект был сконструирован, прежде чем разрешать им пользоваться, если класс имеет нетривиальный конструктор.
Хм. А где-нибудь такой отладчик в реальности существует? В общем, я пока что рискну считать, что при данных условиях копировать можно. Есть в этом некий риск, но IMO, он на практике стремится к нулю.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>На самом деле, все еще проще. Если ты считаешь, что эта функциональность нужна, ты можешь выработать formal proposal, включая точные изменения в тексте стандарта, и комитетчики обязаны будут его рассмотреть. Вопрос: тебе оно настолько нужно?
В данном случае, весь formal proposal заключается в том, чтобы не доводить до абсурда. И просто исключить наличие конструктора из формальных признаков не-POD типа. В самом деле — в структуре может быть некий указатель, при этом она остается POD. Наличие простых (невиртуальных) функций-членов допустимо в POD (или я ошибаюсь?). Но я могу явно написать my_ctor() и my_dtor(), которые буду вызывать вручную. И если они будут аллокировать и освобождать память, то структура сразу перестает быть POD, хотя формально она так ей и остается. С этой точки зрения, конструктор ничем не отличается от функции-члена и не может являться никаким формальным признаком. А вот деструктор — уже может. Ибо в POD-структуре по определению никогда ничего не требуется уничтожать. А вот инициализировать при создании — требуется.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, Шахтер, Вы писали: Ш>Можно так делать Ш>[ccode] Ш>struct Point Ш> { Ш> int x,y; Ш> };
Ш>struct SetPoint : Point Ш> { Ш> SetPoint() : Point() {}
Ш> SetPoint(int x_,int y_) Ш> { Ш> x=x_; Ш> y=y_; Ш> } Ш> };
Это тоже не фонтан из за необходимости использовать явные присвоения заместо списка инициализации. Кстати, явный оператор присвоения в POD-типах разрешен?
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Шахтер,
ПК>> Как это ни забавно, экономическое: ресурсы комитета очень ограничены.
Ш> А эти экономические причины не помешали включить STL в стандарт языка?
Как видишь, не помешали
Ш> Мне, например, непонятно, почему ряд простейших вопросов (типа itoa), Ш> комитет урегулировать не может и эти мелочи годами отравляют жизнь.
Дык, все просто: volunteer. Только данная функция относится к стандарту C,
соответственно, нужно участвовать в работе комитета по стандартизации C.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
McSeem2,
M> В данном случае, весь formal proposal заключается в том, чтобы не M> доводить до абсурда. И просто исключить наличие конструктора из M> формальных признаков не-POD типа.
Так работать не будет. Конструктор по умолчанию включен в определение агрегатных
типов. POD-типы это свойство "наследуют" от агрегатных. Если к агрегатным типам
причислить типы, имеющие конструкторы по умолчанию, то "поплывут" многие места в
стандарте. Если изменить определение POD-типов, тоже много чего "поплывет". Нужно
очень аккуратно проходиться по тексту всего стандарта.
В общем, хочешь что-то в этом изменить -- volunteer.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>А зачем? Чем std::copy не устраивает?
А она умеет яростно копировать большие массивы, так же эффективно, как и memcpy?
Что-то мне подсказывает, что во многих случаях будут мощные накладные расходы, особенно, если структура содержит множество других структур. Да, в определенных частных случаях скорость та же, но полагаться на это нельзя — в других случаях (другая платформа, другой компилятор) — разница в разы. А мне хочется именно гарантии и стабильности.
M>> чисто сишная структура с инициализацией
ПК>Тут два противоречивых положения: сишные структуры не имеют "автоматической" ПК>инициализации по определению.
Ну хорошо, пусть они не будут POD. Тогда можно ввести понятие "bitwise copyable". То есть, такой объект, который 100%-тно сохраняет валидность при побитовом копировании. Тем более, что на практике, так оно и есть для объектов, описанных мной в начальном сообщении. Надо это всего лишь узаконить.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Это все понятно, но это никак не объясняет, почему подобные объекты (POD с конструктором и принципиально прзиционно независимые) нельзя копировать напрямую.
Еще раз. Пусть они не будут POD, и их нельзя инициализировать через "{}". Но пусть они будут bitwise copyable, причем официально. Тем более, что на практике они так copyable.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>А она умеет яростно копировать большие массивы, так же эффективно, как и memcpy? MS>Что-то мне подсказывает, что во многих случаях будут мощные накладные расходы, особенно, если структура содержит множество других структур. Да, в определенных частных случаях скорость та же, но полагаться на это нельзя — в других случаях (другая платформа, другой компилятор) — разница в разы. А мне хочется именно гарантии и стабильности.
Если речь идет о STLport — то умеет. Там есть copy helper, принимающий доп. параметр. В стандартной copy вызывается copy helper и передается ему кроме прочих параметров is_pod_type. Выбор происходит, естественно, в compile-time.
Здравствуйте, CrystaX, Вы писали:
CX>Если речь идет о STLport — то умеет. Там есть copy helper, принимающий доп. параметр. В стандартной copy вызывается copy helper и передается ему кроме прочих параметров is_pod_type. Выбор происходит, естественно, в compile-time.
Спасибо, но на это не подходит. Я не имею права полагаться на какую-либо конкретную имплементацию. Можно полагаться лишь на просто стандартизованную STL, а какая конкретно будет использоваться — мне не ведомо. А memcpy есть везде.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>Здравствуйте, Павел Кузнецов, Вы писали:
ПК>>А зачем? Чем std::copy не устраивает?
MS>А она умеет яростно копировать большие массивы, так же эффективно, как и memcpy? MS>Что-то мне подсказывает, что во многих случаях будут мощные накладные расходы, особенно, если структура содержит множество других структур. Да, в определенных частных случаях скорость та же, но полагаться на это нельзя — в других случаях (другая платформа, другой компилятор) — разница в разы. А мне хочется именно гарантии и стабильности.
Совсем не факт, что использование memcpy решит все твои проблемы.
Посмотри например здесь
В этом коде можно многое улучшить, но идея должна быть понятна.
Более того, если у тебя есть свой класс, который может быть "почти
скопирован" с помощью memcpy (допустим, требуются небольшие изменения) —
делаешь спецификацию шаблона move_traits и наслаждаешься.
Спасибо за ссылки. Во-первых, насчет производительности. Я знаю, что можно копировать быстрее, чем memcpy. Даже простой цикл оптимизатор может векторизовать и задействовать 64-битовые операции MMX. Но здесь нет никаких гарантий. Пусть (даже в большинстве случаев) memcpy работает медленнее на несколько процентов. Но она обладает очень важным свойством — предсказуемостью производительности.
Хорошо, раскрываю карты. Дело даже не в производительности. Как мне при поможи std::copy() корректно "вытащить" данные из памяти, находящиеся по невыровненному адресу? По-моему никак. На всяких SGI сразу получаем автобусную ошибку. Для чего это надо? А вот для чего. У нас есть линейный упакованный поток данных в памяти:
"char cmd; int x; int y".
Этих данных может быть многие мегабайты. Если мы представим их в виде структуры:
struct point
{
char cmd;
int x,y;
};
Мы неизбежно получаем полуторный перерасход по памяти из за выравнивания. Если же мы отделим cmd от x,y, то получаем упакованные и невыровненные данные. "Легализовать" невыровненные данные можно только простым побитовым копированием. Разделить на два потока в данном случае можно, но в реальности, структура этого потока значительно сложнее — что-то типа бинарного XML. И там иногда попадаются достаточно сложные структуры. Конечно же, в этих структурах могут быть и "дырки" от выравнивания, но их процент исчезающе мал, основная масса данных является упакованной. Сложные структуры всегда восстанавливаются корректно, поскольку были записаны ровно тем же путем. Получается, что C++ в чистом виде не справляется с решением подобной задачи. И только лишь из за того, что запретили побитовое копирование объектов, имеющих конструктор. Классическое решение — сделать "нормальные" классы с методами "serialize/deserialize". Но во-первых, от этого появляется много мусорного кода (внутри мне все равно придется использовать побайтное копирование), а во-вторых, это не всегда возможно. Я все-таки склонен взять на себя риск нарушить этот запрет.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
McSeem2 wrote:
> Хорошо, раскрываю карты. Дело даже не в производительности. Как мне > при поможи std::copy() корректно "вытащить" данные из памяти, > находящиеся по невыровненному адресу? По-моему никак.
Данный конкретный случай в std::copy как раз работать будет.
C>В этом коде можно многое улучшить, но идея должна быть понятна.
Идея понятна, спасибо. Но чисто в качестве придирки — это уже полное безобразие (я не хочу наглеть до такой степени): Если объект является bitwise_moveable, то он по определению не может иметь нетривиальный деструктор (который что-то там реально уничтожает). Если же деструктор действительно что-то делает, сразу ставим жирный крест на побитовом копировании. Такое ограничение представляется мне вполне разумным, ибо опасно.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Ну что тут сказать В принципе, я с тобой согласен.
Ну а в желании "взять на себя риск нарушить этот запрет" ты далеко не одинок. По крайней мере я встречал это нарушение не так уж и редко
McSeem2 wrote:
> C>В этом коде можно многое улучшить, но идея должна быть понятна. > Идея понятна, спасибо. Но чисто в качестве придирки — это уже полное > безобразие (я не хочу наглеть до такой степени): Если объект является > bitwise_moveable, то он по определению не может иметь нетривиальный > деструктор (который что-то там реально уничтожает).
Не обязательно, у меня используются бит-копируемые объекты, в которых в
деструкторе делаются assert'ы и некоторые другие проверки. Так что пусть
живет.
> Если же деструктор действительно что-то делает, сразу ставим жирный > крест на побитовом копировании. Такое ограничение представляется мне > вполне разумным, ибо опасно.
У меня по умолчанию побитовое копирование будет работать только для
простых типов. Для более сложных нужно ставить флажок
ENABLE_BITWISE_MOVEABLE — если его явно поставить, то уж сам виноват....
McSeem2 wrote:
> C>Данный конкретный случай в std::copy как раз работать будет. > > // TEMPLATE FUNCTION copy >template<class _II, class _OI> inline > _OI copy(_II _F, _II _L, _OI _X) > {for (; _F != _L; ++_X, ++_F) > *_X = *_F; > return (_X); } > > Как? Здесь будет мгновенная автобусная ошибка.
Компилятор обнаружит, что размер структуры не позволяет срабатывать
выравниванию, и сгенерирует byte-by-byte копирование. А байты везде
читаются без выравнивания.
Здравствуйте, Cyberax, Вы писали:
C>Компилятор обнаружит, что размер структуры не позволяет срабатывать C>выравниванию, и сгенерирует byte-by-byte копирование. А байты везде C>читаются без выравнивания.
Не верю!
struct point { int x,y; };
char array[100];
point p[4];
std::copy((point*)(array+1), (point*)(array+1+sizeof(p)), p);
И что, на SGI не возникнет автобусной ошибки? Я ведь проверю! И очень скоро проверю!
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
#include <algorithm>
struct point { int x,y; };
void main()
{
char array[100];
point p[4];
std::copy((point*)(array+1), (point*)(array+1+sizeof(p)), p);
}
rndusexwally:~>CC buserror.cpp
cc-3563 CC: WARNING File = buserror.cpp, Line = 5
return type of function "main" must be "int"
void main()
^
rndusexwally:~>./a.out
Bus error (core dumped)
rndusexwally:~>
Как и ожидалось... Интересно, откуда компилятор может заранее знать, что указатель не выровнен? Это возможно только в run-time, что есть накладно весьма.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, dad, Вы писали:
dad>"автобусная" ошибка регламентируется стандартом? dad>Внешне криминала не видно, получаешь 4 байта [1-4]
Это регламентируется не стандартом C++, а архитектурой процессоров. Если процессор не позволяет так копировать, а некий (абстрактный) язык позволяет, то bus error в run-time будет в любом языке. А стандарт не имеет права запрещать подобный криминал (да и не может по жизни). То есть, это все — на совести программиста.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
dad>>"автобусная" ошибка регламентируется стандартом? dad>>Внешне криминала не видно, получаешь 4 байта [1-4]
MS>Это регламентируется не стандартом C++, а архитектурой процессоров. Если процессор не позволяет так копировать, а некий (абстрактный) язык позволяет, то bus error в run-time будет в любом языке. А стандарт не имеет права запрещать подобный криминал (да и не может по жизни). То есть, это все — на совести программиста.
А на какой платформе такое у тебя? С чем это связано чисто технически.
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
Здравствуйте, dad, Вы писали:
dad>А на какой платформе такое у тебя? С чем это связано чисто технически.
На SGI, на Sun Ultra Sparc, на HP, на DEC Alpha и многих других. Ну не культурно это обращаться к памяти как к int, если адрес не выровнен на int. На Intel, кстати тоже. Это хоть и работает технически, но значительно медленнее, чем в случае выровненных данных. Именно поэтому в структурах появляюся "дыры" от выравнивания. И sizeof(struct) != sum(sizeof(data_members)) в общем случае.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
McSeem2 wrote:
> На SGI, на Sun Ultra Sparc, на HP, на DEC Alpha и многих других. Ну не культурно это обращаться к памяти как к int, если адрес не выровнен на int. На Intel, кстати тоже. Это хоть и работает технически, но значительно медленнее, чем в случае выровненных данных.
Я читал, что когда данные в кэш линейке, выранивание на скорость доступа на ia32 не оказывает никакого влияния. Сам не проверял.
> Именно поэтому в структурах появляюся "дыры" от выравнивания. И sizeof(struct) != sum(sizeof(data_members)) в общем случае.
В-общем случае размер структуры кратен ее выравниванию. Это связано с тем, что в массивах между элементами не может быть "дыр", поэтому если размер структуры не был бы кратен выравниванию, массив из этих структур создать не было бы возможно.
Я и сложные типы типа std::vector, std::string двигаю по памяти при помощи memcpy... а это совсем не POD-типы. Говорят, возможно в будущем С++ стандарте будет какая-то специальная move-семантика. Но зачем усложнять язык, если это и сейчас возможно при помощи memcpy.
Правильно работающая программа — просто частный случай Undefined Behavior
McSeem2 wrote:
> Не верю! > > struct point { int x,y; }; > char array[100]; > point p[4]; > std::copy((point*)(array+1), (point*)(array+1+sizeof(p)), p); > > И что, на SGI не возникнет автобусной ошибки? Я ведь проверю! И очень > скоро проверю!
Так вот будет:
struct point { int x,y; };
char *array=(char*)malloc(100)
point p[4];
std::copy(&p[0], &p[0]+4, array);
dad wrote:
> MS>Как? Здесь будет *мгновенная автобусная ошибка*. > ?
Bus error (passangers dumped)
На нормальных системах (SPARCах, I64 и т.п.) переменная должна быть
выравнена по своему размеру. То есть 8 байтный double должен быть
выравнен по границе 8 байт, 4-байтный int по границе 4 байт и т.п.
_Winnie wrote:
> Я и сложные типы типа std::vector, std::string *двигаю* по памяти при > помощи memcpy... а это совсем не POD-типы.
Тут ведь как, по Стандарту такое делать нельзя, но на практике обычно
работает
Специально для поддержки быстрого move'а контейнеров я написал свою
STL-совместимую библиотеку. Например, возврат вектора из метода у меня
выглядит так:
При этом обычно происходит всего 1 динамическая аллокация (у меня
используется _grow для увеличения размера блока).
В результате с коллекциями стало работать намного приятнее.
> Говорят, возможно в будущем С++ стандарте будет какая-то специальная > move-семантика. Но зачем усложнять язык, если это и сейчас возможно > при помощи memcpy.
HINT: А если в объекте есть указатели на самого себя? Или если this
используется как ключ в какой-нибудь карте?
Здравствуйте, MaximE, Вы писали:
ME>Я читал, что когда данные в кэш линейке, выранивание на скорость доступа на ia32 не оказывает никакого влияния. Сам не проверял.
Я тоже слышал такую версию. Но с другой стороны, я так же слышал, что для MMX/SSE операций выравнивание очень существенно влияет на производительность (причем, выравнивание на 64 или 128 бит).
>> Именно поэтому в структурах появляюся "дыры" от выравнивания. И sizeof(struct) != sum(sizeof(data_members)) в общем случае.
ME>В-общем случае размер структуры кратен ее выравниванию. Это связано с тем, что в массивах между элементами не может быть "дыр", поэтому если размер структуры не был бы кратен выравниванию, массив из этих структур создать не было бы возможно.
А что такое "выравнивание структуры"? Выравнивание по максимальному элементу данных?
struct v
{
char a;
int b;
char c;
int d;
char e;
int f;
char g;
int h;
};
Здесь все зависит от того, упорядочиват ли компилятор данные в структуре. Если нет, то размер будет таков, как если бы мы все char заменили на int. И соответственно, 4 дырки. Но вооще-то, можно отсортировать по убыванию размера, и тогда будет только небольшое пустое место в конце. Но что-то предполагать заранее нельзя.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
McSeem2 wrote:
> ME>Я читал, что когда данные в кэш линейке, выранивание на скорость > доступа на ia32 не оказывает никакого влияния. Сам не проверял. > Я тоже слышал такую версию. Но с другой стороны, я так же слышал, что > для MMX/SSE операций выравнивание очень существенно влияет на > производительность (причем, выравнивание на 64 или 128 бит).
Насколько я помню, SSE не умеет работать с невыровненными данными.
MS>А что такое "выравнивание структуры"? Выравнивание по максимальному элементу данных? MS>
MS>struct v
MS>{
MS> char a;
MS> int b;
MS> char c;
MS> int d;
MS> char e;
MS> int f;
MS> char g;
MS> int h;
MS>};
MS>
MS>Здесь все зависит от того, упорядочиват ли компилятор данные в структуре.
Переупорядочивать — в данном случае не имеет права. Ибо 9.2/1:
Nonstatic data members of a (non-union) class declared without an intervening access-specifier are allocated so that later members have higher addresses within a class object. The order of allocation of nonstatic data members separated by an access-specifier is unspecified (11.1). Implementation alignment requirements might cause two adjacent members not to be allocated immediately after each other; so might requirements for space for managing virtual functions (10.3) and virtual base classes (10.1).
MS> Если нет, то размер будет таков, как если бы мы все char заменили на int. И соответственно, 4 дырки. Но вооще-то, можно отсортировать по убыванию размера, и тогда будет только небольшое пустое место в конце. Но что-то предполагать заранее нельзя.
On Wed, 27 Jul 2005 19:37:35 +0400, McSeem2 <12737@users.rsdn.ru> wrote:
[]
> В-общем случае размер структуры кратен ее выравниванию. Это связано с тем, что в массивах между элементами не может быть "дыр", поэтому если размер структуры не был бы кратен выравниванию, массив из этих структур создать не было бы возможно. > > А что такое "выравнивание структуры"? Выравнивание по максимальному элементу данных?
Это максимальное выравнивание члена стр-ры. Выравнивание встроенных типов есть их размер.
>
> struct v
> {
> char a;
> int b;
> char c;
> int d;
> char e;
> int f;
> char g;
> int h;
> };
>
> > Здесь все зависит от того, упорядочиват ли компилятор данные в структуре.
В POD компилятор не может переупорядочивать члены.
> Если нет, то размер будет таков, как если бы мы все char заменили на int. И соответственно, 4 дырки. Но вооще-то, можно отсортировать по убыванию размера, и тогда будет только небольшое пустое место в конце. Но что-то предполагать заранее нельзя.
Максимальное выравнивание члена в этой стр-ре — sizeof(int). Поэтому sizeof(v) % sizeof(int) == 0 и alignof(v) == alignof(int).
dad wrote:
> Ну тут понятно, но непонятно где криминал в примере МакСима вот тут: > >char a[5] = {0}; >int i = *(int*)(a + 1); > > что тут по чему выровнено? мы знаем что char это 1 байт, массив всегда > располагается последовательно.
Предположим, что массив "a" начинается с адреса 4. Тогда a+1==5, попытка
разыменовать такой указатель на int вызовет автобусную ошибку. Правильно
надо писать примерно так (опять код из моей библиотеки ):
//This union is used to ensure that COW storage elements will be
properly aligned.
template<class AllocatorT> union cow_storage_element
{
typedef boost::detail::atomic_count counter_type;
typedef typename AllocatorT::value_type value_type;
typename boost::type_with_alignment<
(boost::alignment_of<counter_type>::value >
boost::alignment_of<value_type>::value)
? boost::alignment_of<counter_type>::value
: boost::alignment_of<value_type>::value
>::type a;
char counter_[sizeof(counter_type)];
char data_[sizeof(value_type)];
};
C>Предположим, что массив "a" начинается с адреса 4. Тогда a+1==5, попытка C>разыменовать такой указатель на int вызовет автобусную ошибку.
Почему он вызовает ошибку то?
Мы точно знаем по адресу 5 лежит 1 байт, а 6, 7 и 8 еще три
другое дело, когда мы не значем что лежит там
как в случае с
struct {
char a[2];
int b;
};
(int*)(a + 1) -> тут понятно..
Или предполагается, что даже элементы массива типа char
могут быть выровнены на границу слова, например, без всякого нашего ведома?
C>Правильно надо писать примерно так (опять код из моей библиотеки ):
Блин я такое даже прочитать не могу
Веру-ю-у! В авиацию, в научную революци-ю-у, в механизацию сельского хозяйства, в космос и невесомость! Веру-ю-у! Ибо это объективно-о! (Шукшин)
dad wrote:
> C>Предположим, что массив "a" начинается с адреса 4. Тогда a+1==5, > попытка > C>разыменовать такой указатель на int вызовет автобусную ошибку. > Почему он вызовает ошибку то?
Массив "a" — это массив однобайтных символов (не требующих
выравнивания), компилятор имеет право разместить этот массив как угодно.
В том числе, может разместить по адресу, кратному 4. Следовательно
второй элемент массива (a+1) будет иметь адрес вида 4k+1.
> Мы точно знаем по адресу 5 лежит 1 байт, а 6, 7 и 8 еще три
Неважно что там лежит. Число 5 не делится на 4 (размер int'а), значит
произойдет _аппаратная_ ошибка выравнивания. Ну не может процессор с
такими данными работать.
> C>Правильно надо писать примерно так (опять код из моей библиотеки ): > Блин я такое даже прочитать не могу
Можно записать более просто:
#ifdef _WIN32
//Non-aligned types can be safely used on win32.template<class T> size_t alignment_of()
{
return 1;
}
#else//General fallback implementation.template<class T> size_t alignment_of()
{
return sizeof(T);
}
#endif
...
char a[5+(alignment_of(int)+1)/2]; //This array will have enough space for properly aligned int.
int i=*(int*)(a+a%alignment_of(int)); //Get properly aligned integer.
On Wed, 27 Jul 2005 23:32:21 +0400, Cyberax <37054@users.rsdn.ru> wrote:
> dad wrote: > >> Ну тут понятно, но непонятно где криминал в примере МакСима вот тут: >> >> char a[5] = {0}; >> int i = *(int*)(a + 1); >> >> что тут по чему выровнено? мы знаем что char это 1 байт, массив всегда >> располагается последовательно. > > Предположим, что массив "a" начинается с адреса 4. Тогда a+1==5, попытка > разыменовать такой указатель на int вызовет автобусную ошибку. Правильно > надо писать примерно так (опять код из моей библиотеки ):
[]
За шаблонами кода не видно. Чем не устраивает рабоче-крестьянский:
Здравствуйте, dad, Вы писали:
dad>Почему он вызовает ошибку то? dad>Мы точно знаем по адресу 5 лежит 1 байт, а 6, 7 и 8 еще три dad>другое дело, когда мы не значем что лежит там dad>как в случае с dad>struct { dad> char a[2]; dad> int b; dad>};
Неважно, что и как там лежит. Важно, что адрес нечетный.
mov eax,[esi]
На многих процессорах, аналог этой команды вызовет bus error, в случае, если текущее значение esi не кратно 4.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.