c++ - que - punteros como parametros de funciones en c
Explicación de los punteros de función (4)
Tengo un problema con la comprensión de la sintaxis de C ++ combinada con punteros de función y declaraciones de función, es decir:
Generalmente cuando queremos declarar un tipo de función hacemos algo como:
typedef void(*functionPtr)(int);
y esto esta bien para mi A partir de ahora, functionPtr es un tipo, que representa un puntero a la función, que devuelve void y toma int por un valor como argumento.
Podemos usarlo de la siguiente manera:
typedef void(*functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr fun = function;
fun(5);
return 0;
}
Y conseguimos 5
impresos en una pantalla.
tenemos puntero para funcionar de manera fun
, asignamos un puntero existente a función - function
y ejecutamos esta función con un puntero. Guay.
Ahora, mientras leo en algunos libros, la función y el puntero a función se tratan de alguna manera de la misma manera, de hecho, después de la function()
de declaración de function()
cada vez que decimos función, nos referimos a la función real y al puntero para que funcionen del mismo tipo, así que a continuación se compila y Cada instrucción da el mismo resultado (5 impresos en una pantalla):
int main() {
functionPtr fun = function;
fun(5);
(*fun)(5);
(*function)(5);
function(5);
return 0;
}
Así que ahora, mientras puedo imaginar, que los punteros a las funciones y funciones son casi iguales, entonces de alguna manera está bien para mí.
Entonces pensé, si el puntero a la función y la función real es el mismo, entonces ¿por qué no puedo hacer lo siguiente?
typedef void(functionPtr)(int); //removed *
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr fun = function;
fun(5);
return 0;
}
Esto me da el siguiente error:
prog.cpp: 12: 14: advertencia: la declaración de ''void fun (int)'' tiene ''extern'' y se inicializa functionPtr fun = function;
por lo tanto, entendí que, por alguna razón, el compilador ahora entiende, que la diversión ya es una función existente . Entonces traté de seguir:
int main() {
functionPtr fun;
fun(5);
return 0;
}
Y tengo error de enlace. De alguna manera entiendo, que como el compilador ahora trata a la diversión como una función ya existente, debido al hecho de que la diversión no está definida en ninguna parte, obtendré un error de vinculación. Por eso cambié el nombre de la variable:
typedef void(functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr function;
function(5);
return 0;
}
Así que ahora funciona en la función de nombre global de las sombras principales, así que la function(5)
se usa desde la functionPtr function;
de declaración functionPtr function;
Funciona bien e imprime 5 en la pantalla.
Así que ahora estoy en shock. ¿Por qué pasó esto? Otra cosa engañosa es que cuando el puntero a la función se declara así:
typedef void(*functionPtr)(int);
Puedo crear una función de tipo functionPtr de la siguiente manera:
functionPtr function(int a){
std::cout << a << std::endl;
}
mientras que, al declarar algo como:
typedef void(functionPtr)(int);
hace esto:
functionPtr function(int a){
std::cout << a << std::endl;
}
siendo interpretado por un compilador como función de retorno de función. Si esto es así, ¿por qué la declaración anterior ( typedef void(functionPtr)(int);
) sabía que esta es una función que devuelve void y no la función que devuelve functionPtr?
¿Podría alguien, por favor, explicar lo que realmente está sucediendo debajo de mí?
Estoy usando el compilador g ++ C ++ con la opción C ++ 14 habilitada.
Bueno, es un poco confuso.
El tipo de función y el puntero a tipo de función son de hecho dos tipos diferentes (no más similares que int
y puntero a int
). Sin embargo, hay una regla, que un tipo de función decae al puntero al tipo de función en casi todos los contextos. En este caso, decadencia significa conversión (hay una diferencia entre la conversión de tipo y la decadencia, pero probablemente no esté interesado en ella en este momento).
Lo importante es que casi cada vez que usa un tipo de función, termina con un puntero al tipo de función. Sin embargo, tenga en cuenta que casi no siempre es así.
Y estás golpeando algunos casos cuando no lo hace.
typedef void(functionPtr)(int);
functionPtr fun = function;
Este código intenta copiar una función (¡no el puntero! ¡La función!) A otra. Pero, por supuesto, esto no es posible, no puede copiar funciones en C ++. El compilador no permite esto, y no puedo creer que lo hayas compilado (¿estás diciendo que tienes errores en el enlazador?)
Ahora, este código:
typedef void(functionPtr)(int);
functionPtr function;
function(5);
function
no hace sombra a nada. El compilador sabe que no es un puntero de función al que se puede llamar, y simplemente llama a su function
original.
El más interesante de sus ejemplos es este, reproducido aquí sin el uso de typedef:
void function(int a) { // declaration and definition of ''function''
std::cout << a << std::endl;
}
int main() {
void function(int); // declaration of ''function''
function(5);
}
En la mayoría de los contextos en C ++, la nueva declaración de la function
en el ámbito local sería una sombra de la ::function
global. Por lo tanto, esperar un error de vinculador tiene sentido: la función main()::function
no tiene definición, ¿no?
Excepto las funciones son especiales en este sentido. De una nota en [basic.scope.pdel]:
Las declaraciones de funciones en el ámbito del bloque y las declaraciones de variables con el especificador externo en el ámbito del bloque se refieren a las declaraciones que son miembros de un espacio de nombres adjunto , pero no introducen nuevos nombres en ese ámbito.
Entonces ese ejemplo de código es exactamente equivalente a:
void function(int a) { /* ... */ }
void function(int ); // just redeclaring it again, which is ok
int main() {
function(5);
}
También puede verificar esto colocando la function
global en algún espacio de nombres, N
En este punto, la declaración de alcance local agregaría un nombre a ::
, pero no tendría una definición; por lo tanto, obtienes un error de vinculador.
La otra cosa interesante que mencionaste es la noción de conversión de función a puntero, [conv.func]:
Un lvalue del tipo de función T se puede convertir en un prvalue de tipo "puntero a T". El resultado es un puntero a la función.
Cuando tienes una expresión de llamada de función, si lo que estás llamando es un tipo de función, primero se convierte en puntero a función. Es por eso que estos son equivalentes:
fun(5); // OK, call function pointed to by ''fun''
(*fun)(5); // OK, first convert *fun back to ''fun''
function(5); // OK, first convert to pointer to ''function''
(*function)(5); // OK, unary* makes function get converted to a pointer
// which then gets dereferenced back to function-type
// which then gets converted back to a pointer
Veamos tus ejemplos uno por uno, y lo que realmente significan.
typedef void(functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr fun = function;
fun(5);
return 0;
}
Aquí está creando typedef functionPtr
para funciones que toman e int
, y no devuelven valores. functionPtr
no es realmente un typedef para un puntero de función, sino de una función real.
Entonces estás intentando declarar una nueva función fun
y asignársela. Lamentablemente, no puede asignar funciones, por lo que esto no funciona.
int main() {
functionPtr fun;
fun(5);
return 0;
}
Una vez más, está declarando una función fun
con la firma que especificó. Pero no lo define, por lo que justamente falla en la fase de enlace.
typedef void(functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr function;
function(5);
return 0;
}
¿Qué pasa aquí? Usted define typedef
, y en el main escribe la functionPtr function;
. Básicamente, esto es simplemente un prototipo para la función que ya ha escrito, function
. Reafirma que esta función existe, pero por lo demás no hace nada. De hecho, puedes escribir:
typedef void(functionPtr)(int);
void function(int a){
std::cout << a << std::endl;
}
int main() {
functionPtr function;
functionPtr function;
functionPtr function;
void function(int);
function(5);
return 0;
}
Cuantas veces quieras, no cambiará nada. La function(5)
que llama después es siempre la misma cosa.
Una cosa que puede hacer en C ++ es declarar el prototipo de una función utilizando dicho typedef, pero no puede definirlo de esa manera.
typedef void(*functionPtr)(int);
functionPtr function(int a){
std::cout << a << std::endl;
}
Aquí está definiendo una función que devuelve un puntero a una función, pero luego no la devuelve. El compilador, dependiendo de su configuración, puede o no puede quejarse. Pero nuevamente la function
está completamente separada de functionPtr
. Usted ha escrito esencialmente
void (*)(int) function(int a) {
...
}
El último ejemplo, el que tiene una función que devuelve una función, simplemente no está permitido, ya que no tendría sentido.
typedef void functionPtr (int);
void function (int a){
std::cout << a << std::endl;
}
int main() {
functionPtr *func;
func = function;
func(5);
return 0;
}
Puedes usarlo de esta manera, lo he probado. Realmente hay un montón de trampas sobre este tema.