Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где?
Еще подсказка: GCC 3.4.1 и более поздние выдают следующий результат:
Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
MS> [...]
MS>
Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
MS>Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где?
Мне вот что-то printf не нравится... "X" означает, что ожидается тип int. Если разрядность int отличается от 32, то и возникает Undefined behavior... Правда, является ли это причиной некорректной работы в данном случае, я не знаю...
MS>> [...]
MS>> АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
В дополнение: В GCC это управляется с пом. ключа -fstrict-aliasing.
Здравствуйте, McSeem2, Вы писали:
MS>А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.
Так вот, не могли бы знатоки популярно объяснить, когда точно возникает этот strict aliasing violation?
По идее, всегда при попытке преобразования указателя к другому типу.
Но malloc возващает void*, который по идее надо преобразовать к нужному типу. Что, это становится нелегальным?
Далее, например я делаю свой аллокатор, который, скажем, банально работает в статической памяти.
static char mem_pool[max_size];
. . .
int* allocate_int()
{
. . .
return (int*)(&mem_pool[index_aligned_to_int]); // To UB or not to UB?
}
Имеет ли место strict aliasing violation в этом случае?
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>Но malloc возващает void*, который по идее надо преобразовать к нужному типу. Что, это становится нелегальным?
Нет, преобразование из void* к нужному типу легально. Тебе гарантировано, что void* вернет указатель на блок с наиболее общим alignment'ом.
MS>Далее, например я делаю свой аллокатор, который, скажем, банально работает в статической памяти. MS>
MS>static char mem_pool[max_size];
MS>. . .
MS>int* allocate_int()
MS>{
MS> . . .
MS> return (int*)(&mem_pool[index_aligned_to_int]); // To UB or not to UB?
MS>}
MS>
MS>Имеет ли место strict aliasing violation в этом случае?
Да. Так как поведение такого кода неопределено (откуда компилятор знает что у тебя действительно index_aligned_to_int правильный?). Но на практике обязано работать
Здравствуйте, Cyberax, Вы писали:
MS>>Имеет ли место strict aliasing violation в этом случае? C>Да. Так как поведение такого кода неопределено (откуда компилятор знает что у тебя действительно index_aligned_to_int правильный?). Но на практике обязано работать
Компилятор не может знать ничего про выравнивание в этом случае — это все на моей совести.
По-моему, они чего-то перемудрили с этим строгим алиасингом. Самое неприятное, что предупреждения может и не быть, но оптимизатор возьмет да выкинет реально необходимые операции.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, McSeem2, Вы писали:
MS>>>Имеет ли место strict aliasing violation в этом случае? C>>Да. Так как поведение такого кода неопределено (откуда компилятор знает что у тебя действительно index_aligned_to_int правильный?). Но на практике обязано работать MS>Компилятор не может знать ничего про выравнивание в этом случае — это все на моей совести.
Поэтому с точки зрения Стандарта — это UB.
MS>По-моему, они чего-то перемудрили с этим строгим алиасингом. Самое неприятное, что предупреждения может и не быть, но оптимизатор возьмет да выкинет реально необходимые операции.
Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])
Здравствуйте, Андрей Коростелев, Вы писали:
АК>Здравствуйте, McSeem2, Вы писали:
АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
Блин гавно какоето ... эт я не по поводу ответа , а по вышеописанному
MS>> [...]
MS>> АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C
McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
MS>> [...]
MS>> АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault?!!!
Правильно ли я понял?
Здравствуйте, eao197, Вы писали:
E>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C
E>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.
Вообще, такая проблема наблюдается на процессорах, адресация в которых к данным должна быть выравнена по границе 2, 4 и т.д. (8 не встречал)
Так вот, сразу же налетел на ARM (AT91RM9200) на выравнивание по границе кратной 2.
Потратил на это порядка несольких часов, просто ушли в холостую.
А произошло это из-за того,что надо было из буфера (char*)[1] получить uint32_t или наоборот в (char*)[1] положить uint32_t.
Тут бы по выравниванию памяти вообще механизм блокировок придумать какой-то было бы просто замечательно.
eao197 wrote:
> McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки > для работы непосредственно с железом, то запрещение простого свопинга > байт/слов выглядит полным маразмом.
Маразмом является приведение неприводимого. А задача своппинга решается битовыми операциями.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Здравствуйте, Smal, Вы писали:
S>А еще лучше так (и короче, и переносимо).
Согласен, только вот платформа x86 изначально разваращает и в принципе провоцирует использование в С приведение типов, потому как у нее при чтении невыровненной памяти происходит два чтения смежных областей памяти (на 80x86 — 80x286 соответсвенно двух слов) и все пучком, время выполнения увеличивается только.
Здравствуйте, Alexander Pazdnikov, Вы писали:
E>>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C
E>>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.
AP>Вообще, такая проблема наблюдается на процессорах, адресация в которых к данным должна быть выравнена по границе 2, 4 и т.д. (8 не встречал) AP>Так вот, сразу же налетел на ARM (AT91RM9200) на выравнивание по границе кратной 2. AP>Потратил на это порядка несольких часов, просто ушли в холостую. AP>А произошло это из-за того,что надо было из буфера (char*)[1] получить uint32_t или наоборот в (char*)[1] положить uint32_t. AP>Тут бы по выравниванию памяти вообще механизм блокировок придумать какой-то было бы просто замечательно.
Выравнивание -- это не новая проблема. Но как раз в приведенном McSeem2 механизме свопинга проблем быть не должно. Поскольку uint32_t* будет выравнен на кратную 4-м границу, uint16_t* на кратную 2-м границу, а char* на любой адрес. Такие заморочки были на SPARC-ах когда-то очень давно. И ничего, все работало.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Выравнивание -- это не новая проблема. Но как раз в приведенном McSeem2 механизме свопинга проблем быть не должно. Поскольку uint32_t* будет выравнен на кратную 4-м границу, uint16_t* на кратную 2-м границу, а char* на любой адрес. Такие заморочки были на SPARC-ах когда-то очень давно. И ничего, все работало.
Мда... Сейчас перевожу проект под ARM с Си на С++, так что чувствую, если включить -O2, то в кусках могут появиться интересные глюки, невоспроизводимые при -g
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, Smal, Вы писали:
S>>А еще лучше так (и короче, и переносимо).
S>>
S>>void swap_words(uint32_t* arg)
S>>{
S>> uint16_t hi = (uint16_t) ((*arg & 0xFFFF0000u)>>16);
S>> uint16_t lo = (uint16_t) ((*arg & 0x0000FFFFu));
S>> *arg = hi | (lo << 16);
S>>}
S>>
E>А по скорости?
Лениво тестировать. Но очень хочется верить, что это не bottle-neck %).
Оптимизировать нужно алгоритмы, а не битовые операции. Если окажется, что
данная операция сжирает хоть какое-то значимое время, то значит нужно
оптимизировать алгоритм, её вызывающий.
Кто-нибудь!!!
Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault (при sp[0] = hi)?!!!
Правильно ли я понял?
Здравствуйте, Smal, Вы писали:
S>>>А еще лучше так (и короче, и переносимо).
S>>>
S>>>void swap_words(uint32_t* arg)
S>>>{
S>>> uint16_t hi = (uint16_t) ((*arg & 0xFFFF0000u)>>16);
S>>> uint16_t lo = (uint16_t) ((*arg & 0x0000FFFFu));
S>>> *arg = hi | (lo << 16);
S>>>}
S>>>
E>>А по скорости? S>Лениво тестировать. Но очень хочется верить, что это не bottle-neck %). S>Оптимизировать нужно алгоритмы, а не битовые операции. Если окажется, что S>данная операция сжирает хоть какое-то значимое время, то значит нужно S>оптимизировать алгоритм, её вызывающий.
Угу. Только вот когда придется мегабайты данных преобразовывать из host byte order в network byte order разница в скорости двух простых решений может оказаться слишком существенной. Так что результаты замеров были бы очень интересны.
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Smal, Вы писали:
S>А еще лучше так (и короче, и переносимо).
Первый вариант тоже вполне переносим. И его тоже можно записать достаточно коротко:
void swap_words(uint32_t* arg)
{
union { uint32_t v; uint16_t sp[2]; } u = {*arg};
std::swap(u.sp[0], u.sp[1]);
*arg = u.v;
}
Здравствуйте, eao197, Вы писали:
E>Угу. Только вот когда придется мегабайты данных преобразовывать из host byte order в network byte order разница в скорости двух простых решений может оказаться слишком существенной. Так что результаты замеров были бы очень интересны.
#include <stdio.h>
typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t;
void swap_words1(uint32_t* arg)
{
uint16_t* sp = (uint16_t*)arg;
uint16_t hi = sp[0];
uint16_t lo = sp[1];
sp[1] = hi;
sp[0] = lo;
}
void swap_words2(uint32_t* arg)
{
uint16_t hi = (uint16_t) ((*arg & 0xFFFF0000u)>>16);
uint16_t lo = (uint16_t) ((*arg & 0x0000FFFFu));
*arg = hi | (lo << 16);
}
void swap_words3(uint32_t* arg)
{
union { uint16_t sp[2]; uint32_t v; } u;
u.v = *arg;
uint16_t hi = u.sp[0];
uint16_t lo = u.sp[1];
u.sp[1] = hi;
u.sp[0] = lo;
*arg = u.v;
}
int main()
{
uint32_t v = 0xFFFF0000u;
int i = 0;
for ( i = 0; i != 10000000; ++i )
swap_words1(&v);
for ( i = 0; i != 10000000; ++i )
swap_words2(&v);
for ( i = 0; i != 10000000; ++i )
swap_words3(&v);
printf("%08X\n", v);
return 0;
}
gcc
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
54.17 0.26 0.26 10000000 26.00 26.00 swap_words3
25.00 0.38 0.12 10000000 12.00 12.00 swap_words1
10.42 0.43 0.05 10000000 5.00 5.00 swap_words2
10.42 0.48 0.05 main
gcc -O1
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
41.67 0.05 0.05 10000000 5.00 5.00 swap_words2
25.00 0.08 0.03 10000000 3.00 3.00 swap_words3
16.67 0.10 0.02 10000000 2.00 2.00 swap_words1
16.67 0.12 0.02 main
gcc -O2
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
46.15 0.12 0.12 main
19.23 0.17 0.05 10000000 5.00 5.00 swap_words2
19.23 0.22 0.05 10000000 5.00 5.00 swap_words3
15.38 0.26 0.04 10000000 4.00 4.00 swap_words1
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP>Здравствуйте, Smal, Вы писали:
AP>Кто-нибудь!!! AP>Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault (при sp[0] = hi)?!!! AP>Правильно ли я понял?
Можно получить все что угодно, ибо UB.
В реальности я трейсил адреса. arg и sp совпадают.
Правда при трейсах меняется результат, ибо это гейзенбаг.
Что там внутри происходит, я не смотрел. Надо дизассемблировать и смотреть — лень.
Да и какая разница. А т.к. UB, то можно получить и SF .
А можно увидеть ассемблерный листинг этого чуда? Хочется понять почему для 2-х байтового значения потребовалось более другое выравнивание, чем для 4-х байтового.
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP>Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault (при sp[0] = hi)?!!! AP>Правильно ли я понял?
Нет. Не правильно.
sp будет указывать куда-то на стек. Но вот значение переменной v2 (кажется так её там звали?) может оказаться не на стеке, а в регистре, например, в этот момент.
Я думаю, что в приведённом коде происходит следующее. Функция подставляется, зависимость значения переменной v2 от манипуляций с sp компилятором не отслеживается благодаря обсуждаемому правилу, соответсвенно компилятор видит, что v2 инициализируется, потом от него берут адрес, не являющийся алиасом, потом печатают, и скорее всего просто печатает константу...
Можно код посмотреть, в принципе.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
А>А можно увидеть ассемблерный листинг этого чуда? Хочется понять почему для 2-х байтового значения потребовалось более другое выравнивание, чем для 4-х байтового.
я делал ассемблерные листинги обоих вариантов, скомпиленных GCC 4.1.x (linux)
интересно то что команды в дебажном и релизном варианте не отличаются вообще.
но в релизном некоторые переставлены местами (для выравнивания команд на границу слова?)
А я разве утверждал, что ваш вариант будет самым медленным?
Вот приведенные вами цифры показывают, что на битовых операциях не только переносимее, но и быстрее.
На VC++ 7.1 вариант с union вообще самым медленным оказался.
И кстати, VC++ 7.1 не компилирует вариант swap_words3 (а GCC компилирует), поскольку переменные в функции нужно объявлять до использования:
void swap_words3(uint32_t* arg)
{
union { unsigned short sp[2]; unsigned int v; } u;
unsigned short hi;
unsigned short lo;
u.v = *arg;
hi = u.sp[0];
lo = u.sp[1];
u.sp[1] = hi;
u.sp[0] = lo;
*arg = u.v;
}
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, Smal, Вы писали:
S>>Комментарии?
E>А я разве утверждал, что ваш вариант будет самым медленным?
Просто замеры для таких "мелких" операций меня всегда напрягают.
Мне только один раз действительно пришлось ускорять некоторую элементарную
операцию — вычисление барицентрических координат. Но я делал это уже после того, как
исчерпал все оптимизации по алгоритму, прикрутил кэши и т.п.
E>Вот приведенные вами цифры показывают, что на битовых операциях не только переносимее, но и быстрее.
Только если без оптимизаций. Хотя, если выкинуть первый вариант как не работающий, то да. .
E>На VC++ 7.1 вариант с union вообще самым медленным оказался.
.
E>И кстати, VC++ 7.1 не компилирует вариант swap_words3 (а GCC компилирует), поскольку переменные в функции нужно объявлять до использования:
Это С++-ные привычки .
Здравствуйте, Андрей Коростелев, Вы писали:
АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го.
Почему "введенное в С99"? Если Вы о тексте из 6.5/7, то он представляет собой просто другую редакцию ISO/IEC 9899:1990, 6.3:
An object shall have its stored value accessed only by an lvalue that has one of the following types: 36
— the declared type of the object,
— a qualified version of the declared type of the object,
— a type that is the signed or unsigned type corresponding to the declared type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the declared type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
36 The intent of this list is to specify those circumstances in which an object may or may not be aliased.
Здравствуйте, eao197, Вы писали:
E>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C
В C++ все ровно то же самое.
5.17/8 If the value being stored in an object is accessed from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined.
Здравствуйте, Кодт, Вы писали:
К>А ещё одна версия — правда, дикая — то, что на целевой платформе 32-битный байт! К>Ты уверен, что не для БЭСМ-6 компилируешь?
три вопрпоса
1) Где качнуть gcc для БЭСМ-6
2) АФАИК там байт был 48-битный
3) Как целевая платформа зависит от опции -o ?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Умный вопрос по C/C++
От:
Аноним
Дата:
23.08.07 11:28
Оценка:
Здравствуйте, Awaken, Вы писали:
А>>А можно увидеть ассемблерный листинг этого чуда? Хочется понять почему для 2-х байтового значения потребовалось более другое выравнивание, чем для 4-х байтового.
A>я делал ассемблерные листинги обоих вариантов, скомпиленных GCC 4.1.x (linux) A>интересно то что команды в дебажном и релизном варианте не отличаются вообще. A>но в релизном некоторые переставлены местами (для выравнивания команд на границу слова?)
А у вас получился "нерабочий" вариант? Т.е. тот который выводит неизмененное значение переменной?
Скорее всего должно получиться, как написал Егор в http://rsdn.ru/forum/message/2631549.1.aspx
, т.е. значение переменной меняется, но выводится на экран значение константы, а не переменной. Адреса в таком случае должны быть одинаковыми или это уже похоже на баг оптимизатора.
Здравствуйте, McSeem2, Вы писали:
MS>Так вот, не могли бы знатоки популярно объяснить, когда точно возникает этот strict aliasing violation?
Не знаю, что такое strict aliasing violation, а неопределенное поведение возникает здесь:
MS> uint16_t hi = sp[0];
(Строго говоря, приведенный Вами код ill-formed и не является переносимым, но, как я понимаю, Вас интересовало не это.)
MS>По идее, всегда при попытке преобразования указателя к другому типу.
Нет, приведение указателей тут ни при чем.
MS>Но malloc возващает void*, который по идее надо преобразовать к нужному типу. Что, это становится нелегальным?
Нет, конечно. Указатель, возвращаемый malloc, специально выровнен так, чтобы позволить размещение в этой памяти любого объекта.
MS>Далее, например я делаю свой аллокатор, который, скажем, банально работает в статической памяти.
MS>static char mem_pool[max_size];
MS>. . .
MS>int* allocate_int()
MS>{
MS> . . .
MS> return (int*)(&mem_pool[index_aligned_to_int]); // To UB or not to UB?
MS>}
MS>Имеет ли место strict aliasing violation в этом случае?
Здесь только одна проблема: неверное выравнивание. Никаких гарантий о выравнивании статического массива символов нет.
Здравствуйте, Erop, Вы писали:
AP>>Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault (при sp[0] = hi)?!!! AP>>Правильно ли я понял?
E>Нет. Не правильно. E>sp будет указывать куда-то на стек. Но вот значение переменной v2 (кажется так её там звали?) может оказаться не на стеке, а в регистре, например, в этот момент.
E>Я думаю, что в приведённом коде происходит следующее. Функция подставляется, зависимость значения переменной v2 от манипуляций с sp компилятором не отслеживается благодаря обсуждаемому правилу, соответсвенно компилятор видит, что v2 инициализируется, потом от него берут адрес, не являющийся алиасом, потом печатают, и скорее всего просто печатает константу...
И все же получается, что sp может указывать куда угодно, ну и что хорошего что стек порушиться?
Возможно в приведенном варианте так и получиться, а в более сложном случае где-нибудь в середине функции?
Хотя что тут можно обсуждать, успешно упадет или успешно неправильно обработает!!!
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP>Здравствуйте, eao197, Вы писали:
E>>Выравнивание -- это не новая проблема. Но как раз в приведенном McSeem2 механизме свопинга проблем быть не должно. Поскольку uint32_t* будет выравнен на кратную 4-м границу, uint16_t* на кратную 2-м границу, а char* на любой адрес. Такие заморочки были на SPARC-ах когда-то очень давно. И ничего, все работало.
AP>Мда... Сейчас перевожу проект под ARM с Си на С++, так что чувствую, если включить -O2, то в кусках могут появиться интересные глюки, невоспроизводимые при -g ;-)
Были такие моменты у меня, когда с PC на ARM портировал проект, помню я там reinterpret_cast`ы на memcpy() менял, чтобы все работало.
Здравствуйте, McSeem2, Вы писали:
MS>Здравствуйте, Cyberax, Вы писали:
C>>Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])
MS>Кстати, хрен-на-ны.
Присоединяюсь. Воообще этот aliasing маразм какой-то. А тем более в исполнении GCC
Здравствуйте, McSeem2, Вы писали:
MS>По-моему, это уже клиническая паранойя. И старческий маразм комитета.
Не волнуйся, сейчас напишем багу в GCC, чтобы здесь тоже выдавалось предупреждение
, т.е. значение переменной меняется, но выводится на экран значение константы, а не переменной. Адреса в таком случае должны быть одинаковыми или это уже похоже на баг оптимизатора.
MS>>> [...]
MS>>> АК>>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
E>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C
E>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.
Здесь нет запрещения "простого свопинга байт/слов".
Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода.
Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки.
Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Smal, Вы писали:
S>>А еще лучше так (и короче, и переносимо). B>Первый вариант тоже вполне переносим. И его тоже можно записать достаточно коротко:
B>
Это тоже нелегально. Вот, кстати, цитата из мануаля по GCC.
Pay special attention to code like this:
union a_union {
int i;
double d;
};
int f() {
a_union t;
t.d = 3.0;
return t.i;
}
The practice of reading from a different union member than the one
most recently written to (called “type-punning”) is common. Even with
‘-fstrict-aliasing’, type-punning is allowed, provided the memory is
accessed through the union type. So, the code above will work as expected.
However, this code might not:
int f() {
a_union t;
int* ip;
t.d = 3.0;
ip = &t.i;
return *ip;
}
Здравствуйте, eao197, Вы писали:
E>Насколько я понимаю, в C++ стандарте такого ограничения нет
Всё тоже самое с точностью до запятых (точнее замены type на dynamic type, и добавления пункта про base class type):
If a program attempts to access the stored value of an object through an lvalue of other than one of the following
types the behavior is undefined (48):
— the dynamic type of the object,
— a cv-qualified version of the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to the dynamic type of the object,
— a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of
the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including,
recursively, a member of a subaggregate or contained union),
— a type that is a (possibly cv-qualified) base class type of the dynamic type of the object,
— a char or unsigned char type.
__________________
(48) The intent of this list is to specify those circumstances in which an object may or may not be aliased
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP>Вообще, такая проблема наблюдается на процессорах, адресация в которых к данным должна быть выравнена по границе 2, 4 и т.д. (8 не встречал)
Не путайте aliasing и aligning!
Это обе засады в С++. Но тем не менее — очень разные вещи
aligning - выравнивание данных. Например, 4-ёх байтное слово должно располагаться по адресу кратному 4 байтам. Корни растут из железа.
aliasing - совмещение имен. Т.е. когда 2 указателя указывают на один и тот же объект, говорится, что это есть alias'ы объекта. Корни растут исключительно из компилятора и связано это с проведением возможных оптимизаций в коде.
Здравствуйте, Шахтер, Вы писали:
E>>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.
Ш>Здесь нет запрещения "простого свопинга байт/слов". Ш>Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода. Ш>Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки. Ш>Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.
Ну если оставить в стороне излишнюю самоуверенность, то такие вещи, как:
* буфера ввода-вывода, указатели на которые передаются либо как void*, либо как char*;
* средства отладочных дампов блоков памяти, которые воспринимают void*, а затем преобразовывают его к char*.
они так же нелегальны?
SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Здравствуйте, Андрей Коростелев, Вы писали:
АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
Ты хотел сказать, что компилятор вправе думать, что sp не является алиасом для объекта *arg. И соотв. нагенерить кривого кода. Я правильно тебя понял?
А если sp не указывает на &(*arg), то куда ж ему ещё указывать?!
Во-первых, ему явно присвоили именно этот адрес.
Во-вторых, если указывать куда-то ещё, то надо создать дополнительный объект.
Тут фактически UB идёт только при доступе к объекту через sp, а при создании и инициализации sp UB нет. Т.к. далее я могу привести reinterpret_cast'ом sp обратно к uint32_t и получать доступ только через uint32_t.
[абстрагируемся на время от факта, что программа с UB имеет полностью недетерминированное поведение ]
Поправь меня, но ИМХО приведенный вариант с объединением в точности соответствует приведенному тобой первому варианту, который "will work as expected".
А это точно быстрее обмена слов?
Конечно есть штраф за переход между 16 битами и 32, но если обмен можно произвести задолго до использования, скажем при чтении данных в структуру их потока с неправильным endian.
Хотя, если честно, на практике встречал только задачу перехода между big endian и little endian, а вот так, чтобы полуслова менять -- не встречал
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, remark, Вы писали:
R>А если sp не указывает на &(*arg), то куда ж ему ещё указывать?!
Да он скорее всего туда и указывает.
Просто остальные пользователи *arg могут не заметить твоих последующих усилий и прочитать всё раньше, чем ты запишешь, например
R>Тут фактически UB идёт только при доступе к объекту через sp, а при создании и инициализации sp UB нет. Т.к. далее я могу привести reinterpret_cast'ом sp обратно к uint32_t и получать доступ только через uint32_t. R>[абстрагируемся на время от факта, что программа с UB имеет полностью недетерминированное поведение ]
1) Насколько я понимаю, то если ты приведёшь туда-сюда, то вообще не будет ничего плохого. Не понятен только смысл этой деятельности
2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно. Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB
3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Шахтер, Вы писали:
Ш>>
Ш>> rol ecx, 16 ; 00000010H
Ш>>
E>А это точно быстрее обмена слов?
На большой куче процов будет медленнее...
... << RSDN@Home 1.1.4 stable SR1 rev. 568>>
Забанили по IP, значит пора закрыть эту страницу.
Всем пока
Re[4]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 07:30
Оценка:
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, remark, Вы писали:
R>>А если sp не указывает на &(*arg), то куда ж ему ещё указывать?! E>Да он скорее всего туда и указывает. E>Просто остальные пользователи *arg могут не заметить твоих последующих усилий и прочитать всё раньше, чем ты запишешь, например
R>>Тут фактически UB идёт только при доступе к объекту через sp, а при создании и инициализации sp UB нет. Т.к. далее я могу привести reinterpret_cast'ом sp обратно к uint32_t и получать доступ только через uint32_t. R>>[абстрагируемся на время от факта, что программа с UB имеет полностью недетерминированное поведение ]
E>1) Насколько я понимаю, то если ты приведёшь туда-сюда, то вообще не будет ничего плохого. Не понятен только смысл этой деятельности E>2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно. Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB E>3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например.
Следует ли считать, что reinterpret_cast объявлен deprecated? В силу такого поведения компиляторов, использование reinterpret_cast полностью теряет смысл. Поскольку превращается в гадание на кофейной гуще.
Re: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 07:40
Оценка:
Здравствуйте, McSeem2, Вы писали:
MS>Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.
а может кто-нибудь сказать насколько данное использование reinterpret_cast правильно?
static bool is_not_valid_check_sum(const uint8_t* rec, size_t rec_len)
{
if (rec_len < 3)
return true;
const uint8_t* sz_end = rec + rec_len - sizeof(uint16_t);
uint16_t sum = 0;
for (; rec < sz_end; ++rec)
sum += *rec;
return sum != *reinterpret_cast<const uint16_t *>(sz_end);
}
идея в том что в конце записи есть двухбайтовое поле содержащие checksum,
подсчитывается оно протым сложением байт записи.
ЗЫ код скорее всего надо будет портировать на arm, поэтому мнение людей работавших с
arm тоже очень и очень интересно?
Re[6]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 07:42
Оценка:
Здравствуйте, Awaken, Вы писали:
A>пожалуй стоит привести асмовый код полностью A>там с /O3 эта функция не вызывается — она инлайнится (тогда зачем компилятор оставил ее код?)
A>/O1 A>
A> .file "a.c"
A> .text
A>.globl swap_words
A> .type swap_words, @function
A>swap_words:
A> pushl %ebp
A> movl %esp, %ebp
A> movl 8(%ebp), %eax
A> movzwl 2(%eax), %ecx
A> movzwl (%eax), %edx
A> movw %dx, 2(%eax)
A> movw %cx, (%eax)
A> popl %ebp
A> ret
A> .size swap_words, .-swap_words
A> .section .rodata.str1.1,"aMS",@progbits,1
A>.LC0:
A> .string "%08X %08X\n"
A> .text
A>.globl main
A> .type main, @function
A>main:
A> leal 4(%esp), %ecx
A> andl $-16, %esp
A> pushl -4(%ecx)
A> pushl %ebp
A> movl %esp, %ebp
A> pushl %ecx
A> subl $36, %esp
A> movl $-65536, -8(%ebp)
A> leal -8(%ebp), %eax
A> movl %eax, (%esp)
A> call swap_words
A> movl -8(%ebp), %eax
A> movl %eax, 8(%esp)
A> movl $-65536, 4(%esp)
A> movl $.LC0, (%esp)
A> call printf
A> movl $0, %eax
A> addl $36, %esp
A> popl %ecx
A> popl %ebp
A> leal -4(%ecx), %esp
A> ret
A> .size main, .-main
A> .ident "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
A> .section .note.GNU-stack,"",@progbits
A>
A>/O3 A>
A> .file "a.c"
A> .text
A> .p2align 4,,15
A>.globl swap_words
A> .type swap_words, @function
A>swap_words:
A> pushl %ebp
A> movl %esp, %ebp
A> movl 8(%ebp), %eax
A> movzwl 2(%eax), %ecx
A> movzwl (%eax), %edx
A> movw %cx, (%eax)
A> movw %dx, 2(%eax)
A> popl %ebp
A> ret
A> .size swap_words, .-swap_words
A> .section .rodata.str1.1,"aMS",@progbits,1
A>.LC0:
A> .string "%08X %08X\n"
A> .text
A> .p2align 4,,15
A>.globl main
A> .type main, @function
A>main:
A> leal 4(%esp), %ecx
A> andl $-16, %esp
A> pushl -4(%ecx)
A> pushl %ebp
A> movl %esp, %ebp
A> pushl %ecx
A> subl $36, %esp
A> movzwl -6(%ebp), %edx
A> movzwl -8(%ebp), %eax
A> movl $-65536, -8(%ebp)
A> movw %dx, -8(%ebp)
A> movw %ax, -6(%ebp)
A> movl $-65536, 8(%esp)
A> movl $-65536, 4(%esp)
A> movl $.LC0, (%esp)
A> call printf
A> addl $36, %esp
A> xorl %eax, %eax
A> popl %ecx
A> popl %ebp
A> leal -4(%ecx), %esp
A> ret
A> .size main, .-main
A> .ident "GCC: (GNU) 4.1.2 20061115 (prerelease) (SUSE Linux)"
A> .section .note.GNU-stack,"",@progbits
A>
Похоже, что подозрения подтвердились. Компилятор просто не использовал результат вычисления функции, а подставил исходную константу.
И кому только потребовалась такая оптимизация?
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP>Здравствуйте, Аноним, Вы писали:
AP>
А>> return sum != *reinterpret_cast<const uint16_t *>(sz_end);
А>>}
А>>
AP>Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью. AP>На < ARM9 выравнивание до 4-х байт AP>На ARM9 выравнивание до 2-х байт.
предположительно ARM9,
но вопрос решения остался за рамкой,
надо писать так?
uint16_t valid_sum;
memcpy(&valid_sum, sz_end, sizeof(valid_sum));
return sum != valid_sum;
Re[3]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 08:25
Оценка:
AP>Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью. AP>На < ARM9 выравнивание до 4-х байт AP>На ARM9 выравнивание до 2-х байт.
и еще вопросы (простите за наглость),
1)Какой coding style в связи с этим вы используете? типа никаких reinterpert_cast и т.д.
2)а на ARM9 подобных проблем быть не может, ну скажем если поменть тип checksum на какой-нибудь другой,
или складывать не побайтово, а по x-разрядных чисел?
3)а скажем packed структура, мне приходит поток байт
const uint8_t *data, size_t data_len;
а на самом деле это
unsigned some_number;
Foo foo;
где Foo:
struct Foo {
} __attribute__ ((packed));
и я делаю так:
Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));
Здравствуйте, Аноним, Вы писали: А>1)Какой coding style в связи с этим вы используете? типа никаких reinterpert_cast и т.д. А>2)а на ARM9 подобных проблем быть не может, ну скажем если поменть тип checksum на какой-нибудь другой, А>или складывать не побайтово, а по x-разрядных чисел?
Для надежности и переносимости между разными процами( в том числе и ARM, x86 )
лучше медленнее, но надежнее.
#include <vector>
typedef std::vector<uint8_t> BufferType;
typedef uint32_t CrcType; // обязательно беззнаковый, иначе получим вопросы знакорасширения типов.
CrcType Crc8Mod2CheckSum::calc(BufferType& rBuf, bool crc_included_fl)
{
CrcType cs = 0xFF;
if (crc_included_fl){
BufferType::iterator it_end = rBuf.end() - getCrcLen();
}
for (BufferType::iterator it = rBuf.begin(); it != it_end; ++it){
cs ^= *it;
}
cs &= 0xFF;
return cs;
}
От reinterpert_cast и прочих cast'ов можно отказаться, возможное их применение только для работы с буферами под пакеты приема/передачи.
Как видите, обхожусь без cast'ов
А>или складывать не побайтово, а по x-разрядных чисел?
Безопаснее побайтно. Тем более для телемеханики — надежность на первом месте.
А>а на самом деле это А>unsigned some_number; А>Foo foo;
А>где Foo: А>struct Foo { А>} __attribute__ ((packed));
А>и я делаю так: А>Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));
А>это тоже будет невалидно на ARM?
Это лучше спросить у людей, которые используют разнообразные хаки от gcc/g++.
Здравствуйте, Аноним, Вы писали:
А>Следует ли считать, что reinterpret_cast объявлен deprecated? В силу такого поведения компиляторов, использование reinterpret_cast полностью теряет смысл. Поскольку превращается в гадание на кофейной гуще.
Нет.
Такое использование незаконно. Но остальные использования reinterpret_cast законны.
Например, ты можешь скастить указатель на объект в int, а потом обратно, и нормально с ним работать дальше.
Здравствуйте, Erop, Вы писали:
E>2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно.
А разьве выделенное не то же самое?
E>Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB
Ключевое слово выделено
E>3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например.
Здравствуйте, Аноним, Вы писали:
А>3)а скажем packed структура, мне приходит поток байт А>const uint8_t *data, size_t data_len;
А>а на самом деле это А>unsigned some_number; А>Foo foo;
А>где Foo: А>struct Foo { А>} __attribute__ ((packed));
А>и я делаю так: А>Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));
А>это тоже будет невалидно на ARM?
Вообще здесь больше проблема переносимости и совместимости разного оборудования, в частности самым выжным обмен данными между little endian и big endian. Ваша структура struct Foo возможно коректно будет обрабатываться между одними типами (le -> le) и (be -> be), но при работе (le -> be) и (be -> le) протокол обмена будет работать некорректно, т.к. порядок слудования байтов на этих устройствах будет разный.
В этом случае необходимо применять __leXX_cpu /__cpu_leXX и __beXX_cpu/__cpu_beXX. Просто определиться с протоколом обмена и порядком следования байт в протоколе обмена (LE или BE). и тогда использовать одну из пар макросов.
Re[4]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 10:00
Оценка:
Здравствуйте, Аноним, Вы писали:
AP>>Если ядро ARM < 9, то часто будет получаться работа с невыровненной памятью. AP>>На < ARM9 выравнивание до 4-х байт AP>>На ARM9 выравнивание до 2-х байт.
А>и еще вопросы (простите за наглость), А>1)Какой coding style в связи с этим вы используете? типа никаких reinterpert_cast и т.д. А>2)а на ARM9 подобных проблем быть не может, ну скажем если поменть тип checksum на какой-нибудь другой, А>или складывать не побайтово, а по x-разрядных чисел? А>3)а скажем packed структура, мне приходит поток байт А>const uint8_t *data, size_t data_len;
А>а на самом деле это А>unsigned some_number; А>Foo foo;
А>где Foo: А>struct Foo { А>} __attribute__ ((packed));
А>и я делаю так: А>Foo *p = reinterpret_cast<const Foo *>(data + sizeof(unsigned));
А>это тоже будет невалидно на ARM?
Если у вас ARM процессор работает в отдельном от x86 адресном пространстве, то все указатели, адреса буферов и т.п. ARM определяет сам. В таком случае динамическая память выделяется по правильно выравненной границе. В случае размещения буфера в стеке необходимо корректно задать ему тип, т.е. не uint8_t data[xxx];
а
struct {
unsigned some_number;
Foo foo;
} data;
Тогда можно пользоваться тем способом, как вы написали. (вопрос об aliasing-е не рассматриваем).
Что касается __attribute__ ((packed)), то это определяется компилятором. Он может сам сгенерировать код для доступа к невыравненным данным. Тогда действительно будет плотная упаковка. А может и не иметь такой возможности. Тогда структура будет иметь пустоты, т.е. смещения полей в структуре на ARM и x86 будут разными.
Здравствуйте, Андрей Коростелев, Вы писали:
АК>Здравствуйте, McSeem2, Вы писали:
АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
А, простите, как быть, если у меня два указателя показывают на один и тот же адрес, но один char*, а другой нечто вроде
typedef char (*PCHAR20)[20];
Иными словами, я трактую этот адрес как адрес символа, с одной стороны, и строки символов длины 20 — с другой. Классика, в общем,многомерные массивы...
With best regards
Pavel Dvorkin
Re[6]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 10:25
Оценка:
Здравствуйте, remark, Вы писали:
R>Здравствуйте, Аноним, Вы писали:
А>>Следует ли считать, что reinterpret_cast объявлен deprecated? В силу такого поведения компиляторов, использование reinterpret_cast полностью теряет смысл. Поскольку превращается в гадание на кофейной гуще.
R>Нет. R>Такое использование незаконно. Но остальные использования reinterpret_cast законны. R>Например, ты можешь скастить указатель на объект в int, а потом обратно, и нормально с ним работать дальше.
R>
Польза от такой оптимизации минимальна. Практически во всех случаях изменения типа оптимизатору нельзя отбрасывать это. Поскольку просто так адрес объекта брать никто не будет, пусть даже и используя другой тип указателя.
Ох, чует мое сердце, что это нововведение принесёт больше беды, чем пользы.
Лучшее враг хорошего.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>А, простите, как быть, если у меня два указателя показывают на один и тот же адрес, но один char*, а другой нечто вроде
PD> typedef char (*PCHAR20)[20];
PD>Иными словами, я трактую этот адрес как адрес символа, с одной стороны, и строки символов длины 20 — с другой. Классика, в общем,многомерные массивы...
Выражение PCHAR20[0] имеет тип char& (char lvalue). И именно через него ты получаешь доступ к памяти. Так что в обоих случаях тип, через который ты получаешь доступ к памяти одинаковое. Так что всё законно.
А>Если у вас ARM процессор работает в отдельном от x86 адресном пространстве, то все указатели, адреса буферов и т.п. ARM определяет сам. В таком случае динамическая память выделяется по правильно выравненной границе. В случае размещения буфера в стеке необходимо корректно задать ему тип, т.е. не uint8_t data[xxx];
Это только в случае выделения памяти в куче или на стеке.
А если эти данные в буфере? Буфер будет выровнен, а начало структуры данных может и небыть.
Здравствуйте, remark, Вы писали:
E>>2) UB вовсе не обозначает полностью недетерминированного поведения. Это всего лишь значит, что реализация может делать что угодно.
R>А разьве выделенное не то же самое?
Нет, как только ты фиксируешь реализацию, так большинство UB становятся вполне детерминированными
Возможно я непонятно выражаюсь. Извини.
E>>Часто конкретная реализация имеет вполне известное и детерминированное и даже документированное поведение в случае каких-то UB R>Ключевое слово выделено
Ну UB-то разные бывают. Просто стандарт снимает с себя ответсвенность за последствия. Но никто не обязывает реализацию тоже снимать с себя ответсвенность...
E>>3) В частности, когда ты через хаки лазаешь в "кишки" большого типа, представляя его как коллекцию маленьких, стандарт ничего не гарантирует на тему последствий. Гарантировать может в такой ситуации реализация, железо, например. R>Здесь тоже
Ну, например, если железо фиксирует endian, то ты, читая int как коллекцию char получаешь уже вполне определённое поведение...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Умный вопрос по C/C++
От:
Аноним
Дата:
24.08.07 11:05
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:
А>>Если у вас ARM процессор работает в отдельном от x86 адресном пространстве, то все указатели, адреса буферов и т.п. ARM определяет сам. В таком случае динамическая память выделяется по правильно выравненной границе. В случае размещения буфера в стеке необходимо корректно задать ему тип, т.е. не uint8_t data[xxx];
AP>Это только в случае выделения памяти в куче или на стеке. AP>А если эти данные в буфере? Буфер будет выровнен, а начало структуры данных может и небыть.
Может. Но в таком случае не получится описать такую структуру буфера.
Если буфер описан, например, так:
то смещение поля foo зависит от компилятора. Обычно оно будет кратно 4. Заставить разместить foo по смещению 1 не получится. Если все же нужен именно упакованный буфер и переставить местами поля нельзя, то обращаться к невыравненным полям нужно с помощью соответствующих функций или макросов. В таком случае макросы будут разными для разных платформ.
AP>А если эти данные в буфере? Буфер будет выровнен, а начало структуры данных может и небыть.
Просто скопируй...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Ну, например, если железо фиксирует endian, то ты, читая int как коллекцию char получаешь уже вполне определённое поведение...
Показательный пример ты можешь видеть в первом посте этого топика
Здравствуйте, Шахтер, Вы писали:
Ш>Здесь нет запрещения "простого свопинга байт/слов". Ш>Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода. Ш>Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки. Ш>Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.
Вот ты, как мощный Шахтер и объясни мне, как я могу написать собственный аллокатор, 100% соблюдая правило strict aliasing. Или аллокаторы пишутся тоже из за "патологической глупости помноженной на самоуверенность"?
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Шахтер, Вы писали:
Ш>>
Ш>> rol ecx, 16 ; 00000010H
Ш>>
E>А это точно быстрее обмена слов?
В большинстве современных процессоров самая дорогая операция -- обмен с памятью. Битовые же операции обычно очень дешевые (что не удивительно -- они тривиально реализуютя схемотехнически).
Здравствуйте, eao197, Вы писали:
E>Здравствуйте, Шахтер, Вы писали:
E>>>McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки для работы непосредственно с железом, то запрещение простого свопинга байт/слов выглядит полным маразмом.
Ш>>Здесь нет запрещения "простого свопинга байт/слов". Ш>>Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода. Ш>>Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки. Ш>>Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.
E>Ну если оставить в стороне излишнюю самоуверенность, то такие вещи, как: E>* буфера ввода-вывода, указатели на которые передаются либо как void*, либо как char*; E>* средства отладочных дампов блоков памяти, которые воспринимают void*, а затем преобразовывают его к char*.
E>они так же нелегальны?
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Шахтер, Вы писали:
B>Поправь меня, но ИМХО приведенный вариант с объединением в точности соответствует приведенному тобой первому варианту, который "will work as expected". B>
Смысл: код нелегален, но текущая версия компилятора так делать позволяет даже при включённом strict-aliasing. Потому что надо поддерживать сущестующий код.
E>>А это точно быстрее обмена слов?
Ш>В большинстве современных процессоров самая дорогая операция -- обмен с памятью. Битовые же операции обычно очень дешевые (что не удивительно -- они тривиально реализуютя схемотехнически).
С памятью — да. С кэшем -- не факт...
А с другой стороны тут на 16 двигать надо. Если это таки в цикле делается...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, McSeem2, Вы писали:
MS>Здравствуйте, Шахтер, Вы писали:
Ш>>Здесь нет запрещения "простого свопинга байт/слов". Ш>>Запрет на подобный аллиазинг -- вещь совершенно правильная, особенно для низкоуровнего программирования, поскольку помогает оптимизации кода. Ш>>Ну и как человек, который имеет некий опыт возни с разным железом -- не встречал я ещё ни разу реальной необходимости применять грязные хаки. Ш>>Обычно это делают от недостака опыта. А иногда всречается просто патологическая глупость помноженная на самоуверенность, когда человек начинает memcpy на C писать, думая, что у него получится лучше чем а CRTL.
MS>Вот ты, как мощный Шахтер и объясни мне, как я могу написать собственный аллокатор, 100% соблюдая правило strict aliasing.
Я, кажется понял, что тебя смущает. Ты просто не совсем правильно понимаешь, что именно запрещено делать. Не закапываясь в текст стандарта, просто напишу несколько примеров.
Легально.
int a=...;
unsigned *b=(unsigned *)(void *)&a;
printf("%u\n",*b);
Нелегально.
int a=...;
short *b=(short *)(void *)&a;
printf("%u\n",*b);
Манипуляции с указателями вообще под эту статью не попадают. Попадает только доступ.
Bottomline is. Если есть два объекта A *a; B *b; физическое местоположение которых пересекаются (как такое может случится -- отдельный больной вопрос), то значения этих объектов рассматриваются как "связанные" только в четко перечисленных случаях. Во всех остальных случах попытка изменить значение одного объекта меняя значение другого нелегальна. Проще говоря, результаты оптимизации будут непредсказуемые. Как в приведённом изначально примере, компилятор просто счел что вызов функции не имеет видимых побочных эффектов и выбросил его.
MS>Или аллокаторы пишутся тоже из за "патологической глупости помноженной на самоуверенность"?
Нет. Это я вообще не про тебя. Где моя большая кружка пива?
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Шахтер, Вы писали:
Ш>>>>
Ш>>>> rol ecx, 16 ; 00000010H
Ш>>>>
E>>>А это точно быстрее обмена слов?
Ш>>В большинстве современных процессоров самая дорогая операция -- обмен с памятью. Битовые же операции обычно очень дешевые (что не удивительно -- они тривиально реализуютя схемотехнически).
E>С памятью — да. С кэшем -- не факт...
Не забывай, что в первом случае у нас два чтения и две записи, а во втором по одной.
E>А с другой стороны тут на 16 двигать надо. Если это таки в цикле делается...
Короче. Исследовал я вопрос (вообще, в подобных случаях надо не гадать, а мерить). На моём нотеке (T7200 2Ghz) оба метода практически эквивалентны по скорости.
/* main.cpp */#include <stdio.h>
typedef unsigned long long uint64;
/* CPUClock() */__declspec(naked) uint64 CPUClock()
{
__asm {
rdtsc
ret
}
}
unsigned SwapHalf(unsigned val)
{
return (val<<16)|(val>>16);
}
/* test1() */void test1()
{
static volatile unsigned val=0x11112222;
val=SwapHalf(val);
}
/* test2() */void test2()
{
static volatile unsigned short val[2]={0x1111,0x2222};
unsigned short temp=val[1];
val[1]=val[0];
val[0]=temp;
}
/* Run<func>() */template <void (*func)()>
void Run(const char *name)
{
uint64 result=uint64(-1);
for(unsigned n=1000; n ;n--)
{
uint64 start=CPUClock();
for(unsigned k=1000; k ;k--) func();
uint64 stop=CPUClock();
stop-=start;
if( stop<result ) result=stop;
}
printf("%s result = %llu\n",name,result);
}
/* Run2() */void Run2()
{
uint64 result=uint64(-1);
for(unsigned n=1000; n ;n--)
{
uint64 start=CPUClock();
static volatile unsigned short val[2]={0x1111,0x2222};
__asm {
mov ecx, 1000
L:
sub ecx,1
mov dx, WORD PTR val[0]
mov ax, WORD PTR val[2]
mov WORD PTR val[2], dx
mov WORD PTR val[0], ax
jne L
}
uint64 stop=CPUClock();
stop-=start;
if( stop<result ) result=stop;
}
printf("asm swap result = %llu\n",result);
}
/* main() */int main()
{
Run<&test1>("rot16");
Run<&test1>("rot16");
Run<&test2>("swap");
Run<&test2>("swap");
Run2();
Run2();
return 0;
}
Вот результирующий ассемблер.
test2:
call ?CPUClock@@YA_KXZ ; CPUClock
mov esi, eax
mov edi, edx
; 53 :
; 54 : for(unsigned k=1000; k ;k--) func();
mov ecx, 1000 ; 000003e8H
npad 15
$LL4@Run@2:
sub ecx, 1
mov ax, WORD PTR ?val@?1??test2@@YAXXZ@4RCGC+2
mov dx, WORD PTR ?val@?1??test2@@YAXXZ@4RCGC
movzx eax, ax
mov WORD PTR ?val@?1??test2@@YAXXZ@4RCGC+2, dx
mov WORD PTR ?val@?1??test2@@YAXXZ@4RCGC, ax
jne SHORT $LL4@Run@2
; 55 :
; 56 : uint64 stop=CPUClock();
call ?CPUClock@@YA_KXZ ; CPUClock
test1:
call ?CPUClock@@YA_KXZ ; CPUClock
mov esi, eax
mov edi, edx
; 53 :
; 54 : for(unsigned k=1000; k ;k--) func();
mov ecx, 1000 ; 000003e8H
$LL4@Run:
mov eax, DWORD PTR ?val@?1??test1@@YAXXZ@4IC
rol eax, 16 ; 00000010H
sub ecx, 1
mov DWORD PTR ?val@?1??test1@@YAXXZ@4IC, eax
jne SHORT $LL4@Run
; 55 :
; 56 : uint64 stop=CPUClock();
call ?CPUClock@@YA_KXZ ; CPUClock
asm
call ?CPUClock@@YA_KXZ ; CPUClock
mov esi, eax
mov edi, edx
; 75 :
; 76 : static volatile unsigned short val[2]={0x1111,0x2222};
; 77 :
; 78 : __asm {
; 79 : mov ecx, 1000
mov ecx, 1000 ; 000003e8H
$L$1444:
; 80 : L:
; 81 : sub ecx,1
sub ecx, 1
; 82 :
; 83 : mov dx, WORD PTR val[0]
mov dx, WORD PTR ?val@?3??Run2@@YAXXZ@4RCGC
; 84 : mov ax, WORD PTR val[2]
mov ax, WORD PTR ?val@?3??Run2@@YAXXZ@4RCGC+2
; 85 : mov WORD PTR val[2], dx
mov WORD PTR ?val@?3??Run2@@YAXXZ@4RCGC+2, dx
; 86 : mov WORD PTR val[0], ax
mov WORD PTR ?val@?3??Run2@@YAXXZ@4RCGC, ax
; 87 :
; 88 : jne L
jne SHORT $L$1444
; 89 : }
; 90 :
; 91 : uint64 stop=CPUClock();
call ?CPUClock@@YA_KXZ ; CPUClock
Такты
rot16 result = 6132
rot16 result = 6132
swap result = 6264
swap result = 6276
asm swap result = 6036
asm swap result = 6048
Для продолжения нажмите любую клавишу . . .
Здравствуйте, elcste, Вы писали:
E>Здравствуйте, eao197, Вы писали:
E>>Насколько я понимаю, в C++ стандарте такого ограничения нет. Тем не менее, GCC 3.4.4 для C++ генерирует такой же проблемный код, как и для C
E>В C++ все ровно то же самое.
E>
5.17/8 If the value being stored in an object is accessed from another object that overlaps in any way the storage of the first object, then the overlap shall be exact and the two objects shall have the same type, otherwise the behavior is undefined.
Возможно, я неправильно Вас понял, но мне кажется, что цитата относится к такому:
a = b; где b overlaps a: т.е. "the first object" и "an object" это а. А в оригинальном вопросе, ИМХО, подойдет лучше 3.10/15
Of course, the code must be complete enough to compile and link.
В последних 3 строках считается, что это — big endien платформа.
Я не знаю, как там это называется с точки зрения стандарта, но
это — просто неправильно.
Здравствуйте, McSeem2, Вы писали:
... MS>Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где?
... MS>Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.
Что я могу сказать, за такие стандарты и компиляторы расстреливать надо. Недостатки аппаратуры выдавать за стандарт это не просто глупость. Куда мы катимся?! И это на обычных скалярных процессорах, а что же на векторных будет. Выравнивали бы по 64байта.
И вообще тенденция не здоровая аппаратные проблемы сваливать на программистов, причем в такой извращенной форме. Это ж не вам ассемблер.
А если в конкретной реализации процессора команда умножения не будет работать для умножения на 10. Это тоже попадёт в стандарт? типа a*=10; -- нелегально, опасайтесь такого кода и пишите так: a=a+a+a+a+a+a+a+a+a+a; а еще лучше так: std::algotithms::align_safe_assign(a, std::algorithms::safe_multiply( a, reiterpret_cast<std::typeof(a).type>10UUL ));
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, McSeem2, Вы писали: А>... MS>>Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где? А>... MS>>Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.
А>Что я могу сказать, за такие стандарты и компиляторы расстреливать надо. Недостатки аппаратуры выдавать за стандарт это не просто глупость. Куда мы катимся?! И это на обычных скалярных процессорах, а что же на векторных будет. Выравнивали бы по 64байта. А>И вообще тенденция не здоровая аппаратные проблемы сваливать на программистов, причем в такой извращенной форме. Это ж не вам ассемблер.
Если ты не в курсе, то С/С++ изначально был и назывался как "портабельный ассемблер".
В С++ добавлили некоторые высокоуровневые конструкции, но суть никак не меняли.
Здравствуйте, remark, Вы писали:
E>>Ну, например, если железо фиксирует endian, то ты, читая int как коллекцию char получаешь уже вполне определённое поведение...
R>Показательный пример ты можешь видеть в первом посте этого топика
К char* приводить вроде как законно...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Умный вопрос по C/C++
От:
Аноним
Дата:
04.04.08 13:24
Оценка:
Здравствуйте, remark, Вы писали:
... R>Если ты не в курсе, то С/С++ изначально был и назывался как "портабельный ассемблер". R>В С++ добавлили некоторые высокоуровневые конструкции, но суть никак не меняли.
Ага поэтому его стремятся сделать всё мене портабельным.
Да конешно пусть компилятор умеет обращаться с жуткими абстракциями, но не умеет делать примитивные операции типа борьбы с выравниванием, а взваливает всё с чем было лень бороться разработчикам компилятора на конечных пользователей этого самого компилятора, а ещё лучше включить это в стандарт, чтобы было чем оправдываться. Зачем создавать проблемы на ровном месте? Я вот не понимаю это стремление к сексу стоя в гомоке.
А>>Такой C++ нам не нужен! R>Никто никого и не заставляет...
Еще как заставляет
Re: Умный вопрос по C/C++
От:
Аноним
Дата:
04.04.08 13:56
Оценка:
Здравствуйте, McSeem2, Вы писали:
...
Хорошо. Но вот правильно ли я понимаю, что такой код тоже не всегда будет работать?
Здравствуйте, <Аноним>, Вы писали:
А>Недостатки аппаратуры выдавать за стандарт это не просто глупость.
Например, если так делать для double, даже на x86 можно получить исключение.
Требование обосновано Representations of types
§6.2.6.1/5 Certain object representations need not represent a value of the object type. If the stored
value of an object has such a representation and is read by an lvalue expression that does
not have character type, the behavior is undefined. If such a representation is produced
by a side effect that modifies all or any part of the object by an lvalue expression that
does not have character type, the behavior is undefined.41) Such a representation is called
a trap representation.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, Андрей Коростелев, Вы писали:
АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
Пардон, а что тут нового? Точно такой же текст стоит в С90 6.3. Если речь об этом, конечно:
7 An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
— a type that is the signed or unsigned type corresponding to the effective type of the object,
— a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
— a character type.
Здравствуйте, Аноним, Вы писали:
А>Если да, то мать-перемать, что ж я натворил
Пока твои структуры останутся POD гарантируется, что этот код будет работать правильно
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: Умный вопрос по C/C++
От:
Аноним
Дата:
04.04.08 19:11
Оценка:
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, <Аноним>, Вы писали:
А>>Недостатки аппаратуры выдавать за стандарт это не просто глупость.
GN>Например, если так делать для double, даже на x86 можно получить исключение.
??? правда
volatile double x=1.0;
char* p=(char*)&x;
Теперь по новым стандартам это бред? И я даже не смогу проверить как оно лежит в памяти?
GN>Требование обосновано Representations of types
GN>
§6.2.6.1/5 Certain object representations need not represent a value of the object type. If the stored
GN>value of an object has such a representation and is read by an lvalue expression that does
GN>not have character type, the behavior is undefined. If such a representation is produced
GN>by a side effect that modifies all or any part of the object by an lvalue expression that
GN>does not have character type, the behavior is undefined.41) Such a representation is called
GN>a trap representation.
Пусть они себе такое обоснование засунут куда-нибуть.
Нет чтоб определить "representations of Certain object", так они делают детские грабли, с топором на конце.
И пусть они свои фантазии называют как угодно. Я определил в программе как оно лежит в памяти.
И меня не чешут их филосовские изыскания что на какой то другой машине это будет представлено иначе, мне это нужно для конкретной.
Может я просто хочу выяснить как оно в памяти лежит, а меня тут наглым образом начинает разводить компилятор. Повбивалбы.
Радует только одно что такие стандарты нарушают совместимость с предыдущими версиями. И старый код скорее всего может не работать. Что в свою очередь приведет к тому что на такой стандарт пользователи положут болт.
Здравствуйте, Аноним, Вы писали:
GN>>Например, если так делать для double, даже на x86 можно получить исключение. А>??? правда А>
А>volatile double x=1.0;
А>char* p=(char*)&x;
А>
А>Теперь по новым стандартам это бред? :no: И я даже не смогу проверить как оно лежит в памяти? :???:
Это будет работать (можно кастить к char * и void *). Не обязано работать (double *)someRandomCharArray.
А лезть в байты тебе ни к чему, разные платформы будут иметь разные представления double.
Да и стандарт 1998 г. в. как-то не очень похож на новый.
До последнего не верил в пирамиду Лебедева.
Re[5]: Умный вопрос по C/C++
От:
Аноним
Дата:
04.04.08 21:38
Оценка:
Здравствуйте, Roman Odaisky, Вы писали:
RO>Здравствуйте, Аноним, Вы писали:
GN>>>Например, если так делать для double, даже на x86 можно получить исключение. А>>??? правда А>>
А>>Теперь по новым стандартам это бред? И я даже не смогу проверить как оно лежит в памяти?
RO>Это будет работать (можно кастить к char * и void *). Не обязано работать (double *)someRandomCharArray. RO>А лезть в байты тебе ни к чему, разные платформы будут иметь разные представления double.
И как я по вашему тогда вынужден буду делать эмуляцию чисел с плавающей точкой. Если компилятор мне строит препятствия на ровном месте?
И потом для разных платформ и api разное и abi да и компиляторы у одних 48бит аккумулятор, у других операции векторные (сразу пачками обрабатывает), одни RISC другие CISC, одни DSP а другие только целочисленно умножать умеют. Даже POSIX у всех свой. Короче у меня есть разная реализация под разные платформы и неипёт.
Я хочу сказать что такие синтетические ограничения это полный маразм. Мне пофиг как оно устроено на разных платформах которые я не использую. Мне важен факт что один и тот же компилятор на одной и той же платформе обманывает и генерит ерись. Причем проблема совершенно надуманная и искуственная!
Если arm работает со словами а не с байтами. Какого я должен над этим суетиться, а не компилятор? Если процессор не умеет что то делать, то почему я что дожен это делать за компилятор?
Задача компилятора облегчить написание программ, а тут всё наоборот. (И мы еще боримся за звание дома высокой культуры быта)
RO>Да и стандарт 1998 г. в. как-то не очень похож на новый.
А там то что не так?
А>Теперь по новым стандартам это бред? И я даже не смогу проверить как оно лежит в памяти?
Так можно, я же привёл цитату
GN>>
If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined.
Проблемы могут быть, если последовательность char, представляющую собой репрезентацию NaN, прочитать в duoble. В место "неожданного" результата, как в оригинальном примере, будет сгенереировано исключение (хотя по умолчанию оно замаскировано) — это на самой распростанённой платформе, а с другими может быть еще хуже.
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[5]: Умный вопрос по C/C++
От:
Аноним
Дата:
05.04.08 08:25
Оценка:
Здравствуйте, gear nuke, Вы писали:
GN>Проблемы могут быть, если последовательность char, представляющую собой репрезентацию NaN, прочитать в double. В место "неожданного" результата, как в оригинальном примере, будет сгенереировано исключение (хотя по умолчанию оно замаскировано) — это на самой распростанённой платформе, а с другими может быть еще хуже.
И что NaN этот тоже последовательность бит в памяти. В чем проблема то? Где будет хуже? Что нафиг за цензура еще такая?
Здравствуйте, Аноним, Вы писали:
А>В чем проблема то?
В системе команд CPU
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[7]: Умный вопрос по C/C++
От:
Аноним
Дата:
06.04.08 20:55
Оценка:
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Аноним, Вы писали:
А>>В чем проблема то? E>В системе команд CPU
Система команд CPU. Как раз проблема разработчиков компилятора, а не того кто пишет на C/C++
Здравствуйте, Аноним, Вы писали:
E>>В системе команд CPU А>Система команд CPU. Как раз проблема разработчиков компилятора, а не того кто пишет на C/C++
Ну фокус в том, что то, что нельзя сделать на асме, то и на С++ нельзя тем более. Так что у авторов компиляторов нет проблем. Проблемы есть у проектировщиков ПО, которые хотят сделать то, что аппаратура не очень умеет и не особо должна к тому же
На всякий случай. К char* приводить можно любой указатель и потом по байтам читать память. Это для тех, кто эмуляторы писать хочет очень даже удобно. А для тех, кто хочет то эмуляторы, а то FPU попользоваться ждёт сюрприз в виде того, что даже и не FPU данные иногда в регистры поднимаются, например. И не все состяния процессора и регистров отображаются в биты в памяти при этом...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Умный вопрос по C/C++
От:
Аноним
Дата:
07.04.08 10:44
Оценка:
Здравствуйте, Erop, Вы писали:
E>>>В системе команд CPU А>>Система команд CPU. Как раз проблема разработчиков компилятора, а не того кто пишет на C/C++ E>Ну фокус в том, что то, что нельзя сделать на асме, то и на С++ нельзя тем более.
Категорически не согласен, если это не специализированный чип, то на асме можно сделать всё что угодно. И эффективность реализации тут совершенно не важна.
E>Так что у авторов компиляторов нет проблем. Проблемы есть у проектировщиков ПО, которые хотят сделать то, что аппаратура не очень умеет и не особо должна к тому же
Вот именно что они свои проблемы с себя скидывают на других, при этом еще и в стандарт наравят засунуть что бы было чем оправдываться. А так как стандарт святое для не которых, они тупо молятся на такой бред. И готовы до усеру убеждать остальных что это единственно возможный и верный путь.
E>На всякий случай. К char* приводить можно любой указатель и потом по байтам читать память. Это для тех, кто эмуляторы писать хочет очень даже удобно. А для тех, кто хочет то эмуляторы, а то FPU попользоваться ждёт сюрприз в виде того, что даже и не FPU данные иногда в регистры поднимаются, например. И не все состяния процессора и регистров отображаются в биты в памяти при этом...
Вот очень странно если у меня N чисел (сотни и миллионы) с плавающей точкой, то как процессор может их держать у себя в регистрах? Если регистров у него значительно меньше. Мне совершенно не важна внутренняя разрядность и архитектура математического сопроцессора. Но когда он числа сохраняет в память он это делает вполне детерминированно.
Все эти сюрпризы должны решаться и обходиться именно теми кто делает компилятор под эту платформу, а не тем кто пишет кроссплатформенное приложение, а потом выясняется что на этой архитектуре, что бы работало надо знать множество заклинаний и шаманских обрядов, а также досконально знать что именно данный процессор может и как и что компилятор делает не так как все, а по своему.
Здравствуйте, Аноним, Вы писали:
А>Категорически не согласен, если это не специализированный чип, то на асме можно сделать всё что угодно. И эффективность реализации тут совершенно не важна.
Да? Ну вычисли точно число пи, например...
А>Вот именно что они свои проблемы с себя скидывают на других, при этом еще и в стандарт наравят засунуть что бы было чем оправдываться. А так как стандарт святое для не которых, они тупо молятся на такой бред. И готовы до усеру убеждать остальных что это единственно возможный и верный путь.
Проектировщики ПО? Да хотят... Вот ты, например, хотешь, вроде бы того, чтобы авторы компиляторов эмулировали какое-то нужное тебе поведение...
А>Вот очень странно если у меня N чисел (сотни и миллионы) с плавающей точкой, то как процессор может их держать у себя в регистрах? Если регистров у него значительно меньше. Мне совершенно не важна внутренняя разрядность и архитектура математического сопроцессора. Но когда он числа сохраняет в память он это делает вполне детерминированно.
Даже числа не всегда, но у процессора, кроме чисел, есть ещё и состояние. Состояния он сохранять в память вообще-то не обязан... Скажем такое вот состояние "результат последней операции неотрицателен". Как ты собираешься в формате числа прихранивать?
А>Все эти сюрпризы должны решаться и обходиться именно теми кто делает компилятор под эту платформу, а не тем кто пишет кроссплатформенное приложение, а потом выясняется что на этой архитектуре, что бы работало надо знать множество заклинаний и шаманских обрядов, а также досконально знать что именно данный процессор может и как и что компилятор делает не так как все, а по своему.
Ну некоторым людям (скажу по секрету, их большинстово) нужна быстрая и эффективная работа приложений, а не эмуляция поведения одного железа на другом...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском