Conocimientos previos requeridos: registro de activación, alcance estático y alcance dinámico.
A continuación, no se muestra el código C estándar. (caso 1)
foo (double a, double b){ double square (double z) { return z * z; } return square (a) + square (b); } |
Sin embargo, GNU C admite esta sintaxis de ‘función anidada’ por extensión. También se le permite tomar la dirección de la función anidada y pasarla a otras funciones como esta: (case2)
hack (int *array, int size){ void store (int index, int value){ array[index] = value; } intermediate (store, size); } |
Observe que la función anidada ‘store’ usa ‘array’ de la función ‘hack’.
Ahora, veamos la implementación de la función anidada en el compilador ac / c ++ (lo llamaré compilador de ahora en adelante, pero tenga en cuenta que me refiero específicamente al compilador c / c ++).
El enlace estático en el registro de activación (AR) se utiliza para acceder a datos de otros AR que tienen relación léxica con él.
El compilador AC / C ++ no necesita un enlace estático en AR para implementar el alcance léxico, ya que todas las funciones están en el alcance global, una función solo tiene acceso al montón (asignado usando malloc), área estática, su propio AR, pero no AR de cualquier otro función.
Por lo tanto, si implementamos una función anidada mediante un enlace estático, definitivamente no es rentable porque paga el precio (un campo por cada AR) cada vez, pero el servicio rara vez se usa (la mayoría de los AR no son para funciones anidadas).
Sin embargo, si pudiéramos usar un enlace estático para implementar la función anidada solo cuando sea necesario, es una situación en la que todos ganan. El método es así:
0. Suponga que se utiliza un registro SL para almacenar un enlace estático temporalmente.
1. En llamador, asigne una pequeña memoria en la pila. Ponga en él código de ensamblaje equivalente al siguiente:
004 move caller FP to SL 008 jmp to nested function
2. En callee, cargue SL en el enlace estático y utilícelo para acceder a los datos de la persona que llama.
3. Ahora la dirección 0004 se convierte en la nueva dirección de la función anidada.
El código del paso 2 se llama trampolín. La dirección de la función original junto con el llamador FP se denomina cierre en este caso, lo que le brinda una función para ejecutar y un entorno de datos adicional para acceder. Para obtener más información, puede consultar estas referencias.
Oh, por cierto, el siguiente código es legal en C ++
void foo() { void bar(int); bar(1); } |
Referencias:
1. Funciones anidadas
2. Simular función anidada en C ++
3. Cómo implementar la extensión anidada por trampolín
4. ¿Qué es el cierre?
5. Alcance estático frente a alcance dinámico
6. Alcance de implementación