Я сторонник написания эффективного платформозависимого кода. Т.е. перекладываем рутину на препроцессор, отказавшить от выдумывания лишних слоев абстракций, усложняющих то, что можно не усложнять.
А при чём тут компилятор? Например, в формате TIF (если не ошибаюсь) байты могут идти в любом из этих порядков, что отмечено в заголовке. И как тут быть без информации о порядке байт на хост-системе?
И как тут быть без информации о порядке байт на хост-системе?
Во-первых, под линуксами есть endian.h, в котором уже есть функции типа htobe32/htole32/be32toh/le32toh для 16, 32 и 64. Имена не совпадают с OpenBSD, так что это своего рода привязка к GNU/Linux и FreeBSD.
А при чём тут компилятор?
А во-вторых, никто не мешает сделать свои функции преобразования, работающие по байтам. Компилятор тут притом, что он достаточно умный, чтобы работу с байтами преобразовать в одну-две инструкции.
во-первых, это не стандарт и это плюсы. не всегда они есть в проекте, ну и скорость обработки будет сильно страдать. во-вторых, такие трюки можно использовать только когда ты сам кодируешь последовательности. а в сетевых протоколах или в каком-нибудь обмене с железом, например, 3 байта встречаются довольно часто и никаких подобных приблуд там не используется. просто описание протокола с упакованными структурами. и там может быть сколько угодно байтов.
это стандартный приём. и там всё равно надо использовать те самые макросы архитектуры и ifdef'ы, о которых я писала выше. и для разных длин будет куча таких конверторов, кроме стандартных ntoh(s/l).
при чём тут понты? я много лет работаю с железом и сетями. если для тебя распаковка сетевых протоколов - это понты, то для меня это обычная рутина, которой приходится заниматься постоянно. и это должно работать быстро, до кучи. в некоторых случаях можно дойти до ассемблерных вставок, если структуры слишком неудобны для обработки, а скорость важна. и ваш позикс, напомню вам, далеко не везде, где есть С. вы так уверены в ваших библиотеках, а их может просто не быть на какой-нибудь железяке.
я много лет работаю с железом и сетями. если для тебя распаковка сетевых протоколов - это понты, то для меня это обычная рутина, которой приходится заниматься постоянно. и это должно работать быстро, до кучи. в некоторых случаях можно дойти до ассемблерных вставок, если структуры слишком неудобны для обработки, а скорость важна. и ваш позикс, напомню вам, далеко не везде, где есть С. вы так уверены в ваших библиотеках, а их может просто не быть на какой-нибудь железяке.
— понты.
Я уже выше привёл пример того, как компилятор обрабатывает «медленный» код. Результат — одна-две инструкции.
так всё то же самое: ifdef'ы и работа с байтами. а насчёт «умных компиляторов» под мелкоконтроллеры - я бы не стала так надеяться. ибо в них иногда ещё и ошибки попадались. в каждом конкретном случае надо проверять.
и какой байт у тебя «младший» в этом случае? на мотороле, внезапно, твой код не работает.
Вот она — профессиональная деформация. Начисто отбивает возможность думать о числе как о числе без привязки к способу записи.
Есть переменная n. Она представляет собой число. Ты можешь сделать n = n + 1, не заботясь о том, как именно биты расположены в процессоре. Endianness появлятся только при записи в память. Так что младший байт это всегда n & 0xffu, вне зависимости от того, на каком процессоре ты выполняешься и в каком он режиме.
внезапно, в теме нет ничего про позикс и прочие предположения.
а девайсы могут быть какие угодно. например, мы на работе писали библиотеки, которые работали на разных микроконтроллерах. потому что протокол может быть реализован на разном железе. более того, его можно отлаживать вообще на компе, чаще всего.
куда в спецификации формата сказано, туда и буду. И от хостовой машины это никак не зависит. У нас, на минуточку, есть язык C (и в теме он указан, в тегах), в котором по определению определен беззнаковый целый тип с операторами битовых сдвигов.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if (argc < 2)
return 0;
unsigned int a = atoi(argv[1]);
printf("%02x %02x %02x %02x\n", (a >> 24) & 0xffu, (a >> 16) & 0xffu,
(a >> 8) & 0xffu, a & 0xffu);
return 0;
}
Вот код, который печатает число в big-endian. Сначала старший байт, в конце — младший. На моторолле не будет работать, да? Ifdef'ами придётся код обмазать?
Компилятор тут притом, что он достаточно умный, чтобы работу с байтами преобразовать в одну-две инструкции.
Спасибо за информацию. Для меня действительно стало новостью, что такие спагетти компилятор догадался упаковать в одну команду bswap.
Но это не избавляет программиста от необходимости писать спагетти в исходном тексте. А #ifdef'ы использовать для того, чтоб узнать, нужны они или нет, если по каким-то причинам функции из <endian.h> и <arpa/inet.h> нам не подходят.
А ничего, что в коде вообще atoi, а не strtol используется? Само использование atoi уже показывает, что на такие ошибки плевать. Пример показывает разбор целого на байты. Смотри туда. Ты бы ещё докопался до примеров в стандарте. А что? Они вообще не компилируются, так как не являются полными программами.
Стандартная сигнатура main для микроконтроллеров не всегда подходит.
Тогда это уже не Си.
Использование стандартной библиотеки увеличивает размер программы. Иногда критично.
В Си всегда есть стандартная библиотека, на то она и стандартная. Дополнительные требования вроде работы без стандартной библиотеки — это уже отдельная песня. Возможно, для тебя это обычное дело. Но нужно осознавать, что это не стандартный Си.
Операции извлечения байтов не вынесены в отдельные функции - чтрадает читабельность.
А это уже смешно. Я могу понять, зачем это делать в программах побольше. Хотя бы для изоляции кода, подверженного ошибкам. Но говорить про читаемость в примере из десяти строк это перегиб. Предлагаешь внести побольше сущностей, чтобы превратить простой пример в нечитаемое месиво?
Но это не избавляет программиста от необходимости писать спагетти в исходном тексте.
Да. Но тут ещё вот какое дело. Почему-то люди ошибаются во время оперирования с данными различного формата. Забыли, что где-то данные в структуре лежат в bigendian, и вот уже какой-то странный баг.
Если придерживаться правила, по которому все данные в любой структуре в памяти всегда находятся в родном порядке байт, число таких ошибок можно сократить. Все преобразования оставить для кодов сериализации/десериализации. Тогда просто смотря на код можно будет подобные ошибки ловить. Нет преобразования — ошибка.
Конечно, всегда будут оставаться ситуации, в которых нет смысла эти преобразования делать. В общем, там, где такты экономят. Но не зря для Linux сделали sparse. Видать, ошибки be/le — очень частые.
И я ничего плохого не вижу, чтобы с помощью функции или макроса узнать какая у меня архитектура.
Никто, конечно, не осудит твои исследования в этом. Это нормально.
А плохо может быть то, что ты ненароком «продырявишь» абстракцию в реальном проекте. И это приведет к лишней работе по рефакторингу в будущем. Но, возможно, последствия серьезней, когда эта «дырка» будет считаться единственно правильным вариантом, что отразится на дальнейшей архитектуре.
Поэтому, я (и, вероятно, другие), заостряю внимание на том, что в подавляющем большинстве случаев код вида #if __BYTE_ORDER__ - плох, т.к., вероятно, применен без обоснований.
При размере int 64 бита, половина числа не выведется. При размере 16 - выведутся несуществующие 0х00. Возражение не возникло бы при использовании uint32_t.
В МК обычно нет консоли как таковой. int main ( void ) - описан в стандарте,
Про использование на МК мотороллы ты написал первым.
Это для того, чтобы люди не копировали из интернета тяп-ляп написанный код в рабочие проекты. От 4 строчек с взятием байтов хуже не будет.
Да, в принципе тоже код наглядный, хотя вариант с массивом наглядней, да. Это моя стойкая идиосинкразия на memcpy/memmove во что-то - как цепляюсь глазом, так ищу потенциальную ошибки. Поэтому стараюсь минимизировать использование где возможно, если это не сильно бьёт по читаемости, конечно. Хотя, в данном случае, понятно и видно, что функцию оптимизация выбросит.
Уважаемые beastie, i-rinat, PtiCa и Iron bug, хочу внести свой вброс. Сдвиги это хорошо, когда они нужны, например на машине с LE при пересылке в сетевом формате, но не на машине с BE, там где ничего для этого делать ненадо, так как сдвиги это лишние операции на машине с BE. Почему бы мне не создать макрос котгрый на машине с LE разворачивался бы в сдвиги, а на машине с BE разворачивался бы в простое копирование? Для этого и нужно знать порядок байт на машине. Может ваш компилятор и оптимизируети сдвиги на BE машине сам, но сам язык, на сколько я понимаю, никак не обязывает писателей компиляторов писать оптимизацию.
но сам язык, на сколько я понимаю, никак не обязывает писателей компиляторов писать оптимизацию
Сам язык никак не запрещает писателей компиляторов не вставлять nop'ы или пустые циклы в сгенерированный код. Кстати, GCC таки вставляет в код nop'ы.
Почему бы мне не создать макрос котгрый на машине с LE разворачивался бы в сдвиги, а на машине с BE разворачивался бы в простое копирование?
Советы не делать так были только из-за того, что такие манипуляции сильно подвержены ошибкам. Статистически, ты всё равно накосячишь где-нибудь и когда-нибудь. Разные подходы просто меняют твои шансы допустить ошибку.
А так, если тебе хочется разложить по коду грабли, чтобы их потом собирать, никто не сможет тебе помешать. Удачи.
Условная компиляция только для оптимизации, но семантика не меняется. Все, мы имеем удобные, довольно прозрачные и при этом оптимальные функции, абстрагирующие нас от хост-архитектуры.
у инженера есть масса опыта по отладке и тестированию. и всё проверяется. но так-то да, с опытом проблем гораздо меньше. при чём тут мир и математика - непонятно. математика мир не описывает. хотя в программировании математика может пригодиться.
ага. щаз меня тут неграмотные сопляки будут учить. неважен порядок байт на машине у них... идите в ваши хацкели и сидите там, не высовывайтесь.
Если ты не знаешь, как платформонезависимо упаковать байтики в int и, более того, до сих пор не прочитала об этом в _ЭТОЙ_ВЕТКЕ_, то это много говорит о твоей квалификации.