SlideShare una empresa de Scribd logo
1 de 91
Programación II
Martin Mendieta González Febrero 2024
2. Apuntadores y memoria dinámica.
Objetivo: Al término de este apartado el estudiante habrá
comprendido qué son los apuntadores, las operaciones que se
pueden realizar con ellos y su uso en los arreglos.
Los participantes explorarán estrategias para la implementación de
algoritmos que manipulen la memoria de manera dinámica, a través de
apuntadores bajo la sintaxis de lenguaje C, así como para la
programación de aplicaciones orientadas a la eficiencia del
almacenamiento interno de la computadora.
2. Apuntadores y memoria dinámica.
2.1 Apuntadores (punteros)
2.1.1 Definición
2.1.2 Operaciones
2.1.3 Apuntadores y arreglos
2.1.4 Arreglos de apuntadores
2.1.5 Apuntadores de tipo estructura
2.2 Memoria dinámica
2.2.1 Funciones para asignación de memoria
2.2.2 Reasignación de memoria
2.2.3 Arreglos dinámicos de una y dos dimensiones
“Comprender y usar correctamente los apuntadores es con
seguridad lo más complicado del lenguaje C, pero también se
trata de un mecanismo muy poderoso. Tan poderoso que un
simple puntero descontrolado puede provocar que el
programa se cuelgue irremediablemente o incluso que falle
todo el sistema.”
A.M. VOZMEDIANO
Apuntadores.
Definición:
En el lenguaje de programación C, un apuntador es una variable cuyo
contenido no es un dato, sino la dirección de memoria donde esta
almacenado un dato.
En lugar de contener directamente el valor de los datos, un apuntador
"apunta" a la ubicación de memoria donde se almacenan los datos en
el sistema.
Apuntador Variable X
5
Dentro de la memoria del ordenador, cada dato
almacenado ocupa una o más celdas contiguas de memoria.
El número de celdas de memoria requeridas para almacenar
un dato depende de su tipo.
Por ejemplo, un dato de tipo entero (int) puede ocupar 32
bits (es decir 4 bytes), mientras que un dato de tipo carácter
ocupa 8 bits ( es decir 1 byte).
La computadora almacena el dato en un espacio de la
memoria en una determinada dirección.
Un puntero no es más que una variable cuyo contenido, no
es un dato, sino la dirección de memoria donde está
almacenado un dato.
Ejemplo:
Imaginemos que miLetra es una variable de tipo
carácter y que, por tanto, el sistema le asigna un
byte para ser almacenado.
char miLetra;
miLetra=´A´;
Al ejecutar este código, el sistema asigna una celda
de memoria para el dato.
Supongamos que la celda asignada tiene la
dirección 1200.
Al hacer la asignación miLetra=´A´, el sistema
almacena en la celda 1200 el valor 65, que es el
código ASCII de la letra ´A´.
1197
1198
1199
1200 65
….
Un apuntador sería en este ejemplo
una variable que contendría el valor
1200, es decir la dirección donde se
aloja la variable miLetra
Dirección de
memoria Contenido
Antes de declarar un
apuntador veamos el operador
&
El operador &, u operador de
dirección, se utiliza para
obtener la dirección de
memoria de una variable.
// Uso del operador de dirección para
obtener la dirección de memoria
direccionDeLaVariable = &variable;
Del ejemplo anterior
direccionDeLaVariable = &miLetra;
Daría como resultado 1200 para la variable
direccionDeLaVariable
char a;
scanf ("%c",&a);
Por otro lado, se tiene el operador * u operador de
indirección
Se utiliza para acceder al valor almacenado en la
dirección de memoria a la que apunta un puntero
La declaración de un apuntador se realiza utilizando el
operador de asterisco *.
char *V
Declara que V es una variable apuntador a un Char.
Los valores permitidos siempre incluyen la dirección
especial 0 y un conjunto de enteros positivos que se
interpretan como direcciones en el sistema.
Usando el ejemplo
anterior podemos hacer
que el apuntador v,
“apunte” a la variable
miLetra
v = &miLetra;
miletra
V
Casos especiales
A = 0;
A = NULL; /* equivale a A = 0 */
A = &i; /* A esta refiriéndose a i, conteniendo la dirección de i */
En este caso nótese que cuando un apuntador en C apunta a la dirección de
memoria 0, se dice que el apuntador contiene el valor nulo. El valor nulo en C se
representa comúnmente como NULL.
Un apuntador nulo no apunta a ninguna ubicación válida de memoria.
#include <stdio.h>
int main() {
// Definir una variable de tipo char
char miLetra;
// Asignar la letra 'A' a la variable
miLetra = 'A’;
// Declarar un puntero que apunte a la variable miLetra
char *V = &miLetra;
// Imprimir el valor de la variable
printf("El valor de la variable es: %cn", miLetra);
// Imprimir el valor de la variable usando el puntero
printf("El valor de la variable a través del puntero es: %cn", *V);
return 0; }
¿Cuál es la diferencia
entre la primera
instrucción printf y la
segunda?
¿cuál es la diferencia
entre el operador & y el
operador *?
Recapitulando, los apuntadores (punteros) son variables que
almacenan direcciones de memoria.
Permiten manipular y acceder directamente a la memoria, lo que
brinda mayor flexibilidad y eficiencia en algunas operaciones.
Supongamos:
int x=15, y, *apuntador;
Entonces las dos proposiciones:
apuntador = &x;
y = *apuntador; /* le estamos asignando 15 a y */
Son equivalentes a:
y = *&x; /* precedencia de operadores */
que a su vez equivale a:
y = x;
El operador * es un operador unario, llamado operador indirección, que opera sólo
sobre una variable puntero. Las variables puntero pueden apuntar a direcciones
donde se almacene cualquier tipo de dato: enteros, flotantes, caracteres, cadenas,
arreglos, estructuras, etc..
Ejercicio 1.
#include<stdio.h>
int main(){
char *pt1;
char var_c='W';
pt1 = &var_c;
printf("nEl valor de contenido a donde apunta pt1: %c", *pt1);
return 0;
}
Ejercicio 2.
#include<stdio.h>
int main(){
char *pt1;
char var_c='W';
pt1 = &var_c;
printf("nEl valor de contenido a donde apunta pt1: %c", *pt1);
*pt1= 'A';
printf("nEl valor de contenido a donde apunta pt1 despues de la nueva asignacion: %c", *pt1);
return 0;
}
Ejercicio 3.
#include<stdio.h>
int main()
{
int *pt2;
int b=100;
pt2 = &b;
printf("nEl valor de contenido a donde apunta pt2: %d", *pt2);
return 0;
}
Ejercicio 4.
#include<stdio.h>
int main(){
int *pt2;
int b=100;
pt2 = &b;
printf("nEl valor de contenido a donde apunta *pt2: %d", *pt2);
printf("nLa direccion en donde se almacena el puntero &pt2: %d", &pt2);
printf("nLo que tiene guardado el puntero pt2: %d", pt2);
printf("nLa dirección de la variable b es: %d", &b);
return 0;
}
Ejercicio 5.
#include<stdio.h>
int main(){
int *pt2;
int b=100;
pt2 = &b;
printf("nEl valor de contenido a donde apunta *pt2: %d", *pt2);
printf("nLa direccion en donde se almacena el puntero &pt2: %d", &pt2);
printf("nLo que tiene guardado el puntero pt2: %d", pt2);
printf("nLa dirección de la variable b es: %d", &b);
int *pt1;
pt1=pt2;
printf("nEl valor de contenido a donde apunta *pt1: %d", *pt1);
return 0;
}
Ejercicio 6.
#include<stdio.h>
int main(){
float *ptr;
float x;
ptr=&x;
*ptr=9.5;
printf("nContenido del puntero ptr: %.2f", *ptr);
return 0;
}
Ejercicio 7.
#include<stdio.h>
int main(){
float v1,*qt1;
int v2, *qt2;
char v3, *qt3;
qt1=&v1;
qt2=&v2;
qt3=&v3;
printf("nDar un valor decimal: ");
scanf("%f", &(*qt1));
printf("nDar un valor entero: ");
scanf("%d", &(*qt2));
printf("nDar un caracter: ");
fflush(stdin);
scanf("%c", &(*qt3));
printf("nEl contenido de lo que apunta *qt1 es %.3f", *qt1);
printf("nEl contenido de lo que apunta *qt2 es %d", *qt2);
printf("nEl contenido de lo que apunta *qt3 es %c", *qt3);
return 0; }
Ejercicio 8. Intercambio de valores con punteros
#include<stdio.h>
int main(){
float w1,w2,w3;
float *q1,*q2,*aux;
// crear ligas
q1=&w1;
q2=&w2;
aux=&w3;
//asignar valores
*q1=9.5;
*q2=20.0;
//Imprimir
printf("n q1->|%.1f|",*q1);
printf("n q2->|%.1f|",*q2);
//intercambio
*aux=*q1;
*q1=*q2;
*q2=*aux;
//Imprimir
printf("n Valores intercambiados con punteros");
printf("n q1->|%.1f|",*q1);
printf("n q2->|%.1f|",*q2);
return 0; }
Programar los siguientes escenarios:
Escenario 1)
Escenario 2)
w
3.1436
p
q
s
r
100
9.5
Aritmética de punteros (operaciones)
Con las variables de tipo apuntador sólo se pueden hacer dos
operaciones aritméticas, sumar o restar a aun apuntador u número
entero, y restar dos punteros.
Las operaciones de suma y resta en punteros en C están relacionadas
con la manipulación de direcciones de memoria.
Se utilizan para desplazarse a través de un array (arreglo) o bloque de
memoria.
Es importante destacar que estas operaciones están limitadas a
punteros que apuntan a elementos de un mismo array, ya que se basan
en el tamaño del tipo de datos al que apunta el puntero.
Aritmética de punteros (sumar y restar)
Con las variables de tipo apuntador sólo se pueden hacer dos
operaciones aritméticas, sumar o restar a aun apuntador un número
entero.
El resultado de estas operaciones no es tan trivial como imaginamos
que ocurre con las sumas aritméticas a las que estamos acostumbrados.
Del ejemplo donde tenemos un apuntador, apuntando a la dirección
1200 si le sumamos un 1 a este apuntador, el resultado puede ser
1201… pero también puede ser 1202 ó 1204. Esto se debe a que el
resultado depende del tipo de dato al que apunte nuestro apuntador.
miletra
V
W
1200
V++
miletra
V
W
1201
Aritmética de punteros (sumar y restar)
La suma de un puntero con un valor
entero se utiliza para desplazarse a través
de un array o bloque de memoria.
Cuando sumas un valor entero n a un
puntero, el puntero se desplaza n *
sizeof(tipo_dato) bytes hacia adelante en
memoria, donde tipo_dato es el tipo de
dato al que apunta el puntero.
Es decir, dado que lo que se suma es
un número a la dirección donde
empieza un dato, el resultado
depende del tamaño del dato.
En el caso de un entero:
entero
V
100
1200
V++
entero
V
100
1204
Por ejemplo:
char *p;
p=p+5;
Supongamos que p apunta a la dirección de memoria 800. Como cada carácter
ocupa 1 byte, al incremento en 5 unidades, apuntará a la dirección 805.
int *p;
p=p+5;
En este caso bajo los mismos supuestos, al incremento en 5 unidades pasará a
apuntar a la dirección 820 ( suponiendo que cada entero ocupe 4 bytes)
A los punteros se les puede aplicar las operaciones de incremento (++) y
decremento (--), considerando el efecto que aplique por el tipo de dato del
puntero.
int *p;
p++;
p
W
800
805
p
1865
800
820
Ejercicio 10. Suma aritmética de punteros
#include<stdio.h>
#include<conio.h>
int main(){
char *ptr;
char x='Q';
;
ptr=&x;
printf("nDireccion del puntero ptr: %d", &ptr);
printf("nContenido a donde apunta el puntero ptr: %c", *ptr);
printf("nDireccion almacenada en el puntero ptr: %d", ptr);
printf("nDirección de x: %d", &x);
printf("nContenido de x: %c", x);
ptr++;
printf("nnDireccion del puntero ptr: %d", &ptr);
printf("nContenido a donde apunta el puntero ptr: %c", *ptr);
printf("nDireccion almacenada en el puntero ptr: %d", ptr);
printf("nDirección de x: %d", &x);
printf("nContenido de x: %c", x);
x
ptr
Q
x
ptr
Q
ptr--;
printf("nnDireccion del puntero ptr: %d", &ptr);
printf("nContenido a donde apunta el puntero ptr: %c", *ptr);
printf("nDireccion almacenada en el puntero ptr: %d", ptr);
printf("nDirección de x: %d", &x);
printf("nContenido de x: %c", x);
ptr+=2;
printf("nnDireccion del puntero ptr: %d", &ptr);
printf("nContenido a donde apunta el puntero ptr: %c", *ptr);
printf("nDireccion almacenada en el puntero ptr: %d", ptr);
printf("nDirección de x: %d", &x);
printf("nContenido de x: %c", x);
return 0;
}
x
ptr
Q
x
ptr
Q
x
ptr
Q
Dirección del puntero &ptr = 6553228
Contenido de donde apunta*ptr =Q
Dirección almacenada en ptr =6553227
Dirección de &x =6553227
Valor de x =Q
ptr++
x
ptr
Q
Dirección del puntero &ptr = 6553228
Contenido de donde apunta*ptr = i
Dirección almacenada en ptr =6553228
Dirección de &x =6553227
Valor de x =Q
Ptr-- x
ptr
Q
Ptr+=2 x
ptr
Q
Dirección del puntero &ptr =6553228
Contenido de donde apunta*ptr =Q
Dirección almacenada en ptr =6553227
Dirección de &x =6553227
Valor de x =Q
Dirección del puntero &ptr = 6553228
Contenido de donde apunta*ptr =@
Dirección almacenada en ptr =6553229
Dirección de &x =6553227
Valor de x =Q
Resta de dos punteros:
La resta de punteros se usa para saber cuantos elementos del tipo de dato
apuntado caben entre dos direcciones diferentes.
Supongamos:
int arreglo[15];
int dif;
int *p1, *p2;
p1 = &arreglo[4];
p2 = &arreglo[12];
dif = p2 – p1;
El puntero p1 apunta al quinto elemento del vector, y el puntero p2, al
decimotercero. Al restar los dos punteros obtendremos el valor de 8, que es el
numero de elementos de tipo int que pueden almacenarse entre las direcciones p1
y p2.
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
Arreglo[15]
p1 p2
Ejercicio 11. Resta de punteros
#include <stdio.h>
int main() {
int arreglo[15] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
int dif;
int *p1 = &arreglo[4]; // Apunta al quinto elemento (índice 4)
int *p2 = &arreglo[12]; // Apunta al treceavo elemento (índice 12)
dif=p2-p1;
printf("La diferencia entre los punteros es: %tdn", dif);
return 0;
}
Ejercicio 12.- Operadores relacionales y punteros
#include<stdio.h>
#include<conio.h>
int main(){
double *pt1, *pt2;
double x=3.1416, y = 8.5;
pt1=&x;
pt2=&y; //pt1=pt2;
printf("nDireccion del puntero pt1: %d", &pt1);
printf("nContenido a donde apunta el puntero pt1: %.3lf", *pt1);
printf("nDireccion almacenada en el puntero pt1: %d", pt1);
getch();
printf("nnDireccion del puntero pt2: %d", &pt2);
printf("nContenido a donde apunta el puntero pt2: %.3lf", *pt2);
printf("nDireccion almacenada en el puntero pt2: %d", pt2);
if(pt1<pt2){
printf("nEl puntero pt1 apunta a una direccion mas baja que pt2"); }
else if(pt1>pt2){
printf("nEl puntero pt1 apunta a una direccion mas alta que pt2"); }
else{
printf("nEl puntero pt1 apunta a la misma direccion que pt2");
}
return 0;
}
Punteros a cadenas
include<stdio.h>
#include<conio.h>
int main(){
char *p1, cadena[20];
printf("nCadena: ");
scanf("%s", cadena);
p1=cadena;
printf("nLa cadena leida es: %s", cadena);
printf("nImprimir el elemento 2 de la cadena por indice: %c", cadena[1]);
printf("nImprimir el elemento 2 de la cadena: %c", p1[1]);
printf("nImprimir el elemento 2 de la cadena: %c", *p1++);
printf("nImprimir el elemento 2 de la cadena: %c", *p1++);
printf("nImprimir el elemento 2 de la cadena: %c", *p1++);
return 0;
}
Tarea: simular la función strcmp
strcmp(*S1,*S2)
S1: "Sandy“
S2: "Ana"
1 si s1 >s2
S1: "Sandy"
S2: "Sandy"
0 si s1==s2
S1: "Ana"
S2: "Sandy"
-1 si s1< s2
int a;
a=strcmp(s1,s2);
int main() {
char cadena1[20], cadena2[20];
char *s1=cadena1;
char *s2=cadena2;
printf("nCadena S1: ");
scanf("%s", s1);
printf("nCadena S2: ");
scanf("%s", s2);
printf("nLa cadena s1 leida es: %s", s1);
printf("nLa cadena s2 leida es: %s", s2);
Int a=strcmp(s1,s2);
printf("nLa comparacionarroja: %d", a);
int b=my_strcmp(s1,s2);
printf("nLa comparacion manual arroja: %d", b);
return 0;
}
#include<stdio.h>
#include<conio.h>
#include<string.h>
int my_strcmp(char *s1, char *s2){
while (*s1 != '0' || *s2 != '0') {
if (*s1 < *s2) {
return -1; // s1 es menor que s2
} else if (*s1 > *s2) {
return 1; // s1 es mayor que s2
}
// Avanzar a los siguientes caracteres en ambas cadenas
s1++;
s2++;
}
return 0; // Las cadenas son iguales
}
Apuntadores y Arreglos (como se relacionan)
Un arreglo es una estructura de datos estática que
almacena una colección de elementos del mismo tipo
bajo un único nombre.
Cada elemento en el arreglo se accede mediante un
índice o una clave que indica su posición dentro del
arreglo.
Los arreglos son utilizados para almacenar datos de
manera estructurada y para facilitar el acceso y la
manipulación de conjuntos de datos relacionados.
La memoria
Elem
1
Elem
2
Elem
3
Elem
4
Elem
5
Arreglos (continuación)
Para definir y acceder a un arreglo unidimensional
se utiliza la sintaxis siguiente:
tipoDeDato nombreDelaVariable [numElementos];
La siguiente rutina en C define un arreglo de 5
posiciones
Llena cada posición con números 0 al 40
Los imprime
#include <stdio.h>
int main() {
// Definir un arreglo de 5 posiciones
int numeros[5];
// Llenar el arreglo con un bucle del 0 al 4
for (int i = 0; i < 5; ++i) {
numeros[i] = i * 10;
// Llenar con valores (0, 10, 20, 30, 40)
}
// Mostrar los valores almacenados en el arreglo
printf("Valores en el arreglo:n");
for (int i = 0; i < 5; ++i) {
printf("Posición %d: %dn", i, numeros[i]);
}
return 0;
}
Apuntadores y arreglos:
Los punteros y los arreglos tienen una relación muy estrecha, ya que el nombre de
un arreglo es en realidad un puntero al primer elemento de de ese arreglo.
Si x es un arreglo unidimensional, la dirección del primer elemento puede ser
expresada como:
&x[0] o simplemente como x.
La dirección del elemento i-ésimo se puede expresar como &x[i] o como (x+i)
En este caso la expresión (x+i) no es una operación aritmética convencional, sino
una operación con punteros.
Si,
&x[i] y (x+i) representan la dirección del i-ésimo elemento de x,
Entonces:
x[i] y *(x+i) representan el contenido de esa dirección, o lo que
es lo mismo el valor del i-ésimo elemento de x.
Hasta ahora hemos accedido a los valores de los arreglos de la forma x[i], pero ya
vimos que también podemos utilizarlos con punteros.
Nos puede resultar mas cómodo utilizar la forma x[i] para acceder ala i-ésimo
elemento, sin embargo, debemos considerar que la forma *(x+i) es mucho más
eficiente que x[i], porlo que suele preferirse cuando la velociad de ejecucón es un
factor determinante.
En estructuras de datos, una pila es una colección de elementos con dos operaciones
principales: PUSH and POP.
Estas operaciones siguen el principio de LIFO (Last In, First Out), lo que significa que
el último elemento que se inserta es el primero en ser retirado.
• Push (Empujar): Agrega un elemento a la pila.
• Pop (Retirar): Elimina el elemento más reciente de la pila.
La implementación de una pila puede realizarse
utilizando un arreglo (array) ó una lista enlazada.
PUSH POP
TOPE
N
INICIO
#define n 5
int pila[n];
int *p1, *tope;
main( ){
int value, opci;
tope = pila;
p1=pila;
PUSH POP
TOPE
INICIO
p1
PILA
do {
printf("nn 1. Insert en la pila (PUSH)");
printf("n 2. Extraer en la pila (POP)");
printf("n 3. Salir");
printf("nnElija opción: ");
scanf("%d",&opci);
switch(opci){
case 1:
printf("n Dar el elemento o ítem: ");
scanf("%d",&value);
push(value);
break;
case 2:
pop();
break;
case 3:
printf("n[FIN DE EJECUCIÓN]");
getch();
break;
default:
printf("nOpción incorrecta [Pulse tecla]");
getch();
}
} while (opci != 3);
}
void push(int i){
if(p1 >= tope+n) {
printf("n Posible desbordamiento de pila.nEl dato no fue insertado");
return;
}
*(p1)=i;
p1++;
imprimePila();
}
void pop(){
if(p1 == tope) {
printf("n La pila esta vacia. No hay datos que extraer");
return;
}
p1--;
printf("nSale el dato %d", *p1);
*p1=0;
imprimePila();
}
void imprimePila(){
for(int i=n-1; i>=0; i--){
printf("n%d", pila[i]);
}
}
Ejercicio: Cual es el resultado de cada una de las siguientes asignaciones
int x[100],b,*pa,*pb;
x[50]=10;
pa=&x[50];
b = *pa+1;
b = *(pa+1);
pb = &x[10];
*pb = 0;
*pb += 2;
(*pb)--;.
x[0] = *pb--;
int x[100],b,*pa,*pb;
x[50]=10;
pa=&x[50];
b = *pa+1; //Esto es: Al valor que tiene el array de x[50] sumarle 1.
//Esto es igual a: b=x[50]+1; => Su valor seria igual a 11.
b = *(pa+1); //Esto primero pasa a la siguiente dirección de memoria y luego lo referencia
//El resultado es: b = x[51];
Programación II.-Revisión de apuntadores
pb = &x[10]; //al puntero pb se le asigna la dirección de x[10]
*pb = 0; //Al valor que tiene el puntero se le asigna 0
//Esto es igual que decir: x[10] = 0
*pb += 2; //El valor del puntero se incrementa en dos unidades, es decir x[10] += 2
(*pb)--; //El valor del puntero se decrementa en una unidad.
x[0] = *pb--; //A x[0] se le pasa el valor de x[10] y el puntero pb, pasa a apuntar
a x[9]
//recuerda, que -- es post-decremento, primero asignará y luego restará.
#include<stdio.h>
#include<conio.h>
typedef struct coordenada{
int x;
int y;
} coor;
int main(){
coor *q1;
coor w1;
q1=&w1;
q1->x = 100;
q1->y = 200;
printf("nq1->x|%d|", q1->x);
printf("nq1->y|%d|", q1->y);
return 1;
Apuntadores de tipo estructura: Ejercicio 14
x y
w1
q1
100 200
w1
q1
#include<stdio.h>
#include<conio.h>
typedef union DiferentesTipos{
int x;
char z;
float y;
}uni;
int main(){
uni *pt;
uni ejem;
pt=&ejem;
pt->x = 100;
Apuntadores a union: Ejercicio 15
X y z
ejem
pt
100
ejem
pt
printf("nejem %d", ejem.x);
printf("nejem %f", ejem.y);
printf("nejem %c", ejem.z);
pt->y = 3.14;
printf("nnejem %d", ejem.x);
printf("nejem %f", ejem.y);
printf("nejem %c", ejem.z);
pt->z = 'Q';
printf("nnejem %d", ejem.x);
printf("nejem %f", ejem.y);
printf("nejem %c", ejem.z);
return 0;
}
3.14
ejem
pt
Q
ejem
pt
Ejercicio: Manejo de estructuras con apuntadores
Supongamos que estamos creando una aplicación para gestionar información de
estudiantes.
Define una estructura llamada Estudiante, que contenga la siguiente información:
Nombre del estudiante (cadena de caracteres)
Número de identificación del estudiante (entero)
Nota del estudiante (flotante)
Luego, crea una función llamada ActualizarNota que tome como parámetros un
puntero a una estructura Estudiante y una nueva nota, y actualice la nota del
estudiante utilizando el puntero.
Finalmente, en la función principal (main), crea un estudiante, muestra su información
original, llama a la función ActualizarNota para cambiar la nota y luego muestra la
información actualizada del estudiante.
#include <stdio.h>
#include <string.h>
// Definición de la estructura Estudiante
struct Estudiante {
char nombre[50];
int id;
float nota;
};
// Función para actualizar la nota del estudiante
void actualizarNota(struct Estudiante *estudiante, float nuevaNota) {
estudiante->nota = nuevaNota;
}
int main() {
// Crear un estudiante
struct Estudiante estudiante1;
// Inicializar la información del estudiante
strcpy(estudiante1.nombre, "Juan Perez");
estudiante1.id = 12345;
estudiante1.nota = 85.5;
// Mostrar información original del estudiante
printf("Informacion Original:n");
printf("Nombre: %sn", estudiante1.nombre);
printf("ID: %dn", estudiante1.id);
printf("Nota: %.2fn", estudiante1.nota);
// Actualizar la nota del estudiante
actualizarNota(&estudiante1, 92.0);
// Mostrar información actualizada del
estudiante
printf("nInformacion Actualizada:n");
printf("Nombre: %sn", estudiante1.nombre);
printf("ID: %dn", estudiante1.id);
printf("Nota: %.2fn", estudiante1.nota);
return 0;
}
Caso de negocio: Sistema de Registro de Estudiantes
Una institución educativa desea implementar un sistema de registro de
estudiantes. Cada estudiante tiene la siguiente información asociada: nombre,
edad, número de identificación y calificaciones de tres asignaturas, es necesario
utilizar apuntadores y estructuras de datos estáticas.
Requerimientos del Sistema:
• Permitir el registro de hasta 5 estudiantes.
• Mostrar la información de un estudiante específico.
• Calcular y mostrar el promedio de calificaciones de un estudiante.
• Mostrar la información de todos los estudiantes registrados.
• Identificar al estudiante con la calificación más alta en una asignatura
específica.
Caso de negocio: Sistema de Registro de Estudiantes
El menú debe mostrar las siguientes opciones:
Registrar un estudiante……………………………………………...1
Mostrar la información de un estudiante ( atreves del ID)……….2
Salir…………………………………………………………………….3
Ingrese su opción:
Tips: Se debe estar validando el numero de estudiantes permitidos.
Memoria Dinámica
Es el espacio en memoria que se crea al declarar
variables de cualquier tipo de dato [int,char,float...] o
derivados [struct, matrices, punteros...]). La memoria
que estas variables ocupan no puede cambiarse
durante la ejecución y tampoco puede ser liberada
manualmente.
Memoria Estática
Es memoria que se reserva en tiempo de ejecución.
Su principal ventaja frente a la estática, es que su
tamaño puede variar durante la ejecución del
programa. (En C, el programador es encargado de
liberar esta memoria cuando no la utilice más). El uso
de memoria dinámica es necesario cuando no se
sabe el numero exacto de datos/elementos a tratar.
Memoria Dinámica
Memoria dinámica vs estática, diferencias, ventajas y desventajas
La memoria reservada de forma dinámica suele estar alojada en el heap o
almacenamiento libre, y la memoria estática en el stack o pila.
La pila generalmente es una zona muy limitada. El heap, en cambio, en principio
podría estar limitado por la cantidad de memoria disponible durante la ejecución
del programa y el máximo de memoria que el sistema operativo permita
direccionar a un proceso.
La pila puede crecer de forma dinámica, pero esto depende del sistema operativo.
En cualquier caso, lo único que se puede asumir es que muy probablemente
dispondremos de menor espacio en la pila que en el heap.
Diferencias
Ventajas
La memoria dinámica se puede ir incrementando durante la ejecución del
programa, lo cual hace un uso mas eficiente de la memoria.
Desventajas
Una desventaja de la memoria dinámica es que es más difícil de manejar. La
memoria estática tiene una duración fija, que se reserva y libera de forma
automática. En contraste, la memoria dinámica se reserva de forma explícita y
continúa existiendo hasta que sea liberada, generalmente por parte del
programador.
Memoria dinámica.
Todos los objetos tienen un tiempo de vida, es decir, el tiempo durante el cual se garantiza
que el objeto exista.
En C, existen 3 tipos de duración: estática, automática y asignada. Las variables globales
y las variables locales declaradas con el especificador static tienen duración estática. Se
crean antes de que el programa inicie su ejecución y se destruyen cuando el programa
termina.
Las variables locales no static tienen duración automática. Se crean al entrar al bloque en el
que fueron declaradas y se destruyen al salir de ese bloque.
Por último duración asignada se refiere a los objetos cuya memoria se reserva de forma
dinámica. Esta memoria se crea y se debe liberar de forma explícita por parte del
programador.
La biblioteca estándar de C proporciona las funciones malloc, calloc y realloc y free, para el
manejo de memoria dinámica. Estas funciones están definidas en el archivo de cabecera
<stdlib.h>
Memoria dinámica.
malloc (Memory Allocation)
La función malloc reserva un bloque de memoria y devuelve un puntero void que debe ser
convertido al tipo de puntero deseado, el parámetro tamaño indica la cantidad de bytes que
se deben asignar en la memoria.
void *malloc(size_t tamaño);
Ejemplo:
char * ptr;
ptr = (char*)malloc(100);
free(ptr);
Se esta
especificando que
es un puntero y el
tipo de dato a usar
Tamaño de la
memoria a
reservar
Ejercicio 17.- Uso de malloc (ya no usamos variables)
#include<stdio.h>
#include<stdlib.h>
int main(){
int *ptr;
ptr= (int *) malloc(sizeof(int));
*ptr=1000;
printf("nptr->|%d|", *ptr);
free(ptr);
return 0;
}
1000
ptr
4 bytes
int main(){
float *ptr;
ptr= (float *) malloc(sizeof(float));
*ptr=1000;
printf("nptr->|%f|", *ptr);
free(ptr);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arreglo;
arreglo = (int *)malloc(10 * sizeof(int));
if (arreglo == NULL) {
// La asignación de memoria ha fallado
printf("Error: No se pudo asignar memoria.n");
return 1; // Salir con código de error
}
// Utilizar el arreglo asignado
// Liberar la memoria cuando ya no sea necesaria
free(arreglo);
return 0;
}
arreglo
40 bytes
Memoria dinámica, uso de new.
El uso de malloc o new son dos formas diferentes de asignar memoria dinámicamente,
pero es importante destacar que new no es una función en C, sino que pertenece a
C++.
• malloc es una función de la biblioteca estándar de C (stdlib.h) que se utiliza para
asignar un bloque de memoria de un tamaño específico.
• Devuelve un puntero void al inicio del bloque de memoria asignado.
• Es necesario realizar un casting explícito al tipo de datos deseado al asignar
memoria con malloc.
• No inicializa la memoria asignada; el contenido es indeterminado.
• new es un operador en C++ que se utiliza para asignar memoria dinámicamente y
construir objetos.
• Devuelve un puntero al tipo de dato solicitado y automáticamente realiza el casting
necesario.
• Inicializa la memoria asignada llamando al constructor del tipo de dato.
• Especifica automáticamente el tamaño de la memoria según el tipo de datos.
#include<stdio.h>
int main(){
int *ptr;
ptr= new int;
*ptr=100;
printf("nptr->|%d|", *ptr);
delete(ptr);
return 0;
}
#include<stdio.h>
#include<stdlib.h>
int main(){
char *pt2;
int *pt1;
float *pt3;
pt1 = (int *)malloc(sizeof(int));
pt2=(char*)malloc(sizeof(char));
pt3= (float *) malloc(sizeof(float));
*pt2='W';
int variable=10;
*pt1 = variable;
*pt3=3.1416;
printf("n ptr1->|%d| n",*pt1);
printf("npt2->|%c|",*pt2);
printf("nptr->|%f|", *pt3);
free(pt1);
free(pt2);
free(pt3)
return 0;
}
#include<stdio.h>
int main(){
char *pt2;
int *pt1;
float *pt3;
pt1 = new int;
pt2= new char;
pt3 = new float;
*pt2='W';
int variable=10;
*pt1 = variable;
*pt3=3.1416;
printf("n ptr1->|%d| n",*pt1);
printf("npt2->|%c|",*pt2);
printf("nptr->|%f|", *pt3);
delete (pt3);
delete (pt1);
delete(pt2);
return 0;
}
100
q1
300
q2
0
aux
300
q1
100
q2
100
aux
Ejercicio: Intercambio de valores usando malloc
int
#include<stdio.h>
#include<stdlib.h>
int main(){
int *q1, *q2, *aux;
q1=(int*)malloc(sizeof(int));
q2= (int *) malloc(sizeof(int));
aux =(int *) malloc(sizeof(int));
*q1=100;
*q2=300;
*aux=0;
printf("nq1->|%d|", *q1);
printf("nq2->|%d|", *q2);
printf("naux->|%d|", *aux);
*aux=*q1;
*q1=*q2;
*q2=*aux;
printf("nnDespues del intercambion");
printf("nq1->|%d|", *q1);
printf("nq2->|%d|", *q2);
printf("naux->|%d|", *aux);
free(q1);
free(q2);
free(aux);
return 0;
}
100
q1
300
q2
0
aux
300
q1
100
q2
100
aux
int
100
q1
300
q2
aux
100
q1
300
q2
q3
Intercambio de ligas usando malloc
int
Null
#include<stdio.h>
#include<stdlib.h>
int main(){
int *q1, *q2, *aux;
q1=(int*)malloc(sizeof(int));
q2= (int *) malloc(sizeof(int));
aux = NULL;
*q1=100;
*q2=300;
printf("nq1->|%d|", *q1);
printf("nq2->|%d|", *q2);
printf("naux->NULL");
aux=q2;
q2=q1;
q1=aux;
printf("nnDespues del intercambio de ligasn");
printf("nq1->|%d|", *q1);
printf("nq2->|%d|", *q2);
printf("naux->|%d|", *aux);
free(q1);
free(q2);
free(aux);
Return 0;
}
Simulación de un nodo con estructuras.
#include<stdlib.h>
#include<stdio.h>
typedef struct nodo{
int entero;
char caracter;
} nod;
int main(){
nod *ptr;
ptr=(nod*)malloc(sizeof(nod));
ptr->entero=300;
ptr->caracter='M';
printf("nptr->|%d|%c|", ptr->entero,ptr->caracter);
printf("nSe ha liberado la memoria");
free(ptr);
return 0;
}
300
ptr
M
Lista ligada con estructuras.
#include<stdlib.h>
#include<stdio.h>
typedef struct nodo{
char x;
int y;
struct nodo *z;
} nodi;
int main(){
nodi *ptr;
ptr=(nodi*)malloc(sizeof(nodi));
ptr->x='a';
ptr->y=50;
ptr->z=(nodi*) malloc(sizeof(nodi));
ptr->z->x= 'b';
ptr->z->y= 60;
ptr->z->z= NULL;
printf("nptr->|%c|%d|->|%c|%d|", ptr->x,ptr->y, ptr->z->x,ptr->z->y);
printf("nSe ha liberado la memoria");
free(ptr->z);
free(ptr);
return 0;
}
a
ptr
50 a
x y z
b 60 a
x y z
null
Punteros a punteros:
#include<stdio.h>
int main(){
int n;
int *ptr1;
int **ptr2;
ptr1=&n;
ptr2=&ptr1;
**ptr2=15;
printf("n El valor de n es:%d“, n);
return 0;
}
ptr1
n
ptr2
Ejercicio: Punteros de tipo estructura.
ptr
nom
ptrA
ptrB
ptrC
Juan q
9.5 s
8 t
flo
ent
nom
Rocio q
10 s
15 t
flo
ent
#include <stdio.h>
#include<string.h>
#include<stdlib.h>
#define largo 25
typedef struct cadena{
char nom[largo];
struct cadena *q;
}cad;
typedef struct flotante{
float flo;
struct flotante *s;
}flota;
typedef struct entero{
int ent;
struct entero *t;
}enter;
typedef struct conjunto{
cad *ptrA;
flota *ptrB;
enter *ptrC;
}con;
nom
Juan q
9.5 s
flo
8 t
ent
ptrA
ptrB
ptrC
int main(){
con *ptr1;
ptr1=(con*)malloc(sizeof(con));
ptr1->ptrA=(cad*)malloc(sizeof(cad));
strcpy(ptr1->ptrA->nom,"Juan");
ptr1->ptrA->q=(cad*)malloc(sizeof(cad));
strcpy(ptr1->ptrA->q->nom,"Rocio");
ptr1->ptrA->q->q=NULL;
ptr1->ptrB=(flota*)malloc(sizeof(flota));
ptr1->ptrB->flo=9.5;
ptr1->ptrB->s=(flota*)malloc(sizeof(flota));
ptr1->ptrB->s->flo=10.0;
ptr1->ptrB->s->s=NULL;
ptr1->ptrC=(enter*)malloc(sizeof(enter));
ptr1->ptrC->ent=8;
ptr1->ptrC->t=(enter*)malloc(sizeof(enter));
ptr1->ptrC->t->ent=15;
ptr1->ptrC->t->t= NULL;
ptr1
nom
ptrA
ptrB
ptrC
Juan q
9.5 s
8 t
flo
ent
nom
Rocio q
10 s
15 t
flo
ent
printf("nptr1->|");
printf("tptrA -> |%s| q|-> |%s|NULL|",ptr1->ptrA->nom,ptr1->ptrA->q->nom);
printf("n tptrB -> |%.1f| s|-> |%.1f|NULL|",ptr1->ptrB->flo,ptr1->ptrB->s->flo);
printf("n tptrC -> |%d| t|-> |%d|NULL|",ptr1->ptrC->ent,ptr1->ptrC->t->ent);
printf("n t|");
ptr1
nom
ptrA
ptrB
ptrC
Juan q
9.5 s
8 t
flo
ent
nom
Rocio q
10 s
15 t
flo
ent
printf("nSe ha liberado la memoria");
free(ptr1->ptrA->q);
free(ptr1->ptrA);
free(ptr1->ptrB->s);
free(ptr1->ptrB);
free(ptr1->ptrC->t);
free(ptr1->ptrC);
free(ptr1);
}
ptr1
nom
ptrA
ptrB
ptrC
Juan q
9.5 s
8 t
flo
ent
nom
Rocio q
10 s
15 t
flo
ent
Ejercicio: Punteros a estructuras conformada de 2 estructuras.
p
x[20]
p1
p2
Juan q
10.9 pt1
f i
x[20]
Rocio q
f i
3 20.6 pt1
4
f i
6.6 pt1
5
Ejercicio: Programar el escenario reservando memoria con malloc.
p
p1
p2
p3
i f
10 pt1
3.1416
i f
20 pt1
4.1416
x c
Programacion II
30.45
100
q
Punteros: Funciones y procedimientos.
#include<stdlib.h>
#include<stdio.h>
void procedimientoX(int *ptr){
*ptr +=2;
}
int * funcionY(){
int *qr= (int *) malloc(sizeof(int));
*qr=900;
return qr;
}
int main(){
int *p= (int *) malloc(sizeof(int));
*p=800;
printf("nValor original de *p es: %d", *p);
procedimientoX(p);
printf("nValor de *p despues de regresar del procedimientoX es: %d", *p);
free(p);
p=funcionY();
printf("nValor de *p despues de regresar de la funcionY es: %d", *p);
free(p);
return 0;
}
Reasignación de memoria (realloc):
La función realloc() cambia el tamaño del bloque de memoria reservado
previamente. La sintaxis es:
void *realloc(*ptr, nuevo_tamaño * bytes_tipo );
El argumento ptr apunta al principio del bloque de memoria asignado, mientras que
nuevo_tamaño indica el nuevo tamaño del bloque y bytes_tipo es el número de
bytes correspondiente al tipo de dato. El contenido permanecerá inalterado hasta
el mínimo entre el tamaño inicial y el nuevo; la memoria recién asignada quedará
sin inicializar.
Es importante verificar si ptr (el puntero) es NULL, en el caso afirmativo es
necesario notificar un error en la reasignación de memoria.
Reasignación de memoria (realloc):
La función realloc() puede mover la ubicación de almacenamiento del bloque
designado previamente, ya que todos los elementos que lo conforman el bloque de
almacenamiento reasignado deben permanecer contiguos.
Si no hay suficiente almacenamiento para crecer el tamaño, el bLoque no se
modifica quedando en su estado original y realloc() devuelve NULL.
char *texto;
texto=(char*) malloc(100 * sizeof(char));
texto=(char*) realloc(texto, 500*sizeof(char));
#include<stdlib.h>
#include<stdio.h>
int main(){
float numf;
int cuenta=0, i;
float *numerosLeidos=NULL;
float *mas_numeros=NULL;
do{
printf("nIngresa un dato de punto decimal (0 para finalizar): ");
scanf("%f", &numf);
if(cuenta == 0){
mas_numeros=(float *)malloc(sizeof(float));
printf("nSe uso malloc");
}
else{
mas_numeros= (float *)realloc(numerosLeidos, (cuenta+1)*sizeof(float));
printf("nSe uso realloc");
}
if(mas_numeros!=NULL){
numerosLeidos=mas_numeros;
numerosLeidos[cuenta]=numf;
}
else{
free(numerosLeidos);
printf("nError en la reasignacion de memoria");
exit(1);
}
for(i=0;i<=cuenta; i++)
printf("n %.2f", numerosLeidos[i]);
cuenta++;
}while(numf!=0);
free(numerosLeidos);
return 0;
}
Asignación de memoria (calloc):
La función calloc() funciona de modo similar a malloc, pero además de
reservar memoria, inicializa a 0 la memoria reservada. Se usa comúnmente
para arreglos y matrices. Está definida de esta forma:
void *calloc(size_t nmemb, size_t size);
El parámetro nmemb indica el número de elementos a reservar, y size el
tamaño de cada elemento. El ejemplo anterior se podría reescribir con
calloc de esta forma:
#include<stdlib.h>
#include<stdio.h>
int ren, col;
void leearreglo(int **mat){
for(int i=0;i<ren;i++)
for(int j=0;j<col;j++){
printf("n [%d,%d]: ",i,j);
scanf("%d", &mat[i][j]);
}
}
void imprimearreglo(int **mat){
for(int i=0;i<ren;i++){
for(int j=0;j<col;j++){
printf(" [%d] ",mat[i][j]);
}
printf("n ");
}
}
int main(){
int **mat;
printf("nDar numero de renglones: ");
scanf("%d", &ren);
printf("nDar numero de columnas: ");
scanf("%d", &col);
mat=(int **) calloc (ren, sizeof(int*));
for(int i=0; i<ren; i++)
mat[i]=(int *) calloc(col, sizeof(int));
leearreglo(mat);
imprimearreglo(mat);
return 0;
}
En este programa, se utilizan ** en la
asignación de memoria porque estás
trabajando con un arreglo bidimensional
dinámico. Un arreglo bidimensional
dinámico se representa como un puntero a
punteros, y cada puntero individual apunta
a un arreglo unidimensional.
Cuando se asigna memoria dinámica para
un arreglo bidimensional, se requiere un
doble puntero para manejar tanto los
renglones como las columnas del arreglo.
Ejercicio: Manejo de arreglos dinámicos.
Escribe un programa en C que solicite al usuario un número entero n.
Luego, crea dinámicamente un arreglo de enteros de tamaño n, e inicialízalo con
valores ingresados por el usuario.
Después, encuentra el valor máximo y la posición en la que se encuentra en el
arreglo.
Finalmente, libera la memoria asignada.
Conclusión: ¿Para que nos sirven los apuntadores?
• Acceso directo a la memoria: Esto es útil para operaciones eficientes y para
trabajar con estructuras de datos complejas.
• Dinámica de asignación de memoria: Los punteros se utilizan para asignar y
liberar memoria dinámicamente durante la ejecución del programa.
• Funciones y paso por referencia: Los punteros permiten pasar parámetros por
referencia a funciones, lo que significa que la función puede modificar el
contenido de la memoria apuntada por el puntero. Esto es especialmente útil
cuando se trabaja con grandes conjuntos de datos o se quiere evitar la sobrecarga
de copiar datos.
• Manipulación de cadenas de caracteres: En C, las cadenas de caracteres son
esencialmente matrices de caracteres. Los punteros son ampliamente utilizados
para manipular y trabajar con cadenas de caracteres de manera eficiente.
• Estructuras de datos complejas: Los punteros permiten la implementación de
estructuras de datos más complejas, como listas enlazadas, árboles y grafos. Estas
estructuras de datos pueden ser gestionadas eficientemente utilizando punteros
para enlazar nodos o elementos.
• Arreglos dinámicos: Los punteros permiten la creación de arreglos dinámicos,
cuyo tamaño puede ser determinado en tiempo de ejecución. Esto es útil cuando
no se conoce el tamaño exacto del conjunto de datos antes de que el programa
se ejecute.
• Operaciones de bajo nivel: Los punteros proporcionan un nivel de abstracción
más bajo que otros tipos de datos en C, lo que permite realizar operaciones de
bajo nivel, como la manipulación directa de bits o la implementación de
estructuras de datos personalizadas.
Aunque los punteros en C brindan un control preciso sobre la memoria, también
pueden conducir a errores de programación como desreferenciación incorrecta o
fugas de memoria si no se manejan correctamente. Por lo tanto, se debe tener
cuidado al trabajar con punteros y seguir buenas prácticas de programación para
evitar problemas.

Más contenido relacionado

Similar a Apuntadorkeurjeh4jj4by un 4hh4j4u4jj4h4y4jh4

Clase 11- fundamentos de la programacion
Clase 11- fundamentos de la programacionClase 11- fundamentos de la programacion
Clase 11- fundamentos de la programacion
diego MC
 
PUNTEROS (APUNTADORES) EN C++
PUNTEROS (APUNTADORES) EN C++PUNTEROS (APUNTADORES) EN C++
PUNTEROS (APUNTADORES) EN C++
die_dex
 
Lenguaje c diapositivas
Lenguaje c diapositivasLenguaje c diapositivas
Lenguaje c diapositivas
starduslex
 
Lenguajes diapositivas
Lenguajes diapositivasLenguajes diapositivas
Lenguajes diapositivas
starduslex
 
Lenguajes diapositivas
Lenguajes diapositivasLenguajes diapositivas
Lenguajes diapositivas
starduslex
 
Algoritmos y lenjuage de programacion
Algoritmos y  lenjuage de programacionAlgoritmos y  lenjuage de programacion
Algoritmos y lenjuage de programacion
Ysaac Ruiz
 

Similar a Apuntadorkeurjeh4jj4by un 4hh4j4u4jj4h4y4jh4 (20)

Trabajo programacion 1 jose silva
Trabajo programacion 1 jose silvaTrabajo programacion 1 jose silva
Trabajo programacion 1 jose silva
 
Administración de memoria y apuntadores
Administración de memoria y apuntadoresAdministración de memoria y apuntadores
Administración de memoria y apuntadores
 
Gestión Dinámica de la Memoria
Gestión Dinámica de la MemoriaGestión Dinámica de la Memoria
Gestión Dinámica de la Memoria
 
Tema 8 www.fresymetal.com
Tema 8 www.fresymetal.comTema 8 www.fresymetal.com
Tema 8 www.fresymetal.com
 
nodo
nodonodo
nodo
 
Apuntadores
Apuntadores Apuntadores
Apuntadores
 
Programación en c (iii parte)
Programación en c (iii parte)Programación en c (iii parte)
Programación en c (iii parte)
 
Clase 11- fundamentos de la programacion
Clase 11- fundamentos de la programacionClase 11- fundamentos de la programacion
Clase 11- fundamentos de la programacion
 
PUNTEROS (APUNTADORES) EN C++
PUNTEROS (APUNTADORES) EN C++PUNTEROS (APUNTADORES) EN C++
PUNTEROS (APUNTADORES) EN C++
 
Apunfun1
Apunfun1Apunfun1
Apunfun1
 
Lenguaje c diapositivas
Lenguaje c diapositivasLenguaje c diapositivas
Lenguaje c diapositivas
 
Lenguajes diapositivas
Lenguajes diapositivasLenguajes diapositivas
Lenguajes diapositivas
 
Lenguajes diapositivas
Lenguajes diapositivasLenguajes diapositivas
Lenguajes diapositivas
 
Algoritmos y lenjuage de programacion
Algoritmos y  lenjuage de programacionAlgoritmos y  lenjuage de programacion
Algoritmos y lenjuage de programacion
 
Estructura de Datos
Estructura de DatosEstructura de Datos
Estructura de Datos
 
Arreglos
ArreglosArreglos
Arreglos
 
Matrices Y Punteros - Sergio Cabello
Matrices Y Punteros - Sergio CabelloMatrices Y Punteros - Sergio Cabello
Matrices Y Punteros - Sergio Cabello
 
08 - Punteros en lenguaje C
08 - Punteros en lenguaje C08 - Punteros en lenguaje C
08 - Punteros en lenguaje C
 
Tipos de datos en C
Tipos de datos en CTipos de datos en C
Tipos de datos en C
 
Introducción a los Punteros en el lenguaje C
Introducción a los Punteros en el lenguaje CIntroducción a los Punteros en el lenguaje C
Introducción a los Punteros en el lenguaje C
 

Último

Tesis_liderazgo_desempeño_laboral_colaboradores_cooperativa_agraria_rutas_Inc...
Tesis_liderazgo_desempeño_laboral_colaboradores_cooperativa_agraria_rutas_Inc...Tesis_liderazgo_desempeño_laboral_colaboradores_cooperativa_agraria_rutas_Inc...
Tesis_liderazgo_desempeño_laboral_colaboradores_cooperativa_agraria_rutas_Inc...
MIGUELANGELLEGUIAGUZ
 
senati-powerpoint_5TOS-_ALUMNOS (1).pptx
senati-powerpoint_5TOS-_ALUMNOS (1).pptxsenati-powerpoint_5TOS-_ALUMNOS (1).pptx
senati-powerpoint_5TOS-_ALUMNOS (1).pptx
nathalypaolaacostasu
 
DIAPOSITIVAS LIDERAZGO Y GESTION INTERGENERACION (3).pptx
DIAPOSITIVAS LIDERAZGO Y GESTION INTERGENERACION (3).pptxDIAPOSITIVAS LIDERAZGO Y GESTION INTERGENERACION (3).pptx
DIAPOSITIVAS LIDERAZGO Y GESTION INTERGENERACION (3).pptx
7500222160
 
SENTENCIA COLOMBIA DISCRIMINACION SELECCION PERSONAL.pdf
SENTENCIA COLOMBIA DISCRIMINACION SELECCION PERSONAL.pdfSENTENCIA COLOMBIA DISCRIMINACION SELECCION PERSONAL.pdf
SENTENCIA COLOMBIA DISCRIMINACION SELECCION PERSONAL.pdf
JaredQuezada3
 
260813887-diagrama-de-flujo-de-proceso-de-esparrago-fresco-verde.pptx
260813887-diagrama-de-flujo-de-proceso-de-esparrago-fresco-verde.pptx260813887-diagrama-de-flujo-de-proceso-de-esparrago-fresco-verde.pptx
260813887-diagrama-de-flujo-de-proceso-de-esparrago-fresco-verde.pptx
i7ingenieria
 
CARPETA PEDAGOGICA 2024 ARITA.sadasdasddocx
CARPETA PEDAGOGICA 2024 ARITA.sadasdasddocxCARPETA PEDAGOGICA 2024 ARITA.sadasdasddocx
CARPETA PEDAGOGICA 2024 ARITA.sadasdasddocx
WILIANREATEGUI
 
CRITERIOS DE EVALUACIÓN - NIVEL INICIAL.docx
CRITERIOS DE EVALUACIÓN - NIVEL INICIAL.docxCRITERIOS DE EVALUACIÓN - NIVEL INICIAL.docx
CRITERIOS DE EVALUACIÓN - NIVEL INICIAL.docx
geuster2
 
GUIA UNIDAD 3 costeo variable fce unc.docx
GUIA UNIDAD 3 costeo variable fce unc.docxGUIA UNIDAD 3 costeo variable fce unc.docx
GUIA UNIDAD 3 costeo variable fce unc.docx
AmyKleisinger
 

Último (20)

CORRIENTES DEL PENSAMIENTO ECONÓMICO.pptx
CORRIENTES DEL PENSAMIENTO ECONÓMICO.pptxCORRIENTES DEL PENSAMIENTO ECONÓMICO.pptx
CORRIENTES DEL PENSAMIENTO ECONÓMICO.pptx
 
Tesis_liderazgo_desempeño_laboral_colaboradores_cooperativa_agraria_rutas_Inc...
Tesis_liderazgo_desempeño_laboral_colaboradores_cooperativa_agraria_rutas_Inc...Tesis_liderazgo_desempeño_laboral_colaboradores_cooperativa_agraria_rutas_Inc...
Tesis_liderazgo_desempeño_laboral_colaboradores_cooperativa_agraria_rutas_Inc...
 
DISEÑO DE ESTRATEGIAS EN MOMENTOS DE INCERTIDUMBRE
DISEÑO DE ESTRATEGIAS EN MOMENTOS DE INCERTIDUMBREDISEÑO DE ESTRATEGIAS EN MOMENTOS DE INCERTIDUMBRE
DISEÑO DE ESTRATEGIAS EN MOMENTOS DE INCERTIDUMBRE
 
senati-powerpoint_5TOS-_ALUMNOS (1).pptx
senati-powerpoint_5TOS-_ALUMNOS (1).pptxsenati-powerpoint_5TOS-_ALUMNOS (1).pptx
senati-powerpoint_5TOS-_ALUMNOS (1).pptx
 
EL REFERENDO para una exposición de sociales
EL REFERENDO para una exposición de socialesEL REFERENDO para una exposición de sociales
EL REFERENDO para una exposición de sociales
 
Correcion del libro al medio hay sitio.pptx
Correcion del libro al medio hay sitio.pptxCorrecion del libro al medio hay sitio.pptx
Correcion del libro al medio hay sitio.pptx
 
Distribuciones de frecuencia cuarto semestre
Distribuciones de frecuencia cuarto semestreDistribuciones de frecuencia cuarto semestre
Distribuciones de frecuencia cuarto semestre
 
DIAPOSITIVAS LIDERAZGO Y GESTION INTERGENERACION (3).pptx
DIAPOSITIVAS LIDERAZGO Y GESTION INTERGENERACION (3).pptxDIAPOSITIVAS LIDERAZGO Y GESTION INTERGENERACION (3).pptx
DIAPOSITIVAS LIDERAZGO Y GESTION INTERGENERACION (3).pptx
 
Presentacion encuentra tu creatividad papel azul.pdf
Presentacion encuentra tu creatividad papel azul.pdfPresentacion encuentra tu creatividad papel azul.pdf
Presentacion encuentra tu creatividad papel azul.pdf
 
SENTENCIA COLOMBIA DISCRIMINACION SELECCION PERSONAL.pdf
SENTENCIA COLOMBIA DISCRIMINACION SELECCION PERSONAL.pdfSENTENCIA COLOMBIA DISCRIMINACION SELECCION PERSONAL.pdf
SENTENCIA COLOMBIA DISCRIMINACION SELECCION PERSONAL.pdf
 
liderazgo guia.pdf.............................
liderazgo guia.pdf.............................liderazgo guia.pdf.............................
liderazgo guia.pdf.............................
 
DECRETO-2535-DE-1993-pdf.pdf VIGILANCIA PRIVADA
DECRETO-2535-DE-1993-pdf.pdf VIGILANCIA PRIVADADECRETO-2535-DE-1993-pdf.pdf VIGILANCIA PRIVADA
DECRETO-2535-DE-1993-pdf.pdf VIGILANCIA PRIVADA
 
260813887-diagrama-de-flujo-de-proceso-de-esparrago-fresco-verde.pptx
260813887-diagrama-de-flujo-de-proceso-de-esparrago-fresco-verde.pptx260813887-diagrama-de-flujo-de-proceso-de-esparrago-fresco-verde.pptx
260813887-diagrama-de-flujo-de-proceso-de-esparrago-fresco-verde.pptx
 
Las sociedades anónimas en el Perú , de acuerdo a la Ley general de sociedades
Las sociedades anónimas en el Perú , de acuerdo a la Ley general de sociedadesLas sociedades anónimas en el Perú , de acuerdo a la Ley general de sociedades
Las sociedades anónimas en el Perú , de acuerdo a la Ley general de sociedades
 
CARPETA PEDAGOGICA 2024 ARITA.sadasdasddocx
CARPETA PEDAGOGICA 2024 ARITA.sadasdasddocxCARPETA PEDAGOGICA 2024 ARITA.sadasdasddocx
CARPETA PEDAGOGICA 2024 ARITA.sadasdasddocx
 
CRITERIOS DE EVALUACIÓN - NIVEL INICIAL.docx
CRITERIOS DE EVALUACIÓN - NIVEL INICIAL.docxCRITERIOS DE EVALUACIÓN - NIVEL INICIAL.docx
CRITERIOS DE EVALUACIÓN - NIVEL INICIAL.docx
 
____ABC de las constelaciones con enfoque centrado en soluciones - Gabriel de...
____ABC de las constelaciones con enfoque centrado en soluciones - Gabriel de...____ABC de las constelaciones con enfoque centrado en soluciones - Gabriel de...
____ABC de las constelaciones con enfoque centrado en soluciones - Gabriel de...
 
GUIA UNIDAD 3 costeo variable fce unc.docx
GUIA UNIDAD 3 costeo variable fce unc.docxGUIA UNIDAD 3 costeo variable fce unc.docx
GUIA UNIDAD 3 costeo variable fce unc.docx
 
Empresa Sazonadores Lopesa estudio de mercado
Empresa Sazonadores Lopesa estudio de mercadoEmpresa Sazonadores Lopesa estudio de mercado
Empresa Sazonadores Lopesa estudio de mercado
 
Manual de Imagen Personal y uso de uniformes
Manual de Imagen Personal y uso de uniformesManual de Imagen Personal y uso de uniformes
Manual de Imagen Personal y uso de uniformes
 

Apuntadorkeurjeh4jj4by un 4hh4j4u4jj4h4y4jh4

  • 1. Programación II Martin Mendieta González Febrero 2024
  • 2. 2. Apuntadores y memoria dinámica. Objetivo: Al término de este apartado el estudiante habrá comprendido qué son los apuntadores, las operaciones que se pueden realizar con ellos y su uso en los arreglos. Los participantes explorarán estrategias para la implementación de algoritmos que manipulen la memoria de manera dinámica, a través de apuntadores bajo la sintaxis de lenguaje C, así como para la programación de aplicaciones orientadas a la eficiencia del almacenamiento interno de la computadora.
  • 3. 2. Apuntadores y memoria dinámica. 2.1 Apuntadores (punteros) 2.1.1 Definición 2.1.2 Operaciones 2.1.3 Apuntadores y arreglos 2.1.4 Arreglos de apuntadores 2.1.5 Apuntadores de tipo estructura 2.2 Memoria dinámica 2.2.1 Funciones para asignación de memoria 2.2.2 Reasignación de memoria 2.2.3 Arreglos dinámicos de una y dos dimensiones
  • 4. “Comprender y usar correctamente los apuntadores es con seguridad lo más complicado del lenguaje C, pero también se trata de un mecanismo muy poderoso. Tan poderoso que un simple puntero descontrolado puede provocar que el programa se cuelgue irremediablemente o incluso que falle todo el sistema.” A.M. VOZMEDIANO
  • 5. Apuntadores. Definición: En el lenguaje de programación C, un apuntador es una variable cuyo contenido no es un dato, sino la dirección de memoria donde esta almacenado un dato. En lugar de contener directamente el valor de los datos, un apuntador "apunta" a la ubicación de memoria donde se almacenan los datos en el sistema. Apuntador Variable X 5
  • 6. Dentro de la memoria del ordenador, cada dato almacenado ocupa una o más celdas contiguas de memoria. El número de celdas de memoria requeridas para almacenar un dato depende de su tipo. Por ejemplo, un dato de tipo entero (int) puede ocupar 32 bits (es decir 4 bytes), mientras que un dato de tipo carácter ocupa 8 bits ( es decir 1 byte). La computadora almacena el dato en un espacio de la memoria en una determinada dirección. Un puntero no es más que una variable cuyo contenido, no es un dato, sino la dirección de memoria donde está almacenado un dato.
  • 7. Ejemplo: Imaginemos que miLetra es una variable de tipo carácter y que, por tanto, el sistema le asigna un byte para ser almacenado. char miLetra; miLetra=´A´; Al ejecutar este código, el sistema asigna una celda de memoria para el dato. Supongamos que la celda asignada tiene la dirección 1200. Al hacer la asignación miLetra=´A´, el sistema almacena en la celda 1200 el valor 65, que es el código ASCII de la letra ´A´. 1197 1198 1199 1200 65 …. Un apuntador sería en este ejemplo una variable que contendría el valor 1200, es decir la dirección donde se aloja la variable miLetra Dirección de memoria Contenido
  • 8. Antes de declarar un apuntador veamos el operador & El operador &, u operador de dirección, se utiliza para obtener la dirección de memoria de una variable. // Uso del operador de dirección para obtener la dirección de memoria direccionDeLaVariable = &variable; Del ejemplo anterior direccionDeLaVariable = &miLetra; Daría como resultado 1200 para la variable direccionDeLaVariable char a; scanf ("%c",&a);
  • 9. Por otro lado, se tiene el operador * u operador de indirección Se utiliza para acceder al valor almacenado en la dirección de memoria a la que apunta un puntero La declaración de un apuntador se realiza utilizando el operador de asterisco *. char *V Declara que V es una variable apuntador a un Char. Los valores permitidos siempre incluyen la dirección especial 0 y un conjunto de enteros positivos que se interpretan como direcciones en el sistema. Usando el ejemplo anterior podemos hacer que el apuntador v, “apunte” a la variable miLetra v = &miLetra; miletra V
  • 10. Casos especiales A = 0; A = NULL; /* equivale a A = 0 */ A = &i; /* A esta refiriéndose a i, conteniendo la dirección de i */ En este caso nótese que cuando un apuntador en C apunta a la dirección de memoria 0, se dice que el apuntador contiene el valor nulo. El valor nulo en C se representa comúnmente como NULL. Un apuntador nulo no apunta a ninguna ubicación válida de memoria.
  • 11. #include <stdio.h> int main() { // Definir una variable de tipo char char miLetra; // Asignar la letra 'A' a la variable miLetra = 'A’; // Declarar un puntero que apunte a la variable miLetra char *V = &miLetra; // Imprimir el valor de la variable printf("El valor de la variable es: %cn", miLetra); // Imprimir el valor de la variable usando el puntero printf("El valor de la variable a través del puntero es: %cn", *V); return 0; } ¿Cuál es la diferencia entre la primera instrucción printf y la segunda? ¿cuál es la diferencia entre el operador & y el operador *?
  • 12. Recapitulando, los apuntadores (punteros) son variables que almacenan direcciones de memoria. Permiten manipular y acceder directamente a la memoria, lo que brinda mayor flexibilidad y eficiencia en algunas operaciones.
  • 13. Supongamos: int x=15, y, *apuntador; Entonces las dos proposiciones: apuntador = &x; y = *apuntador; /* le estamos asignando 15 a y */ Son equivalentes a: y = *&x; /* precedencia de operadores */ que a su vez equivale a: y = x; El operador * es un operador unario, llamado operador indirección, que opera sólo sobre una variable puntero. Las variables puntero pueden apuntar a direcciones donde se almacene cualquier tipo de dato: enteros, flotantes, caracteres, cadenas, arreglos, estructuras, etc..
  • 14. Ejercicio 1. #include<stdio.h> int main(){ char *pt1; char var_c='W'; pt1 = &var_c; printf("nEl valor de contenido a donde apunta pt1: %c", *pt1); return 0; }
  • 15. Ejercicio 2. #include<stdio.h> int main(){ char *pt1; char var_c='W'; pt1 = &var_c; printf("nEl valor de contenido a donde apunta pt1: %c", *pt1); *pt1= 'A'; printf("nEl valor de contenido a donde apunta pt1 despues de la nueva asignacion: %c", *pt1); return 0; }
  • 16. Ejercicio 3. #include<stdio.h> int main() { int *pt2; int b=100; pt2 = &b; printf("nEl valor de contenido a donde apunta pt2: %d", *pt2); return 0; }
  • 17. Ejercicio 4. #include<stdio.h> int main(){ int *pt2; int b=100; pt2 = &b; printf("nEl valor de contenido a donde apunta *pt2: %d", *pt2); printf("nLa direccion en donde se almacena el puntero &pt2: %d", &pt2); printf("nLo que tiene guardado el puntero pt2: %d", pt2); printf("nLa dirección de la variable b es: %d", &b); return 0; }
  • 18. Ejercicio 5. #include<stdio.h> int main(){ int *pt2; int b=100; pt2 = &b; printf("nEl valor de contenido a donde apunta *pt2: %d", *pt2); printf("nLa direccion en donde se almacena el puntero &pt2: %d", &pt2); printf("nLo que tiene guardado el puntero pt2: %d", pt2); printf("nLa dirección de la variable b es: %d", &b); int *pt1; pt1=pt2; printf("nEl valor de contenido a donde apunta *pt1: %d", *pt1); return 0; }
  • 19. Ejercicio 6. #include<stdio.h> int main(){ float *ptr; float x; ptr=&x; *ptr=9.5; printf("nContenido del puntero ptr: %.2f", *ptr); return 0; }
  • 20. Ejercicio 7. #include<stdio.h> int main(){ float v1,*qt1; int v2, *qt2; char v3, *qt3; qt1=&v1; qt2=&v2; qt3=&v3; printf("nDar un valor decimal: "); scanf("%f", &(*qt1)); printf("nDar un valor entero: "); scanf("%d", &(*qt2)); printf("nDar un caracter: "); fflush(stdin); scanf("%c", &(*qt3)); printf("nEl contenido de lo que apunta *qt1 es %.3f", *qt1); printf("nEl contenido de lo que apunta *qt2 es %d", *qt2); printf("nEl contenido de lo que apunta *qt3 es %c", *qt3); return 0; }
  • 21. Ejercicio 8. Intercambio de valores con punteros #include<stdio.h> int main(){ float w1,w2,w3; float *q1,*q2,*aux; // crear ligas q1=&w1; q2=&w2; aux=&w3; //asignar valores *q1=9.5; *q2=20.0; //Imprimir printf("n q1->|%.1f|",*q1); printf("n q2->|%.1f|",*q2); //intercambio *aux=*q1; *q1=*q2; *q2=*aux; //Imprimir printf("n Valores intercambiados con punteros"); printf("n q1->|%.1f|",*q1); printf("n q2->|%.1f|",*q2); return 0; }
  • 22. Programar los siguientes escenarios: Escenario 1) Escenario 2) w 3.1436 p q s r 100 9.5
  • 23. Aritmética de punteros (operaciones) Con las variables de tipo apuntador sólo se pueden hacer dos operaciones aritméticas, sumar o restar a aun apuntador u número entero, y restar dos punteros. Las operaciones de suma y resta en punteros en C están relacionadas con la manipulación de direcciones de memoria. Se utilizan para desplazarse a través de un array (arreglo) o bloque de memoria. Es importante destacar que estas operaciones están limitadas a punteros que apuntan a elementos de un mismo array, ya que se basan en el tamaño del tipo de datos al que apunta el puntero.
  • 24. Aritmética de punteros (sumar y restar) Con las variables de tipo apuntador sólo se pueden hacer dos operaciones aritméticas, sumar o restar a aun apuntador un número entero. El resultado de estas operaciones no es tan trivial como imaginamos que ocurre con las sumas aritméticas a las que estamos acostumbrados. Del ejemplo donde tenemos un apuntador, apuntando a la dirección 1200 si le sumamos un 1 a este apuntador, el resultado puede ser 1201… pero también puede ser 1202 ó 1204. Esto se debe a que el resultado depende del tipo de dato al que apunte nuestro apuntador. miletra V W 1200 V++ miletra V W 1201
  • 25. Aritmética de punteros (sumar y restar) La suma de un puntero con un valor entero se utiliza para desplazarse a través de un array o bloque de memoria. Cuando sumas un valor entero n a un puntero, el puntero se desplaza n * sizeof(tipo_dato) bytes hacia adelante en memoria, donde tipo_dato es el tipo de dato al que apunta el puntero. Es decir, dado que lo que se suma es un número a la dirección donde empieza un dato, el resultado depende del tamaño del dato. En el caso de un entero: entero V 100 1200 V++ entero V 100 1204
  • 26. Por ejemplo: char *p; p=p+5; Supongamos que p apunta a la dirección de memoria 800. Como cada carácter ocupa 1 byte, al incremento en 5 unidades, apuntará a la dirección 805. int *p; p=p+5; En este caso bajo los mismos supuestos, al incremento en 5 unidades pasará a apuntar a la dirección 820 ( suponiendo que cada entero ocupe 4 bytes) A los punteros se les puede aplicar las operaciones de incremento (++) y decremento (--), considerando el efecto que aplique por el tipo de dato del puntero. int *p; p++; p W 800 805 p 1865 800 820
  • 27. Ejercicio 10. Suma aritmética de punteros #include<stdio.h> #include<conio.h> int main(){ char *ptr; char x='Q'; ; ptr=&x; printf("nDireccion del puntero ptr: %d", &ptr); printf("nContenido a donde apunta el puntero ptr: %c", *ptr); printf("nDireccion almacenada en el puntero ptr: %d", ptr); printf("nDirección de x: %d", &x); printf("nContenido de x: %c", x); ptr++; printf("nnDireccion del puntero ptr: %d", &ptr); printf("nContenido a donde apunta el puntero ptr: %c", *ptr); printf("nDireccion almacenada en el puntero ptr: %d", ptr); printf("nDirección de x: %d", &x); printf("nContenido de x: %c", x); x ptr Q x ptr Q
  • 28. ptr--; printf("nnDireccion del puntero ptr: %d", &ptr); printf("nContenido a donde apunta el puntero ptr: %c", *ptr); printf("nDireccion almacenada en el puntero ptr: %d", ptr); printf("nDirección de x: %d", &x); printf("nContenido de x: %c", x); ptr+=2; printf("nnDireccion del puntero ptr: %d", &ptr); printf("nContenido a donde apunta el puntero ptr: %c", *ptr); printf("nDireccion almacenada en el puntero ptr: %d", ptr); printf("nDirección de x: %d", &x); printf("nContenido de x: %c", x); return 0; } x ptr Q x ptr Q
  • 29. x ptr Q Dirección del puntero &ptr = 6553228 Contenido de donde apunta*ptr =Q Dirección almacenada en ptr =6553227 Dirección de &x =6553227 Valor de x =Q ptr++ x ptr Q Dirección del puntero &ptr = 6553228 Contenido de donde apunta*ptr = i Dirección almacenada en ptr =6553228 Dirección de &x =6553227 Valor de x =Q Ptr-- x ptr Q Ptr+=2 x ptr Q Dirección del puntero &ptr =6553228 Contenido de donde apunta*ptr =Q Dirección almacenada en ptr =6553227 Dirección de &x =6553227 Valor de x =Q Dirección del puntero &ptr = 6553228 Contenido de donde apunta*ptr =@ Dirección almacenada en ptr =6553229 Dirección de &x =6553227 Valor de x =Q
  • 30. Resta de dos punteros: La resta de punteros se usa para saber cuantos elementos del tipo de dato apuntado caben entre dos direcciones diferentes. Supongamos: int arreglo[15]; int dif; int *p1, *p2; p1 = &arreglo[4]; p2 = &arreglo[12]; dif = p2 – p1; El puntero p1 apunta al quinto elemento del vector, y el puntero p2, al decimotercero. Al restar los dos punteros obtendremos el valor de 8, que es el numero de elementos de tipo int que pueden almacenarse entre las direcciones p1 y p2. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Arreglo[15] p1 p2
  • 31. Ejercicio 11. Resta de punteros #include <stdio.h> int main() { int arreglo[15] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}; int dif; int *p1 = &arreglo[4]; // Apunta al quinto elemento (índice 4) int *p2 = &arreglo[12]; // Apunta al treceavo elemento (índice 12) dif=p2-p1; printf("La diferencia entre los punteros es: %tdn", dif); return 0; }
  • 32. Ejercicio 12.- Operadores relacionales y punteros #include<stdio.h> #include<conio.h> int main(){ double *pt1, *pt2; double x=3.1416, y = 8.5; pt1=&x; pt2=&y; //pt1=pt2; printf("nDireccion del puntero pt1: %d", &pt1); printf("nContenido a donde apunta el puntero pt1: %.3lf", *pt1); printf("nDireccion almacenada en el puntero pt1: %d", pt1); getch(); printf("nnDireccion del puntero pt2: %d", &pt2); printf("nContenido a donde apunta el puntero pt2: %.3lf", *pt2); printf("nDireccion almacenada en el puntero pt2: %d", pt2); if(pt1<pt2){ printf("nEl puntero pt1 apunta a una direccion mas baja que pt2"); } else if(pt1>pt2){ printf("nEl puntero pt1 apunta a una direccion mas alta que pt2"); } else{ printf("nEl puntero pt1 apunta a la misma direccion que pt2"); } return 0; }
  • 33. Punteros a cadenas include<stdio.h> #include<conio.h> int main(){ char *p1, cadena[20]; printf("nCadena: "); scanf("%s", cadena); p1=cadena; printf("nLa cadena leida es: %s", cadena); printf("nImprimir el elemento 2 de la cadena por indice: %c", cadena[1]); printf("nImprimir el elemento 2 de la cadena: %c", p1[1]); printf("nImprimir el elemento 2 de la cadena: %c", *p1++); printf("nImprimir el elemento 2 de la cadena: %c", *p1++); printf("nImprimir el elemento 2 de la cadena: %c", *p1++); return 0; }
  • 34. Tarea: simular la función strcmp strcmp(*S1,*S2) S1: "Sandy“ S2: "Ana" 1 si s1 >s2 S1: "Sandy" S2: "Sandy" 0 si s1==s2 S1: "Ana" S2: "Sandy" -1 si s1< s2 int a; a=strcmp(s1,s2);
  • 35. int main() { char cadena1[20], cadena2[20]; char *s1=cadena1; char *s2=cadena2; printf("nCadena S1: "); scanf("%s", s1); printf("nCadena S2: "); scanf("%s", s2); printf("nLa cadena s1 leida es: %s", s1); printf("nLa cadena s2 leida es: %s", s2); Int a=strcmp(s1,s2); printf("nLa comparacionarroja: %d", a); int b=my_strcmp(s1,s2); printf("nLa comparacion manual arroja: %d", b); return 0; }
  • 36. #include<stdio.h> #include<conio.h> #include<string.h> int my_strcmp(char *s1, char *s2){ while (*s1 != '0' || *s2 != '0') { if (*s1 < *s2) { return -1; // s1 es menor que s2 } else if (*s1 > *s2) { return 1; // s1 es mayor que s2 } // Avanzar a los siguientes caracteres en ambas cadenas s1++; s2++; } return 0; // Las cadenas son iguales }
  • 37. Apuntadores y Arreglos (como se relacionan) Un arreglo es una estructura de datos estática que almacena una colección de elementos del mismo tipo bajo un único nombre. Cada elemento en el arreglo se accede mediante un índice o una clave que indica su posición dentro del arreglo. Los arreglos son utilizados para almacenar datos de manera estructurada y para facilitar el acceso y la manipulación de conjuntos de datos relacionados. La memoria Elem 1 Elem 2 Elem 3 Elem 4 Elem 5
  • 38. Arreglos (continuación) Para definir y acceder a un arreglo unidimensional se utiliza la sintaxis siguiente: tipoDeDato nombreDelaVariable [numElementos]; La siguiente rutina en C define un arreglo de 5 posiciones Llena cada posición con números 0 al 40 Los imprime #include <stdio.h> int main() { // Definir un arreglo de 5 posiciones int numeros[5]; // Llenar el arreglo con un bucle del 0 al 4 for (int i = 0; i < 5; ++i) { numeros[i] = i * 10; // Llenar con valores (0, 10, 20, 30, 40) } // Mostrar los valores almacenados en el arreglo printf("Valores en el arreglo:n"); for (int i = 0; i < 5; ++i) { printf("Posición %d: %dn", i, numeros[i]); } return 0; }
  • 39. Apuntadores y arreglos: Los punteros y los arreglos tienen una relación muy estrecha, ya que el nombre de un arreglo es en realidad un puntero al primer elemento de de ese arreglo. Si x es un arreglo unidimensional, la dirección del primer elemento puede ser expresada como: &x[0] o simplemente como x. La dirección del elemento i-ésimo se puede expresar como &x[i] o como (x+i) En este caso la expresión (x+i) no es una operación aritmética convencional, sino una operación con punteros.
  • 40. Si, &x[i] y (x+i) representan la dirección del i-ésimo elemento de x, Entonces: x[i] y *(x+i) representan el contenido de esa dirección, o lo que es lo mismo el valor del i-ésimo elemento de x. Hasta ahora hemos accedido a los valores de los arreglos de la forma x[i], pero ya vimos que también podemos utilizarlos con punteros. Nos puede resultar mas cómodo utilizar la forma x[i] para acceder ala i-ésimo elemento, sin embargo, debemos considerar que la forma *(x+i) es mucho más eficiente que x[i], porlo que suele preferirse cuando la velociad de ejecucón es un factor determinante.
  • 41. En estructuras de datos, una pila es una colección de elementos con dos operaciones principales: PUSH and POP. Estas operaciones siguen el principio de LIFO (Last In, First Out), lo que significa que el último elemento que se inserta es el primero en ser retirado. • Push (Empujar): Agrega un elemento a la pila. • Pop (Retirar): Elimina el elemento más reciente de la pila. La implementación de una pila puede realizarse utilizando un arreglo (array) ó una lista enlazada. PUSH POP TOPE N INICIO
  • 42. #define n 5 int pila[n]; int *p1, *tope; main( ){ int value, opci; tope = pila; p1=pila; PUSH POP TOPE INICIO p1 PILA
  • 43. do { printf("nn 1. Insert en la pila (PUSH)"); printf("n 2. Extraer en la pila (POP)"); printf("n 3. Salir"); printf("nnElija opción: "); scanf("%d",&opci); switch(opci){ case 1: printf("n Dar el elemento o ítem: "); scanf("%d",&value); push(value); break; case 2: pop(); break; case 3: printf("n[FIN DE EJECUCIÓN]"); getch(); break; default: printf("nOpción incorrecta [Pulse tecla]"); getch(); } } while (opci != 3); }
  • 44. void push(int i){ if(p1 >= tope+n) { printf("n Posible desbordamiento de pila.nEl dato no fue insertado"); return; } *(p1)=i; p1++; imprimePila(); }
  • 45. void pop(){ if(p1 == tope) { printf("n La pila esta vacia. No hay datos que extraer"); return; } p1--; printf("nSale el dato %d", *p1); *p1=0; imprimePila(); }
  • 46. void imprimePila(){ for(int i=n-1; i>=0; i--){ printf("n%d", pila[i]); } }
  • 47. Ejercicio: Cual es el resultado de cada una de las siguientes asignaciones int x[100],b,*pa,*pb; x[50]=10; pa=&x[50]; b = *pa+1; b = *(pa+1); pb = &x[10]; *pb = 0; *pb += 2; (*pb)--;. x[0] = *pb--;
  • 48. int x[100],b,*pa,*pb; x[50]=10; pa=&x[50]; b = *pa+1; //Esto es: Al valor que tiene el array de x[50] sumarle 1. //Esto es igual a: b=x[50]+1; => Su valor seria igual a 11. b = *(pa+1); //Esto primero pasa a la siguiente dirección de memoria y luego lo referencia //El resultado es: b = x[51]; Programación II.-Revisión de apuntadores
  • 49. pb = &x[10]; //al puntero pb se le asigna la dirección de x[10] *pb = 0; //Al valor que tiene el puntero se le asigna 0 //Esto es igual que decir: x[10] = 0 *pb += 2; //El valor del puntero se incrementa en dos unidades, es decir x[10] += 2 (*pb)--; //El valor del puntero se decrementa en una unidad. x[0] = *pb--; //A x[0] se le pasa el valor de x[10] y el puntero pb, pasa a apuntar a x[9] //recuerda, que -- es post-decremento, primero asignará y luego restará.
  • 50. #include<stdio.h> #include<conio.h> typedef struct coordenada{ int x; int y; } coor; int main(){ coor *q1; coor w1; q1=&w1; q1->x = 100; q1->y = 200; printf("nq1->x|%d|", q1->x); printf("nq1->y|%d|", q1->y); return 1; Apuntadores de tipo estructura: Ejercicio 14 x y w1 q1 100 200 w1 q1
  • 51. #include<stdio.h> #include<conio.h> typedef union DiferentesTipos{ int x; char z; float y; }uni; int main(){ uni *pt; uni ejem; pt=&ejem; pt->x = 100; Apuntadores a union: Ejercicio 15 X y z ejem pt 100 ejem pt
  • 52. printf("nejem %d", ejem.x); printf("nejem %f", ejem.y); printf("nejem %c", ejem.z); pt->y = 3.14; printf("nnejem %d", ejem.x); printf("nejem %f", ejem.y); printf("nejem %c", ejem.z); pt->z = 'Q'; printf("nnejem %d", ejem.x); printf("nejem %f", ejem.y); printf("nejem %c", ejem.z); return 0; } 3.14 ejem pt Q ejem pt
  • 53. Ejercicio: Manejo de estructuras con apuntadores Supongamos que estamos creando una aplicación para gestionar información de estudiantes. Define una estructura llamada Estudiante, que contenga la siguiente información: Nombre del estudiante (cadena de caracteres) Número de identificación del estudiante (entero) Nota del estudiante (flotante) Luego, crea una función llamada ActualizarNota que tome como parámetros un puntero a una estructura Estudiante y una nueva nota, y actualice la nota del estudiante utilizando el puntero. Finalmente, en la función principal (main), crea un estudiante, muestra su información original, llama a la función ActualizarNota para cambiar la nota y luego muestra la información actualizada del estudiante.
  • 54. #include <stdio.h> #include <string.h> // Definición de la estructura Estudiante struct Estudiante { char nombre[50]; int id; float nota; }; // Función para actualizar la nota del estudiante void actualizarNota(struct Estudiante *estudiante, float nuevaNota) { estudiante->nota = nuevaNota; }
  • 55. int main() { // Crear un estudiante struct Estudiante estudiante1; // Inicializar la información del estudiante strcpy(estudiante1.nombre, "Juan Perez"); estudiante1.id = 12345; estudiante1.nota = 85.5; // Mostrar información original del estudiante printf("Informacion Original:n"); printf("Nombre: %sn", estudiante1.nombre); printf("ID: %dn", estudiante1.id); printf("Nota: %.2fn", estudiante1.nota); // Actualizar la nota del estudiante actualizarNota(&estudiante1, 92.0); // Mostrar información actualizada del estudiante printf("nInformacion Actualizada:n"); printf("Nombre: %sn", estudiante1.nombre); printf("ID: %dn", estudiante1.id); printf("Nota: %.2fn", estudiante1.nota); return 0; }
  • 56. Caso de negocio: Sistema de Registro de Estudiantes Una institución educativa desea implementar un sistema de registro de estudiantes. Cada estudiante tiene la siguiente información asociada: nombre, edad, número de identificación y calificaciones de tres asignaturas, es necesario utilizar apuntadores y estructuras de datos estáticas. Requerimientos del Sistema: • Permitir el registro de hasta 5 estudiantes. • Mostrar la información de un estudiante específico. • Calcular y mostrar el promedio de calificaciones de un estudiante. • Mostrar la información de todos los estudiantes registrados. • Identificar al estudiante con la calificación más alta en una asignatura específica.
  • 57. Caso de negocio: Sistema de Registro de Estudiantes El menú debe mostrar las siguientes opciones: Registrar un estudiante……………………………………………...1 Mostrar la información de un estudiante ( atreves del ID)……….2 Salir…………………………………………………………………….3 Ingrese su opción: Tips: Se debe estar validando el numero de estudiantes permitidos.
  • 58. Memoria Dinámica Es el espacio en memoria que se crea al declarar variables de cualquier tipo de dato [int,char,float...] o derivados [struct, matrices, punteros...]). La memoria que estas variables ocupan no puede cambiarse durante la ejecución y tampoco puede ser liberada manualmente. Memoria Estática Es memoria que se reserva en tiempo de ejecución. Su principal ventaja frente a la estática, es que su tamaño puede variar durante la ejecución del programa. (En C, el programador es encargado de liberar esta memoria cuando no la utilice más). El uso de memoria dinámica es necesario cuando no se sabe el numero exacto de datos/elementos a tratar. Memoria Dinámica
  • 59. Memoria dinámica vs estática, diferencias, ventajas y desventajas La memoria reservada de forma dinámica suele estar alojada en el heap o almacenamiento libre, y la memoria estática en el stack o pila. La pila generalmente es una zona muy limitada. El heap, en cambio, en principio podría estar limitado por la cantidad de memoria disponible durante la ejecución del programa y el máximo de memoria que el sistema operativo permita direccionar a un proceso. La pila puede crecer de forma dinámica, pero esto depende del sistema operativo. En cualquier caso, lo único que se puede asumir es que muy probablemente dispondremos de menor espacio en la pila que en el heap. Diferencias Ventajas La memoria dinámica se puede ir incrementando durante la ejecución del programa, lo cual hace un uso mas eficiente de la memoria. Desventajas Una desventaja de la memoria dinámica es que es más difícil de manejar. La memoria estática tiene una duración fija, que se reserva y libera de forma automática. En contraste, la memoria dinámica se reserva de forma explícita y continúa existiendo hasta que sea liberada, generalmente por parte del programador.
  • 60. Memoria dinámica. Todos los objetos tienen un tiempo de vida, es decir, el tiempo durante el cual se garantiza que el objeto exista. En C, existen 3 tipos de duración: estática, automática y asignada. Las variables globales y las variables locales declaradas con el especificador static tienen duración estática. Se crean antes de que el programa inicie su ejecución y se destruyen cuando el programa termina. Las variables locales no static tienen duración automática. Se crean al entrar al bloque en el que fueron declaradas y se destruyen al salir de ese bloque. Por último duración asignada se refiere a los objetos cuya memoria se reserva de forma dinámica. Esta memoria se crea y se debe liberar de forma explícita por parte del programador. La biblioteca estándar de C proporciona las funciones malloc, calloc y realloc y free, para el manejo de memoria dinámica. Estas funciones están definidas en el archivo de cabecera <stdlib.h>
  • 61. Memoria dinámica. malloc (Memory Allocation) La función malloc reserva un bloque de memoria y devuelve un puntero void que debe ser convertido al tipo de puntero deseado, el parámetro tamaño indica la cantidad de bytes que se deben asignar en la memoria. void *malloc(size_t tamaño); Ejemplo: char * ptr; ptr = (char*)malloc(100); free(ptr); Se esta especificando que es un puntero y el tipo de dato a usar Tamaño de la memoria a reservar
  • 62. Ejercicio 17.- Uso de malloc (ya no usamos variables) #include<stdio.h> #include<stdlib.h> int main(){ int *ptr; ptr= (int *) malloc(sizeof(int)); *ptr=1000; printf("nptr->|%d|", *ptr); free(ptr); return 0; } 1000 ptr 4 bytes int main(){ float *ptr; ptr= (float *) malloc(sizeof(float)); *ptr=1000; printf("nptr->|%f|", *ptr); free(ptr); return 0; }
  • 63. #include <stdio.h> #include <stdlib.h> int main() { int *arreglo; arreglo = (int *)malloc(10 * sizeof(int)); if (arreglo == NULL) { // La asignación de memoria ha fallado printf("Error: No se pudo asignar memoria.n"); return 1; // Salir con código de error } // Utilizar el arreglo asignado // Liberar la memoria cuando ya no sea necesaria free(arreglo); return 0; } arreglo 40 bytes
  • 64. Memoria dinámica, uso de new. El uso de malloc o new son dos formas diferentes de asignar memoria dinámicamente, pero es importante destacar que new no es una función en C, sino que pertenece a C++. • malloc es una función de la biblioteca estándar de C (stdlib.h) que se utiliza para asignar un bloque de memoria de un tamaño específico. • Devuelve un puntero void al inicio del bloque de memoria asignado. • Es necesario realizar un casting explícito al tipo de datos deseado al asignar memoria con malloc. • No inicializa la memoria asignada; el contenido es indeterminado. • new es un operador en C++ que se utiliza para asignar memoria dinámicamente y construir objetos. • Devuelve un puntero al tipo de dato solicitado y automáticamente realiza el casting necesario. • Inicializa la memoria asignada llamando al constructor del tipo de dato. • Especifica automáticamente el tamaño de la memoria según el tipo de datos.
  • 65. #include<stdio.h> int main(){ int *ptr; ptr= new int; *ptr=100; printf("nptr->|%d|", *ptr); delete(ptr); return 0; }
  • 66. #include<stdio.h> #include<stdlib.h> int main(){ char *pt2; int *pt1; float *pt3; pt1 = (int *)malloc(sizeof(int)); pt2=(char*)malloc(sizeof(char)); pt3= (float *) malloc(sizeof(float)); *pt2='W'; int variable=10; *pt1 = variable; *pt3=3.1416; printf("n ptr1->|%d| n",*pt1); printf("npt2->|%c|",*pt2); printf("nptr->|%f|", *pt3); free(pt1); free(pt2); free(pt3) return 0; } #include<stdio.h> int main(){ char *pt2; int *pt1; float *pt3; pt1 = new int; pt2= new char; pt3 = new float; *pt2='W'; int variable=10; *pt1 = variable; *pt3=3.1416; printf("n ptr1->|%d| n",*pt1); printf("npt2->|%c|",*pt2); printf("nptr->|%f|", *pt3); delete (pt3); delete (pt1); delete(pt2); return 0; }
  • 68. #include<stdio.h> #include<stdlib.h> int main(){ int *q1, *q2, *aux; q1=(int*)malloc(sizeof(int)); q2= (int *) malloc(sizeof(int)); aux =(int *) malloc(sizeof(int)); *q1=100; *q2=300; *aux=0; printf("nq1->|%d|", *q1); printf("nq2->|%d|", *q2); printf("naux->|%d|", *aux); *aux=*q1; *q1=*q2; *q2=*aux; printf("nnDespues del intercambion"); printf("nq1->|%d|", *q1); printf("nq2->|%d|", *q2); printf("naux->|%d|", *aux); free(q1); free(q2); free(aux); return 0; } 100 q1 300 q2 0 aux 300 q1 100 q2 100 aux int
  • 70. #include<stdio.h> #include<stdlib.h> int main(){ int *q1, *q2, *aux; q1=(int*)malloc(sizeof(int)); q2= (int *) malloc(sizeof(int)); aux = NULL; *q1=100; *q2=300; printf("nq1->|%d|", *q1); printf("nq2->|%d|", *q2); printf("naux->NULL"); aux=q2; q2=q1; q1=aux; printf("nnDespues del intercambio de ligasn"); printf("nq1->|%d|", *q1); printf("nq2->|%d|", *q2); printf("naux->|%d|", *aux); free(q1); free(q2); free(aux); Return 0; }
  • 71. Simulación de un nodo con estructuras. #include<stdlib.h> #include<stdio.h> typedef struct nodo{ int entero; char caracter; } nod; int main(){ nod *ptr; ptr=(nod*)malloc(sizeof(nod)); ptr->entero=300; ptr->caracter='M'; printf("nptr->|%d|%c|", ptr->entero,ptr->caracter); printf("nSe ha liberado la memoria"); free(ptr); return 0; } 300 ptr M
  • 72. Lista ligada con estructuras. #include<stdlib.h> #include<stdio.h> typedef struct nodo{ char x; int y; struct nodo *z; } nodi; int main(){ nodi *ptr; ptr=(nodi*)malloc(sizeof(nodi)); ptr->x='a'; ptr->y=50; ptr->z=(nodi*) malloc(sizeof(nodi)); ptr->z->x= 'b'; ptr->z->y= 60; ptr->z->z= NULL; printf("nptr->|%c|%d|->|%c|%d|", ptr->x,ptr->y, ptr->z->x,ptr->z->y); printf("nSe ha liberado la memoria"); free(ptr->z); free(ptr); return 0; } a ptr 50 a x y z b 60 a x y z null
  • 73. Punteros a punteros: #include<stdio.h> int main(){ int n; int *ptr1; int **ptr2; ptr1=&n; ptr2=&ptr1; **ptr2=15; printf("n El valor de n es:%d“, n); return 0; } ptr1 n ptr2
  • 74. Ejercicio: Punteros de tipo estructura. ptr nom ptrA ptrB ptrC Juan q 9.5 s 8 t flo ent nom Rocio q 10 s 15 t flo ent
  • 75. #include <stdio.h> #include<string.h> #include<stdlib.h> #define largo 25 typedef struct cadena{ char nom[largo]; struct cadena *q; }cad; typedef struct flotante{ float flo; struct flotante *s; }flota; typedef struct entero{ int ent; struct entero *t; }enter; typedef struct conjunto{ cad *ptrA; flota *ptrB; enter *ptrC; }con; nom Juan q 9.5 s flo 8 t ent ptrA ptrB ptrC
  • 77. printf("nptr1->|"); printf("tptrA -> |%s| q|-> |%s|NULL|",ptr1->ptrA->nom,ptr1->ptrA->q->nom); printf("n tptrB -> |%.1f| s|-> |%.1f|NULL|",ptr1->ptrB->flo,ptr1->ptrB->s->flo); printf("n tptrC -> |%d| t|-> |%d|NULL|",ptr1->ptrC->ent,ptr1->ptrC->t->ent); printf("n t|"); ptr1 nom ptrA ptrB ptrC Juan q 9.5 s 8 t flo ent nom Rocio q 10 s 15 t flo ent
  • 78. printf("nSe ha liberado la memoria"); free(ptr1->ptrA->q); free(ptr1->ptrA); free(ptr1->ptrB->s); free(ptr1->ptrB); free(ptr1->ptrC->t); free(ptr1->ptrC); free(ptr1); } ptr1 nom ptrA ptrB ptrC Juan q 9.5 s 8 t flo ent nom Rocio q 10 s 15 t flo ent
  • 79. Ejercicio: Punteros a estructuras conformada de 2 estructuras. p x[20] p1 p2 Juan q 10.9 pt1 f i x[20] Rocio q f i 3 20.6 pt1 4 f i 6.6 pt1 5
  • 80. Ejercicio: Programar el escenario reservando memoria con malloc. p p1 p2 p3 i f 10 pt1 3.1416 i f 20 pt1 4.1416 x c Programacion II 30.45 100 q
  • 81. Punteros: Funciones y procedimientos. #include<stdlib.h> #include<stdio.h> void procedimientoX(int *ptr){ *ptr +=2; } int * funcionY(){ int *qr= (int *) malloc(sizeof(int)); *qr=900; return qr; } int main(){ int *p= (int *) malloc(sizeof(int)); *p=800; printf("nValor original de *p es: %d", *p); procedimientoX(p); printf("nValor de *p despues de regresar del procedimientoX es: %d", *p); free(p); p=funcionY(); printf("nValor de *p despues de regresar de la funcionY es: %d", *p); free(p); return 0; }
  • 82. Reasignación de memoria (realloc): La función realloc() cambia el tamaño del bloque de memoria reservado previamente. La sintaxis es: void *realloc(*ptr, nuevo_tamaño * bytes_tipo ); El argumento ptr apunta al principio del bloque de memoria asignado, mientras que nuevo_tamaño indica el nuevo tamaño del bloque y bytes_tipo es el número de bytes correspondiente al tipo de dato. El contenido permanecerá inalterado hasta el mínimo entre el tamaño inicial y el nuevo; la memoria recién asignada quedará sin inicializar. Es importante verificar si ptr (el puntero) es NULL, en el caso afirmativo es necesario notificar un error en la reasignación de memoria.
  • 83. Reasignación de memoria (realloc): La función realloc() puede mover la ubicación de almacenamiento del bloque designado previamente, ya que todos los elementos que lo conforman el bloque de almacenamiento reasignado deben permanecer contiguos. Si no hay suficiente almacenamiento para crecer el tamaño, el bLoque no se modifica quedando en su estado original y realloc() devuelve NULL. char *texto; texto=(char*) malloc(100 * sizeof(char)); texto=(char*) realloc(texto, 500*sizeof(char));
  • 84. #include<stdlib.h> #include<stdio.h> int main(){ float numf; int cuenta=0, i; float *numerosLeidos=NULL; float *mas_numeros=NULL; do{ printf("nIngresa un dato de punto decimal (0 para finalizar): "); scanf("%f", &numf); if(cuenta == 0){ mas_numeros=(float *)malloc(sizeof(float)); printf("nSe uso malloc"); } else{ mas_numeros= (float *)realloc(numerosLeidos, (cuenta+1)*sizeof(float)); printf("nSe uso realloc"); }
  • 85. if(mas_numeros!=NULL){ numerosLeidos=mas_numeros; numerosLeidos[cuenta]=numf; } else{ free(numerosLeidos); printf("nError en la reasignacion de memoria"); exit(1); } for(i=0;i<=cuenta; i++) printf("n %.2f", numerosLeidos[i]); cuenta++; }while(numf!=0); free(numerosLeidos); return 0; }
  • 86. Asignación de memoria (calloc): La función calloc() funciona de modo similar a malloc, pero además de reservar memoria, inicializa a 0 la memoria reservada. Se usa comúnmente para arreglos y matrices. Está definida de esta forma: void *calloc(size_t nmemb, size_t size); El parámetro nmemb indica el número de elementos a reservar, y size el tamaño de cada elemento. El ejemplo anterior se podría reescribir con calloc de esta forma:
  • 87. #include<stdlib.h> #include<stdio.h> int ren, col; void leearreglo(int **mat){ for(int i=0;i<ren;i++) for(int j=0;j<col;j++){ printf("n [%d,%d]: ",i,j); scanf("%d", &mat[i][j]); } } void imprimearreglo(int **mat){ for(int i=0;i<ren;i++){ for(int j=0;j<col;j++){ printf(" [%d] ",mat[i][j]); } printf("n "); } }
  • 88. int main(){ int **mat; printf("nDar numero de renglones: "); scanf("%d", &ren); printf("nDar numero de columnas: "); scanf("%d", &col); mat=(int **) calloc (ren, sizeof(int*)); for(int i=0; i<ren; i++) mat[i]=(int *) calloc(col, sizeof(int)); leearreglo(mat); imprimearreglo(mat); return 0; } En este programa, se utilizan ** en la asignación de memoria porque estás trabajando con un arreglo bidimensional dinámico. Un arreglo bidimensional dinámico se representa como un puntero a punteros, y cada puntero individual apunta a un arreglo unidimensional. Cuando se asigna memoria dinámica para un arreglo bidimensional, se requiere un doble puntero para manejar tanto los renglones como las columnas del arreglo.
  • 89. Ejercicio: Manejo de arreglos dinámicos. Escribe un programa en C que solicite al usuario un número entero n. Luego, crea dinámicamente un arreglo de enteros de tamaño n, e inicialízalo con valores ingresados por el usuario. Después, encuentra el valor máximo y la posición en la que se encuentra en el arreglo. Finalmente, libera la memoria asignada.
  • 90. Conclusión: ¿Para que nos sirven los apuntadores? • Acceso directo a la memoria: Esto es útil para operaciones eficientes y para trabajar con estructuras de datos complejas. • Dinámica de asignación de memoria: Los punteros se utilizan para asignar y liberar memoria dinámicamente durante la ejecución del programa. • Funciones y paso por referencia: Los punteros permiten pasar parámetros por referencia a funciones, lo que significa que la función puede modificar el contenido de la memoria apuntada por el puntero. Esto es especialmente útil cuando se trabaja con grandes conjuntos de datos o se quiere evitar la sobrecarga de copiar datos. • Manipulación de cadenas de caracteres: En C, las cadenas de caracteres son esencialmente matrices de caracteres. Los punteros son ampliamente utilizados para manipular y trabajar con cadenas de caracteres de manera eficiente.
  • 91. • Estructuras de datos complejas: Los punteros permiten la implementación de estructuras de datos más complejas, como listas enlazadas, árboles y grafos. Estas estructuras de datos pueden ser gestionadas eficientemente utilizando punteros para enlazar nodos o elementos. • Arreglos dinámicos: Los punteros permiten la creación de arreglos dinámicos, cuyo tamaño puede ser determinado en tiempo de ejecución. Esto es útil cuando no se conoce el tamaño exacto del conjunto de datos antes de que el programa se ejecute. • Operaciones de bajo nivel: Los punteros proporcionan un nivel de abstracción más bajo que otros tipos de datos en C, lo que permite realizar operaciones de bajo nivel, como la manipulación directa de bits o la implementación de estructuras de datos personalizadas. Aunque los punteros en C brindan un control preciso sobre la memoria, también pueden conducir a errores de programación como desreferenciación incorrecta o fugas de memoria si no se manejan correctamente. Por lo tanto, se debe tener cuidado al trabajar con punteros y seguir buenas prácticas de programación para evitar problemas.