2011-06-26 12 views
7

Załóżmy Mam plik źródłowy próbkę, test.c, której mam kompilacji tak:C przezwyciężaniu ograniczeń aliasing (?) Związki

$ gcc -03 -Wall

test. c wyglądać tak ..

/// 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 

niech też powiedzieć, że akceptują ograniczenia, że ​​jeśli przejdzie w dwóch nakładających się wskaźniki, masz niezdefiniowanej re Sults.

Próbowałem coś takiego (wyobraź sobie te makra są prawidłowo sformatowany z backslash-uciekł nowej linii na końcu każdej linii)

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

ale kiedy dereference A w makro (A [ 0] < b [0]) Otrzymuję "dereferencje łamie zasady ścisłego aliasingu" z gcc

Myślałem, że powinniście używać związków, aby właściwie odnosić się do jednego miejsca w pamięci na dwa różne sposoby, więc następnie próbowałem coś takiego jak

#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 
}) 

Tyle że mam te same błędy z kompilatora o zasadach ścisłego-aliasingu.

A więc: czy jest jakiś sposób na zrobienie tego bez łamania ścisłego aliasingu, bez tak naprawdę KOPIOWANIE pamięci?

(may_alias Ilość robi, po prostu pozwala na ominięcie zasad ścisłego-aliasing)

EDIT: Redakcyjne memcmp to zrobić. Złapałem zasady dotyczące aliasingu i nie pomyślałem o tym.

+1

Aby używać związków w sposób zgodny ze standardami, możesz czytać tylko od członka, do którego ostatnio pisałeś. –

+8

@Kerrek: not true - C99 umożliwia karanie za pomocą związków, przypis przypisany temu wyraźnie został dodany za pomocą TC3; jednak kod Todda wciąż jest niepoprawny ... – Christoph

+0

@Christoph: C1x jest jeszcze lepszy, poprawiono dodatek J (UB), aby umożliwić odczytanie bajtów odpowiadających ostatniemu zapisanemu elementowi (co oczywiście było celem w C99, ale najwyraźniej załącznik J został przeoczony wtedy). – ninjalj

Odpowiedz

5

Kompilator jest poprawny, ponieważ reguły aliasingu są określane przez tzw. "Efektywny typ" obiektu (tj. Lokalizację pamięci), do której uzyskujesz dostęp, bez względu na magię wskaźnika. W tym przypadku wykreślanie wskaźników za pomocą zrostu nie różni się od rzutowania jawnego - użycie rzutowania jest w rzeczywistości lepsze, ponieważ norma nie gwarantuje, że dowolne typy wskaźników mają zgodne reprezentacje, tj. Niepotrzebnie zależy od zdefiniowanej implementacji zachowanie.

Jeśli chcesz zachować zgodność ze standardem, musisz skopiować dane do nowych zmiennych lub użyć zjednoczenia podczas deklaracji oryginalnych zmiennych.

Jeśli twoje 128-bitowe liczby całkowite są dużymi endianami lub little-endianami (tzn. Nie są mieszanymi endianami), możesz również użyć wartości memcmp() (bezpośrednio lub po zanegowaniu wartości zwracanej) lub porównać bajtowo : dostęp przez wskaźniki typu postaci jest wyjątkiem od reguły aliasingu.

+1

Powinienem był pomyśleć o memcmp. To w zasadzie dokładnie to, co chcę robić. –