Почему в C ++ нет модификатора порядка байтов, как для подписи?

(Думаю, этот вопрос может относиться ко многим типизированным языкам, но я решил использовать в качестве примера C ++.)

Почему нет возможности просто написать:

struct foo {
    little int x;   // little-endian
    big long int y; // big-endian
    short z;        // native endianness
};

указать порядок байтов для конкретных членов, переменных и параметров?

Сравнение с подписью

Я понимаю, что тип переменной не только определяет, сколько байтов используется для хранения значения, но и то, как эти байты интерпретируются при выполнении вычислений.

Например, каждое из этих двух объявлений выделяет по одному байту, и для обоих байтов каждая возможная 8-битная последовательность является допустимым значением:

signed char s;
unsigned char u;

но одна и та же двоичная последовательность может интерпретироваться по-разному, например 11111111 будет означать -1 при присвоении s, но 255 при назначении u. Когда знаковые и беззнаковые переменные участвуют в одном и том же вычислении, компилятор (в основном) заботится о правильных преобразованиях.

В моем понимании, порядок байтов - это просто вариант того же принципа: другая интерпретация двоичного шаблона, основанная на информации времени компиляции о памяти, в которой он будет храниться.

Кажется очевидным наличие этой функции в типизированном языке, допускающей низкоуровневое программирование. Однако это не является частью C, C ++ или любого другого языка, который я знаю, и я не нашел обсуждения этого в Интернете.

Обновлять

Я постараюсь обобщить некоторые выводы из множества комментариев, которые я получил в первый час после вопроса:

  1. подписанность является строго двоичной (либо со знаком, либо без знака) и всегда будет, в отличие от порядка байтов, который также имеет два хорошо известных варианта (большой и маленький), но также и менее известные варианты, такие как смешанный / средний порядок байтов. В будущем могут быть изобретены новые варианты.
  2. Порядок байтов имеет значение при побайтовом доступе к многобайтовым значениям. Есть много аспектов, помимо порядка байтов, которые влияют на структуру памяти многобайтовых структур, поэтому такой доступ в большинстве случаев не рекомендуется.
  3. C ++ нацелен на абстрактную машину и минимизирует количество предположений о реализации. . Эта абстрактная машина не имеет никакого порядка байтов.

Кроме того, теперь я понимаю, что подписывание и порядок байтов - не идеальная аналогия, потому что:

  • Порядок байтов определяет только как что-то представляется в виде двоичной последовательности, но теперь что может быть представлено. И big int, и little int будут иметь одинаковый диапазон значений.
  • подписанность определяет как биты и фактические значения сопоставляются друг с другом, но также влияет на то, что может быть представлено, например -3 не может быть представлен unsigned char и (при условии, что char имеет 8 бит) 130 не может быть представлен signed char.

Таким образом, изменение порядка байтов некоторых переменных никогда не повлияет на поведение программы (за исключением побайтового доступа), тогда как изменение подписи обычно изменится.


person Lena Schimmel    schedule 28.11.2017    source источник
comment
Потому что C ++ описывает поведение абстрактной машины, не имеющей понятия о порядке байтов?   -  person YSC    schedule 28.11.2017
comment
Я не думаю, что низкоуровневое программирование в контексте C / C ++ означает то, что вы думаете. Например, вы предполагаете, что char имеет 8 бит. Это только минимум, а не фиксированное требование. Вы также предполагаете, что подписанная версия является дополнением до 2, но это совсем не обязательно. C / C ++ являются низкоуровневыми только в той степени, в которой это полезно для разработчиков спецификаций и практично для разработчиков; кроме того, они могут и должны использовать абстракцию так же широко, как и любой другой язык. Большинство программистов никогда не узнают и не позаботятся о порядке байтов, представлении знаков и т. Д .; они просто хотят, чтобы все происходило.   -  person underscore_d    schedule 28.11.2017
comment
Если вы хотите поиграть с порядком байтов, вы можете проверить Endian библиотека   -  person AndyG    schedule 28.11.2017
comment
Хорошо, я собираюсь представить новую концепцию порядка байтов - я называю это «обратным порядком байтов» в том смысле, что это большой порядок байтов, но обратный порядок следования битов, а не порядок байтов. Вы хотите, чтобы весь язык изменился только для моей новой архитектуры?   -  person UKMonkey    schedule 28.11.2017
comment
@UKMonkey: вы не можете адресовать биты в C ++, поэтому я действительно не вижу, чем ваш обратный прямой порядок байтов отличается от прямого порядка байтов.   -  person geza    schedule 28.11.2017
comment
Я бы сказал, что польза от этого ограничена. Преобразование в порядок байтов требуется очень редко, в этом языке нет реальной необходимости.   -  person geza    schedule 28.11.2017
comment
@geza UKMonkey был саркастичен. Его точка зрения: порядок байтов зависит от архитектуры, и каждый, в том числе UKMonkey, находящийся под кислотами, может спроектировать новую архитектуру. Язык C ++ не должен принимать во внимание пользователей SO.   -  person YSC    schedule 28.11.2017
comment
Возможный дубликат C Endian нейтрален?   -  person underscore_d    schedule 28.11.2017
comment
@YSC: Излишний сарказм. За последние 20 лет у нас не было из чего выбирать.   -  person geza    schedule 28.11.2017
comment
Я не понимаю, почему это очевидная особенность. Какую проблему это решит?   -  person molbdnilo    schedule 28.11.2017
comment
Я думаю, можно с уверенностью сказать, что понятие знака можно считать абстрактным, в то время как порядок байтов во многом зависит от реализации. Думаю, лучшее сравнение было бы со спецификациями центровки.   -  person StoryTeller - Unslander Monica    schedule 28.11.2017
comment
@geza всякий раз, когда кто-то делал предположение о том, что произойдет в будущем, они сжигались. Штамп с датой 2000 года был прекрасным примером того, что происходит, когда вы говорите, но пока это работает. Сейчас, когда квантовые компьютеры постоянно совершенствуются, как вы можете предсказать, как они будут хранить данные?   -  person UKMonkey    schedule 28.11.2017
comment
@molbdnilo: это очевидная особенность. Стандарт мог бы определить (возможно, необязательную) функцию, как должны располагаться лежащие в основе байты. Это не сильно влияет на язык. Это может помочь в обработке данных, исходящих от другого порядка байтов. Бьюсь об заклад, если бы этой функции требовалось много, для нее была бы поддержка.   -  person geza    schedule 28.11.2017
comment
@UKMonkey: так? Насколько они актуальны? Я просто говорю, что у нас может быть поддержка big_endian / little_endian, которая указывает, как число должно храниться в памяти. Это платформенно-нейтральная вещь, может быть реализована на всех машинах.   -  person geza    schedule 28.11.2017
comment
@geza Как он платформенно-нейтральный? Порядок байтов основан на существовании байтов, т. Е. Данных, разделенных на определенные блоки. Что, если они представят троичную архитектуру, в которой нет байтов, а есть только строки битов, разделенные новой третьей цифрой? Как в этом случае можно говорить о байтах, а тем более реализовывать их? Это было бы шаром и цепью для дизайна и развития языка в других пространствах.   -  person underscore_d    schedule 28.11.2017
comment
@underscore_d: Будет ли эта машина вообще совместима с C ++? Если да, то я думаю, что с помощью битовых операций он может обрабатывать текущие данные с прямым порядком байтов.   -  person geza    schedule 28.11.2017
comment
@geza Конечно, может, но для этого потребуется, чтобы язык поддерживал определенные представления данных, чего, как я думал, мы пытались избежать, определяя только абстрактную машину - не говоря уже о том факте, что тогда нам потребовалось бы он поддерживает определенные и неродные представления, что является еще одним мостом, выходящим за рамки этого.   -  person underscore_d    schedule 28.11.2017
comment
@underscore_d: да, именно так. С другой стороны, когда нам нужно обрабатывать реальный низкоуровневый материал, эта абстрактная машина становится на пути. Так что C ++ мог бы иметь поддержку для этого, это совсем не повредило бы. Но, как я уже сказал, эта функция редко нужна, она того не стоит (на мой взгляд). Если это будет часто используемая функция, компиляторы C ++ могут иметь ее как расширение. Но ни один из известных мне компиляторов C ++ не реализовал это.   -  person geza    schedule 28.11.2017
comment
@geza Я понимаю вашу точку зрения. Я не чувствую, что концепции абстрактных машин действительно мешают; во всех случаях, которые я видел, почти тривиально кодировать наши собственные процедуры для чтения и записи данных с определенным порядком байтов или любыми другими деталями реализации, и я не думаю, что небольшой объем работы для относительно небольшого числа программистов, которым нужны такие вещи оправдывают хлопоты, которые это может вызвать у комитета, поставщиков и т. д.   -  person underscore_d    schedule 28.11.2017
comment
@underscore_d: абсолютно, я согласен с вами (в основном то, что я пытался сказать).   -  person geza    schedule 28.11.2017
comment
Вау, это привело к большему количеству дискуссий на форуме, чем я ожидал. Возможно, речь идет о границе между аппаратными особенностями, которые язык / компилятор полностью скрывает от программиста, и деталями, которые не могут быть скрыты и поэтому нуждаются в некотором представлении на языке. Думаю, я понимаю, почему подпись попадает на одну сторону этой границы, а порядок следования байтов - на другую.   -  person Lena Schimmel    schedule 28.11.2017
comment
@LenaSchimmel В каком смысле представление подписи является менее абстрактной, непрозрачной деталью реализации? (Конечно, я исключаю необязательные [u]intN_t типы точной ширины, которые должны быть дополнением до 2, если они существуют)   -  person underscore_d    schedule 28.11.2017
comment
Связано с @underscore_d: Как std :: cout выводит отрицательный ноль в системе с дополнением до единицы?. Иногда в стандарте неясно, где заканчивается указание представления целого числа со знаком.   -  person YSC    schedule 28.11.2017
comment
Я прочитал ваше обновление. На мой взгляд, это достаточно информативно, чтобы быть ответом. Я буду за это.   -  person YSC    schedule 28.11.2017
comment
@geza: Почему вы не можете адресовать биты в C? По общему признанию, я бы предпочел использовать C для таких вещей, но разве C ++ не поддерживает все побитовые операторы C? Включая битовые поля, по-видимому: stackoverflow.com/questions/4240974/   -  person jamesqf    schedule 28.11.2017
comment
@jamesqf: обратный прямой порядок байтов имеет смысл только на машине, где вы можете адресовать отдельные биты. Побитовые операторы не предназначены для адресации битов. Адресация битов означает предоставление мне индексного бита. Например, если вы записываете 128 в байт, 0-й бит равен 1 для машины с обратным порядком байтов и 0 для обычной машины (или наоборот).   -  person geza    schedule 28.11.2017
comment
@geza: Немного сбился с пути, но IIRC, когда я играл с битами, я делал именно это, в том числе менял местами в зависимости от порядка байтов машины.   -  person jamesqf    schedule 29.11.2017
comment
Я не знаю, способствует ли это обсуждению (ям), но нигде на этой странице об этом не упоминалось. Ховард Хиннант сделал предложение по поводу порядка байтов: open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0463r1.html   -  person engf-010    schedule 29.11.2017
comment
В языке C есть целые числа со знаком и без знака, поскольку сборка PDP-11 поддерживает целочисленные инструкции со знаком и без знака. C не поддерживает множественные порядки байтов, потому что сборка PDP-11 не поддерживает множественные порядки байтов. Абстрактная машина - это модель некоторых очень конкретных машин.   -  person    schedule 29.11.2017
comment
@molbdnilo: проблемы, подобные этой: stackoverflow.com/questions/6732127/   -  person vsz    schedule 29.11.2017
comment
Я бы хотел иметь такие типы, как big uint16_t: это позволило бы отправлять структуры по сети, не беспокоясь о переносимости. Нет необходимости сначала сериализовать структуру, просто позвольте компилятору делать свое дело. ‹/Day-dream›   -  person cmaster - reinstate monica    schedule 29.11.2017
comment
@LenaSchimmel, ты все еще не удовлетворен каким-либо ответом? Если это так, возможно, вам стоит предложить улучшения или подзадачи, на которые еще предстоит ответить.   -  person YSC    schedule 09.04.2018


Ответы (9)


Что говорит стандарт

[intro.abstract]/1:

Семантические описания в этом документе определяют параметризованную недетерминированную абстрактную машину. Этот документ не предъявляет требований к структуре соответствующих реализаций. В частности, им не нужно копировать или имитировать структуру абстрактной машины. Скорее, соответствующие реализации требуются для имитации (только) наблюдаемого поведения абстрактной машины, как объяснено ниже.

C ++ не может определить квалификатор порядка байтов, поскольку в нем нет концепции порядка байтов.

Обсуждение

О разнице между знаками и порядком байтов OP писал

В моем понимании, порядок байтов - это просто вариант того же принципа [(знак)]: другая интерпретация двоичного шаблона, основанная на информации времени компиляции о памяти, в которой он будет храниться.

Я бы сказал, что у подписи есть семантический и репрезентативный аспект 1. [intro.abstract]/1 подразумевает, что C ++ заботится только о семантике и никогда не обращается к способу представления числа со знаком в памяти 2. Фактически, "бит знака" появляется только один раз в спецификации C ++ и относятся к значению, определяемому реализацией.
С другой стороны, порядок байтов имеет только репрезентативный аспект: порядок байтов не имеет значения.

В C ++ 20 отображается std::endian. Он по-прежнему определяется реализацией, но давайте проверим порядок байтов хоста, не полагаясь на старые приемы, основанные на неопределенном поведении.


1) Семантический аспект: целое число со знаком может представлять значения ниже нуля; репрезентативный аспект: нужно, например, зарезервировать бит для передачи положительного / отрицательного знака.
2) В том же ключе C ++ никогда не описывает, как должно быть представлено число с плавающей запятой, IEEE-754 часто используется, но это выбор, сделанный реализацией, в любом случае предусмотренный стандартом: _ 4_ "Представление значения типов с плавающей запятой определяется реализацией".

person YSC    schedule 28.11.2017

В дополнение к ответу YSC, давайте возьмем ваш пример кода и рассмотрим, чего он может достичь.

struct foo {
    little int x;   // little-endian
    big long int y; // big-endian
    short z;        // native endianness
};

Вы можете надеяться, что это точно укажет макет для независимого от архитектуры обмена данными (файл, сеть, что угодно)

Но это не может сработать, потому что некоторые вещи все еще не определены:

  • размер типа данных: вам нужно будет использовать little int32_t, big int64_t и int16_t соответственно, если это то, что вы хотите
  • заполнение и выравнивание, которые нельзя строго контролировать в пределах языка: используйте #pragma или __attribute__((packed)) или какое-либо другое расширение, специфичное для компилятора
  • фактический формат (подпись с дополнением до 1 или 2, макет типа с плавающей запятой, представления ловушек)

В качестве альтернативы вы можете просто отразить порядок байтов некоторого указанного оборудования, но big и little здесь не охватывают все возможности (только два наиболее распространенных).

Таким образом, предложение является неполным (в нем не различаются все разумные механизмы упорядочения байтов), неэффективным (не достигается того, к чему оно стремится) и есть дополнительные недостатки:

  • Представление

    Изменение порядка байтов переменной из собственного порядка байтов должно либо отключить арифметику, сравнения и т. Д. (Поскольку аппаратное обеспечение не может правильно выполнять их для этого типа), либо должно незаметно вводить дополнительный код, создавая временные библиотеки, упорядоченные в собственном порядке. работать на.

    Аргумент здесь не в том, что ручное преобразование в / из собственного порядка байтов быстрее, а в том, что его явное управление упрощает минимизацию количества ненужных преобразований и гораздо проще рассуждать о том, как код будет вести себя, чем если бы преобразования были неявными.

  • Сложность

    Все, что перегружено или специализируется на целочисленных типах, теперь требует вдвое больше версий, чтобы справиться с тем редким событием, когда ему передается значение, отличное от собственного порядка байтов. Даже если это просто оболочка пересылки (с парой приведений для перевода в / из нативного упорядочивания), это все равно много кода без заметной выгоды.

Последний аргумент против изменения языка для поддержки этого заключается в том, что вы можете легко сделать это в коде. Изменение синтаксиса языка - это большое дело, и оно не дает никаких очевидных преимуществ по сравнению с чем-то вроде оболочки типа:

// store T with reversed byte order
template <typename T>
class Reversed {
    T val_;
    static T reverse(T); // platform-specific implementation
public:
    explicit Reversed(T t) : val_(reverse(t)) {}
    Reversed(Reversed const &other) : val_(other.val_) {}
    // assignment, move, arithmetic, comparison etc. etc.
    operator T () const { return reverse(val_); }
};
person Useless    schedule 28.11.2017
comment
Я действительно не понимаю, почему Производительность является аргументом. Если вам нужно использовать другой порядок байтов, то вы делаете это не зря. Если для них нет языковой поддержки, то это нужно запрограммировать. Производительность будет равной. Или даже версия на языке будет быстрее, поскольку компилятор может реализовать оптимизированный код для преобразования с порядком байтов. По поводу сложности: да, если это будет новый тип, то это сложно. Но что, если тип такой же? Точно так же, как квалификатор const. Это могло работать без серьезных сложностей. - person geza; 28.11.2017
comment
В частности, обратите внимание, что были реальные машины, для которых 4-байтовые целые числа не были ни большим, ни малым порядком байтов (PDP / 11 со смешанным порядком байтов и S-режим Prime, где 4-байтовое целое число составляло 3 1 бита. , могут быть и другие) - person Martin Bonner supports Monica; 28.11.2017
comment
@MartinBonner: но почему этот факт мешает C ++ иметь квалификаторы с прямым / обратным порядком байтов? Это помогло бы людям взаимодействовать с этими представлениями. Это не означает, что у нас должны быть все возможные целочисленные представления в языке. Если вы хотите интерпретировать изображение PNG на PDP / 11, вам нужно создать код для чтения чисел с прямым порядком байтов. Вместо этого вы можете просто использовать big_endian int x;, и компилятор сделает код за вас. - person geza; 28.11.2017
comment
Я думаю, что было бы трудно привести убедительные доводы в пользу расширения базового языка, чтобы добавить явную поддержку для неполной группы деталей архитектуры. Сравните атомику - выбор двух из самых популярных порядков памяти и игнорирование других действительно используемых порядков подорвет смысл их стандартизации в первую очередь. - person Useless; 29.11.2017
comment
Конечно, это не причина, по которой C ++ не может иметь эти квалификаторы, но причина того, что это не очень убедительная идея. Вы уже можете делать все, что требуется для конкретной платформы, поэтому от стандартизации не будет никакой реальной пользы, если вы действительно не охватите все проблемное пространство. Я думаю, вы недооцениваете проделанную работу, переоцениваете вероятные преимущества и, откровенно говоря, просто приводите аргументы. - person Useless; 29.11.2017
comment
По крайней мере, класс Reversed можно было бы предложить для включения в стандартную библиотеку с лучшей интеграцией. Это не так просто, как кажется, заставить тип класса вести себя как встроенный тип, есть много подводных камней, которых следует избегать (он должен быть тривиальным, стандартный макет, следует позаботиться о том, чтобы он всегда продвигался до достаточно большого размера. целое число, если возможно, но все же арифметические операторы над двумя Reversed должны давать Reversed и т. д. ...). Более того, чтобы быть эффективной, функция reverse должна быть реализована в сборке. И самое главное, как его интегрировать с потоками (бинарными / форматированными?) - person Oliv; 29.11.2017
comment
Я не испытываю этой навязчивой идеи (по общему признанию, от geza, а не от вас) встраивать ее в язык. Порядок байтов зависит от платформы, у большинства платформ уже есть эффективные встроенные функции для его изменения, он не очень полезен без стандартизации специфичного для платформы контроля над структурой и т. Д. И т. Д. Это большое изменение, добавляющее много сложности в систему типов. для того, что вы уже можете сделать. - person Useless; 29.11.2017
comment
Если C ++ отказался от своего выбора, чтобы не беспокоиться о представлении значений, они должны определять не только порядок байтов, но и представление чисел с плавающей запятой, целых чисел со знаком, структуры памяти и т. Д. Это много! - person YSC; 29.11.2017
comment
@Useless Стандартизированные библиотечные функции существуют для порядка байтов, используемого в нескольких стандартизованных форматах проводов, поэтому существует приоритет для стандартизации функций для поддержки одного определенного формата проводов. Так что аргумент о том, что это не исчерпывающий список форматов, используемых архитектурами ЦП, не кажется мне веским аргументом. Также существует приоритет для расширения системы типов функциями для управления компоновкой структур данных, таких как битовые поля и упакованные структуры. Я не уверен, что они стандартизированы. - person kasperd; 29.11.2017
comment
Какие это функции? И какая часть системы типов контролирует упаковку? - person Useless; 30.11.2017

Целые числа (как математическое понятие) имеют понятие положительных и отрицательных чисел. Эта абстрактная концепция знака имеет несколько аппаратных реализаций.

Порядок байтов - это не математическое понятие. Little-endian - это уловка с аппаратной реализацией для повышения производительности арифметических операций с многобайтовыми целыми числами с дополнением до двух на микропроцессоре с 16- или 32-разрядными регистрами и 8-разрядной шиной памяти. Его создание потребовало использования термина big-endian для описания всего остального, что имеет тот же порядок байтов в регистрах и в памяти.

Абстрактная машина C включает в себя концепцию знаковых и беззнаковых целых чисел без подробностей - без необходимости в арифметике с дополнением до двух, 8-битных байтах или способах хранения двоичного числа в памяти.

PS: Я согласен с тем, что совместимость двоичных данных в сети или в памяти / хранилище является PIA.

person Chad Farmer    schedule 28.11.2017

Это хороший вопрос, и я часто думал, что что-то подобное будет полезно. Однако вам нужно помнить, что C нацелен на независимость от платформы, а порядок байтов важен только тогда, когда подобная структура преобразуется в некоторую базовую схему памяти. Это преобразование может произойти, когда вы, например, преобразуете буфер uint8_t в int. Хотя модификатор порядка байтов выглядит аккуратно, программисту все же необходимо учитывать другие различия платформ, такие как размеры int, выравнивание и упаковка структуры. Для защитного программирования, когда вы хотите найти контроль степени детализации того, как некоторые переменные или структуры представлены в буфере памяти, лучше всего закодировать явные функции преобразования, а затем позволить оптимизатору компилятора сгенерировать наиболее эффективный код для каждой поддерживаемой платформы.

person D Dowling    schedule 28.11.2017
comment
Это неплохой ответ, и он дополняет мою точку зрения. Но его можно было немного переработать. - person YSC; 28.11.2017
comment
Это преобразование может произойти, когда вы, например, преобразуете буфер uint8_t в int. Простое преобразование является неопределенным поведением из-за нарушения правил сглаживания; a memcpy() - единственный четко определенный способ выполнить это преобразование. Тогда да, значимость результата зависит от соответствующих байтовых макетов исходного буфера и типа назначения. - person underscore_d; 28.11.2017

Порядок байтов по своей сути не является частью типа данных, а скорее является его структурой хранения.

Таким образом, это не будет действительно похоже на подписанный / беззнаковый, а скорее на ширину битового поля в структурах. Подобно тем, они могут использоваться для определения двоичных API.

Итак, у вас будет что-то вроде

int ip : big 32;

который будет определять как макет хранилища, так и целочисленный размер, оставляя компилятору выполнение наилучшей работы по сопоставлению использования поля с его доступом. Для меня не очевидно, какими должны быть разрешенные декларации.

person Community    schedule 29.11.2017

Краткий ответ: если нельзя использовать объекты в арифметических выражениях (без перегруженных операторов) с использованием целых чисел, то эти объекты не должны быть целочисленными типами. И нет смысла разрешать сложение и умножение прямого и прямого порядка байтов в одном выражении.

Более длинный ответ:

Как кто-то упомянул, порядок байтов зависит от процессора. Что на самом деле означает, что именно так представляются числа, когда они используются как числа на машинном языке (как адреса и как операнды / результаты арифметических операций).

То же самое и с вывесками. Но не в такой степени. Чтобы использовать числа в качестве чисел, необходимо преобразовать языково-семантические обозначения в обозначения, приемлемые для процессора. Чтобы использовать числа в качестве данных (отправлять их по сети или представлять метаданные о данных, отправляемых по сети, например о длине полезной нагрузки), необходимо выполнить преобразование от big-endian к little-endian и обратное.

Тем не менее, это решение, по-видимому, в основном обусловлено вариантами использования. Обратной стороной является то, что есть веская прагматическая причина игнорировать определенные варианты использования. Прагматизм возникает из-за того, что преобразование порядка байтов обходится дороже, чем большинство арифметических операций.

Если бы в языке была семантика для сохранения чисел с прямым порядком байтов, это позволило бы разработчикам выстрелить себе в ногу, заставляя числа с прямым порядком байтов в программе, которая выполняет много арифметических операций. Если бы она была разработана на машине с прямым порядком байтов, такое обеспечение порядка байтов было бы бесполезным. Но при переносе на машину с прямым порядком байтов возникало много неожиданных замедлений. И если бы рассматриваемые переменные использовались как для арифметических операций, так и в качестве сетевых данных, это сделало бы код полностью непереносимым.

Отсутствие этой семантики порядка байтов или принуждение их быть явным образом зависящим от компилятора вынуждает разработчиков пройти мысленный этап представления чисел как «считываемых» или «записываемых» в / из сетевого формата. Это сделало бы код, который преобразует между сетью и порядком байтов хоста в середине арифметических операций, громоздким и менее предпочтительным способом написания для ленивых разработчиков.

А поскольку развитие - это человеческое усилие, делать плохой выбор неудобным - это хорошо (TM).

Изменить: вот пример того, как это может пойти плохо: Предположим, что представлены типы little_endian_int32 и big_endian_int32. Тогда little_endian_int32(7) % big_endian_int32(5) - постоянное выражение. Каков его результат? Преобразовываются ли числа в исходный формат неявно? Если нет, то каков результат? Что еще хуже, какова ценность результата (который в этом случае, вероятно, должен быть одинаковым на всех машинах)?

Опять же, если многобайтовые числа используются как простые данные, то массивы символов также хороши. Даже если они являются «портами» (которые на самом деле являются значениями поиска в таблицах или их хэшах), они представляют собой просто последовательности байтов, а не целочисленные типы (с которыми можно выполнять арифметические операции).

Теперь, если вы ограничите разрешенные арифметические операции над числами с прямым порядком байтов только теми операциями, которые разрешены для типов указателей, тогда у вас может быть лучший случай для предсказуемости. Тогда myPort + 5 действительно имеет смысл, даже если myPort объявлен как что-то вроде little_endian_int16 на машине с прямым порядком байтов. То же самое для lastPortInRange - firstPortInRange + 1. Если арифметика работает так же, как для типов указателей, тогда это будет делать то, что вы ожидаете, но firstPort * 10000 будет незаконным.

Затем, конечно, вы вступаете в спор о том, оправдано ли раздувание функции какой-либо возможной выгодой.

person Dmitry Rubanovich    schedule 29.11.2017
comment
Как всегда, те, кто проголосовал против, объясните, почему вы это сделали. - person YSC; 29.11.2017

С точки зрения прагматичного программиста, ищущего Stack Overflow, стоит отметить, что на дух этого вопроса можно ответить с помощью служебной библиотеки. У Boost есть такая библиотека:

http://www.boost.org/doc/libs/1_65_1/libs/endian/doc/index.html.

Особенность библиотеки, которая больше всего похожа на обсуждаемую языковую функцию, - это набор арифметических типов, таких как big_int16_t.

person Peter    schedule 29.11.2017

Потому что никто не предлагал добавить его в стандарт и / или потому, что разработчик компилятора никогда не чувствовал в этом необходимости.

Может быть, вы могли бы предложить это комитету. Я не думаю, что это сложно реализовать в компиляторе: компиляторы уже предлагают фундаментальные типы, которые не являются фундаментальными типами для целевой машины.

Разработкой C ++ занимаются все программисты на C ++.

@Schimmel. Не слушайте людей, оправдывающих статус-кво! Все приведенные аргументы в оправдание этого отсутствия более чем непрочны. Студент-логик может найти их несогласованность, ничего не зная о компьютерных науках. Просто предложите это и не обращайте внимания на патологических консерваторов. (Совет: предлагайте новые типы, а не квалификатор, потому что ключевые слова unsigned и signed считаются ошибками).

person Oliv    schedule 28.11.2017
comment
Было бы сюрпризом, если бы такое предложение действительно удалось. Проблемы с порядком следования байтов возникают очень редко, и их легко решить с помощью служебных функций. Им не обязательно владеть языком. - person geza; 28.11.2017
comment
@geza Отклонено, потому что люди думают, что это бесполезно, это действительно хорошая причина отклонить его. Это всегда одни и те же дебаты о C ++, полезности того, что предоставляет язык. У программистов на C есть свое мнение по этому поводу, у программистов на C ++ - другое. Я думаю, что последняя группа больше ориентирована на продуктивность. - person Oliv; 28.11.2017
comment
Единственный способ внести что-то в стандарт C - указать на успешную реализацию в существующем компиляторе, поэтому для начала нужно попросить gcc / clang / кого-то реализовать. Затем пингуйте комитет. - person pipe; 28.11.2017
comment
@pipe, статья Страуструпа SELL защищает библиотеки, которые добавляют семантику к языкам в качестве проверки того, следует ли включать в язык новые семантические конструкции (а не настраиваемые языковые расширения, встроенные в реализации компилятора). - person Dmitry Rubanovich; 29.11.2017
comment
@Oliv, ты уверен, что это не было предложено? Я ожидал, что он будет отклонен, потому что он включит много кода, который усложнит оптимизацию. Любой код, который имеет целые числа сетевого порядка байтов, участвующих в вычислениях, потребует либо большого количества преобразований, либо оптимизатора, который удалит эти преобразования. В нынешнем состоянии типов сложно писать код, который смешивал бы целые байты хоста и целые байты сети в одних и тех же вычислениях. Сделать целые байты базовыми типами было бы слишком просто. - person Dmitry Rubanovich; 29.11.2017
comment
@DmitryRubanovich Я понятия не имею, предлагалось ли это, но если это было отклонено, это не значит, что так и будет, контекст изменился, похоже, что комитет осознал, что сетевое взаимодействие имеет решающее значение сейчас! Типы с неосновным порядком байтов должны быть продвинуты, это продвижение действительно будет включать одну машинную инструкцию (ROR на x86). Но это уже относится к битовым полям, которые (должны быть) извлечены с помощью BEXTR. - person Oliv; 29.11.2017
comment
@DmitryRubanovich Я был удивлен оптимизатором, есть возможность упростить до отдельных инструкций, код, который фактически выполняет некоторые общие манипуляции с битами, такие как подсчет числа 0 бит как BSF. Вывод таков: мы всегда должны писать целые функции на ассемблере. Это доказательство того, что в языках C / C ++ есть дыры. - person Oliv; 29.11.2017
comment
@Oliv, re: необходимо продвигать типы с неосновным порядком байтов. Я предполагаю, что вы имеете в виду неродную конечность, когда говорите «нефундаментальный». Пожалуйста, поправьте меня, если я ошибаюсь. См. Часть моего ответа stackoverflow.com/a/47543039/1219722 после Edit . Постоянные выражения должны оцениваться во время компиляции. Как можно переносить проблемы с постоянными выражениями, которые я поднял? Потому что, если результаты константных выражений (в которых все размеры данных четко определены, а не имплицированы) не переносимы, это будет шагом назад. - person Dmitry Rubanovich; 30.11.2017
comment
@DmitryRubanovich Нет, я имел в виду фундаментальный, независимо от того, что термин «родной» не определен в стандарте C ++, термин «фундаментальный тип» есть. Я прочитал твой ответ. Дело в том, чтобы определить типы, набор значений которых совпадает с набором значений фундаментального арифметического типа, но с переносимым представлением (последовательность символов без знака). Под переносимым я подразумеваю переносимое (передаваемое) значение: я отправляю представление значения A в виде потока unsigned char на вторую машину, и эта вторая машина должна интерпретировать эту последовательность как значение A. - person Oliv; 30.11.2017
comment
@DmitryRubanovich Посмотрите на class Reversed в ответе Бесполезного и подумайте, что будет. На одной машине важна только ценность. Но для общения мы должны установить стандартизированное представление, как в реальном мире. - person Oliv; 30.11.2017
comment
@Oliv, я знаю о пользе. Но вопрос был не в том, каковы преимущества ...? Почему его там нет? Так что естественное место для поиска - обратная сторона. Обратный класс в этом ответе не даст согласованных результатов для целочисленного оператора по модулю % для константных выражений (вычисляемых во время компиляции). Целочисленные типы должны давать согласованные результаты при выполнении арифметических операций. Все, что отправлено по сети, - это просто данные. С таким же успехом это могут быть байты. - person Dmitry Rubanovich; 30.11.2017
comment
Давайте продолжим это обсуждение в чате. - person Oliv; 30.11.2017
comment
Вывод таков: мы всегда должны писать целые функции на ассемблере. это сарказм o0? - person YSC; 30.11.2017
comment
@YSC Да, я действительно разочарован тем, что мне приходится писать какой-либо фрагмент кода на ассемблере! Но часто у меня нет другого выбора. И мне приходится писать гораздо больше, чем одну инструкцию в asm. Я действительно разочарован тем, что компилятор не использует даже 1% инструкций или не может распознать базовые шаблоны, которые можно упростить до одной инструкции или нескольких оптимальных наборов. Вера в то, что компилятор сможет оптимально использовать процессор, была чистой утопической мыслью, мы должны вернуться к этой парадигме АЛГОЛА и предоставить языку правильный плагин asm для C ++. - person Oliv; 30.11.2017
comment
@Oliv, странно, не раз удивлялся качеству оптимизаций GCC. Если компилятор действительно создает двоичный файл низкого качества, мы все равно облажаемся: все наши инструменты вывода и высокоуровневые инструменты (стандартные контейнеры, алгоритмы и т. Д.) Сильно зависят от способности компилятора оптимизировать их прочь. - person YSC; 30.11.2017
comment
@YSC, например, самая большая проблема с оптимизацией GCC - это неспособность преобразовать switch-case в поиск по таблице. Но ладно, clang работает, и его можно реализовать без сборки. Просто попробуйте написать алгоритм, который вычисляет неотъемлемую часть log2 от int: вам просто нужно вычесть до 31 количество самых тяжелых битов, установленных в 0. В сборке это 2 инструкции, но ни GCC, ни clang не производят этого. И будет хуже, если вы попытаетесь использовать тот факт, что инструкция может установить целевой операнд в одном случае или флаг нуля в другом случае, как BSF. - person Oliv; 30.11.2017
comment
@YSC Но для целей этого вопроса и GCC, и Clang распознают вращение, когда оно выражается в терминах сдвигов и побитового или. - person Oliv; 30.11.2017

Порядок байтов специфичен для компилятора в результате того, что он специфичен для машины, а не как механизм поддержки независимости от платформы. Стандарт - это абстракция, которая не принимает во внимание навязывание правил, которые делают вещи «легкими» - его задача - создать сходство между компиляторами, что позволяет программисту создать «платформенную независимость» для своего кода - если они захотят это сделать. так.

Первоначально между платформами велась большая конкуренция за долю на рынке, а также - компиляторы чаще всего писались производителями микропроцессоров как проприетарные инструменты и для поддержки операционных систем на определенных аппаратных платформах. Intel, вероятно, не очень беспокоилась о написании компиляторов, поддерживающих микропроцессоры Motorola.

В конце концов, C был изобретен Bell Labs для переписывания Unix.

person jinzai    schedule 28.11.2017