2011-06-26 24 views
7

Supongamos que tengo un archivo de origen, test.c, que soy compilar este modo:C superar las restricciones de aliasing (? Sindicatos)

$ gcc -Wall -03

prueba. c ve algo como esto ..

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

Digamos también acepto la restricción de que si se pasa en dos punteros que se solapan, se obtiene re indefinido sults.

He intentado algo como esto (imaginar estas macros tienen el formato correcto con saltos de línea escapados al final de cada línea)

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

pero cuando eliminar la referencia a en la macro (un [ 0] < b [0]) me sale "rompe eliminación de referencias a normas estrictas-aliasing" errores de gcc

había pensado que se suponía que utilizar uniones haga referencia correctamente a un solo lugar en la memoria de dos maneras diferentes, por lo A continuación probé algo como

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

Excepto que tengo exactamente los mismos errores del compilador acerca de las reglas estrictas-aliasing.

Entonces, ¿hay alguna forma de hacer esto sin romper el alias estricto, menos COPIA la memoria?

(may_alias recuento no funciona, sólo se le permite eludir las estrictas reglas-aliasing)

EDIT: memcmp uso para hacer esto. Me quedé atrapado en las reglas de aliasing y no pensé en eso.

+1

Para utilizar las uniones de forma estándar, solo puede leer el miembro al que escribió por última vez. –

+8

@Kerrek: no es cierto: C99 permite escribir mensajes a través de uniones, una nota al pie que menciona explícitamente que se agregó con TC3; sin embargo, el código de Todd sigue siendo incorrecto ... – Christoph

+0

@Christoph: C1x es aún mejor, el apéndice J (UB) es fijo, para permitir la lectura de los bytes correspondientes al último miembro escrito (que obviamente fue el propósito en C99, pero aparentemente el apéndice J fue pasado por alto). – ninjalj

Respuesta

5

El compilador es correcto ya que las reglas de aliasing están determinadas por el llamado 'tipo efectivo' del objeto (es decir, la ubicación de la memoria) a la que accede, independientemente de cualquier magia del puntero. En este caso, escribir punteo de punteros con una unión no es diferente de un reparto explícito; usar el reparto es preferible ya que el estándar no garantiza que los tipos de punteros arbitrarios tengan representaciones compatibles, es decir, depende innecesariamente de la definición de implementación. comportamiento.

Si desea cumplir con la norma, debe copiar los datos a nuevas variables o utilizar una unión durante la declaración de las variables originales.

Si sus enteros de 128 bits son big-endian o little-endian (es decir, no mixed-endian), también puede usar memcmp() (ya sea directamente o después de anular el valor de retorno) o hacer una comparación byte : el acceso a través de punteros de tipo de carácter es una excepción a la regla de aliasing.

+1

Debería haber pensado en memcmp. Eso es básicamente lo que quiero hacer. –

Cuestiones relacionadas