¿El preprocesador de C elimina las instancias de "&*"?
standards c-preprocessor (2)
El preprocesador no elide &*
. Sin embargo, la norma C 2011 dice en 6.5.3.2 3, sobre &
:
Si el operando [de & ] es el resultado de un operador unario * , ni ese operador ni el operador & se evalúan y el resultado es como si ambos se omitieran, excepto que las restricciones sobre los operadores aún se aplican y el resultado no es un lvalue
Como lo requieren las restricciones mencionadas anteriormente, el operando de * debe tener un tipo de puntero. Por lo tanto, &*3
no se cambia a 3
por esta cláusula; viola las restricciones porque 3
no tiene tipo puntero. Además, &*x
no se ha cambiado completamente a x
, ya que el resultado no es un lvalue. Por ejemplo, esto no está permitido:
int a, *x;
x = &a; // Normal, allowed.
&*x = &a; // Not allowed; `&*x` does not became exactly the same as `x`.
Estaba jugando con gcc
y probé el siguiente código:
int A = 42;
int *B = &A;
int *C = &*B;
Y C == &A
, como se esperaba. Pero cuando lo intento:
int *B = NULL;
int *C = &*B;
Resulta que C == NULL
, y no segfault. Entonces &*B
no es realmente la anulación de referencia B
antes de tomar su dirección.
Supongo que el preprocesador está eliminando las instancias de &*
y *&
antes de que lleguen al compilador, ya que se niegan entre sí, pero no puedo encontrar ninguna documentación para verificar si esto es C estándar o específico del compilador.
¿Se está eliminando el preprocesador &*
y *&
, y puedo esperar este comportamiento de cualquier compilador dado?
Esto no está siendo eliminado por el preprocesador, &*
simplemente termina siendo equivalente al puntero en sí, podemos ver esto al ir al borrador del estándar C99 6.5.3.2
Operadores de direccionamiento e indirecto, párrafo 4, que dice:
El operador unario * denota indirección. Si el operando apunta a una función, el resultado es un designador de función; si apunta a un objeto, el resultado es un lvalue que designa el objeto. Si el operando tiene el tipo "puntero a tipo", el resultado tiene el tipo "tipo". Si se ha asignado un valor no válido al puntero, el comportamiento del operador unario * no está definido. 87)
y la nota 87 dice:
Por lo tanto, & * E es equivalente a E (incluso si E es un puntero nulo), [...]
y el párrafo 3 dice ( énfasis mío ):
El operador unario y produce la dirección de su operando. Si el operando tiene el tipo '''' tipo '''', el resultado tiene el tipo '''' puntero al tipo ''''. Si el operando es el resultado de un operador unario *, ni ese operador ni el operador & se evalúan y el resultado es como si ambos se omitieran , excepto que las restricciones sobre los operadores todavía se aplican y el resultado no es un valor lime.
Actualizar
Probablemente vale la pena tener en cuenta que para gcc
y clang
puede ver los resultados del preprocesamiento utilizando el indicador -E
( véalo en vivo ) y en Visual Studio /EP ( véalo en vivo ).
Además, vale la pena señalar que, como dijo MSalters en su comentario, el solo hecho de tener los dos tokens &*
no es suficiente para comprender el contexto, como muestra su ejemplo:
int *p, *q ;
int foo = *p & *q ;
Por lo tanto, solo eliminar &*
no sería posible durante la etapa de preprocesamiento, ya que no tendría suficiente información disponible para determinar si &
era la dirección del operador o bitwise y operador.