Logo Studenta

RESUMEN_PROGRAMACION_EN_C

¡Este material tiene más páginas!

Vista previa del material en texto

PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
1 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
Introducción 
En esta guía de repaso estudiaremos un resumen de los principales conceptos vistos durante el semestre 
en Programación. Es importante aclarar al lector que esta guía solo cubre aspectos fundamentales de la 
materia, pero existen otros detalles que no serán analizados (por temas de espacio). Para estudiar con esta 
guía te recomendamos revisar el material disponible en Webcursos y en CS50, y luego responder la guía 
de ejercicios que realizaremos en clases. Buena suerte! 
 
 NÚMEROS BINARIOS I.
El bit es la unidad básica que genera un número binario y sólo posee dos símbolos {0, 1}. El valor 0 
representa el bit apagado, y el valor 1 el bit encendido. A medida que empleamos más bits, podemos 
generar una mayor combinación de números. Una secuencia de 8 bits equivale a 1byte. 
 
 
 
 
 
 
 
 
Como vimos anteriormente, el alfabeto de los números binarios lo componen dos símbolos, es decir, 
posee base 2. En cambio, los números decimales están compuestos por diez símbolos {0, 1, …9} y su base 
es diez. Por ejemplo, para generar el número decimal 231, empleamos la siguiente operación (note como 
empleamos la base y su exponente para el desarrollo): 
 
2 × 102 + 3 × 101 + 1 × 100 = 231 (DECIMAL) 
 
Para transformar un número binario a decimal desarrollamos la siguiente formulación, por ejemplo, para el 
número binario 101100 (observe cómo opera la base 2 y el exponente en la posición del número binario): 
 
1 × 25 + 0 × 24 + 1×23 + 1×22 +0 × 21 + 0×20 = 32+0+8+4+0+0 = 44 (DECIMAL) 
 
Si deseamos transformar un número Decimal a Binario, dividimos el número por dos, y almacenamos la 
parte entera, hasta alcanzar como resultado de la operación el cero. En el siguiente ejemplo, observe que al 
leer los resultados del resto de la división desde el menor número hasta el mayor, este corresponde 
exactamente al número binario buscado. 
 
 
 
 
 
 
 
 
 
44/2 = 22 ( 22×2+0=44 ) resto 0) 
22/2= 11 ( 11×2+0=22 ) resto 0) 
11/2= 5 ( 5×2+1=11 ) resto 1) 
5/2= 2 ( 2×2+1= 5 ) resto 1) 
2/2= 1 ( 1×2+0= 2 ) resto 0) 
1/2= 0 ( 0×2 +1= 1 ) resto 1) 
0 
0 
1 
1 
0 
1 
 
Lea los valores desde 
abajo hacia arriba 
Codigo binario
Encendido Apagado Encendido
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
2 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 NÚMEROS HEXADECIMALES II.
Los números Hexadecimales poseen una base de 16 símbolos, los cuales corresponden a los dígitos 
{0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F}. Al igual que en el caso anterior, podemos representar un número en una 
determinada representación y transformarlo a otra. Por ejemplo, el número F corresponde al número 
decimal 15 y al número binario 1111. Por convención, se utiliza un símbolo para indicar que estamos 
empleando un número hexadecimal. En este caso el número 15 (en decimal) se escribe como 0xF en 
hexadecimal. Los computadores utiliza este tipo de base, ya que requiere una menor combinación de 
símbolos para alcanzar una dirección determinada en memoria. 
 
Para transformar un número binario a uno decimal, simplemente debemos agrupar en grupos de 4 bits. 
Así, para escribir el número 44 (decimal) a Hexadecimal, podemos simplemente emplear la representación 
binaria y luego transformarlo a Hexadecimal. Veamos un ejemplo: 
 
44 (DEC) >> 101100 (bin) >> 0010 1100 (bin) >> 2C >> 0x2C 
 
En este caso para completar los bits en grupos de cuatro, agregamos dos ceros antes, y así completamos 
8 bits (1 Byte). Luego, dividimos el grupo en dos partes, donde 0010 corresponde al valor 2 (hex) y 1100 al 
valor C (hex) (12 en valor decimal). Finalmente escribimos el número 0X2C como el valor hexadecimal. 
 
 
 OVERFLOW DE DATOS III.
Las variables que creamos en el lenguaje de programación C poseen un tipo de datos definido que el 
programador define explícitamente. El tipo de datos indica tanto el tipo de variable como la cantidad 
máxima de valores que podemos almacenar en ella. El overflow ocurre cuando sobrepasamos dicho límite. 
En este caso, el computador comienza a recorrer los valores desde el rango negativo. 
 
 En la siguiente tabla podemos ver la cantidad de valores y rangos admitidos según el tipo de datos 
definido en las primeras versiones del lenguaje C. Por ejemplo 
 
 
 
 
 
 
 
 
 
 
Si nos remontamos a la época en que fue diseñado el lenguaje C, los computadores no poseían mucha 
capacidad en memoria. Por esta razón los primeros programadores se veían enfrentados a diseñar 
programas con una fuerte restricción en memoria. Esto permitió diseñar programas elegantes y eficientes en 
términos del uso de recursos. Es importante aclarar que los computadores actuales, el tipo de dato entero 
(int) se ha extendido a 4 bytes (32 bits). 
 
 
2 (DEC) 12 (DEC) 
Representación hexadecimal 
Tipo de datos Valores Bits Bytes Rango de valores admitidos
char carácter 8 1 -128 a 127
int entero 16 2 -32.768 a 32.767
unsigned int entero 16 2 0 a 65.535
long int entero 32 4 -2.147.483.648 a + 2.147.483.647
float flotante 32 4 - 3.40284 e+38 a +3.40284 e+38
double flotante 64 8 -1.79769 e+308 a 1.79769 e+308
long double flotante 80 10 -1.189731 e-4932 a +1.189731 e+4932
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
3 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ESTRUCTURA DEL PROGRAMA IV.
Todos los programas escritos en el lenguaje C comienzan su ejecución desde la función principal “main()”. 
Para que el programa pueda manipular datos, desplegar información por pantalla, leer cadenas de texto, 
entre otros, el lenguaje dispone de librerías, las cuales permiten la manipulación y manejo de datos. Entre 
ellas, la más utilizada es la librería “stdio.h”, ya que dispone de funcionalidades para imprimir en pantalla a 
través de la función “printf”. El nombre de la librería indica el acrónimo Standard Input Output, la cual 
permite la entrada y salida de datos. 
 
Las librerías son un conjunto de archivos que contienen funciones y definiciones las cuales facilitan al 
programador abstraerse de la implementación de sub problemas, y enfocarse sólo en la solución de su 
problema. De esta forma, las funcionalidades más conocidas ya se encuentran implementadas por el 
propio lenguaje. 
 
Otros aspectos de la estructura tienen relación con la sintaxis. El lenguaje C, al igual que el lenguaje 
Español (Inglés, Alemán, Francés, etc) posee reglas precisas y claras sobre la forma como se combinan las 
palabras y su significado. En el caso de los computadores, las reglas son aún más explicitas, ya que el 
código está diseñado tanto para la lectura e interpretación del computador, como además la lectura del 
programador. Por ello si omitimos algunas reglas sintácticas, el compilador indicará que existe un error. 
 
Por ejemplo, si analizamos el programa en la versión de la derecha, el compilador (o traductor) fallará su 
ejecución indicando los siguientes errores: (1) La función printf está siendo empleada sin haber declarado 
la librería stdio.h que contiene su implementación. (2) falta un punto y coma al final de la instrucción printf 
 
 
 
 
 
 
 
 
 
Los primeros pasos en la programación en C corresponden a un aprendizaje de la sintaxis y semántica del 
lenguaje. Afortunadamente las reglas son muy pocas y las enumeramos a continuación (sólo a nivel 
general) 
 
1) Todo bloque de funciones debe llevar una llave que abre el bloque “{“ y otra que lo cierra “}” 
2) Toda instrucción debe finalizar con el símbolo ; 
3) Cualquier función que utilicemos debe está previamente declarada, ya sea en una librería, o bien en 
nuestro propio programa a través de la declaración de prototipos. El programador debe indicar la(s) 
librería(s) que se debe(n) incluir en el programa. 
4) Las estructuras de control nunca llevan el símbolo ; (a menos que sea parte del diseño del 
programador, poco usual). 
5) Todos los tipos de datos deben estar declarados y definidos en su espacio en memoria. 
 
int main() 
{ 
 printf("hola mundo") 
} 
#include<stdio.h>int main() 
{ 
 printf("hola mundo"); 
} 
Versión incorrecta 
Versión sin errores 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
4 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 VARIABLES V.
La memoria del computador está compuesta por millones de bloques individuales los cuales permiten 
almacenar diferentes datos, desde símbolos, números o letras. Sin embargo, hasta el momento no 
sabemos cómo lograr acceder a dichos datos. Para almacenar datos en la memoria, sólo es necesario 
definir un tipo de datos apropiado y un nombre que identifique dicho bloque. Este concepto se denomina 
variable. 
 
Gracias a las variables el programa puede acceder a la 
memoria y emplear la información almacenada, 
pudiendo modificarla, borrarla o crear nuevas 
variables. Formalmente diremos que toda variable 
representa una estructura de datos. Todas las 
variables son almacenadas en un área específica de la 
memoria, para ello el sistema operativo es el encargado de otorgar el suficiente espacio necesario para 
crearla. Las variables pueden ser de tamaño fijo o variable durante la ejecución del programa. (1) Si su 
tamaño es fijo, ésta se mantiene constante y no cambia durante la vida del programa -entenderemos por 
vida del programa mientras éste se encuentre funcionando-. (2) Si su tamaño es variable, ésta puede ser 
modificada, es decir, puede emplear más o menos bloques de memoria de lo que originalmente se 
encontraba definida; concepto conocido como memoria dinámica. En particular, la estructura de datos es 
la forma en que se organiza la información. En relación a éstas últimas, existen diferentes estructuras de 
datos sobre las cuales podemos generar una variable, pero mientras tanto comenzaremos con la simple la 
cual consiste en una variable con un tipo de datos ya definido. 
 
El siguiente ejemplo muestra el proceso de asignación de dos variables enteras {a,b} a medida que 
transcurre el tiempo de ejecución del programa. Note cómo al definir las variables, el sistema operativo 
reserva espacio de memoria que luego es empleado en el proceso de asignación (=). 
 
A través del operador 
asignación, los valores 
que están almacenados 
en las variables pueden 
ser modificados durante 
la ejecución del 
programa. Como veremos 
más adelante, existen dos 
tipos de variables, 
conocidas como variables 
locales y variables 
globales. Además, existe 
un caso especial donde 
una variable no puede 
modificar su valor, y éste 
ocurre cuando la variable 
ha sido declarada como 
una constante (ej.: const 
int a). 
Variables en la memoria
06ep?g ep
angnombre
1
#include <stdio.h>
int main()
{
}
#include <stdio.h>
int main()
{
 int a,b;
}
#include <stdio.h>
int main()
{
 int a,b;
 a= 4;
 
}
#include <stdio.h>
int main()
{
 int a;
 a= 4;
 b= a*a;
 printf("%d",b);
}
tiempo
INICIO
Bloques de memoria
0
1
2
3
4
5
2 3 4
Bloques de memoria
0
a:1
2
3
b:4
5
Bloques de memoria
0
a:1
2
3
b:4
5
4
Bloques de memoria
0
a:1
2
3
b:4
5
4
16
espacio
reservado
espacio
reservado
D
ir
e
c
c
io
n
e
s
Proceso de asignación de valores en memoria
impresión 
a pantalla
FIN
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
5 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 
 
 ESTRUCTURAS DE CONTROL: IF-ELSE VI.
Las estructuras de control son el corazón de nuestros programas, con ellas podemos modificar el curso de 
ejecución y control de nuestro programa. Existen diferentes tipos de estructuras de control, pero todas 
ellas tienen en común el uso de condiciones lógicas para su uso. Las condiciones permiten evaluar una o 
más preguntas sobre el estado o valor de las variables. Por ejemplo, tmp>32, donde tmp es una variable, 
o bien nivel_agua<20. También podemos emplear condiciones anidadas a través de los operadores 
lógicos1 AND "&&"; o el operador OR "||". Por ejemplo (tmp>32 && nivel_agua<20) 
 
En este apartado nos centraremos brevemente en el uso de la estructura de control IF-ELSE. La estructura 
general se compone de la siguiente forma2: El IF posee un argumento donde se declara una condición 
lógica. Los valores posibles en su evaluación son TRUE o FALSE (1 o 0). Si la condición es verdadera, 
entonces el control del programa ingresará al bloque de instrucciones IF. En cambio si la condición es 
falsa, el control del programa ingresará al bloque de instrucciones ELSE. En ambos casos recuerde que 
dicho bloque está limitado por las llaves {...}. 
 
 
 
 
 
 
 
 
Observe que no existe una condición en el argumento del ELSE. Solo el IF tiene un único argumento 
donde se evalúa(n) la (o las) condicion(es) (dicho argumento va entre paréntesis). Cuando el control del 
programa escoge un camino, no puede regresar al bloque anterior. Por ello el programador debe definir 
correctamente el curso de acción que seguirá su programa cuando se encuentre en ejecución. Tomando 
en consideración la estructura anterior, podemos definir la misma estructura al interior de cada bloque 
(infinitas veces), por ejemplo: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1 Revise el material adicional del curso para ver más detalles sobre los operadores lógicos. 
2 Suponga que el código está contenido en un programa principal. 
if ( <condición_1> ) 
{ 
 if ( <condición_2> ) 
 { 
 // entra solo si condición 1 y 2 es verdadera 
 } 
 else 
 { 
 //entra solo si condición 1 es verdadera y 2 es falsa 
 } 
 
} 
else 
{ 
 // entra si la condición 1 es falsa. Nunca evalúa la condición 2 
 
} 
if ( <condición(es)> ) 
{ 
 // instrucciones del bloque IF 
} 
else 
{ 
 // instrucciones del bloque ELSE 
} 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
6 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ESTRUCTURAS DE CONTROL: FOR VII.
La estructura de control FOR nos permite ejecutar ciclos en forma controlada, y un determinado número 
de veces según el programador defina. El ciclo se realiza mientras la condición es verdadera. Es muy útil 
cuando se conoce de antemano si el problema emplea índices tanto para incrementar o decrementar 
(disminuir) valores cada vez que completa un ciclo. Un ciclo se realiza cuando el programa realiza las 
instrucciones contenidas en un bloque y regresa nuevamente al inicio del FOR. 
 
 
 
 
 
 
 
 
 
 
 
 
La estructura genérica del ciclo FOR posee tres secciones (argumentos) de la siguiente forma: 
 
 
 
La inicialización corresponde a la declaración de las variables con valores iniciales, por ejemplo, i=0, 
count=0; color=100. Puede ser declarada más de una inicialización. La(s) condición(es) lógicas permiten 
que en algún momento de ejecución el ciclo termine (si el ciclo no terminase, entonces el programa nunca 
finalizaría). Finalmente el modificador permite que la variable empleada en la inicialización actualice su 
valor cada vez que completa un ciclo, por ejemplo i++, i--, i+=2, entre otros. 
 
Al igual que la estructura de control IF-ELSE, 
podemos emplear los ciclos FOR en forma más 
compleja, y con una combinación de 
estructuras. Analice por un momento el 
funcionamiento del siguiente programa, y 
como en cada ciclo se evalúa una condición 
lógica. 
 
A medida que adquiere más experiencia con 
el lenguaje, el uso de ciclos y condiciones de 
evaluación dentro del bloque resulta más 
natural. En la mayoría de los programas que a 
futuro realice, dispondrá de condiciones y 
otras estructuras dentro del ciclo. 
 
for (<inicialización>; <condición(es)>; <modificador>) 
{ 
 //instrucciones del bloque 
} 
i=0 i=1 
Una vez que termina la última 
instrucción, el control del 
programa incrementa el 
contador y luego evalúa la 
condición lógica. Si ésta es 
verdadera, entonces se ejecuta 
nuevamente el ciclo. Si la 
condición es falsa, el ciclo 
termina 
El programa inicializa una o más 
variables y evalúa una o más 
condiciones lógicas. Si la 
condición es verdadera, 
entonces ingresa al bloque del 
FOR. Si la condición es falsa, sale 
del ciclo 
#include<stdio.h> 
#include<cs50.h> 
 
int main(){ 
 
 int i, a, cont=0;printf("Ingrese un numero entero: "); 
 a= GetInt(); 
 
 for(i=1; i<=a; i++){ 
 if (a%i==0){ 
 cont++; 
 } 
 } 
 if (cont==2){ 
 printf("El numero es primo\n"); 
 } 
} 
 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
7 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ESTRUCTURAS DE CONTROL: WHILE VIII.
El ciclo WHILE es una estructura de control que permite que se ejecute un ciclo mientras la condición 
evaluada es verdadera. Cuando la condición es falsa, el ciclo termina. A diferencia del ciclo FOR, el ciclo 
WHILE sólo posee un argumento donde se evalúa una condición lógica. A medida que usted adquiere 
más experiencia con el uso de los ciclos, podrá diferenciar el uso entre el ciclo FOR y el ciclo WHILE. 
 
Otra forma de ver el ciclo es a través de un diagrama de flujo. En el 
diagrama, el óvalo representa el inicio o fin del programa. El 
rectángulo una instrucción, y el rombo, una decisión. Cuando el 
programa alcanza la decisión, debe evaluar una condición lógica. 
De lo visto anteriormente, los resultados posibles son verdadero o 
falso (TRUE, FALSE). Solo cuando la condición es verdadera, el 
ciclo se ejecuta nuevamente, ingresando al bloque de 
instrucciones. 
 
Si la condición del ciclo nunca se hace falsa, entonces el programa 
quedará en un loop infinito. Es tarea del programador diseñar una 
condición que en algún punto del programa se haga falsa. 
 
 
 
Al igual que las estructuras vistas anteriormente, el ciclo WHILE puede combinarse con otras estructuras 
de control. Inclusive, puede realizar la misma función que un ciclo FOR. Revise en el siguiente ejemplo un 
programa que realiza el mismo objetivo, pero uno de ellos se realiza con ciclo WHILE y la otra con ciclo 
FOR. Analice si alguno tiene una ventaja respecto al otro. ¿En qué casos recomendaría usted emplear un 
ciclo WHILE y en cuales no? 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
while ( <condicion(es)> ) 
{ 
 //instrucciones del bloque 
} 
!"#$%& '()%&
#include<stdio.h> 
#include<cs50.h> 
 
int main(){ 
 
 int i, a, cont=0; 
 printf("Ingrese un numero: "); 
 a= GetInt(); 
 
 i=1; 
 while(i<=a){ 
 if (a%i==0){ 
 cont++; 
 } 
 i++; 
 } 
 if (cont==2){ 
 printf("El numero es primo\n"); 
 } 
} 
 
#include<stdio.h> 
#include<cs50.h> 
 
int main(){ 
 
 int i, a, cont=0; 
 
 printf("Ingrese un numero entero: "); 
 a= GetInt(); 
 
 for(i=1; i<=a; i++){ 
 if (a%i==0){ 
 cont++; 
 } 
 } 
 if (cont==2){ 
 printf("El numero es primo\n"); 
 } 
 
} 
Versión ciclo FOR Versión ciclo WHILE 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
8 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 MANEJO DE STRINGS (ASCII) IX.
Gracias a las funciones implementadas en la librería cs50.h y string.h, es posible la manipulación de texto en 
el computador. Entre las principales funcionalidades se encuentran el almacenamiento, la manipulación, la 
búsqueda. En este resumen revisaremos las tres primeras funcionalidades brevemente. 
 
Almacenamiento 
Para almacenar una cadena de caracteres (STRINGS) empleamos 
una funcionalidad provista en la librería cs50.h. El siguiente 
código muestra un ejemplo mínimo que permite ingresar una 
cadena de texto en una variable; al igual como lo haría al ingresar 
un número entero. Primero, declare una variable de tipo “string”. 
Segundo, utilice la función GetString() para asignar el texto leído a 
una variable. Tercero, utilice la variable declarada con la cadena 
leída para su manipulación 
 
Manipulación 
La manipulación requiere un conocimiento del 
número de caracteres que existen en la variable. 
Para ello empleamos la librería string.h, ya que 
contiene funciones permiten manipular cadenas de 
string (entre muchas otras funciones). Una de las 
que más utilizaremos es el largo de la cadena, a 
través de la función strlen. 
 
Este número es muy relevante ya que si deseamos 
manipular una cadena de caracteres, es necesario 
conocer la posición del último caracter (o símbolo). 
¿Qué ocurriría si no conocemos el largo de la 
cadena?, podríamos ingresar a una posición de la 
cadena que esté en otra parte de la memoria, lo 
cual podría generar un "segmentation fault". 
 
Búsqueda 
Para realizar operaciones más complejas sobre la cadena, primero revisemos la forma en cómo se almacena 
la información en la variable. Cuando el computador almacena la cadena de texto en la variable S (del 
ejemplo anterior), cada caracter (o símbolo) es almacenado en una determinada casilla (caja). De esta forma 
podemos recorrer, contar, buscar información dentro de la cadena al comparar cada casilla con algún valor. 
Inclusive podemos cambiar los datos contenidas en ella por otros en nuevas posiciones. 
 
 
 
 
 
#include<stdio.h> 
#include<cs50.h> 
 
int main(){ 
 
 string S; 
 S= GetString(); 
 
 printf("texto: %s\n",S); 
} 
 
#include<stdio.h> 
#include<cs50.h> 
#include<string.h> 
 
int main(){ 
 
 int p; 
 string S; 
 S= GetString(); 
 
 //largo de la cadena 
 p=strlen(S); 
 
 printf("%s tiene %i caracteres\n",S, p); 
 
} 
o l c m h a o o e s t a s ? \0 
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 
16 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
9 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
Note en el ejemplo que ingresamos la cadena 
"hola como estas?". Observe que el espacio 
en blanco también se almacena en la cadena 
de texto. Internamente la cadena termina al 
alcanzar el símbolo '\0', el cual indica al 
computador que corresponde el final de una 
cadena. 
 
En el siguiente ejemplo utilizaremos esta 
propiedad al modificar los índices. Observe 
que podemos determinar una posición del 
arreglo. Por ejemplo, la posición S[i] 
determina el símbolo (caracter) en la posición 
i-ésima (línea 13). En el código se indica la 
posición S[p-1-i], analice a qué posición 
corresponde en el arreglo a medida que la 
variable i se incrementa. 
 
Note que fácilmente podemos recorrer el arreglo a través de un ciclo FOR, el cual avanza posición por 
posición en el arreglo. Dado que conocemos el largo (a través de la función strlen), la condición de término 
es recorrer el arreglo hasta terminar de revisar el último símbolo almacenado en la variable. 
 
En el siguiente programa vamos a modificar cada símbolo 
por otro. Recuerde que cada símbolo se almacena 
internamente con un valor numérico (con su correspondiente 
valor en la tabla ASCII) . 
 
Observe con detalle la condición (S[i]!=' ') en la línea 13. 
Esta condición compara si el símbolo almacenado en la 
posición i-ésima es distinta al símbolo ' '. Este último 
símbolo corresponde a un espacio en blanco, y solo 
podemos realizar esta comparación entre caracteres. Si la 
condición es verdadera, es decir, no estamos leyendo el 
espacio en blanco en una determinada posición, entonces, el 
programa incrementa en una unidad el número contenido 
en el texto en la i-ésima posición. La explicación de esta 
operación tiene relación en la forma como se almacenan los 
caracteres en el computador. Veamos un ejemplo de esta 
operación. 
 
El código ASCII codifica todo el alfabeto que está en su computador, 
incluidos símbolos y letras. En el caso del símbolo 'a', este posee el valor 97. 
Internamente de este modo se almacena la información en su computador 
(en realidad con códigos binarios). Si sumamos al número 97 una unidad 
(97+1), para efectos del computador, estamos utilizando el símbolo 'b' (línea 
14) 
#include<stdio.h> 
#include<cs50.h> 
#include<string.h> 
 
int main(){ 
 
 int p, cont=0; 
 string S= GetString(); 
 
 p=strlen(S); 
 
 for(int i=0; i<p; i++){ 
 if (S[p-1-i]==S[i]){ 
 cont++; 
 } 
 } 
 if (cont==p) 
 printf("La palabra es palíndroma"); 
} 
 
#include<stdio.h> 
#include<cs50.h> 
#include<string.h> 
 
int main(){ 
 
 int p; 
 string S= GetString(); 
 
 p=strlen(S); 
 
 for(int i=0; i<p; i++){ 
 if (S[i]!=' '){ 
 S[i] = S[i]+1; 
 } 
 } 
 
 printf("cifrado: %s", S); 
} 
 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
 
 
 
1. 
2. 
3.4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
 
 
 
a 97 
ASCII code 
b 98 
Símbolo 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
10 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ARREGLOS UNIDIMENSIONALES X.
Hasta ahora siempre hemos almacenado un número por cada variable definida. Sin embargo, ¿es posible 
almacenar más información en una variable? La respuesta es afirmativa, y esto se realiza a través de 
arreglos unidimensionales (1D). Los arreglos son estructura de datos que permite almacenar muchos 
valores en una misma variable. Para crear un arreglo, primero debemos definir su tipo, segundo indicar el 
nombre de la variable, y tercero indicar el tamaño del arreglo. Este tipo de arreglo no puede ser 
modificado mientras el programa se ejecuta. 
 
 
 
 
 
 
 
 
 
 
 
 
Una gran ventaja de los arreglos es que en una sola variable podemos almacenar muchos valores, y 
posteriormente podemos procesarlos. Aplicaciones usuales para este tipo de información son 
ordenamiento, búsqueda, operaciones matemáticas, estadísticas, análisis de patrones, entre otros. 
Veamos a continuación dos ejemplos de programas que utilizan operaciones con arreglos. Código 
Izquierda: A medida que ingresamos los valores en el arreglo, acumulamos el valor almacenado en la 
variable acum. Note que antes de ingresar los valores, dicha variable posee el valor cero (línea 7). El 
resultado final del programa arroja la sumatoria de los valores leídos por teclado a través de la función 
GetInt(). Código derecha: A medida que se ingresan los valores, va comparando si el nuevo valor 
ingresado es mayor al máximo almacenado en la primera iteración. Esta comparación la realiza en la línea 
13. Si dicha condición se cumple, se actualiza el valor almacenado en la variable max. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include<stdio.h> 
#include<cs50.h> 
 
int main(){ 
 
 int M[10], acum; 
 acum=0; 
 
 for(int i=0; i<10; i++){ 
 
 M[i] = GetInt(); 
 acum = acum+M[i]; 
 } 
 
 printf("Suma: %i", acum); 
} 
 
 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
 
 
#include<stdio.h> 
#include<cs50.h> 
 
int main(){ 
 
 int M[10], max; 
 
 for(int i=0; i<10; i++){ 
 M[i]= GetInt(); 
 if(i==0) 
 max= M[i]; 
 else{ 
 if(max < M[i]) 
 max = M[i]; 
 } 
 } 
 printf("Maximo es %i", max); 
} 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
 
 
#include<stdio.h> 
 
int main(){ 
 
 int a,b,c,d,e; 
 a=10; 
 b=11; 
 c=12; 
 d=13; 
 e=14; 
} 
 
#include<stdio.h> 
 
int main(){ 
 
 int a[5]; 
 a[0]=10; 
 a[1]=11; 
 a[2]=12; 
 a[3]=13; 
 a[4]=14; 
} 
 
Versión sin arreglos Versión con arreglos 
#include<stdio.h> 
 
int main(){ 
 
 int a[5]={10,11,12,13,14}; 
} 
Versión con arreglos 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
11 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ARREGLOS MULTIDIMENSIONALES XI.
Si ya has descifrado la complejidad de los arreglos 
unidimensional (una dimensión), este concepto será 
sumamente simple. Se trata de los arreglos 
multidimensionales (más de una dimensión). En este 
punto sólo revisaremos el caso de dos dimensiones, 
pero es fácilmente replicable a un mayor número. Los 
arreglos con dos dimensiones corresponden 
básicamente a una estructura del tipo matriz. En ella 
podemos almacenar números o letras, y podemos 
aplicar operaciones sobre ellas, tales como 
ordenamiento, búsqueda u otras operaciones más 
complejas. A diferencia del caso en una dimensión, 
tener más dimensiones implica que debemos 
considerar el uso de nuevos índices; uno por cada 
nueva dimensión que utilicemos. 
 
 En el código del ejemplo empleamos los índices i, j 
(puede ser cualquier nombre). Note cómo se puede 
generar en la inicialización el ingreso de datos a la 
matriz. Usted puede ver en el código que existen dos 
ciclos FOR. Uno interno, y otro externo. El ciclo interno 
controla el movimiento entre las columnas, y se 
completa hasta llegar a la última columna. El ciclo 
externo cambia las filas, de esta forma cuando se fija el 
valor i=0 (en el ciclo externo), el valor de j cambia de 
0, a 1, hasta llegar a j=3. En dicho punto el ciclo 
interno finaliza, y el control del programa regresa 
nuevamente al ciclo externo, actualizando el valor de la 
variable i, al valor i=1. 
 
Observe en detalle la sintaxis para recuperar la 
información dentro de la matriz (data[i][j]). A 
diferencia de los arreglos unidimensionales, 
simplemente agregamos la nueva dimensión al 
incorporar el término [j]. En el siguiente código 
(izquierda), observe como agregamos información a la 
matriz (arreglo multidimensional). 
 
Al igual que en el caso de los arreglos, podemos realizar 
operaciones simplemente usando los índices de las 
posiciones. Es importante recordar que nunca debemos 
emplear posiciones que no estén dentro del matriz. De 
otro modo el programa arrojaría un error en la 
ejecución (no en la compilación). 
 
#include<stdio.h> 
 
int main(){ 
 
 int data[4][4]={ {4,2,3,7}, 
 {1,5,9,2}, 
 {8,3,6,4}, 
 {3,1,5,0}}; 
 int i,j; 
 
 for (i=0; i<4; i++){ 
 for (j=0; j<4; j++){ 
 printf("%i ",data[i][j]); 
 } 
 printf("\n"); //salto de linea 
 } 
} 
4 ! 2! 3! 7 !
1 ! 5! 9! 2 !
8 ! 3! 6! 4 !
3 ! 1! 5! 0 !
j : columnas!
i=0 !
i=1 !
i=2 !
i=3 !
j=0 ! j=1 ! j=2 ! j=3 !
i
:
 
f
i
l
a
s
!
#include<stdio.h> 
#include<stdlib.h> 
#include<time.h> 
 
int main(){ 
 
 srand(time(NULL)); 
 
 int data[4][4]; 
 int i,j; 
 
 for (i=0; i<4; i++){ 
 for (j=0; j<4; j++){ 
 data[i][j]=rand()%10; 
 printf("%i ", data[i][j]); 
 } 
 printf("\n"); 
 } 
} 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
12 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 FUNCIONES XII.
Las funciones son bloques de código que nos permite encapsular y dividir un problema en sub partes. 
Esto permite al programador concentrarse en diseñar una solución al problema general, empleando un 
conjunto de soluciones/construcciones parciales. Al diseñar una función, usted está implementando un 
bloque de código que puede ser invocado (llamado) por otras funciones, y al mismo tiempo, ésta puede 
invocar(llamar) a otras. Los pasos para construir una función son muy simples. Solo debemos tener en 
cuenta los siguientes requerimientos básicos que se cumplen en toda función. 
 
I. Debe tener un único nombre. 
II. Debe declarar un tipo de datos si retorna (o no) un valor. Si no retorna debe emplear el tipo void 
III. Debe indicar si recibe valores y de qué tipo 
IV. Debe especificar si retorna un valor (si no retorna, no empleamos return) [opcional] 
 
La estructura que toda función 
tiene es de la siguiente forma: (1) 
Posee un tipo de dato. El tipo de 
dato de la función tiene relación 
con el valor que se retorna luego 
de ser invocada. (2) El nombre de la 
función especifíca cómo será 
invocada, (3) el argumento tiene 
dos partes: tipo de datos y nombre 
de la variable. Cuando se ejecuta 
una función, los datos se 
“transfieren” o copian o referencian a dichas variables. Si tenemos más de una variable, esta debe ir 
separadas por comas simples, (4) Para que una función pueda retornar (devolver) su resultado a quien la 
invocó (llamó) debemos emplear la instrucción return. Dentro de ella se puede especificar sólo una 
variable de retorno. 
 
Durante la ejecución de una función existen dos pasos 
fundamentales, los cuales tienen relación a la forma como se 
“transfiere” la información al código de la función, y 
posteriormente como esta la “recibe”; proceso conocido como 
paso de parámetros. 
 
 Existen dos formas para “transferir” datos a la función. (1) paso 
por valor, y (2) paso por referencia. En el primer caso, los valores 
“transferidos” son copiados a las variables que los reciben. La 
función puede emplear dichos valores como en una copia local a 
la función. En el segundo caso, sólo se transfiere una dirección de 
memoria, y las variables que recibenlos datos, solo pueden 
almacenar una dirección. También podemos emplear una 
combinación de ambos tipos. 
 
En el código de ejemplo, inventamos una función que se llama abc. En la línea 11 del código, la función es 
invocada, y el valor contenido en la variable “a” es transferido a la variable “o”. De esta forma la función 
#include<stdio.h> 
 
int abc(int); //prototipo 
 
 
int main(){ 
 
 int a=10; 
 int b; 
 
 b= abc(a); 
 printf("%i",b); 
} 
 
int abc(int o){ 
 return(o+2); 
} 
 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
 
int duplicar(int a)
{
 int b;
 b= a*2;
 return (b);
}
tipo de datos de salida
nombre de la función
parámetro de entrada
Bloque de 
instrucciones
variable local
instrucción de retorno
variable de salida
Encabezado
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
13 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
“recibe” el valor 10 y será almacenado en la variable “o”. Esta función luego realiza la suma “o+2”, y dicho 
valor es “retornado” al llamado de la función. De otra forma, la expresión “abc(a)” se transforma en el 
valor 12. Posteriormente el valor 12 es asignado a la variable b. En este punto del programa podemos 
realizar cualquier operación con dicho resultado. 
 
El siguiente diagrama presenta un ejemplo completo de creación de variables y el proceso de paso de 
datos a una función. Observe que empleamos la función scanf() ya que permite ingresar los valores en 
una forma mucho más específica que la versión GetInt() de la librería <cs50.h>. Puede encontrar más 
información sobre su uso en el material del curso. 
 
 
 
Biblioteca main Inicio variables Ingreso de datos LLamada a función Impresión resultado
Paso por valor
#include <stdio.h>
Inicio variables Operación Retorno función
return(b);
}
INICIO
Ejemplo de ejecución secuencial
Bloque de 
memoria
3 3 6
3
3
3
3
3
36
3
36
Reserva de 
bloques
Llamado a
función
Impresión
resultado
Copia del 
valor
Reserva del 
bloque
Asignación 
Asignación
Retorno de 
resultado
6 3
Vista de la memoria 
desde el programa 
principal
Vista de la memoria 
desde la función 
invocada
Variable
Valores no 
accesables
tiempo
FIN
1 3 4 5 6
int main()
{
int a,b;
printf("numero: ");
scanf("%d", &a);
 b = duplicar(a);
 printf("%d", b);
}
int duplicar(int a)
{
int b;
b= a*2;
2
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
14 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ÁMBITO DE LAS VARIABLES XIII.
Hasta ahora hemos empleado variables en distintas partes del programa, tanto en la función MAIN, como 
en nuestras funciones. Sin embargo, las variables posee dos formas que pueden ser declaradas: variables 
globales y variables locales. En forma global, implica que dicha variable puede ser empleada en cualquier 
función del programa, y cualquier cambio que realicemos a dicha variable afecta a todo el programa. En 
forma local, significa que la variable que declaremos sólo será conocida por dicha función, y los cambios 
que realicemos sobre ella durante la ejecución del programa, sólo afectarán a la función y no al resto del 
programa. 
 
Es muy relevante comprender estos 
dos modos, ya que nos permitirá 
comunicar en forma eficiente la 
información en la ejecución del 
programa. Por regla general, no se 
recomienda emplear variables globales 
ya que la lógica del programa se 
pierde. Solo en casos específicos serán 
empleadas 
 
A continuación detallamos brevemente los 
principales pasos en la ejecución del programa: 
 
! Línea 3. declaramos una variable 
global (int C) con valor inicial 3. Esta variable 
puede ser empleada (y modificada) en 
cualquier parte de nuestro programa. 
! Línea 8. declaramos una variable local 
(int A) en la función main. Solo dicha función 
tiene acceso a su valor. 
! Línea 11. ejecutamos la función 
cambiar . Observe que pasamos el contenido 
de la variable A (paso por valor). 
! Línea 17. La función crea una variable A y 
recibe el valor de quien la invocó, es decir 
recibe el valor 4. Cuando la función recibe el 
valor, las variables A, y B son locales, y 
cualquier cambio en ella solo afecta el ámbito 
de ejecución de dicha función. 
! Línea 24. Modificamos el valor de la 
variable global C. Observe que C no está 
declarada en la función ya que es global. 
! Línea 27. La función retorna el número entero que está en la variable B. 
! Línea 11. La función cambiar() retorna su valor y éste es asignado a la variable local A (local a la función 
main). 
 
variables globales 
programa!
alcance el 
programa 
variables locales 
main()!
variables locales 
mi_funcion() !
alcance 
local 
#include<stdio.h> 
 
int C=3; //variable global 
int cambiar (int); //prototipo 
 
int main(){ 
 //variable A es local a main 
 int A; 
 
 A=4; 
 A= cambiar(A); 
 
 printf("Local en main: %i\n",A); 
 printf("global en main: %i\n",C); 
} 
 
int cambiar(int A){ 
 
 //variables A y B son locales 
 int B; 
 
 A = A+1; 
 B = 7; 
 C = A-2; 
 printf("Local en funcion: %i\n", A); 
 printf("global en funcion: %i\n",C); 
 return(B); 
} 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
20. 
21. 
22. 
23. 
24. 
25. 
26. 
27. 
28. 
29. 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
15 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ORDENAMIENTO XIV.
El ordenamiento de números es una de las operaciones algorítmicas más útiles, ya que muchas 
operaciones matemáticas, léxicas, y búsqueda parten de la base que existe un conjunto ordenado de 
números (o palabras). En la literatura se han descrito un extenso número de algoritmos de ordenamiento, 
tales como bubble sort, selection sort, insertion sort, merge sort, entre otros. Cada uno está descrito 
según un orden de complejidad (el cual tiene relación con la cantidad de instrucciones que requiere un 
algoritmo para realizar un conjunto de instrucciones). En el caso de los algoritmos bubble sort, insertion 
sort y selection sort, todos posee una complejidad de O(n2). Por el contrario, un algoritmo eficiente para el 
ordenamiento es Merge Sort, cuyo orden es O(nlog2n). 
 
A modo de completitud, revisaremos brevemente el algoritmo bubble sort. Suponga que dispone de un 
arreglo con 5 valores en forma desordenada (aleatoria) ¿Cómo ordenarlos de mayor a menor? En el código 
de ejemplo tenemos un ciclo externo y otro interno FOR. El ciclo interno regula la variable j y el ciclo 
externo la variable i. Revise detalladamente todo el proceso completo de ordenamiento hasta alcanzar la 
quinta iteración. Note el uso del swap como técnica 
de intercambio de valores. 
 
 
 
 
4! 2! 3! 7! 0 ! 2! 4 ! 3! 7! 0!
swap!
j=0 ! j=1 ! j=1 ! j=2 !
2! 3 ! 4! 7! 0!
j=3 !
2! 3 ! 4! 7! 0!
j=4 !
2! 3 ! 4! 0! 7!
swap !
Fin primera 
iteración !
j=3 !
j=2 !
swap!
2 ! 3 ! 4 ! 0! 7 ! 2 ! 3 ! 4 ! 0 ! 7 !
j=0 ! j=1 ! j=1 ! j=2 !
2 ! 3 ! 4 ! 0 ! 7 !
j=3 !
2 ! 3 ! 0 ! 4 ! 7 !
j=4 !
2 ! 3 ! 0 ! 4 ! 7 !
Fin segunda!
iteración !
j=3 !
j=2 !
swap !
2 ! 3 ! 0 ! 4 ! 7 ! 2 ! 3 ! 0 ! 4 ! 7 !
j=0 ! j=1 ! j=1 ! j=2 !
2 ! 0 ! 3 ! 4 ! 7 !
j=3 !
2 ! 0 ! 3 ! 4 ! 7 !
j=4 !
2 ! 0 ! 3 ! 4 ! 7 !
Fin tercera !
iteración !
j=3 !
j=2 !
swap !
2! 0 ! 3! 4 ! 7! 0! 2 ! 3! 4! 7!
j=0 ! j=1 ! j=1 ! j=2 !
0! 2 ! 3! 4! 7!
j=3 !
0! 2 ! 3! 4! 7!
j=4 !
0! 2 ! 3! 4! 7!
Fin cuarta !
iteración !
j=3 !
j=2 !
swap!
0! 2! 3! 4! 7 ! 0! 2! 3! 4! 7!
j=0 ! j=1 ! j=1 ! j=2 !
0! 2! 3! 4! 7!
j=3 !
0! 2! 3! 4! 7!
j=4 !
0! 2! 3! 4! 7!
Fin quinta !
iteración!
j=3 !
j=2 !
#include<stdio.h> 
 
int main(){ 
 int i,j,A[5]={4,2,3,7,0}, tmp; 
 for (i=0; i<5; i++){ 
 for(j=0; j<4; j++){ 
 if (A[j]>A[j+1]){ 
 tmp=A[j]; 
 A[j]=A[j+1]; 
 A[j+1]=tmp; 
 } 
 } 
 } 
 printf("\n arreglo ordenado: "); 
 for( i=0; i<5; i++) 
 printf("%i ",A[i]); 
} 
 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
16 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 BÚSQUEDA XV.
La búsqueda de información engrandes cantidades de datos se ha transformado en un problema 
creciente debido a que cada día más información se genera en Internet. Suponga que Usted desea buscar 
el número 89 en una lista ordenada, ¿cuál sería la estrategia que ocuparía? ¿tiene alguna utilidad que la 
lista se encuentre ordenada? 
 
Como usted no sabe a priori si el número 89 se encuentra en la lista, en la búsqueda lineal debe comenzar 
desde el comienzo de la lista y comparar si el número 89 se encuentra en la caja 1 y así sucesivamente 
hasta encontrarlo. Este caso realizamos ocho comparaciones antes de encontrar el número buscado (ver 
figura). Sin embargo, si usted sabe que la lista está ordenada, puede emplear esta información para 
realizar una búsqueda mucho más eficiente. Este procedimiento se conoce como búsqueda binaria y 
consiste en dividir la lista en dos y revisar en cuál de las dos mitades se encuentra el número. Esto lo hace 
preguntando ¿mi número es mayor (o menor)al valor que está en la mitad de la lista?. En el ejemplo vemos 
que este proceso sólo requiere de tres pasos, es decir, si la lista tiene 8 valores, podemos fácilmente 
verificar que !"#!8=3. ya que 2! = 8. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Ahora podemos generalizar el problema. Suponga que debe buscar un número entre mil millones de 
valores (1.000.000.000). Suponiendo que el número buscado está en la última posición y que cada 
comparación requiere 1 segundo, le tomaría 31 años y 7 meses en encontrar el valor (en el peor caso). En 
cambio si aplicara la estrategia de búsqueda binaria a una lista ordenada, sólo le tomaría 30 segundos 
¿cuál de las opciones emplearía? ¿Por qué 30 segundos? Utilicemos la misma lógica del problema anterior: 
 
!"#!1.000.000.000! ≈ 30 
 
Como puede ver, emplear una estrategia de búsqueda eficiente tiene una gran relevancia al momento de 
diseñar un algoritmo. La búsqueda binaria debe su nombre a que sólo tiene dos caminos a elegir. Existen 
otras técnicas de búsqueda eficiente, las cuales escapan de este resumen. 
!"#
$"#
%"#
1! 3 ! 34! 36! 45! 56! 67! 89!
búsqueda lineal!
búsqueda binaria!
1! 3 ! 34! 36! 45! 56! 67! 89!
%# $# !# &# '# (# )# *#
45! 56! 67! 89!
67! 89!
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
17 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 RECURSIVIDAD XVI.
La recursividad es un concepto elegante y complejo que requiere un conocimiento profundo sobre cómo 
operan los datos en el computador. Sin embargo, es sumamente simple de aplicar en matemáticas. 
Suponga que Usted tiene una lista de números y desea sumarlos. Una forma recursiva de expresar esta 
formulación puede ser desarrollada con las siguientes dos reglas 
Como ejemplo, vamos a suponer que nuestra lista posee los siguientes elementos {1, 2, 3, 4} 
 
sum({1, 2, 3, 4}) 
 = 1 + sum({2, 3, 4}) //aplicamos segunda regla 
 = 1 + 2 + sum({3, 4}) //aplicamos segunda regla 
 = 1 + 2 + 3 + sum({4}) //aplicamos segunda regla 
 = 1 + 2 + 3 + 4 + sum({}) //aplicamos primera regla 
 = 1 + 2 + 3 + 4 + 0 
 = 1 + 2 + 3 + 4 
 = 1 + 2 + 7 
 = 1 + 9 
 = 10 
 
Como puede notar, una vez que aplicamos la primera regla, comienzan a ser empleadas las operaciones 
de suma entre los últimos elementos. Si aplicáramos este método a un lenguaje de programación, 
fácilmente podríamos implementar la sumatoria de n elementos sin emplear ningún ciclo. Veamos a 
continuación una implementación de dicho proceso. Como puede observar, el proceso recursivo comienza 
con el primer llamado a la función "sumatoria(A,4)" (línea 11). Cuando el control del programa ingresa a 
dicha función, compara el valor de la variable p en la condición del if(p==0). Si p no es cero, entonces 
realiza el bloque else. En el return del bloque else notamos que se vuelve a invocar la función sumatoria, 
pero esta vez con el argumento “p-1”. El proceso continúa hasta llegar al último valor de p que ocurre 
cuando p es cero (línea 18). El proceso comienza a retornar en cascada los resultados de la función que lo 
invocó, alcanzando finalmente el primer llamado a la función, es decir, "sumatoria(A,4)". 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
primera regla: suma (lista vacía de números) = 0 
segunda regla: suma (lista de n números) = primer elemento lista + suma (lista n-1 números restantes) 
#include<stdio.h> 
 
int sumatoria(int A[], int largo); 
 
int main(){ 
 
 int A[5]={4,2,3,7,0}; 
 
 //llamado a la funcion 
 int suma= sumatoria(A, 4); 
 printf("sumatoria es: %d\n", suma); 
} 
 
//funcion recursiva 
int sumatoria(int A[], int p){ 
 
 if (p==0){ 
 return(A[0]); 
 } 
 else{ 
 return(A[p]+sumatoria(A,p-1)); 
 } 
} 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
20. 
21. 
22. 
23. 
 
sumatoria (A, 4)!
4 ! 2! 3! 7! 0!
4!p= !
A= !
!" sumatoria (A, 3) !0!
4! 2! 3! 7! 0!
3 !p= !
A= !
!" sumatoria (A, 2) !7!
4! 2! 3! 7! 0!
2 !p= !
A= !
!" sumatoria (A, 1) !3!
4! 2! 3! 7! 0!
1 !p= !
A= !
!"sumatoria (A, 0) !2!
4! 2! 3! 7! 0!
0 !p= !
A= !
sumatoria (A, 0) !
4!
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
18 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 PUNTEROS: MEMORIA DINÁMICA XVII.
Los punteros son simplemente variables que almacenan direcciones de memoria. Al igual como 
declaramos una variable en C (ej. int data), los punteros también son declarados con un tipo de datos. La 
mayor ventaja al trabajar con punteros es la forma en que podemos manipular los datos. Para ello el 
lenguaje cuenta con una nomenclatura específica, que le permite al programador diferenciar entre una 
variable que almacena un valor (número, texto, caracter), o bien una dirección de memoria. 
 
 En este punto, debe preguntarse, ¿qué es una dirección de memoria? El computador dispone de dos 
tipos de memoria, (1) la memoria permanente, los cuales son discos duros, dvd, cd, disquetes, etc. y (2) la 
memoria volátil, siendo la más conocida la memoria RAM. Cuando Usted enciende su computador, 
grandes cantidades de datos (de sus programas y del sistema operativo) se almacenan en esta memoria, 
principalmente por que puede ser hasta 10.000 veces más rápida que la memoria permanente. Pero para 
que el computador administre los datos, el sistema debe asignar una dirección específica, la cual indica al 
programa donde guardar la información. La cantidad de direcciones disponibles depende tanto de la 
memoria RAM instalada, como el tipo de computador que utilice. 
 
En relación a la nomenclatura, existen cuatro puntos fundamentales para el manejo de punteros. 
 
I. Creación. Para indicar que una variable es de tipo puntero, 
simplemente debemos anteponer el símbolo "*" (asterisco) frente 
a la variable. En el código de ejemplo (línea 6), la variable y es de 
tipo puntero a entero. 
 
II. Asignación de memoria. Cuando utilice punteros, debe asignar 
un espacio en memoria (o al menos apuntar a uno), ya que de lo 
contrario, el programa dejará de funcionar. Para asignar un espacio 
de memoria, empleamos el símbolo "&". Cuando se utiliza este 
símbolo frente a una variable, obtenemos su dirección de memoria. 
En este caso, como la variable "y" es de tipo puntero, y además &x retorna la dirección de memoria 
donde se encuentra dicha variable, entonces, podemos realizar la asignación y=&x (línea 7). 
 
III. Asignación por indirección. Si 
deseamos modificar el contenido de lo 
apuntado, podemos emplear la 
expresión *<variable_puntero>= Con 
ello estaremos modificando el contenido 
de lo apuntado (ver ejemplo, línea 8). 
 
IV. Acceso a contenido. Finalmente, si 
deseamos obtener el contenido de lo 
apuntado, anteponemos el símbolo * 
frente a la variable puntero al lado 
derecho de la igualdad (línea 9). Te 
recomendamos que revises con detalle las cuatro reglas anteriores, las cuales se detallan en la figura. En 
la siguiente sección se explicará por qué es necesario su uso, y donde se utiliza regularmente. 
 
#include<stdio.h>int main(){ 
 
 int x=1; 
 int *y; 
 y=&x; 
 *y=3; 
 x=*y+1; 
 printf("%i",x); 
} 
 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
x !
1 !
0x12F !
línea 5!
*y !
0x6C !
línea 6 !
y=&x !
0x12F !
0x6C !
línea 7!
*y=3 !
0x12F !
0x6C!
línea 8!
x !
3!
0x12F !
x= *y+1 !
0x12F !
0x6C!
línea 9!
x !
4!
0x12F !
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
19 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 PASO POR VALOR Y POR REFERENCIA XVIII.
Como hemos visto anteriormente los punteros son un tipo especial de variables que permite almacenar 
una dirección de memoria. Anteriormente, cuando utilizamos las funciones empleamos el concepto de 
paso por valor. Esto quiere decir que los valores “transferidos” a la función son copiados dentro de ella. 
En cambio, el paso por referencia, sólo se “transfiere” una referencia a ellos, o bien una dirección de 
memoria (no se copian los valores). 
 
En el primer ejemplo, analicemos qué ocurre a medida que se ejecuta un programa con el paso por valor 
de una variable a una función. Es importante que el lector observe con calma cómo se almacenan los 
valores en la memoria del computador. ¿Qué ocurre cuando un valor se copia dentro de la función, y 
¿cómo se devuelve (retorna) el resultado a quien lo invocó? Esto se encuentra detallado en el siguiente 
diagrama: 
 
 
 
 
 
 
 
 
 
 
1
tiempo
INICIO
#include <stdio.h>
int main()
{
 int a,b;
}
Bloques de memoria
0
a:1
2
3
b:4
5
2 3
5
FIN
6 7 8
#include <stdio.h>
int main()
{
 int a,b;
 a= 4;
}
Bloques de memoria
0
a:1
2
3
b:4
5
4
int duplicar(int a)
{
}
Bloques de memoria
0
a:1
a:2
3
b:4
5
4
4
int duplicar(int a)
{
 int b;
}
Bloques de memoria
0
a:1
a:2
3
b:4
5
4
4
#include <stdio.h>
int main()
{
}
Bloques de memoria
0
1
2
3
4
5
D
ir
e
c
c
io
n
e
s
int duplicar(int a)
{
 int b;
 b = a*a;
}
Bloques de memoria
0
a:1
a:2
b:3
b:4
5
4
4
16
9
bloque disponible
bloque reservado
bloque no accesible
0
a:1
a:2
b:3
b:4
5
int duplicar(int a)
{
 int b;
 b = a*a
 return(b);
}
Bloques de memoria
4
4
16
Retorno de 
resultado
Bloques de memoria
0
a:1
2
3
b:4
5
4
4
16
#include <stdio.h>
int main()
{
 int a,b;
 a= 4;
 b= duplicar(a);
}
16
Asignación 
del valor
bloques con 
valores no 
accesables
#include <stdio.h>
int main()
{
 int a,b;
 a= 4;
 b= duplicar(a);
}
Bloques de memoria
0
a:1
2
3
b:4
5
4
paso por 
valor
llamado a 
la función
4
nomenclatura
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
20 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
En el paso por referencia, las variables son referenciadas dentro de la función, es decir, sólo “reciben” 
direcciones de memoria. A través de los conceptos de asignación por indirección y acceso a contenido, los 
valores de los punteros pueden ser modificados. 
 
El objetivo de este ejemplo es intercambiar los valores dos valores en las variables enteras a y b. En clases 
hemos analizado que para hacer este proceso dentro de una función, requerimos una tercera variable 
(variable auxiliar). Sin embargo, la única forma para hacerlo dentro de una función es a través de punteros. 
Como vemos en la ilustración, empleamos el acceso al contenido de lo apuntado, como al contenido por 
indirección. El concepto es sumamente simple, pero requiere un gran conocimiento sobre el 
funcionamiento de la memoria del computador. 
 
Probablemente Usted debe preguntarse ¿para qué hacer esto?, si recuerda los algoritmos de 
ordenamiento, estos utilizan arreglos en memoria para almacenar la información. Por ello la única forma 
para modificar dichos valores es a través del uso de las direcciones de memoria. Esto otorga una mayor 
libertad al programador, pero al mismo tiempo, una mayor complejidad. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
tiempo
INICIO
#include <stdio.h>
int main()
{
 int a,b;
}
Bloques de memoria
0
a:1
2
3
b:4
5
2 3
5
FIN
6 7
#include <stdio.h>
int main()
{
 int a,b;
 a= 5;
 b= 8;
}
Bloques de memoria
0
a:1
2
3
b:4
5
5
8
void swap(int *y, int *p)
{
}
Bloques de memoria
0
a:1
y:2
p:3
b:4
5
5
0x1
0x4
8
void swap(int *y, int *p)
{
}
void swap(int *y, int *p)
{
}
void swap(int *y, int *p)
{
}
 
int tmp=*y;
 
int tmp=*y;
*y = *p;
 
int tmp=*y;
*y = *p;
*p = tmp;
#include <stdio.h>
int main()
{
}
Bloques de memoria
0
1
2
3
4
5
D
ir
e
c
c
io
n
e
s
Bloques de memoria
0
a:1
y:2
p:3
b:4
tmp:5 5
5
8
0X1
0x4
Bloques de memoria
0
a:1
y:2
p:3
b:4
tmp:5 5
8
8
0X1
0x4
Bloques de memoria
0
a:1
y:2
p:3
b:4
tmp:5 5
8
5
0X1
0x4
8
bloque disponible
bloque reservado
bloque no accesible
bloque referenciado
Indirección 
de variable Indirección 
de variable
Acceso a 
contenido
acceso a 
bloque por 
referencia
#include <stdio.h>
int main()
{
 int a,b;
 a= 5;
 b= 8;
 swap(&a,&b);
}
Bloques de memoria
0
a:1
2
3
b:4
5
5
8
#include <stdio.h>
int main()
{
 int a,b;
 a= 5;
 b= 8;
 swap(&a,&b);
}
Bloques de memoria
0
a:1
2
3
b:4
5
8
0x1
0x4
5
paso por 
referencia
llamado a 
la función
4
nomenclatura
9
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
21 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
PARTE II 
Una vez estudiado los principales conceptos relacionados con el uso y manejo de datos, arreglos, 
matrices, funciones, ordenamiento, búsqueda en el lenguaje C, comenzaremos el segundo módulo 
estudiando conceptos más avanzados del lenguaje C, tales como manejo de memoria dinámica para 
arreglos, estructuras, estructuras dinámicas, pilas, colas, árboles, funciones de inserción, eliminación, 
búsqueda, etc. 
 
Todo el contenido de este capítulo requiere un conocimiento profundo y completo de los contenidos 
vistos anteriormente. Por ello te recomendamos que si existe algún punto de la materia que está poco 
claro, primero resuelve dicha dificultad antes de proseguir con el estudio de este material. 
 
 
 
 
 
 
 
DISCLAIMER 
Toda la materia vista hasta este punto corresponde al contenido mínimo 
que será evaluado en la primera prueba. Recuerda que este material es un 
resumen, y no constituye en si todo el contenido de la materia vista en 
clases, así como en las tareas. Te recomendamos trabajar en grupos y 
resolver las guías de ejercicios desarrolladas en clases. 
pointer!
data !
pointer !
data !
pointer !
data !
NULL!
COPYRIGHT SCOOT ADAMS. 1995 © United Feature Syndicate, Inc. (NYC) 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
22 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ARITMÉTICA DE PUNTEROS XIX.
Como estudiamos en la sección de arreglos unidimensionales, 
si deseamos recorrer un arreglo, simplemente indicamos su 
posición y modificamos el valor del índice (por ejemplo: 
arr[i], donde i es una variable que comienza en la posición 0 
y termina la posición 3). Note en la figura que cada vez que 
avanzamos en el arreglo, recorremos cuatro direcciones de 
memoria3. Esto se debe a que en realidad requerimos 4 bytes 
por cada valor que almacenamos en una determinada posición; 
ya que nuestro arreglo es de tipo entero (int)4. Internamente, 
el computador ejecuta la siguiente operación aritmética de 
puntero: 
 
 
 
La expresión anterior implica que la variable que 
declaramos como arreglo, en realidad es un puntero 
a su primera posición, y para recorrerlo, solo es 
necesario adicionar una nueva posición. 
Probablemente cause confusión la expresión arr+1 
lo cual puede representar 100+1 que es 101, y no 
104 (con i==1). Internamente, el computador 
reconoce el tipo de datos que utilizamos, y efectúa 
dicha aritmética, a pesar de la inconsistencia 
anterior. Esto ocurre cuando indicamos el arreglo al 
momento de crear la variable. A continuación 
explicamos brevemente el código del ejemplo: 
 
 
Línea 6. Creamos un puntero de tipo entero. 
 
Línea 7. Empleamos la instrucción malloc() para reservar un espacioen memoria. El argumento debe 
indicar el número de bytes que deseamos reservar en memoria. Para indicar con precisión el tamaño en 
bytes que ocupa un entero, empleamos la instrucción sizeof() la cual determina el número de bytes de 
cualquier tipo de datos (inclusive uno definido por nosotros). Como en este caso deseamos reservar 
espacio para 10 valores (un arreglo de largo 10), entonces simplemente multiplicamos 10 por el tamaño en 
bytes del tipo entero. 
 
Línea 10. Tal como indicamos anteriormente, cuando empleamos arreglos, internamente empleamos una 
aritmética de punteros. En este caso, asignamos un número entero a cada posición del arreglo. Compile y 
luego ejecute el código anterior para verificar los valores de salida. 
 
 
 
3 el número de bytes empleados en cada posición del arreglo depende según el tipo de datos, ver pág.1. 
4 más información en https://goo.gl/7vEpfy 
arr[i] ! *(arr+i) "
arr[0] !
100!
arr[1] !
arr[2] !
arr[3] !
arr!
104!
108!
112!
arr+1!
arr+2!
arr+3!
dirección!arreglo !
#include<stdio.h> 
#include<stdlib.h> 
 
int main(){ 
 
 int *arr; 
 arr =(int*)malloc(10*sizeof(int)); 
 
 for (int i=0; i<10; i++){ 
 *(arr+i)=i; 
 
 //forma como arreglo 
 for (int i=0; i<10; i++){ 
 printf("%i\n", arr[i]); 
 } 
} 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
23 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ESTRUCTURAS DE DATOS XX.
En los ejemplos anteriores hemos creado y manipulado arreglos y matrices los cuales almacenan 
información en cada posición de dicha estructura. Por lo general, esta información ha estado compuesta 
por números; pudiendo ser cualquier tipo de datos. Ahora bien, suponga que usted desea almacenar 
información compuesta por valores combinados, tales como número de teléfono, nombres, apellidos, etc. 
de tal forma de crear una agenda. El lenguaje C 
permite la creación de este tipo de variables a través 
de estructuras struct. 
 
La estructura struct permite agrupar variables de 
distinto tipo en una única variable, la cual puede 
almacenar toda la información. Como puede observar, 
es extremadamente útil cuando en nuestro problema 
deseamos almacenar información. Esto lo podemos 
hacer empleando esta agrupación. 
 
 
Una vez que la estructura ha sido declarada 
(como en el ejemplo) es necesario indicar una 
variable para su utilización. Esto se realiza 
empleando la siguiente nomenclatura: 
 
 
Usted puede definir todas las variables que 
desee de la misma estructura (en este caso 
solo empleamos una variable: mi_agenda). 
Para acceder a los campos de cada variable, 
solo empleamos el nombre de la variable, el 
operador punto, y el nombre de la variable 
declarado dentro de la estructura del siguiente 
modo. 
 
 
 
En el ejemplo del programa, definimos una estructura antes de la función 
MAIN, y luego definimos una variable (mi_agenda) del tipo estructura 
(línea 13) en la función MAIN. Usted puede declarar la variable dentro o 
fuera de una función según dependa el ámbito (scope) de uso. En la línea 
16 hasta la 18 llenamos cada uno de los campos definidos en la estructura 
a través de una asignación, del mismo modo como empleamos la 
asignación de una variable. En realidad la estructura es una variable la cual 
tiene “campos” los cuales son accesados por el operador punto aplicado 
sobre la variable que almacena la estructura (líneas 16-18, y 20-22). Otra 
propiedad de las estructuras es que la información de una estructura 
puede ser “transferida” a otra con todos sus campos empleando 
simplemente una asignación (ver código a la izquierda). 
struct ! nombre_estructra !
}! ;!
{!
tipo_1 !variable_1;!
tipo_2 !variable_2;!
!
tipo_n !variable_n;!
nombre de la 
estructura 
Variables de 
la estructura 
#include<stdio.h> 
#include<cs50.h> 
 
struct agenda{ 
 
 string nombre; 
 string correo; 
 int id; 
}; //<<< note el punto y coma!! 
 
int main(){ 
 
 struct agenda mi_agenda; 
 
 //agregamos información a la estructura 
 mi_agenda.nombre = GetString(); 
 mi_agenda.correo = GetString(); 
 mi_agenda.id = 1; 
 
 printf("Id: \t%i\n", mi_agenda.id); 
 printf("Nombre: \t%s\n", mi_agenda.nombre); 
 printf("Correo: \t%s\n", mi_agenda.correo); 
} 
 
struct! nombre_estructra ! nombre_variable!
nombre_variable ! variable!!"
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
20. 
21. 
22. 
23. 
 
#include<stdio.h> 
#include<cs50.h> 
 
struct agenda{ 
 string nombre; 
}; 
 
 
int main(){ 
 struct agenda A, B; 
 A.nombre= GetString(); 
 
 //copiamos A en B 
 B = A; 
} 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
24 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ARREGLO DE ESTRUCTURAS: LISTA DENSA XXI.
Antes de comenzar a revisar las estructuras dinámicas, introduciremos brevemente el concepto de 
redefinición de tipos con el comando typedef. Typedef permite definir distintos tipos de datos complejos a 
otros más simples empleando un alias. Typedef no crea tipos de datos, sólo define un nuevo identificador. 
 
En el ejemplo, si deseamos declarar una 
variable del tipo estructura, debemos escribir 
struct nombre_estructura variable. Lo 
anterior puede ser reducido al emplear 
typedef, ya que al declarar un alias, creamos 
un nuevo identificador el cual agrupa la 
declaración anterior; en este caso como 
tagenda. 
 
Una de las ventajas de las estructuras, 
es que podemos crear un arreglos de 
estructuras, o bien emplear punteros 
a ellas. Revisaremos a continuación la 
creación de arreglos de estructuras, al 
igual que cuando creamos arreglos 
unidimensionales. 
 
Líneas 5-8. Creamos un estructura 
agenda, sin embargo empleamos una 
redifinición de tipos. Lo anterior se 
realiza anteponiendo el comando 
typedef antes de la estructura, y 
luego indicando un nuevo alias para 
dicha redefinición. Observe que al 
término de la estructura hay un punto-
y-coma. 
 
Línea 11. Una vez que hemos creado el identificador tagenda (línea 8) creamos una variable arreglo fichas 
con la misma nomenclatura de un arreglo ficha[3]. Lo anterior reserva espacio en memoria para tres 
estructuras del tipo tagenda. El espacio definido para esto considera el espacio del número entero y el tipo 
string. 
 
Línea 15-16. A diferencia del formato de ingreso visto anteriormente, ahora vamos a ingresar datos en un 
arreglo de estructuras. Por ello debemos indicar la posición donde ingresar los datos. Lo anterior se realiza 
del mismo modo que operábamos los índices de arreglos, pero ahora indicando el campo que debemos 
completar. Por ejemplo ficha[i].id = i; significa que ingresamos un número entero en la ficha i-ésima 
en el campo id. Operamos del mismo modo para emplear el campo nombre. 
 
Línea 21. Del mismo modo como ingresamos los datos, podemos emplear la misma estructura para acceder 
al contenido en la estructura. Esto lo hacemos mostrando el contenido de la i-ésima ficha como ficha[i].id. 
 
#include<stdio.h> 
#include<cs50.h> 
 
//estructura con redifinición de tipos 
typedef struct agenda{ 
 int id; 
 string nombre; 
}tagenda; //no olvide cerrar con punto-y-coma 
 
int main(){ 
 tagenda ficha[5]; //empleamos el alias tagenda 
 
 //ingreso de datos 
 for (int i=0; i<5; i++){ 
 ficha[i].id = i; 
 ficha[i].nombre = GetString(); 
 } 
 
 //despliegue de datos 
 for (int i=0; i<5; i++){ 
 printf("%i\t%s\n", ficha[i].id, ficha[i].nombre); 
 } 
} 
typedef ! tipo_de_datos_actual! nuevo tipo de datos!
struct ! nombre_estructra!
typedef ! tagenda !struct ! agenda !
; !
; !
Ejemplo: 
1.
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
20. 
21. 
22. 
23. 
 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
25 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 RETORNO DE FUNCIONESCON TIPOS DE DATOS XXII.
Anteriormente analizamos el ingreso de datos directamente desde el programa principal a través de una 
estructura. Como hemos estudiado en este resumen, siempre debemos diseñar programas que utilicen 
funciones, de esta forma será más simple el proceso de revisión, simplificación y resolución de cualquier 
problema bajo el paradigma “divide para conquistar”. Sin embargo, ¿cómo podemos retornar un tipo de 
dato más complejo de los ya conocidos int, float, char, void? 
 
Para realizar un retorno de una estructura de datos, nuestra función simplemente debe emplear el mismo 
tipo de datos definido para la estructura. En el programa sin función, los datos son ingresados a la variable 
ficha en forma directa dentro de la función main(). ¿Cómo podemos realizar este proceso ingresando los 
datos a una función, y que ella retorne una estructura del mismo tipo que la ficha? En este caso vamos a 
crear una función, cuyos parámetros de entrada sean un entero y un string. 
 
Una vez que los datos son “transferidos” a la función, vamos a retornar una estructura de datos. Para ello el 
tipo de datos de la función debe corresponde con el tipo de datos que retorna (con la instrucción return). 
En el ejemplo, la función es de tipo tagenda, y lo retornado, debe ser de tipo tagenda. De esta forma, 
cuando la función retorna el resultado, la variable que recibe dicho valor debe poseer el mismo tipo de 
datos. En la versión con función, la variable que recibe el retorno de la función es la variable m (línea 22, 
versión con función). 
 
 
 
 
 
 
 
tagenda ingreso(int i, string data){ 
 
 tagenda ficha; //creamos una ficha 
 ficha.id = i; 
 ficha.nombre = data; 
 return ficha; 
} 
#include<stdio.h> 
#include<cs50.h> 
 
//estructura 
typedef struct agenda{ 
 int id; 
 string nombre; 
}tagenda; 
 
 
 
 
 
 
 
 
 
 
//programa principal 
int main(){ 
 
 tagenda ficha; //creamos una ficha 
 ficha.id = 3; 
 ficha.nombre = GetString(); 
 printf("%s\n", ficha.nombre); 
} 
#include<stdio.h> 
#include<cs50.h> 
 
//estructura 
typedef struct agenda{ 
 int id; 
 string nombre; 
}tagenda; 
 
//funcion 
tagenda ingreso(int i, string data){ 
 
 tagenda ficha; //creamos una ficha 
 ficha.id = i; 
 ficha.nombre = data; 
 return ficha; 
} 
 
//programa principal 
int main(){ 
 
 tagenda m; 
 m = ingreso(3, GetString() ); 
 printf("%s\n", m.nombre); 
 
} 
Versión sin función Versión con función 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
20. 
21. 
22. 
23. 
24. 
25. 
26. 
27. 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
20. 
21. 
22. 
23. 
24. 
25. 
26. 
27. 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
26 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ESTRUCTURAS DINÁMICAS XXIII.
A medida que nuestros programas se hacen más complejos y flexibles, será necesario la utilización de la 
memoria dinámica en extenso. En este punto Usted debe haber comprendido en profundidad todos los 
conceptos sobre punteros, funciones, paso por referencia, estructuras y manejo de memoria. A diferencia 
de la forma anterior, al emplear memoria dinámica podemos agregar, eliminar, buscar, recorrer tantas 
fichas como queramos. Esto permite que el sistema pueda incorporar nueva información. 
 
En una primera versión, crearemos una sola ficha 
sobre la cual reservaremos memoria, y haremos 
ingreso de los datos solicitados dentro de la 
función main. Para ello declaramos una ficha del 
tipo puntero (línea 11), y luego reservamos 
memoria según su tipo de datos (que en este 
caso es tagenda). En las líneas 14 en adelante 
observamos un nuevo modo para direccionar el 
campo de la estructura. Esto se debe a que el 
operador “.” punto tiene prioridad sobre el 
puntero, y por ello la expresión *ficha.id es 
ilegal. Por ello, empleamos el operador “->”, el 
cual se utiliza únicamente cuando las estructuras 
utilizan memoria dinámica. 
 
En un segundo ejemplo, creamos la función 
add_ficha que recibe un número y un 
nombre como parámetros de entrada, y 
luego ingresa los valores dentro de la lista. 
Dentro de la función (líneas 13–20) se realiza 
la operatoria de crear una ficha reservando 
un espacio de memoria y la asignación de 
datos. En la línea 21 asignamos el puntero 
de una_ficha a la lista vacía “lista”. 
 
En la función principal, los datos se ingresan 
a través de la lectura de datos por teclado 
(línea 29). Una vez que se ejecuta la función, 
y se asigna la ficha a la lista (línea 21), ésta se 
asigna al puntero a la primera ficha 
ingresada. Por esto podemos desplegar la 
información de la ficha con los datos 
ingresados a través de la función 
“add_ficha”. Sin embargo, este código sólo 
permite agregar una ficha al listado. Para 
agregar más fichas al conjunto debemos 
modificar la estructura para que cada ficha 
almacene un puntero (dirección) a la 
siguiente ficha. 
#include<stdio.h> 
#include<cs50.h> 
 
typedef struct agenda{ 
 int id; 
 string nombre; 
}tagenda; 
 
int main(){ 
 
 tagenda *ficha; 
 ficha = (tagenda*)malloc(sizeof(tagenda)); 
 
 //ingreso de datos 
 ficha->id = 1; 
 ficha->nombre =GetString(); 
 
 //despliegue de datos 
 printf("%i, \t%s\n", ficha->id, ficha->nombre); 
} 
1.
2. 
3. 
4. 
5. 
6.
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
20. 
 
#include<stdio.h> 
#include<cs50.h> 
 
// ESTRUCTURA // 
typedef struct agenda{ 
 int id; 
 string nombre; 
}tagenda; 
 
tagenda *lista=NULL; //variable global 
 
// FUNCION // 
void add_ficha(int i, string nombre){ 
 
 tagenda *una_ficha; 
 una_ficha = (tagenda*)malloc(sizeof(tagenda)); 
 
 //asignación de datos 
 una_ficha->id = i; 
 una_ficha->nombre = nombre; 
 lista = una_ficha; 
} 
 
// MAIN // 
int main(){ 
 printf("Ingresa un número y tu nombre: "); 
 
 //llamado a la funcion 
 add_ficha(GetInt(), GetString()); 
 printf("%i, \t%s\n", lista->id, lista->nombre); 
} 
1.
2. 
3. 
4. 
5. 
6.
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
20. 
21. 
22. 
23. 
24. 
25. 
26. 
27. 
28. 
29. 
30. 
31. 
 
 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
27 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 LISTAS ENLAZADAS XXIV.
Empleando los conceptos vistos anteriormente, ahora vamos a construir una lista generada en forma 
dinámica (lista, declarada como variable global), lo cual nos permite realizar operaciones más complejas 
sobre ella. Las operaciones más comunes son {Buscar, Insertar, Eliminar, Buscar_k_esimo, Visualizar}. A 
diferencia de la estructura anterior, ahora creamos un campo en la estructura que permite almacenar una 
dirección de memoria (línea 8, struct agenda *next) del mismo tipo que la estructura. Este punto es clave 
ya que permite almacenar una dirección de memoria del siguiente nodo en la lista dentro de la misma 
estructura. En el siguiente ejemplo, estudiaremos brevemente el caso de insersión de un nodo. 
 
Cuando comienza la ejecución del 
programa en la función main, se realiza un 
llamado a la función add_nodo(). Dentro 
de ella se solicita al computador reservar un 
espacio de memoria para almacenar los 
campos de la estructura (id y nombre). Si la 
lista está vacía, -lo cual es verdadero 
siempre en el primer caso-, entonces la 
primera ficha se asigna a la lista (lista = 
ficha, línea 25). En caso contrario, vamos a 
recorrer la lista hasta que el último nodo 
sea vacío. En ese punto vamos a insertar la 
nueva ficha (líneas 29-40). Este proceso se 
realiza avanzando en la lista hasta que el 
siguiente nodo sea nulo (el cual es el último 
nodo, línea 32,) 
 
Estando en dicho punto insertamos el 
nuevo nodo (línea 36). Lo anterior ocurre 
cada vez que llamamos a la función 
add_nodo() en la línea 51, dentro de la 
funciónmain(). El resultado del proceso 
anterior se representa gráficamente como 
el ingreso sucesivo de fichas a la lista 
global. En nuestra aplicación, cada vez que 
se inserta un nuevo nodo, este se inserta al 
final de la lista. Note que el último nodo 
siempre apunta a NULL de tal forma que 
siempre podamos determinar cuando 
terminar de recorrer la lista. 
 
A continuación revisaremos en detalle el 
proceso de inserción. Suponga que ya 
posee una lista con datos previamente 
ingresados (con el mismo programa), y 
desea ingresar la ficha con los siguientes 
datos {id=3, nombre=”marta”} a la lista. 
#include<stdio.h> 
#include<cs50.h> 
 
// ESTRUCTURA // 
typedef struct agenda{ 
 int id; 
 string nombre; 
 struct agenda *next; 
}tagenda; 
 
tagenda *lista=NULL; //variable global 
 
// FUNCION ADD_NODO// 
void add_nodo(int i, string nombre){ 
 
 //creamos un nodo de la lista 
 tagenda *ficha= malloc(sizeof(tagenda)); 
 ficha->id = i; 
 ficha->nombre = nombre; 
 ficha->next=NULL; 
 
 //si es el primero de la lista 
 if(lista==NULL){ 
 printf("insertando %i\n", i); 
 lista = ficha; 
 } 
 else{ 
 //vamos a insertar un nodo al final de la lista 
 tagenda *nodo_virtual= lista; 
 
 while(true){ 
 if (nodo_virtual->next!=NULL){ 
 nodo_virtual= nodo_virtual->next; 
 } 
 else{ 
 nodo_virtual->next = ficha; 
 printf("insertando %i\n", i); 
 break; 
 } 
 } 
 } 
} 
 
// MAIN // 
int main(){ 
 
 for(int i=0; i<3; i++){ 
 //llamado a la funcion 
 printf("Ingresa tu nombre: "); 
 add_nodo(i, GetString()); 
 } 
 //vamos a recorrer la lista FIFO 
 printf("\nListado: \n"); 
 tagenda* virtual_ptr = lista; 
 
 while (virtual_ptr != NULL){ 
 printf("%i\t%s\n",virtual_ptr->id, virtual_ptr->nombre); 
 //avazamos al siguiente nodo 
 virtual_ptr = virtual_ptr->next; 
 } 
} 
1. 
2. 
3. 
4. 
5. 
6. 
7. 
8. 
9. 
10. 
11. 
12. 
13. 
14. 
15. 
16. 
17. 
18. 
19. 
20. 
21. 
22. 
23. 
24. 
25. 
26. 
27. 
28. 
29. 
30. 
31. 
32. 
33. 
34. 
35. 
36. 
37. 
38. 
39. 
40. 
41. 
42. 
43. 
44. 
45. 
46. 
47. 
48. 
49. 
50. 
51. 
52. 
53. 
54. 
55. 
56. 
57. 
58. 
59. 
60. 
61. 
 
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
28 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
En primera instancia, los datos son ingresados dentro de la función main (línea 50), y luego son transferidos a 
la función add_nodo a través de un paso por valor. Cuando el control del programa ingresa a la función, 
primero reserva espacio para un nodo del tamaño de la estructura (línea 17), y luego ingresa los datos a 
dicho nodo. Note que este nodo apunta a NULL o vacío. 
 
El segundo paso consiste en buscar la dirección de memoria del nodo que está al final de la lista, de tal 
forma que podamos incorporar un nuevo nodo en dicha posición. En este ejemplo, este proceso se realiza a 
través de un ciclo while evaluando la condición (nodo_virtual->next!=NULL). Como no sabemos a priori 
cuantos nodos tenemos en la lista., debemos avanzar entre los nodos, simplemente asignando a la dirección 
actual, el nodo con la siguiente dirección: 
 
nodo_virtual= nodo_virtual->next; 
 
El tercer paso permite que el nodo sea insertado en la posición final de la lista. Este proceso se realiza una 
vez que en el paso anterior el puntero se encuentre justo en la última posición de la lista. Estando en dicho 
punto, entonces asignamos como última posición el nuevo nodo creado en el primer paso: 
 
nodo_virtual->next = ficha; 
 
Una vez asignado el nuevo nodo, terminamos la ejecución del ciclo con la instrucción break. 
 
 
NULL!
NULL!
NULL !
2!
Juan !
!"#$%
0x4C !
1 !
Pedro!
!"#&%
NULL !
3 !
Marta !
!"'(%
*nodo_virtual!
Nuevo nodo 
a insertar 
2 !
1 !
3 !
0x3F!
2!
Juan !
!"#$%
0x4C !
1 !
Pedro!
!"#&%
NULL !
NULL !
3 !
Marta!
!"'(%
Nodo 
buscado 
una vez encontrado este nodo, asignamos la 
dirección de memoria del nuevo nodo 
tagenda *nodo_virtual= lista; !
!
while(true){!
 if (nodo_virtual->next!=NULL){!
" nodo_virtual= nodo_virtual->next;!
 } !
!
!
!
} !
!
void add_nodo(int i, string nombre){ !
 //creamos un nodo de la lista!
 tagenda *ficha= malloc(sizeof(tagenda));!
!
 ficha->id = i; !
 ficha->nombre = nombre; !
 ficha->next=NULL;!
while(true){!
 if (nodo_virtual->next!=NULL){!
" nodo_virtual= nodo_virtual->next;!
 } !
 else{ !
 nodo_virtual->next = ficha; !
"printf("insertando %i\n", i);!
"break;!
 } !
} !
*ficha!
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
29 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 PILAS Y COLAS XXV.
Cómo vimos anteriormente, el manejo de memoria dinámica permite crear y manipular estructuras de datos 
más complejas y ricas en información. Otras estructuras muy relevantes en ciencias de la computación son 
las pilas y colas. Estas estructuras se diferencian de las listas generales en el modo de operación y manejo 
de los datos. 
 
Pilas (Stacks): son listas cuyo orden se define como el primero en entrar es el último en salir. Imagine una 
pila de libros donde el primer libro es aquél que está en la primera posición. Para obtener el primer libro, es 
necesario remover todos los anteriores uno a uno. Este modelo se conoce como LIFO (Last-In-Last-Out). Las 
operaciones básicas de las pilas son: {apilar, desapilar, crear_pila} 
 
Colas (Queues): son listas cuyo orden se define como el primero en entrar es el primero en salir. Por 
ejemplo, una cola de supermercado, donde el primer cliente en llegar a la caja es el primero en salir. Este 
modelo de funcionamiento se conoce como FIFO (First-In-Fist-Out). Las operaciones básicas de las colas 
son: {insertar, quitarPrimero, primero} 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
item 2!
item 1!
item 3!
item 4 ! queue ! dequeue! item 3 !
Modelo de Pila!
item 2!
item 3!
item 4! queue!
item 1!
dequeue!
Modelo de Cola!
item 1!
 
 
PROGRAMACIÓN. 2016. MIGUEL CARRASCO 
 
30 
RESUMEN 
PROGRAMACIÓN 
18-04-2016 
 ÁRBOLES BINARIOS XXVI.
Una vez que hayas dominado los misterios de las listas y sus aplicaciones (pilas y colas), revisaremos una de 
las estructuras más relevantes en ciencias de la computación: los árboles binarios. Un árbol binario es una 
estructura de datos donde cada nodo posee dos hijos (izquierdo y derecho) que son punteros y un número 
que permite indicar su valor. A medida que el árbol se va poblando de información, los datos se ingresan en 
forma ordenada de tal forma que la lectura y búsqueda de información se realiza en forma eficiente. Esta 
estructura puede ser mejorada aún más de tal forma que los nodos se ingresen en forma balanceada; 
técnica conocida como binary heap. Gracias a este tipo de estructuras, es posible comprimir la información, 
siendo el método más conocido el árbol de Huffman (Huffman Tree). Las aplicaciones pueden ir desde la 
compresión de la música en formato MP3, compresión de archivos (ZIP, RAR), telecomunicaciones, 
imágenes satelitales, por nombrar algunas. 
 
Primero comenzaremos redefiniendo la estructura de 
datos de tal forma que en cada nodo podamos almacenar 
información de dos punteros; nodo izquierdo y nodo 
derecho, además del número (u letra) que queramos 
almacenar dentro de la estructura. Este valor es muy 
importante ya que nos servirá para ordenar la información 
en forma estructurada. 
 
Antes de explicar el código, primero analicemos el proceso de creación de un árbol empleando la siguiente 
secuencia de números {-5, 2, -6, 0, 7}, y cuyos datos se ingresan el orden como se lee el arreglo. 
 
Explicación Nodo Resultado de árbol 
Primero creamos un nodo, e insertamos el 
primer valor de nuestra secuencia. 
Resultado, creamos el primer nodo del 
árbol. 
 
Nos movemos al nodo del árbol {-5}. 
Luego preguntamos {2} es mayor al nodo 
{-5}. La respuesta es verdadera, entonces 
creamos un nodo y lo asignamos la 
dirección de memoria