2008-11-04 8 views
23

He estado tratando de entender las reglas de aliasing estrictas que se aplican al puntero de char.¿Cuándo es seguro * para el alias de puntero estricto?

Here Esto se afirma:

Siempre se presume que un char * puede hacer referencia a un alias de cualquier objeto.

Ok por lo que en el contexto del código de zócalo, que puedo hacer esto:

struct SocketMsg 
{ 
    int a; 
    int b; 
}; 

int main(int argc, char** argv) 
{ 
    // Some code... 
    SocketMsg msgToSend; 
    msgToSend.a = 0; 
    msgToSend.b = 1; 
    send(socket, (char*)(&msgToSend), sizeof(msgToSend); 
}; 

Pero luego está esta declaración

Lo contrario no es cierto. Lanzar un char * a un puntero de cualquier tipo que no sea char * y quitarle la referencia por lo general infringe la regla de aliasing estricta.

¿Quiere decir esto que cuando Recv una matriz de caracteres, no puedo reinterpretar fundido a una estructura cuando sé que la estructura del mensaje:

struct SocketMsgToRecv 
{ 
    int a; 
    int b; 
}; 

int main() 
{ 
    SocketMsgToRecv* pointerToMsg; 
    char msgBuff[100]; 
    ... 
    recv(socket, msgBuff, 100); 
    // Ommiting make sure we have a complete message from the stream 
    // but lets assume msgBuff[0] has a complete msg, and lets interpret the msg 

    // SAFE!?!?!? 
    pointerToMsg = &msgBuff[0]; 

    printf("Got Msg: a: %i, b: %i", pointerToMsg->a, pointerToMsg->b); 
} 

Será este segundo ejemplo no funcionar debido a que el El tipo base es una matriz char y la estoy lanzando a una estructura. ¿Cómo manejas esta situación en un mundo estrictamente alias?

+0

¿No es necesario que la segunda parte del código se llame explícitamente? ¿Ha habilitado todas las advertencias? –

Respuesta

6

Correcto, el segundo ejemplo infringe las reglas de aliasing estrictas, por lo que si compila con el indicador -fstrict-aliasing, existe la posibilidad de que pueda obtener un código de objeto incorrecto. La solución totalmente correcta sería utilizar una unión aquí:

union 
{ 
    SocketMsgToRecv msg; 
    char msgBuff[100]; 
}; 

recv(socket, msgBuff, 100); 

printf("Got Msg: a: %i, b: %i", msg.a, msg.b); 
+1

¿Esto está en conformidad con el compilador estándar o simplemente que le permite salirse con la suya escribiendo a un miembro y leyendo de otro? –

+9

La unión es completamente innecesaria. Simplemente pase un puntero a la estructura (emitida a 'char *') a 'recv'. –

+0

Tenga en cuenta que '-fstrict-aliasing' está activado por defecto en' -O2' y más alto en gcc –

7

Re @ Adam Rosenfield: La unión será lograr la alineación, siempre que el proveedor de la char * comenzó a hacer algo similar.

Puede ser útil pararse atrás y descubrir de qué se trata.

La base para la regla de aliasing es el hecho de que los compiladores pueden colocar valores de diferentes tipos simples en diferentes límites de memoria para mejorar el acceso y que el hardware en algunos casos puede requerir dicha alineación para poder usar el puntero. Esto también puede aparecer en estructuras donde hay una variedad de elementos de diferentes tamaños. La estructura puede comenzar en un buen límite. Además, el compilador aún puede introducir mordiscos flojos en el interior de la estructura para lograr una alineación adecuada de los elementos estructurales que lo requieren.

Teniendo en cuenta que los compiladores a menudo tienen opciones para controlar cómo se maneja todo esto, o no, puede ver que hay muchas formas en que pueden ocurrir sorpresas. Esto es especialmente importante para tener en cuenta al pasar punteros a estructuras (emitidas como char * o no) en bibliotecas compiladas para esperar diferentes convenciones de alineación.

¿Qué pasa con char *?

La presunción sobre char * es esa sizeof (char) == 1 (relativa a los tamaños de todos los otros datos considerables) y que los caracteres * no tienen ningún requisito de alineación. Por lo tanto, un char * genuino siempre se puede pasar con seguridad y usar con éxito sin preocuparse por la alineación, y eso vale para cualquier elemento de una matriz char [], ejecutando ++ y - en los punteros, y así sucesivamente. (Curiosamente, el vacío * no es lo mismo.)

Ahora debería poder ver cómo si transfiere algún tipo de datos de estructura a una matriz char [] que no estaba alineada correctamente, intentando volver a un puntero que requiere alineación (es) puede ser un serio problema.

Si realiza una unión de una matriz char [] y una estructura, la alineación más exigente (es decir, la de la estructura) será respetada por el compilador. Esto funcionará si el proveedor y el consumidor están utilizando efectivamente uniones que coincidan para que la conversión de struct * a char * y viceversa funcione bien.

En ese caso, espero que la información se haya creado en una unión similar antes de que el puntero se haya convertido en char * o se haya transferido de otra forma como una matriz de tamaño de bytes (char). También es importante asegurarse de que las opciones del compilador sean compatibles entre las bibliotecas en las que se basa y su propio código.

+0

son los 3 de 'char',' signed char', 'unsigned char' OK para aliasing? y con cualquier combinación de calificación CV también? –

+2

Las reglas de aliasing no tienen nada que ver con la alineación. Según el razonamiento C89, dadas declaraciones globales como 'int i; float * fp; ', el propósito es permitir que los compiladores mantengan' i' en un registro a través de los accesos a '* fp'. La idea era que un compilador no debería tener que suponer de manera pesimista que una escritura en '* fp' podría alterar' i' * cuando no tenía ninguna razón para esperar * que '* fp' apuntara a algo que no era' flotar' *. No creo que la regla haya tenido la intención de permitir que los compiladores ignoren los casos en los que el aliasing es obvio (tomar la dirección de un objeto debería darle una buena pista al compilador ... – supercat

+0

... que el objeto en cuestión está a punto de ser accedido a través de un puntero, y el lanzamiento de un 'int *' a un 'float *' debería darle al compilador una fuerte pista de que es probable que un 'int' se modifique escribiendo en un' float * ', pero gcc ya no siente ninguna obligación de observe tales cosas. – supercat

Cuestiones relacionadas