Está en la página 1de 13

Programación

I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

Tema 4 Punteros y datos estructurados


Conferencia 04 Punteros.
Sumario:

1. Concepto de puntero. 
2. Operadores de punteros. 
3. Paso por valor y paso por referencia. 
4. Inicialización de punteros. 
5. Aritmética de punteros. 
6. Tipo void y punteros. 
Objetivos:

1. Saber definir y utilizar punteros.


2. Dominar la utilización de punteros para pasar argumentos a las funciones en llamada por referencia.
Bibliografía:
- Deitel. H. M. Como programar en C/C++. Capítulo 7 pág. 260- 315

Estudio Independiente

- Ejercicios de Autoevaluación pp. 299-301


- Ejercicios 7.7-7.11, 7.21-7.23 pp. 302

En la conferencia anterior comenzamos a estudiar la programación de funciones en C. Este tema lo


completaremos en la conferencia de hoy pues hasta el momento no hemos estudiado cómo una función de C
puede modificar el valor de una variable en nuestro programa, como lo hace, por ejemplo, la función scanf:

int N=4;

scanf(“%d”, &N); /*Cambia el valor 4 por el que tecleemos para N*/

printf(“El valor de N ahora es %d\n”,N);

Observe que en la función scanf no pasamos como parámetro la variable N como en la función printf sino que
pasamos &N. La expresión &N, como veremos a continuación es un puntero a la variable N.

Desarrollo

En este tema se comenzará a estudiar una de las características más potentes del lenguaje de
programación C: el uso de los punteros.
A través del uso de punteros en C es posible simular el llamado de funciones por referencia, es decir,
de modo que las funciones puedan modificar el contenido de variables que están definidas fuera de la
función.

Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu



Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

El concepto de puntero

Un puntero contiene una dirección de memoria. Cuando una variable contiene la dirección de otra variable se dice
que la primera variable apunta a la segunda, de ahí que se le llame también apuntadores a los punteros.

Un puntero es una variable cuyo contenido es la dirección de una localización de la memoria


de la computadora, es decir, la dirección de otra variable.

Las variables apuntadores contienen direcciones de memoria como sus valores. Por lo general, una variable
contiene directamente un valor específico. Sin embargo, un apuntador contiene la dirección de memoria de
una variable que, a su vez, contiene un valor específico. En este sentido, el nombre de una variable hace
referencia directa a un valor, y un apuntador hace referencia indirecta a un valor. Al proceso de hacer
referencia a un valor a través de un apuntador se le conoce comúnmente como indirección.


Declaración e inicialización de variable puntero.
Una variable puntero normalmente debe apuntar a otra variable de un tipo de dato determinado.
A continuación se declara una variable puntero a otra variable de tipo entero (int):
int *Ptr;
En este caso se está declarando una variable llamada Ptr que puede contener la dirección en la memoria de
cualquier variable del tipo entero.
Para declarar una variable puntero se indica el tipo de dato al que apuntará, y a continuación se emplea el
símbolo * seguido del nombre que se le dará a la variable puntero.
Los apuntadores deben ser inicializados cuando son declarados o en un enunciado de asignación antes de
ser utilizados.
Un apuntador puede ser inicializado con 0 (NULL) o con una dirección. Cuando un puntero contiene 0 (NULL)
se toma como convenio que no está apuntando a alguna variable. 0 es el único valor entero que puede ser
asignado a una variable de tipo puntero.

La forma general para declarar una variable puntero es: tipo *nombre; donde tipo es cualquier tipo válido de
C (también llamado tipo base) y nombre es el nombre de la variable puntero. Nota: A cada variable que se declara
como apuntador se le debe anteponer un asterisco (*).
Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu

Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

Una variable puntero normalmente debe apuntar a otra variable de un tipo de dato determinado.
Aunque no es un requerimiento, incluir las letras Ptr en los nombres de las variables apuntadores deja claro que
estas variables son apuntadores, y que deben tratarse de manera acorde. Los apuntadores deben ser inicializados
cuando son declarados o en un enunciado de asignación antes de ser utilizados.
Operadores de punteros

Existen dos operadores especiales de punteros: & y *. Estos dos operadores son monarios.

OPERADOR DIRECCION & OPERADOR INDIRECCION *

El operador & devuelve la dirección de El operador * es el complemento de &. y


memoria de su operando. devuelve el valor de la variable localizada
en la dirección que contiene el puntero.

El operador & aplicado a una variable devuelve la dirección de la variable a la que se aplique. Por ejemplo si se
ejecutan las siguientes declaraciones:

int y = 5;

int *yPtr = &y;

Se obtendrá una variable entera y cuyo valor inicial es 5, así como una variable yPtr cuyo valor inicial es la
dirección de la variable y. Esta última relación se suele indicar esquemáticamente como aparece en la figura
a continuación:

El operador de dirección también se puede aplicar a las variables puntero, por ejemplo, si además se ejecuta la
instrucción:

int **yPtrPtr = &yPtr;

Entonces se obtendrá, además, una variable yPtrPtr puntero a puntero a entero, la cual contiene la dirección de
la variable yPtr, esquemáticamente:

El operador de indirección *

El símbolo * cuando se aplica a una variable puntero se le llama operador de indirección y devuelve el contenido
de la variable apuntada por el puntero.
Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu

Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

Continuando con el ejemplo anterior las siguientes instrucciones printf imprimirían el valor 5:
printf ( “%d\n”, *yPtr );
printf ( “%d\n”, **yPtrPtr );

Una expresión como **yPtrPtr se evalúa de la forma siguiente *(*yPtrPtr) y por ello también daría el contenido
de la variable y.
Por otra parte, cuando usamos el operador de indirección aplicado a una variable a la izquierda de una expresión
de asignación, es posible cambiar el contenido de dicha variable, por ejemplo
*yPtr = 6;
printf ( “%d\n”, *yPtr );
printf ( “%d\n”, **yPtrPtr );
Ahora ambas instrucciones printf mostrarán en la pantalla el valor 6.
La posibilidad de cambiar el contenido de una variable a través de un puntero es lo que permitirá definir
funciones que sean capaces de modificar variables que están declaradas fuera de ellas, es decir, simular
parámetros por referencia en C.
Ejemplos:
Realizar un programa que permita modificar el valor de una variable utilizando un puntero. El programa deberá
imprimir en pantalla las direcciones de memoria de la variable y del puntero.

#include <stdio.h> Salida:

void main (void) x = 10

{ &x = 8FC4:0FFE

int x = 10; px = 8FC4:0FFE

int *px; &px = 8FC4:0FFA

px = &x; *px = 5  x = 5

printf (" x = %d\n", x); px  x

printf (" &x = %p\n", &x);

printf (" px = %p\n", px); Dirección valor

printf (" &px = %p\n", &px); 8FC4:0FFE 10

*px = 5; 8FC4:0FFA 8FC4:0FFE

printf (" x = %d", x);


Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu



Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

Observaciones:

En el ejemplo anterior se observa que hay tres valores asociados a los punteros:

‐ Dirección en la que se encuentra.

‐ Dirección a la que apunta.

‐ Valor contenido en la dirección apuntada.

Inicialización de punteros

Un puntero puede inicializarse con la dirección de memoria de otra variable:

float A=2.1, *ptrA = &A;

También un puntero se puede inicializar para que no apunte a ninguna dirección inicial, esto se hace utilizando la
constante simbólica NULL, que está definida como 0. En la práctica a la dirección de memoria 0 no es posible acceder
pues está reservada para uso exclusivo del BIOS/SO, por lo que este valor se usa para indicar que el puntero no
apunta a ninguna variable en particular.

char* C1 = NULL;

Paso por valor y paso por referencia

En C, todos los llamados de función son llamados por valor. Es decir, en C las funciones solamente pueden tener
parámetros por valor. Esto significa que al llamarse la función se maneja el parámetro como una variable local en la
cual se copia el valor que hemos pasado como argumento, de ahí que no sea posible modificar el contenido de una
variable declarada fura de la función sustituyéndola como argumento en un parámetro por valor.

Para que una función de C pueda modificar una variable que ha sido declarada fuera de ella, debemos pasar como
parámetro la dirección de ésta, es decir, el llamado por referencia se simula usando parámetros puntero por valor,
como en la función scanf. Resumiendo:

Existen dos formas de pasar argumentos a una función:

 Por valor (conocidas hasta el momento)

 Por referencia (utilizando apuntadores)

o En el caso de que necesitemos devolver más de un valor debemos utilizar este método.

Esta sería la primera aplicación de la utilización de punteros en que se pueden utilizar los apuntadores y el operador
de indirección para simular llamadas por referencia.

 Cuando usted necesite modificar los argumentos de una función, debe pasar la dirección de las
variables y no su valor.
 Esto se lleva a cabo utilizando el operador &
 Luego utilizando el operador indirección (*) se puede modificar el valor de esa posición de memoria.

Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu



Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

Ejemplo:

Defina una función en C qué al recibir el radio y altura de un cilindro permita devolver el área y volumen del mismo.
Escriba un programa principal que permita obtener el radio y la altura del cilindro de teclado e imprima en pantalla,
usando la función definida, el volumen y área de la figura.

void Area_Volumen(float r, float h, float* #include<stdio.h>


A, float* V)
void Area_Volumen(float, float, float*, float*);
{
void main() {
const float pi=3.14;
float area, volumen, radio, altura;
*A = pi*r*r+2*pi*r*h;
printf(“introduzca radio y altura:”);
*V= pi*r*r*h;
scanf(“%f %f”,&radio, &altura);
}
Area_Volumen(radio, altura, &area, &volumen);

printf(“el area es: %f”,&area);

printf(“el volume es: %f”, &volumen);

Pon tus conocimientos a prueba

¿Sería posible una versión del programa anterior usando solamente dos variables en el programa principal?

Ejemplo

Suponga que se quiere realizar una función que permite intercambiar los contenidos de dos variables flotantes
cualesquiera. A esta función se le denominará Swap (que se traduce al español como intercambio).

Es decir, partiendo inicialmente de las declaraciones

float x = 2.5 , y = 6.3 ;

Si se le aplica la función Swap a estas dos variables el resultado que obtendríamos sería el mostrado en la
figura siguiente:

Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu



Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

El algoritmo en seudocódigo para la función Swap es realmente sencillo, solo debemos tener en cuenta que
debemos usar una variable flotante extra para guardar temporalmente el valor de una de las variables, o sea:

- Guardar el valor de x en una variable temp, o sea, con temp = x ;


- Asignar el valor de y a la variable x, o sea, con x = y;
- Asignar el valor de temp a la variable y , o sea, con y = temp;

¿Sería correcto programar la función Swap de la forma siguiente?:

void Swap ( float x , float y )

{
float temp = x;
x = y;
y = temp;
}
La función anterior es correcta sintácticamente pero no lo es semánticamente ya que los parámetros x e y serían
parámetros por valor, por lo que los argumentos que se sustituyan por estos parámetros al llamar a la función
se copiarían en las variables x e y, las cuales son variables con alcance de función y que, por tanto, no tienen
nada que ver con las variables x e y que están declaradas fuera de la función. El resultado es que la función
anterior, al llamarla, no intercambia el contenido de las variables que se sustituyan como argumentos, solamente
intercambiaría el contenido de dos copias de esas variables, las cuales se destruyen una vez que termine el
llamado a la función.

La forma correcta de programar Swap es usando parámetros de tipo puntero a float en lugar de parámetros
de tipo float, o sea:

void Swap ( float *ptrx, float *ptry )


{
float temp = *ptrx;
*ptrx = *ptry;
*ptry = temp;
}
Al llamar o invocar a esta función para cambiar los contenidos de las variables x e y se emplea la siguiente
instrucción

Swap (&x, & y );

Observe un esquema paso a paso de lo que ocurre al hacer el llamado anterior a la función:

- Cuando comienza a ejecutarse la función se crean las variables locales ptrx y ptry cuyos valores iniciales
son la dirección de la variable x y de la variable y respectivamente, o sea:

- Al ejecutarse la instrucción
float temp = *ptrx;
Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu

Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

Se crea la variable local temp cuyo valor inicial es el contenido de la variable apuntada por ptrx, es decir, el
contenido de x, que es 2.5.

- Al ejecutarse la instrucción
*ptrx = *ptry;

Se asigna a la variable apuntada por ptrx el contenido de la variable apuntada por ptry, es decir, es como si se
asignara a x el valor de la variable y.

- Finalmente al ejecutar la instrucción


*ptry = temp;

Se asigna a la variable apuntada por ptry el valor que está guardado en temp

Al retornar la función se destruyen las variables locales de la función y como consecuencia de la ejecución de
esta función quedan intercambiados los contenidos de las variables x e y, es decir:

Aritmética de punteros

Sean P una variable de tipo puntero a cualquier tipo T. Sobre P solo se pueden aplicar los operadores aritméticos de
suma y resta. La cantidad adicionada o restada al puntero es siempre multiplicada por el tamaño de la variable a la
que apunta P. Esto es:

T* P; //P tiene dirección BASE


Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu

Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

P++; // ahora P tiene dirección BASE+sizeof(T)


P+=5 // ahora P tiene dirección BASE+5*sizeof(T)
P‐= 2 // ahora P tiene dirección BASE‐ 2*sizeof(T)
Observaciones:

Este comportamiento es muy utilizado en el trabajo con arreglos que estudiaremos en la clase próxima.

La palabra reservada typedef.


Introduciremos aquí esta palabra reservada del lenguaje C, la cual sirve para la creación de nuevos nombres
de tipos de datos.
Por ejemplo, mediante las siguientes declaraciones:
typedef int Entero;
typedef int *EnteroPtr;
creamos el tipo Entero ( un int ) y el tipo EnteroPtr (un puntero a int), por lo que tendremos que las
declaraciones de variables:
Entero i;
EnteroPtr iptr;
serían equivalentes a :
int i;
int *iptr;

Tipo void y punteros

Hasta ahora se ha visto que el tipo void tiene dos usos:

1.‐ Para indicar que una función no devuelve nada.

2.‐ Para indicar que una función no acepta ningún argumento.

Aplicados a punteros tiene otro uso: los punteros a void son punteros genéricos que pueden apuntar a cualquier
objeto o tipo de dato. Pero los punteros a void no pueden ser referenciados (utilizando *) sin utilizar moldes, puesto
que el compilador no puede determinar el tamaño del objeto al que apunta el puntero. Vea el siguiente segmento de
código:

int x; float f;

void *p = &x; /* p apunta a x *

*(int *) p = 2;

p = &f; /* p apunta a f */

*(float *) p = 1.1;

Conclusiones:

Se vio el concepto de puntero, que es una variable cuyo valor es una dirección de memoria donde se encuentra otra
variable, cómo declararlos, inicializarlos y la importancia. Se vieron varias aplicaciones de los punteros: paso por
paso por referencia y aritmética de punteros.
Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu

Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

Estudio Individual:

1. Estudio de la recursividad. 
2. Variables estáticas. Uso. 

Estudio de la recursividad.

Hasta ahora los programas que hemos estudiado llaman a otras funciones de forma "disciplinada" y jerárquica. Pero:

¿Es posible que una función se llame a sí misma?

El lenguaje C proporciona mecanismos para este tipo de llamadas.

Una función recursiva es aquella que se llama a sí misma, ya sea directa o indirectamente a través de otra función:

Ej:

f1 ‐ f2‐ f1

f1‐ f1‐ f1‐ ...... ‐ f1

El tema de recursividad es un tema complejo analizado con profundidad en cursos de ciencia de la computación. LT
Se trata en Capítulos del 5‐12

Conceptualmente la recursividad:


Es descomponer una tarea en tareas más simples del mismo tipo que la original.

La función recursiva solo resuelve el caso más simple devolviendo un resultado. A este caso más simple se le
llama CASO O CASOS BASE.
 Si la función es llamada para resolver el caso BASE devuelve un resultado, de lo contrario, la función divide
el problema en dos partes, una que ya sabe cómo resolver, y otra que no sabe cómo ejecutar.
 De esta manera el problema inicial complejo se resuelve:
1‐. Llamando a la función se divide el problema en un problema con solución conocida y una algo más simple
que el primero del mismo tipo .
2‐. La función llama a una copia de sí misma para que resuelva ahora el problema ligeramente más sencillo.
3‐. Se repite el paso 1 hasta que el problema desconocido se convierta de tanto dividirlo en más simples
cada vez en un problema con solución conocida caso base .
4‐. Cuando ya no es necesario seguir llamando a la función recursivamente, esta retorna el resultado de
resolver el problema a la copia que la llamó, y esta a su vez a la otra, y así hasta que se llega a la inicial, la cual,
ahora con la solución del problema, regresa el valor final.
Este análisis debe de realizarse antes de crear cualquier algoritmo. Sin tener el caso base, ni la descomposición del
problema original en problemas del mismo tipo, pero más simples NO se puede programar recursivamente.

Veremos Ejemplos simples y luego a lo largo del curso se verán otros ejemplos.

Ejemplo 1 de recursividad:

Calcular el factorial de un entero no negativo n.

Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu



Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

Como se sabe esto se denota como n! y se pronuncia "factorial de n". La expresión que lo resuelve es:
n* n‐1 * n‐2 * n‐3 ......*1 con 1! 1 y 0! definido como 1.
Ej: 5! 5*4*3*2*1 120.
Esta expresión puede ser reescrita de matemáticamente de forma recursiva:
n! n* n‐1 !
5! 5*4!
4! 4*3!
3! 3*2!
2! 2*1!
1! 1
o sea, 5! 5*4! 5*4*3! 5*4*3*2! 5*4*3*2*1! 5*4*3*2*1 120
Veamos finalmente la solución en C:

#include stdio.h int factorial int x


int factorial int ; // prototipo
void main void if x 1
return 1;
unsigned long var, result; else
printf "Deme el numero para calcular el factorial: return x * factorial x‐1 ;
" ;
scanf "%d",&var ;
result factorial var ;
printf "factorial de %d es %d", var, result ;
getchar ;

Suponga que var tomó valor 3. El orden en que se realiza la ejecución del programa y los llamados recursivos es el
siguiente:

1‐. printf invoca a factorial 3 esperando como tipo de valor de regreso un entero.
a factorial 3 devuelve 3*factorial 2 1ra llamada recursiva
b factorial 2 devuelve 2*factorial 1 2da llamada recursiva
c factorial 1 devuelve 1 llegamos al caso base
d se regresa a la copia que llamó a factorial de 1.
e la copia factorial 2 devuelve 2*1 2; 2 es devuelto y se regresa a la copia que llamó a factorial de 2.
f la copia factorial 3 devuelve 3*2 6; 6 es devuelto y se regresa a la copia que llamó a factorial de 3.
¿Cuándo usar recursividad en la programación de nuestras funciones?

Es muy importante conocer cuando utilizar la programación recursiva pues no siempre es más eficiente ni es más
evidente llegar a un algoritmo recursivo. Algunas reflexiones importantes sobre este tema son:

1‐. Casi cualquier problema puede resolverse de forma iterativa y de forma recursiva.
2‐. Las dos implican repetición: en la variante iterativa se hace de forma explícita; la recursión lo consigue mediante
llamados sucesivos a la función.
3‐. Las dos involucran una prueba de terminación: la iteración termina cuando la condición de continuación del ciclo
es FALSE; la recursión termina cuando se llega al caso base.

Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu



Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

4‐. Ambas pueden producir ciclos infinitos: la iteración si la condición de continuación del ciclo nunca es FALSE; la
recursión si nunca se llega al caso base.
5‐. La recursión tiene muchas desventajas: invoca de forma repetida al mecanismo y por tanto a la sobrecarga de
llamadas a la función. Esto puede resultar costoso en tiempo y en memoria cada llamada consume tiempo y cada
copia de la función de hecho de las variables de la función consume memoria .
6‐. La iteración por lo general ocurre dentro de una función y por tanto no ocurre la sobrecarga de llamadas repetidas
de función y asignación extra de memoria.
¿Cuándo y por qué escoger entonces la recursión?

1‐. Cuando el enfoque recursivo es más natural al problema y resulta en un algoritmo y un programa más fácil de
comprender y de depurar.
2‐. Cuando la solución iterativa no resulta aparente.
¿Cuándo evitarlo?

1‐. Cuando se requiere rendimiento. Las llamadas recursivas consumen tiempo y memoria adicional.
2‐. Cuando no sea evidente este tipo de solución.
Variables estáticas.

En clases anteriores vimos que cuando se declaran variables, el alcance de ellas queda definido por el contexto donde
son creadas, o sea, si se crean dentro de una función existen solo dentro de la función no se puede acceder a sus
atributos fuera de la función , y durante el tiempo de ejecución de la función se crean en memoria cuando comienza
la función y se destruyen al concluir la ejecución de la función . Lo mismo sucede si las variables se crean dentro de
un bloque identificados por .
En ocasiones, es conveniente indicarle al procesador que no elimine las variables locales, para así poder utilizar el
valor que ellas tienen en sucesivas llamadas a la función. En estos casos se debe utilizar una clase de almacenamiento
diferente a la que hemos estado utilizando hasta el momento por defecto llamada auto . La clase de almacenamiento
será static.
Esta clase de almacenamiento, que se define para cada variable en el momento en que se crea de la forma: clase
almacenamiento tipo nombre_variable, le indica al compilador que esa variable cumplirá las siguientes reglas:

 Se creará al comienzo del programa y no de la función donde está definida .


 Se inicializará una sola vez en el momento en que se crea .
 Se mantiene que solo se tendrá acceso a ella por parte de la función donde es definida.
Veamos un ejemplo de uso de dicha variable:

Supongamos que en el ejemplo anterior queremos ver la cantidad de llamadas recursivas que se realizan a la función
factorial. El código habría que modificarlo solo en la función factorial de manera que quede:
int factorial int x

static int i 1;
printf "Llamada recursiva no.%d \n",i ;
if x 1
return 1;
else
return x * factorial x‐1 ;


Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu

Programación I
“El único lugar donde el éxito viene antes del trabajo es en el diccionario.”               

Por ejemplo, si ahora ejecutamos el programa para calcular el valor de 5, nos imprimirá:

Deme el numero para calcular el factorial: 5


Llamada recursiva no.1
Llamada recursiva no.2
Llamada recursiva no.3
Llamada recursiva no.4
Llamada recursiva no.5
factorial de 5 es 120
Process returned 10 0xA execution time : 2.420 s
Press any key to continue.
Note que la variable estática i conserva su valor entre un llamado a la función y otro, lo que nos permite contar la
cantidad de veces que la función factorial es invocada recursivamente.

Si no hubiéramos especificado que i es estática, entonces la ejecución hubiera dado como resultado:

Deme el numero para calcular el factorial: 5

Llamada recursiva no.1


Llamada recursiva no.1
Llamada recursiva no.1
Llamada recursiva no.1
Llamada recursiva no.1
factorial de 5 es 120
Process returned 10 0xA execution time : 2.106 s
Press any key to continue.
Siempre se imprime la línea Llamada recursiva no.1 porque en cada llamado a la función factorial se crea una nueva
variable local i no es estática y se inicializa con valor 1, que es el que se imprime dentro de la función.

Dpto. Automática, FIAB Ybrain Hernández López ybra@automatica.cujae.edu.cu

También podría gustarte