Разыменование каламбурившего типом указателя нарушит строго искажающие правила

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

double data_read(FILE *stream,int code) {
        char data[8];
        switch(code) {
        case 0x08:
            return (unsigned char)fgetc(stream);
        case 0x09:
            return (signed char)fgetc(stream);
        case 0x0b:
            data[1] = fgetc(stream);
            data[0] = fgetc(stream);
            return *(short*)data;
        case 0x0c:
            for(int i=3;i>=0;i--)
                data[i] = fgetc(stream);
            return *(int*)data;
        case 0x0d:
            for(int i=3;i>=0;i--)
                data[i] = fgetc(stream);
            return *(float*)data;
        case 0x0e:
            for(int i=7;i>=0;i--)
                data[i] = fgetc(stream);
            return *(double*)data;
        }
        die("data read failed");
        return 1;
    }

Теперь мне говорят использовать -O2 и я добираюсь после предупреждения gcc: warning: dereferencing type-punned pointer will break strict-aliasing rules

Googleing я нашел два ортогональных ответа:

по сравнению с

В конце я не хочу игнорировать предупреждения.Что Вы порекомендуете?

[обновление] я заменил игрушечным примером с реальной функцией.

45
задан Community 23 May 2017 в 10:30
поделиться

3 ответа

Похоже, вы действительно хотите использовать fread:

int data;
fread(&data, sizeof(data), 1, stream);

Тем не менее, если вы действительно хотите чтобы пройти путь чтения символов, а затем переинтерпретировать их как int, безопасный способ сделать это в C (но не в C ++) - использовать объединение:

union
{
    char theChars[4];
    int theInt;
} myunion;

for(int i=0; i<4; i++)
    myunion.theChars[i] = fgetc(stream);
return myunion.theInt;

Я не уверен, почему длина данных в вашем исходном коде равна 3. Я предполагаю, что вам нужно 4 байта; по крайней мере, я не знаю ни одной системы, в которой int составляет 3 байта.

Обратите внимание, что и ваш код, и мой очень непереносимы.

Редактировать: Если вы хотите читать целые числа различной длины из файла в переносном режиме, попробуйте что-нибудь вроде этого:

unsigned result=0;
for(int i=0; i<4; i++)
    result = (result << 8) | fgetc(stream);

(Примечание: в реальной программе вы бы дополнительно захотели проверить возвращаемое значение fgetc () на соответствие EOF.)

Читает 4-байтовый беззнаковый файл из файла в формате с прямым порядком байтов, независимо от порядка байтов в системе. Он должен работать практически в любой системе, где длина без знака составляет не менее 4 байтов.

Если вы хотите быть нейтральным к порядку байтов, не используйте указатели или объединения; вместо этого используйте битовые сдвиги.

26
ответ дан 26 November 2019 в 21:18
поделиться

Обычно вы можете прочитать сообщение gcc как парень, которого вы ищете, не говорите, что я вас не предупреждал .

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

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

2
ответ дан 26 November 2019 в 21:18
поделиться

Очевидно, стандарт позволяет sizeof (char *) отличаться от sizeof (int *), поэтому gcc жалуется, когда вы пытаетесь выполнить прямое приведение. void * немного особенный в том, что все может быть преобразовано туда и обратно в void * и обратно. На практике я не знаю многих архитектур / компиляторов, в которых указатель не всегда одинаков для всех типов, но gcc имеет право выдавать предупреждение, даже если оно раздражает.

Я думаю, что безопасным способом будет

int i, *p = &i;
char *q = (char*)&p[0];

или

char *q = (char*)(void*)p;

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

char *q = reinterpret_cast<char*>(p);
-4
ответ дан 26 November 2019 в 21:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: