Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 22.08.07 20:52
Оценка: 54 (6)
#include <stdio.h>

void swap_words(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;
} 

int main()
{
   uint32_t v1 = 0xFFFF0000u;
   uint32_t v2 = 0xFFFF0000u;

   swap_words(&v2);

   printf("%08X %08X\n", v1, v2);
   return 0;
}



Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где?
Еще подсказка: GCC 3.4.1 и более поздние выдают следующий результат:
gcc -O1 -Wall a.c
./a
FFFF0000 0000FFFF

gcc -O3 -Wall a.c
./a
FFFF0000 FFFF0000


Это не глюк компилятора, компилятор ведет себя совершенно корректно. Просьба к гуру — подождать пару дней с ответом, чтобы любопытные, но менее опытные попытались сами докопаться до истины. А потом я хочу задать дополнительные вопросы, возникшие в связи с этим.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re: Умный вопрос по C/C++
От: . Великобритания  
Дата: 22.08.07 21:59
Оценка:
McSeem2 wrote:

> uint16_t* sp = (uint16_t*)arg;

> Undefined Behavior. Вопрос — Где?
Я не считаю себя гуру, но по-моему здесь.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re: Умный вопрос по C/C++
От: Андрей Коростелев Голландия http://www.korostelev.net/
Дата: 22.08.07 22:18
Оценка: 72 (7) +1
Здравствуйте, McSeem2, Вы писали:

MS>
MS>void swap_words(uint32_t* arg)
MS>{
MS>   uint16_t* sp = (uint16_t*)arg;// Тут UB 
MS>   [...]
MS>

Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).
-- Андрей
Re: Умный вопрос по C/C++
От: Ужасть бухгалтера  
Дата: 22.08.07 22:31
Оценка:
MS>Вопрос такой — что не так в этой программе? Подсказка — здесь имеется Undefined Behavior. Вопрос — Где?

Мне вот что-то printf не нравится... "X" означает, что ожидается тип int. Если разрядность int отличается от 32, то и возникает Undefined behavior... Правда, является ли это причиной некорректной работы в данном случае, я не знаю...
Re[2]: Умный вопрос по C/C++
От: c-smile Канада http://terrainformatica.com
Дата: 22.08.07 23:49
Оценка:
Здравствуйте, Андрей Коростелев, Вы писали:

АК>Здравствуйте, McSeem2, Вы писали:


MS>>
MS>>void swap_words(uint32_t* arg)
MS>>{
MS>>   uint16_t* sp = (uint16_t*)arg;// Тут UB 
MS>>   [...]
MS>>

АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).

В дополнение: В GCC это управляется с пом. ключа -fstrict-aliasing.
Re: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 02:05
Оценка:
Здравствуйте, 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
Я жертва цепи несчастных случайностей. Как и все мы.
Re[2]: Умный вопрос по C/C++
От: Cyberax Марс  
Дата: 23.08.07 02:25
Оценка:
Здравствуйте, 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 правильный?). Но на практике обязано работать
Sapienti sat!
Re[3]: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 03:12
Оценка:
Здравствуйте, Cyberax, Вы писали:

MS>>Имеет ли место strict aliasing violation в этом случае?

C>Да. Так как поведение такого кода неопределено (откуда компилятор знает что у тебя действительно index_aligned_to_int правильный?). Но на практике обязано работать

Компилятор не может знать ничего про выравнивание в этом случае — это все на моей совести.

По-моему, они чего-то перемудрили с этим строгим алиасингом. Самое неприятное, что предупреждения может и не быть, но оптимизатор возьмет да выкинет реально необходимые операции.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[4]: Умный вопрос по C/C++
От: Cyberax Марс  
Дата: 23.08.07 03:18
Оценка:
Здравствуйте, McSeem2, Вы писали:

MS>>>Имеет ли место strict aliasing violation в этом случае?

C>>Да. Так как поведение такого кода неопределено (откуда компилятор знает что у тебя действительно index_aligned_to_int правильный?). Но на практике обязано работать
MS>Компилятор не может знать ничего про выравнивание в этом случае — это все на моей совести.
Поэтому с точки зрения Стандарта — это UB.

MS>По-моему, они чего-то перемудрили с этим строгим алиасингом. Самое неприятное, что предупреждения может и не быть, но оптимизатор возьмет да выкинет реально необходимые операции.

Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])
Sapienti sat!
Re[5]: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 03:34
Оценка:
Здравствуйте, Cyberax, Вы писали:


C>Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])


Ох, ёлы-палы. Это уже какие-то интриги начинаются...
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[5]: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 03:40
Оценка: +1
Здравствуйте, Cyberax, Вы писали:

C>Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])


Кстати, хрен-на-ны.
#include <stdio.h>

void swap_words(uint32_t* arg)
{
    uint16_t* sp = (uint16_t*)(void*)arg;
    uint16_t  hi = sp[0];
    uint16_t  lo = sp[1];
    
    sp[1] = hi;
    sp[0] = lo;
} 

int main()
{
   uint32_t v1 = 0xFFFF0000u;
   uint32_t v2 = 0xFFFF0000u;

   swap_words(&v2);

   printf("%08X %08X\n", v1, v2);
   return 0;
}


$ gcc -O3 -Wall a.c
a.c: In function `main':
a.c:20: warning: unsigned int format, uint32_t arg (arg 2)
a.c:20: warning: unsigned int format, uint32_t arg (arg 3)

$ ./a
FFFF0000 FFFF0000


А должен был бы понять намек...
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[5]: Умный вопрос по C/C++
От: McSeem2 США http://www.antigrain.com
Дата: 23.08.07 03:45
Оценка: +2 :))) :)))
Здравствуйте, Cyberax, Вы писали:

C>Можешь явно сказать компилятору, что ты знаешь что делаешь: (int*)(void*)(&someChar[..])


О! Мне удалось добиться предупреждения. Надо писать так:
void swap_words(uint32_t* arg)
{
    uint16_t* sp = (uint16_t*)(*(void**)&arg);
    uint16_t  hi = sp[0];
    uint16_t  lo = sp[1];
    
    sp[1] = hi;
    sp[0] = lo;
}


По-моему, это уже клиническая паранойя. И старческий маразм комитета.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[2]: Умный вопрос по C/C++
От: carpenter Голландия  
Дата: 23.08.07 07:37
Оценка: +1
Здравствуйте, Андрей Коростелев, Вы писали:

АК>Здравствуйте, McSeem2, Вы писали:



АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).


Блин гавно какоето ... эт я не по поводу ответа , а по вышеописанному
Весь мир — Кремль, а люди в нем — агенты
Re[2]: Умный вопрос по C/C++
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 23.08.07 07:47
Оценка: +2
Здравствуйте, Андрей Коростелев, Вы писали:

MS>>
MS>>void swap_words(uint32_t* arg)
MS>>{
MS>>   uint16_t* sp = (uint16_t*)arg;// Тут UB 
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++.
Re[2]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 23.08.07 07:59
Оценка: -1
Здравствуйте, Андрей Коростелев, Вы писали:

АК>Здравствуйте, McSeem2, Вы писали:


MS>>
MS>>void swap_words(uint32_t* arg)
MS>>{
MS>>   uint16_t* sp = (uint16_t*)arg;// Тут UB 
MS>>   [...]
MS>>

АК>Правило введенное в С99 и запрещающее создание алиаса объекта с типом отличного от типа объекта-оригинала (ISO/IEC 9899 6.5/7) поддерживается в gcc 3.4.1 и выше на уровнях оптимизации начиная со 2-го. В данном случе sp не будет являться корректным алиасом для объекта *arg, потому компилятор вправе сгенерировать код, при котором sp не указывает на &(*arg).

Т.е. получается, что sp будет указывать по непонятному адресу? Т.е. реально вообще получить Segmentation Fault?!!!
Правильно ли я понял?
Re: Умный вопрос по C/C++
От: ilnar Россия  
Дата: 23.08.07 08:03
Оценка:
Здравствуйте, McSeem2, Вы писали:

...skiped

а что если написать так?

void swap_words(uint32_t* arg)
{
    uint16_t* sp = reinterpret_cast<uint16_t*>(arg);
    uint16_t  lo = sp[1];
    sp[1] = sp[0];
    sp[0] = lo;
}
Re[2]: Умный вопрос по C/C++
От: Smal Россия  
Дата: 23.08.07 08:08
Оценка: 8 (2)
Здравствуйте, ilnar, Вы писали:

I>Здравствуйте, McSeem2, Вы писали:


I>...skiped


I>а что если написать так?


I>
I>void swap_words(uint32_t* arg)
I>{
I>    uint16_t* sp = reinterpret_cast<uint16_t*>(arg);
I>    uint16_t  lo = sp[1];
I>    sp[1] = sp[0];
I>    sp[0] = lo;
I>} 
I>


Тоже самое. Надо так.

void swap_words(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;
}
С уважением, Александр
Re[3]: Умный вопрос по C/C++
От: Alexander Pazdnikov  
Дата: 23.08.07 08:09
Оценка:
Здравствуйте, 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.
Тут бы по выравниванию памяти вообще механизм блокировок придумать какой-то было бы просто замечательно.
Re[3]: Умный вопрос по C/C++
От: . Великобритания  
Дата: 23.08.07 08:13
Оценка:
eao197 wrote:

> McSeem2 прав -- такая паранойя. Если уж C/C++ позиционируются как языки

> для работы непосредственно с железом, то запрещение простого свопинга
> байт/слов выглядит полным маразмом.
Маразмом является приведение неприводимого. А задача своппинга решается битовыми операциями.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: Умный вопрос по C/C++
От: Smal Россия  
Дата: 23.08.07 08:14
Оценка: +1
Здравствуйте, Smal, Вы писали:

....

А еще лучше так (и короче, и переносимо).

void swap_words(uint32_t* arg)
{
    uint16_t  hi = (uint16_t) ((*arg & 0xFFFF0000u)>>16);
    uint16_t  lo = (uint16_t) ((*arg & 0x0000FFFFu));

    *arg = hi | (lo << 16);
}
С уважением, Александр
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.