C преодоление ограничений на сглаживание (союзы?)

Предположим, у меня есть исходный исходный файл test.c, который я компилирую так:

$gcc -03 -Wall

test.c выглядит примерно так.

/// CMP128(x, y)
//
// arguments
//  x - any pointer to an 128-bit int
//  y - any pointer to an 128-bit int
//
// returns -1, 0, or 1 if x is less than, equal to, or greater than y
//
#define CMP128(x, y) // magic goes here

// example usages

uint8_t  A[16];
uint16_t B[8];
uint32_t C[4];
uint64_t D[2];
struct in6_addr E;
uint8_t* F;

// use CMP128 on any combination of pointers to 128-bit ints, i.e.

CMP128(A, B);
CMP128(&C[0], &D[0]);
CMP128(&E, F);

// and so on

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

Я пробовал что-то вроде этого (представьте, что эти макросы должным образом отформатированы с использованием строк с обратной косой чертой в конце каждой строки)

#define CMP128(x, y) ({
  uint64_t* a = (void*)x;
    uint64_t* b = (void*)y;

  // compare a[0] with b[0], a[1] with b[1]
})

но когда я разыгрываю a в макросе (a [0] < b [0]), я получаю ошибки "разыменования разрывов строгого сглаживания" из gcc

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

#define CMP128(x, y) ({
    union {
        typeof(x) a;
        typeof(y) b;
        uint64_t* c;
    }   d = { .a = (x) }
        , e = { .b = (y) };

    // compare d.c[0] with e.c[0], etc
})

За исключением того, что я получаю те же самые ошибки от компилятора о правилах строгого сглаживания.

Итак: есть ли способ сделать это, не нарушая строгого сглаживания, а не КОПИРОВАТЬ память?

( may_alias не учитывает, он просто позволяет обойти правила строгого сглаживания)

EDIT: для этого используйте memcmp. Я попался на правила псевдонимов и не думал об этом.

Ответы

Ответ 1

Компилятор корректен, поскольку правила псевдонимов определяются так называемым "эффективный тип" объекта (т.е. место памяти), к которому вы обращаетесь, независимо от какой-либо магии указателя. В этом случае тип-punning указателей с объединением ничем не отличается от явного приведения - использование акта фактически предпочтительнее, поскольку стандарт не гарантирует, что суровые типы указателей имеют совместимые представления, т.е. Вы необоснованно зависят от определенных реализацией поведение.

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

Если ваши 128-битные целые числа являются либо большими или мало-конечными (т.е. не смешанными), вы также можете использовать memcmp() (либо напрямую, либо после отрицания возвращаемого значения), либо делать побайтное сравнение самостоятельно: доступ через указатели символьного типа является исключением из правила псевдонимов.