Сергей Каличев представил перевод (http://lex.iki.rssi.ru/~serj/docs/lemmings_ru/lemmings_ru.html) статьи с описанием типичных проблем, возникающих при написании переносимого кода, некоторые из проблемы очевидны, но ошибки, с ними связанные, продолжают регулярно появляться в реальном коде.URL: http://lex.iki.rssi.ru/~serj/docs/
Новость: http://www.opennet.me/opennews/art.shtml?num=14963
Куча грамматических ошибок :(
Мне вот интересно - написание переносимого кода описывается уже лет 20 так - зачем писать что-то еще?
revision 1.8
date: 2001/11/29 22:55:57; author: ross; state: Exp; lines: +1 -6
Delete the old BUGS section entry:
> On the VAX bytes are handled backwards from most everyone else in
> the world. This is not expected to be fixed in the near future.не понял при чём тут вакс. Это, насколько я знаю, компьютеры, которые использовались ещё в 1970... так к чему они здесь?
это такой тончайший юмор, в конце статьи пояснение:
>В названии (``Fighting the lemmings'') нет какого-то особого смысла. Раньше мы говорили >``весь мир VAX'', а теперь весь мир -- i386 и везде работает linux. Все авторы просто >следуют за этим ``леммингом -- вожаком'' и пишут (а ещё хуже -- тестируют) код >исключительно под i386/linux.
>revision 1.8
>date: 2001/11/29 22:55:57; author: ross; state: Exp; lines: +1 -6
>Delete the old BUGS section entry:
>> On the VAX bytes are handled backwards from most everyone else in
>> the world. This is not expected to be fixed in the near future.
>
>не понял при чём тут вакс. Это, насколько я знаю, компьютеры, которые
>использовались ещё в 1970... так к чему они здесь?всё, понял
> Хотя портирование на NetBSD/sparc64 практически является таким тестом ;-)собственно я сейчас там на sparc64.
и вот такая конструкция:
uint8_t a[] = { 41, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 3 };uint32_t u32 = *((uint32_t *) &a[1]);
дает Bus error.
это просто пример, пришлось определить (не дословно):
#define LOAD32(p, b) b=(((((((uint32_t)(((b) & 0) | *(((uint8_t *)p) + 3)) << 8) | *(((uint8_t *)p) + 2)) << 8) | *(((uint8_t *)p) + 1)) << 8) | *((uint8_t *)p))но это думаю медленно, можно как нибудь оптимизировать?
typedef union {
char a[4];
uint32_t b;
} c_t;c_t c;
c.a[0] = 41;c.a[1] = 0;c.a[2] = 0;c.a[0] = 0;
printf("%u", c.b);
и вот такая конструкция:
uint8_t a[] = { 41, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 3 };uint32_t u32 = *((uint32_t *) &a[1]);
дает Bus error.
ЧУВАААК, почитай доки к gcc, в частности non-bugs.txt или как там. ЗАПРЕЩЕНО такое делать.
Чтобы это заработало у gcc есть ключик, но они говорят что в стандартах на C такое поведение не описано, поэтому сосямба. Ну и ничего что этот код совершенно непереносим из-за endianess?
>ЧУВАААК, почитай доки к gcc, в частности non-bugs.txt или как там. ЗАПРЕЩЕНО
>такое делать.
>Чтобы это заработало у gcc есть ключик, но они говорят что в
>стандартах на C такое поведение не описано, поэтому сосямба. Ну и
>ничего что этот код совершенно непереносим из-за endianess?ЧУВАК это просто ПРИМЕР чтобы смысл в чем ОШИБКА показать..
> и вот такая конструкция:
> uint8_t a[] = { 41, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 3 };
> uint32_t u32 = *((uint32_t *) &a[1]);
> дает Bus error.И это ты типа удивлен? `Вон из профессии!' (C)
ну чи то разорались? умные все что ли?
пытаюсь перенести игрушку одну с DOS с РЕСУРСАМИ это вам вообще о чем нибудь говорит?
а
для тех кто все еще ТРАМВАЕ - РЕСУРСЫ БИНАРНЫЕ
Сергей Каличев - статья полезная, перевод нужный...Давеча, пролетало, https://www.securecoding.cert.org/confluence/display/seccode...
Уже можете переводить :)
Мда, "К сожалению, NetBSD заботится о двоичной совместимости".
Как раз к счастью NetBSD заботиться о бинарной совместимости, и очень жаль тех, кто о ней не заботится.
Статья на слабую тройку. Перечислено только около 10% типичных ошибок программистов. Автор похоже коммитит код в NetBSD и наивно считает ее самой портабельной, да и еще незаслуженно обижает разработчиков под Linux, которая BTW поддерживает в стабильной ветке на 6 CPU архитектур больше чем NETBSD и как минимум в два раза больше платформ и MMU BSP.
----------------------------------------------------
Недопонимание переводчика:О приведении типов указателей
В главе о приведении типов указателей попалась одна непонятная фраза: You are allowed to cast to the ``right'' type of an object when assigning via a pointer, so the compiler assumes all variables of the casted-to type might have changed. Вот комментарии автора по этому поводу:<<Скажем, у вас есть поток байтов, который включает в себя некоторый участок, и вы хотите обратиться к этому участку, как к структуре. Поток байтов не имеет выравнивания. Для указателя на байт используется char* или void*. Теперь, если привести тип указателя к типу struct* (указатель на вашу структуру), компилятор будет считать указатель допустимым, включая все требования по выравниванию, относящиеся к указателям на вашу структуру.
Это также справедливо в случае, если вы сперва выполнили приведение типа указателя, а потом использовали memcpy() для копирования содержимого структуры.
Однако, если вы выполните memcpy(), т.е. скопируете данные по указателю с неприведённым типом void*/char* в локальную типизированную структуру, то компилятор не станет делать никаких предположений и всё будет работать нормально.>>
Этот комментарий не до конца прояснил смысл фразы. Более понятно стало после комментария от gena2x. Речь идёт об особенностях оптимизации. Был приведён следующий пример:
$ cat > test.c
#include <stdio.h>void f(int *i, long *l) {
printf("1.v=%ld\n", *l);
*i = 11;
printf("2.v=%ld\n", *l);
}int main() {
long b = 10;
f((int*)&b, &b);
printf("3.v=%ld\n", b);
}$ gcc -O2 test.c -o test
$ ./test
1.v=10
2.v=10
3.v=11Неожиданное значение ``2. v=10''. Обратите внимание, при компиляции использовалась оптимизация -O2. Если оптимизацию не использовать, то вывод будет другим:
$ gcc test.c -o test
$ ./test
1.v=10
2.v=11
3.v=11При использовании оптимизации компилятор ``не заметил'', что значение *l изменилось. Если же строку ``*i = 11;'' заменить на ``*(long *)i = 11;'', то компилятор сделает предположение, что могли измениться все значения по указателям, имеющим тип long * (тип к которому приводили), и соответственно при использовании *l будет получено уже новое значение (``2.v=11'').
----------------------------------------------------------------------------
Автор перевода и gena2x жгут...
Как можно перепутать обращение к невыравненным данным (по их размеру) с проблеммой strict алиасинга в (я бы посоветовал gena2x попробовать свой тест на ветке 2.x.x - 3.x.x например )?
gcc-3.3.6 -O3 test.c
1.v=10
2.v=11
3.v=11
Результат правильный... На других 4.x.x в основном неправильный...А если так gcc -O3 -fno-strict-aliasing test.c
то всегда то что ожидалось :)Да вы правильно догадались начиная с 4.x.x -O2 подразумевает -fstrict-aliasing , что дает инструкцию компилятору считать что все указатели разных типов указывают на непересекающиеся области памяти.
Таким образом рекомендуется при использовании оптимизации в GCC обязательно указывать флаг: -Wstrict-aliasing=2 тогда все проблемные места будет сразу видно:
$ gcc -O3 test.c -Wall -Wstrict-aliasing=2
test.c: In function ‘main’:
test.c:11: warning: dereferencing type-punned pointer will break strict-aliasing rulesМои CFLAGS обычно включают седующие флаги:
-Wall -Wstrict-prototypes -Wcast-qual -Wcast-align -Wbad-function-cast -Wconversion -Wstrict-aliasing=2
>Как можно перепутать обращение к невыравненным данным (по их размеру) с проблеммой strict алиасинга в (я бы посоветовал gena2x попробовать свой тест на ветке 2.x.x - 3.x.x например )?Пример приводился для x86/linux/gcc. sizeof(int)==sizeof(long) - о каком выравнивании (по размеру) речь?
Да, надо было дописать что -fstrict-aliasing только в четвертой ветке по умолчанию включается в -O2.
>Wstrict-aliasing=2 тогда все проблемные места будет сразу видно:
Не все, к сожалению, посему придётся разобраться в проблеме.
> То дает инструкцию компилятору считать что все указатели разных типов указывают на непересекающиеся области памяти.
Почти все, char* например - исключение.