Significado de int (*) (int *) = 5 (o cualquier valor entero)

88

No puedo resolver esto:

int main() {
    int (*) (int *) = 5;
    return 0;
}

La asignación anterior se compila con g ++ c ++ 11. Sé que int (*) (int *)es un puntero a una función que acepta un (int *)argumento como y devuelve un int, pero no entiendo cómo podrías equipararlo a 5. Al principio pensé que era una función que constantemente devuelve 5 (de mi aprendizaje reciente en F #, probablemente, jaja), luego pensé, brevemente, que el puntero de función apunta a la ubicación de memoria 5, pero eso no funciona, claramente, y tampoco los valores hexadecimales.

Pensando que podría deberse a que la función devuelve un int, y que asignar un int está bien (de alguna manera), también probé esto:

int * (*) (int *) = my_ptr

donde my_ptres de tipo int *, el mismo tipo que este segundo puntero de función, como en el primer caso con el tipo int. Esto no se compila. Asignar 5, o cualquier valor int, en lugar de my_ptr, tampoco se compila para este puntero de función.

Entonces, ¿qué significa la asignación?

Actualización 1

Tenemos la confirmación de que es un error, como se muestra en la mejor respuesta. Sin embargo, todavía no se sabe qué sucede realmente con el valor que asigna al puntero de función, o qué sucede con la asignación. ¡Cualquier (buena) explicación al respecto sería muy apreciada! Consulte las ediciones a continuación para obtener más claridad sobre el problema.

Editar 1

Estoy usando gcc versión 4.8.2 (en Ubuntu 4.8.2)

Editar 2

En realidad, equipararlo con cualquier cosa funciona en mi compilador. Incluso equipararlo a una variable std :: string, o un nombre de función que devuelve un doble, funciona.

Editar 2.1

Curiosamente, convertirlo en un puntero de función a cualquier función que devuelva un tipo de datos que no sea un puntero, permitirá que se compile, como

std::string (*) () = 5.6;

Pero tan pronto como el puntero de la función es a una función que devuelve algún puntero, no se compila, como con

some_data_type ** (*) () = any_value;
Konrad Kapp
fuente
3
Hmm ... no se ve bien, y clang no lo acepta. Podría ser una extensión de gcc (o error).
Wintermute
4
g ++ compila, pero gcc no funciona:error: expected identifier or '(' before ')' token
tivn
3
@ 0x499602D Tenga en cuenta que el código no le da un nombre al puntero. Con int *x = 5tu nombre x. Con int * (*x) (int *) = 5él no se compilará. (aunque se compilará como código C).
nos
5
Caso de prueba reducido: int(*) = 5;yint(*);
Johannes Schaub - litb

Respuestas:

60

Es un error en g ++.

 int (*) (int *) 

es un nombre de tipo.

En C ++ no puede tener una declaración con un nombre de tipo sin un identificador.

Entonces esto se compila con g ++.

 int (*) (int *) = 5;

y esto también se compila:

 int (*) (int *);

pero ambas son declaraciones inválidas.

EDITAR :

TC menciona en los comentarios bugzilla bug 60680 con un caso de prueba similar pero aún no ha sido aprobado . El error se confirma en bugzilla.

EDIT2 :

Cuando las dos declaraciones anteriores están en el alcance del archivo, g ++ emite correctamente un diagnóstico (no emite el diagnóstico en el alcance del bloque).

EDIT3 :

Verifiqué y puedo reproducir el problema en la última versión de g ++ versión 4 (4.9.2), la última versión preliminar 5 (5.0.1 20150412) y la última versión experimental 6 (6.0.0 20150412).

ouah
fuente
5
MSVC rechazó el código publicado editado conerror C2059: syntax error : ')'
Weather Vane
Si es un nombre de tipo, ¿por qué no 'int (*) (int *) int_func;' ¿trabajo?
Konrad Kapp
1
Para el bugzilla de GCC, "NUEVO" es un error confirmado. (Los errores no confirmados son "NO CONFIRMADOS").
TC
4
@KonradKapp: funciona bien si dices int (*int_func)(int *); cuál declara un puntero de función llamado int_func.
Edward
3
@KonradKapp C ++ usa notación infija para colocar el identificador; la misma razón es int x[5];y noint[5] x;
MM
28

No es C ++ válido. Recuerde que debido a que su compilador particular se compila, no lo hace válido. Los compiladores, como todo software complejo, a veces tienen errores y este parece ser uno.

Por el contrario se clang++queja:

funnycast.cpp:3:11: error: expected expression
    int (*) (int *) = 5;
          ^
funnycast.cpp:3:18: error: expected '(' for function-style cast or type construction
    int (*) (int *) = 5;
             ~~~ ^
funnycast.cpp:3:19: error: expected expression
    int (*) (int *) = 5;
                  ^
3 errors generated.

Este es el comportamiento esperado porque la línea ofensiva no es C ++ válido. Pretende ser una asignación (debido a =) pero no contiene ningún identificador.

Eduardo
fuente
9

Como han señalado otras respuestas, es un error que

int (*) (int *) = 5;

compila. Una aproximación razonable de esta afirmación que se esperaría que tuviera un significado es:

int (*proc)(int*) = (int (*)(int*))(5);

Ahora proces un puntero a función que espera que la dirección 5sea ​​la dirección base de una función que toma una int*y devuelve unaint .

En algunos microcontroladores / microprocesadores 5podría haber una dirección de código válida, y podría ser posible ubicar dicha función allí.

En la mayoría de las computadoras de uso general, la primera página de la memoria (direcciones 0-1023para páginas 4K) no es válida (sin asignar) a propósito para capturar nullaccesos de puntero.

Por lo tanto, aunque el comportamiento depende de la plataforma, es razonable esperar que se produzca un error de página cuando *procse invoca (por ejemplo, (*proc)(&v)). Antes del momento en el que *procse invoca, no ocurre nada inusual.

A menos que esté escribiendo un enlazador dinámico, es casi seguro que no debería estar calculando direcciones numéricamente y asignándolas a variables de puntero a función.

Atsby
fuente
2
/usr/lib/gcc/x86_64-pc-cygwin/4.9.2/cc1plus.exe -da so.cpp

Esta línea de comando genera muchos archivos intermedios. El primero de ellos so.cpp.170r.expand, dice:

...
int main() ()
{
  int D.2229;
  int _1;

;;   basic block 2, loop depth 0
;;    pred:       ENTRY
  _1 = 0;
;;    succ:       3

;;   basic block 3, loop depth 0
;;    pred:       2
<L0>:
  return _1;
;;    succ:       EXIT

}
...

Esto todavía no responde exactamente a lo que sucede, pero debería ser un paso en la dirección correcta.

Roland Illig
fuente
Interesante. ¿Cuál es el propósito de estos archivos intermedios?
Konrad Kapp
@KonradKapp Producir código de máquina a partir de código humano es un proceso bastante complejo (especialmente si desea que su compilador optimice su salida). Dado que la compilación es tan compleja, no se hace en un solo paso, la mayoría de los compiladores tienen alguna forma de Representación Intermedia (IR).
11684
2
Otra razón para tener un IR es que si tiene un IR bien definido, puede separar el front-end y el back-end de su compilador. (Por ejemplo, el front-end compila C en su IR, el back-end compila el código de máquina IR en Intel. Ahora, si desea agregar soporte ARM, solo necesita un segundo back-end. Y si desea compilar Go, solo segundo front-end, y encima de que el compilador Ir apoya inmediatamente Intel y ARM ya que se puede volver a utilizar las dos back-ends.
11684
@ 11684 Vale, tiene sentido. Muy interesante. No pude determinar el idioma que Roland dio en esta respuesta, aunque ... se ve como una especie de mezclado montaje con C
Konrad Kapp
IR no necesita ser imprimible; No tengo idea de lo que usa gcc, esto puede ser simplemente una representación imprimible @KonradKapp
11684