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

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

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

#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]

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

#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 не считается, он просто позволяет обойти правила строгого алиасинга)

РЕДАКТИРОВАТЬ: используйте memcmp, чтобы сделать это. Я увлекся правилами псевдонимов и не думал об этом.

1 ответ

Решение

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

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

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

Другие вопросы по тегам