C преодоление ограничений псевдонимов (союзы?) - PullRequest
7 голосов
/ 27 июня 2011

Предположим, у меня есть пример исходного файла 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 Ответ

5 голосов
/ 27 июня 2011

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

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

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

...