Портативная реинтерпретация данных - PullRequest
14 голосов
/ 15 декабря 2011

Я хочу переинтерпретировать данные одного типа как другой тип переносимым способом (C99).Я не говорю о кастинге, я хочу реинтерпретации некоторых данных.Кроме того, под portable я имею в виду, что это не нарушает правила C99 - я понимаю, что not означает, что реинтерпретируемое значение одинаково во всех системах.способ переинтерпретации данных, но только два из них являются переносимыми:

  1. Это не переносимо - оно нарушает правило строгого наложения имен.

    /* #1 Type Punning */
    
    float float_value = 3.14;
    int *int_pointer = (int *)&float_value;
    int int_value = *int_pointer;
    
  2. Это зависит от платформы, потому что оно читает значение int из объединения после записи в него float.Но это не нарушает никаких правил C99, поэтому должно работать (если sizeof(int) == sizeof(float)).

    /* #2 Union Punning */
    
    union data {
      float float_value;
      int int_value;
    };
    
    union data data_value;
    data_value.float_value = 3.14;
    int int_value = data_value.int_value;
    
  3. Должно быть в порядке, пока sizeof(int) == sizeof(float)

    /* #3 Copying */
    
    float float_value = 3.14;
    int int_value = 0;
    memcpy(&int_value, &float_value, sizeof(int_value));
    

Мои вопросы:

  1. Это правильно?
  2. Знаете ли вы другие способы реинтерпретации данных в переносном способе??

Ответы [ 5 ]

18 голосов
/ 15 декабря 2011

Решение 2 является переносимым типом - соединение через профсоюзы всегда было законным в C99, и это было сделано явным образом в TC3, который добавил следующую сноску в раздел 6.5.2.3:

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

Приложение J все еще перечисляет его как неопределенное поведение, которое является известным дефектом и было исправлено с помощью C11, который изменил

Значение члена объединения, кроме последнего, сохраненного в [, не указано ]

до

Значения байтов, которые соответствуют членам объединения, кроме последнего сохраненного в [ не указаны ]

Это не так уж важно, так как приложение только информативное, а не нормативное.

Имейте в виду, что вы все равно можете получить неопределенное поведение, например

  • путем создания представления ловушки
  • путем нарушения правил псевдонимов в случае элементов с типом указателя (которые в любом случае не следует преобразовывать с помощью определения типов, так как не требуется единообразное представление указателя)
  • если члены объединения имеют разные размеры - только байты члена, последнего использовавшегося в магазине, имеют указанное значение; в частности, сохранение значений в меньшем элементе также может сделать недействительными завершающие байты большего элемента
  • если элемент содержит байты заполнения, которые всегда принимают неопределенные значения
2 голосов
/ 15 декабря 2011
  1. Решение объединения определено как memcpy в C (AFAIK, это UB в C ++), см. DR283

  2. Можно привести указатель к указателю на (подписанный / без знака /) символ, поэтому

    unsigned char *ptr = (unsigned char*)&floatVar;
    

    , а затем доступ к ptr [0] к ptr [sizeof (floatVar) -1] допустим.

0 голосов
/ 15 декабря 2011

Если вы хотите избежать строгого правила псевдонимов, вам нужно сначала привести к указателю на символ:

float float_value = 3.14;
int *int_pointer = (int *)(char *)&float_value;
int int_value = *int_pointer;

Обратите внимание, что у вас может быть sizeof(int) > sizeof(float), в этом случае вы по-прежнему получаете неопределенное поведение

0 голосов
/ 15 декабря 2011

тип данных int является примером непереносимого типа, поскольку порядковый номер может изменить порядок байтов между платформами.

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

0 голосов
/ 15 декабря 2011

, чтобы быть в безопасности, я бы пошел с байтовым массивом (unsigned char) вместо 'int' для хранения значения.

...