Está en la página 1de 27

ARREGLOS Y

PUNTEROS
PUNTEROS
Los punteros en C++ sirven para señalar objetos, y también para manipularlos.
Para entender qué es un puntero veremos primero cómo se almacenan los datos en un
ordenador.
Memoria de un bit de ferrita.
Recordemos q la memoria de un ordenador está compuesta por unidades básicas llamadas bits.
Cada bit sólo puede tomar dos valores, normalmente denominados alto y bajo, ó 1 y 0. Pero
trabajar con bits no es práctico, y por eso se agrupan.
Cada grupo de 8 bits forma un byte u octeto. En realidad el microprocesador, y por lo tanto
nuestro programa, sólo puede manejar directamente bytes o grupos de dos o cuatro bytes. Para
acceder a los bits hay que acceder antes a los bytes.
Cada byte de la memoria de un ordenador tiene una dirección, llamada dirección de memoria.

Por otra parte, la mayor parte de los objetos que usamos en nuestros programas no caben en
una dirección de memoria. La solución utilizada para manejar objetos que ocupen más de un
byte es usar posiciones de memoria correlativas. De este modo, la dirección de un objeto es la
dirección de memoria de la primera posición que contiene ese objeto.
Dicho de otro modo, si para almacenar un objeto se precisan cuatro bytes, y la dirección de
memoria de la primera posición es n, el objeto ocupará las posiciones desde n a n+3, y la
dirección del objeto será, también, n.
PUNTERO
S
Intentemos ver con mayor claridad el funcionamiento de los punteros. Podemos
considerar la memoria del ordenador como un gran array, de modo que podemos
acceder a cada celda de memoria a través de un índice. Podemos considerar que la
primera posición del array es la 0 celda[0].

Indice <-> puntero


Si usamos una variable para almacenar el índice, por ejemplo, indice=0,
entonces celda[0] == celda[indice]. Finalmente, si prescindimos de la notación de
los arrays, podemos ver que el índice se comporta exactamente igual que un
puntero.
El puntero indice podría tener por ejemplo, el valor 3, en ese caso, el valor
apuntado por indice tendría el valor 'val3'.
Las celdas de memoria tienen una existencia física, es decir son algo real y existirán
siempre, independientemente del valor del puntero. Existirán incluso si no existe el
puntero.
De forma recíproca, la existencia o no existencia de un puntero no implica la
existencia o la inexistencia del objeto.
QUE ES UN PUNTERO
Una de las herramientas mas potentes son los punteros, que ofrecen la
capacidad de manipular directamente la memoria del ordenador. Un puntero
es una variable contiene una dirección de memoria. Asimismo como el puntero
es una variable, por lo tanto puede ser almacenado en un arreglo.
Declaración:
tipo *nombre_variable;

tipo *nombre_variable[tamaño];

xyz xyz
Qwertyuiopasdfg Qwertyuiopasdfg
Abcdefghi Abcdefghi

1
OBTENER PUNTEROS A OBJETOS
Ejemplos:

int *pEntero;
char *pCaracter;
struct stPunto *pPunto;
Los punteros sólo pueden apuntar a objetos de un tipo determinado, en el ejemplo, pEntero sólo puede
apuntar a un objeto de tipo int.
La forma:
<tipo>* <identificador>;
con el (*) junto al tipo, en lugar de junto al identificador del objeto, también está permitida. De hecho,
también es legal la forma:
<tipo> * <identificador>;
Los punteros apuntan a objetos, por lo tanto, lo primero que tenemos que saber hacer con nuestros
punteros es asignarles direcciones de memoria válidas de objetos.
Para averiguar la dirección de memoria de cualquier objeto usaremos el operador de dirección (&),
que leeremos como "dirección de".
Por supuesto, los tipos tienen que ser "compatibles", no podemos almacenar la dirección de un objeto
de tipo char en un puntero de tipo int.
Por ejemplo:
int A;
int *pA;
pA = &A;
Según este ejemplo, pA es un puntero a int que apunta a la dirección donde se almacena el valor del
EJEMPLO
1 #include <iostream>
#include <conio.h>
using namespace std;

int main(){

int numero, *dir_numero; //creo puntero tipo entero

numero=100;//asigno valor a variable

dir_numero = &numero;//guardo posición exacta de la variable en la memoria

cout << "El numero es:"<< *dir_numero<< endl; //mostrar el valor guardado en la
posición
cout << "El numero es:"<< &numero<< endl;
cout << "La direccion de la memoria:"<< dir_numero<< endl;

getch();
return 0;
}
EJEMPLO
#include <iostream>
2 #include <conio.h>
using namespace std;

int main(){

int numero, *dir_numero; //creo puntero tipo entero


cout << "INTRODUZCA UN NUMERO:"<< endl;
cin >> numero;

dir_numero = &numero;//guardo posicion exacta de la variable en la memoria

if (*dir_numero%2==0){
cout << "El numero "<< *dir_numero<<" es par"<< endl; //mostrar el valor guardado
en la posicion
cout << "La posicion en memoria: "<< dir_numero<< endl;
}
else{
cout << "El numero "<< *dir_numero<<" es impar"<< endl; //mostrar el valor
guardado en la posicion
cout << "La posicion en memoria: "<< dir_numero<< endl;
}getch();
return 0;
}
PUNTEROS CON ARREGLOS O ARRAY
Hemos visto que los arrays pueden ser una potente herramienta para el
almacenamiento y tratamiento de información, pero tienen un inconveniente: hay que
definir su tamaño durante el diseño del programa, y después no puede ser modificado.
La gran similitud de comportamiento de los punteros y los arrays nos permiten crear
arrays durante la ejecución, y en este caso además el tamaño puede ser variable.
Usaremos un puntero normal para crear vectores dinámicos, uno doble para tablas, etc.

Por ejemplo, crearemos una tabla dinámicamente. Para ello se usan los punteros a
punteros. Veamos la declaración de un puntero a puntero:
int **tabla;
"tabla" es un puntero que apunta a un objeto de tipo puntero a int.
Sabemos que un puntero se comporta casi igual que un array, por lo tanto nada nos
impide que "tabla" apunte al primer elemento de un array de punteros:
int n = 134;
tabla = new int*[n];
EJEMPLO
1 #include <iostream>
#include <conio.h>
using namespace std;

int main(){

int numero[]={1,2,3,4,5};
int *dir_numero; //creo puntero tipo entero

dir_numero = &numero[0];//guardo posición


exacta de la variable en la memoria
dir_numero = numero;

for ( int i=0;i<5;i++) {


cout << "La posicion de memoria "<<
numero[i]<<" es "<<dir_numero++<<endl;
}
getch();
return 0;
}
EJEMPLO
2 #include <stdio.h>
 
int main( ){  
 
int lista[20],i,*list;
for (i=0;i<10;i++) // Lleno los 10 primeros elementos
lista[i]=i*i;
list = &lista[10]; //Pasamos la dirección del elemento 10 al
puntero list
for(i=0;i<10;i++)
*(list+i)=-i*i; //Lleno los siguientes 10 elementos usando el
puntero list
for(i=0;i<20;i++) {
printf("lista[%d]=%d\n",i,lista[i]);//Muestra los 20 elementos del
arreglo lista
}
return 0;
}
CREACIÓN DE OBJETOS EN FORMA DINÁMICA
New y Delete.
En C++ se puede asignar memoria utilizando el operador new y liberarse mediante el
operador delete. Estos operadores no se pueden combinar unos con otros, es decir debe
llamarse a delete solo con un puntero obtenido mediante new.
Los objetos también se les puede pasar un valor inicial con la sentencia new.

También se pueden crear arreglos de objetos asignados dinámicamente, estos arrays


pueden utilizar la sentencia new.
La sintaxis general es:
EJEMPLO
#include<iostream>
1 using namespace std;
int main( )
{int *puntero; // Se declara el puntero
int i;
puntero= new int[5]; /* al puntero se le asigna la memoria en forma dinámica y se crea
el arreglo */
system("cls");
if (!puntero) // Se verifica que exista el puntero
{ cout<<"Error de asignacion\n";
exit(1);
}
for (i = 0; i < 5; i++) // Para introducir datos al arreglo puntero[i] = i+1;

for (i = 0; i < 5; i++) // Para mostrar los datos del arreglo


{ cout<<"Este es el entero en puntero["<<i<<"]:";
cout<<puntero[i]<<"\n";
}
delete[ ] puntero; // Se libera la memoria reservada
system("pause");
return 0;
}
EJEMPLO 2
//Programa de asignación dinámica de arreglos
#include <iostream>
#include <conio.h>
#include <stdlib.h>
using namespace std;

void solicitar_notas();//prototipo de funcion


void mostrar_notas();
int cantidad_notas, *pnotas;

int main( )
{
cout<<"\t\t\tBIENVENIDOS AL SISTEMA DE NOTAS"<< endl;
solicitar_notas();
mostrar_notas();
delete[] pnotas;

getch();
system("pause");
return 0;

void solicitar_notas(){
cout<<"Introduzca las cantidad de notas a registrar\n";
cin >> cantidad_notas;
pnotas = new int[cantidad_notas]; // creo arreglo y reservo la primera posicion
for (int i = 0; i <cantidad_notas; i++){
cout<<"Ingrese la notas \n";
cin >> pnotas[i];
}
}

void mostrar_notas(){
cout<<"Las notas son: \n";
for (int i = 0; i <cantidad_notas; i++){
cout<< pnotas[i]<< endl;
}
}
ARGUMENTOS EN LA FUNCIÓN MAIN

Dos formas equivalentes para declarar los argumentos:


main(int argc, char **argv){....}

main(int argc, char *argv[]){....}

Ejemplo: $z ertyuiopasdfg cdefghi


argv:
z\0

ertyuiopasdfg\0

cdefghi\0
0
1
ARGUMENTO DE MAIN()

• El estándar ANSI define dos argumentos de


main(): argc y argv.
• Permite pasar información al programa de C++
mediante argumentos de línea de órdenes.
./programa argumento1 argumento2 ….

Línea de órdenes

argc : contiene el número de argumentos, y es


entero.
argv : es un puntero a un arreglo de caracteres.

23
EJEMPLO
1
#include <iostream>
using namespace std;

int main(int argc, char *argv[]){


int i;
for(i=0;i<argc;i++){
cout <<"El argumento es:" <<i<<endl
<<argv[i]<<endl;
return 0;
}
}

23
ESTRUCTURAS
ESTRUCTURAS
Las estructuras son el segundo tipo de datos estructurados. Al contrario que los arrays,
las estructuras nos permiten agrupar varios datos, que mantengan algún tipo de
relación, aunque sean de distinto tipo, permitiendo manipularlos todos juntos, usando
un mismo identificador, o cada uno por separado.
Las estructuras son llamadas también muy a menudo registros, o en inglés records.
Tienen muchos aspectos en común con los registros usados en bases de datos. Y
siguiendo la misma analogía, cada objeto de una estructura se denomina a menudo
campo, o field. struct [<identificador>] { [<tipo>
Sintaxis: <nombre_objeto>[,<nombre_objeto>,...]]; }
[<objeto_estructura>[,<objeto_estructura>,.
..];

El identificador de la estructura es un nombre opcional para referirse a la estructura.


Los objetos de estructura son objetos declarados del tipo de la estructura, y su
inclusión también es opcional. Sin bien, aún siendo ambos opcionales, al menos uno
de estos elementos debe existir.
En el interior de una estructura, entre las llaves, se pueden definir todos los
elementos que consideremos necesarios, del mismo modo que se declaran los
objetos.
ESTRUCTURAS
En C++ la palabra struct es opcional en la declaración de objetos, al contrario
de lo que sucede en C, en el que es obligatorio usarla.
Ejemplo:

struct Persona {
char Nombre[65];
char Direccion[65];
int AnoyNacimiento;
} Fulanito;

Este ejemplo define la estructura Persona y declara a Fulanito como un objeto


de ese tipo. Para acceder al nombre de Fulanito, por ejemplo para visualizarlo,
usaremos la forma:
cout << Fulanito.Nombre;
EJEMPLO 1 include <iostream>
using namespace std;
struct stPareja {
int A, B;
Usando constructores nos
aseguramos los valores iniciales int LeeA()
para los elementos de la { return A;} // Devuelve el valor de A
estructura. Veremos que esto
int LeeB() {
puede ser una gran ventaja, return B;} // Devuelve el valor de B
sobre todo cuando combinemos
estructuras con punteros, en void GuardaA(int n) {
capítulos posteriores. A = n;} // Asigna un nuevo valor a A

void GuardaB(int n) {
B = n;} // Asigna un nuevo valor a B
} Par;
int main() {
Par.GuardaA(15);
Par.GuardaB(63);
cout << Par.LeeA() << endl;
cout << Par.LeeB() << endl;
return 0; }
INICIALIZACION DE ESTRUCTURAS
De un modo parecido al que se inicializan los arrays, se pueden inicializar estructuras,
tan sólo hay que tener cuidado con las estructuras anidadas. Por ejemplo:
struct A {
int i;
int j;
int k;
};

struct B {
int x;
struct C {
char c;
char d;
} y;
int z;
};

A ejemploA = {10, 20, 30};


B ejemploB = {10, {'a', 'b'}, 20};
Cada nueva estructura anidada deberá inicializarse usando la pareja correspondiente
de llaves "{}", tantas veces como sea necesario.
ASIGNACION DE ESTRUCTURAS
La asignación de estructuras está permitida, pero sólo entre objetos del
mismo tipo de estructura, (salvo que se usen constructores), y funciona
como la intuición nos dice que debe hacerlo.
Veamos un ejemplo:
struct Punto {
int x, y;
Punto() {x = 0; y = 0;}
} Punto1, Punto2;

int main() {
Punto1.x = 10;
Punto1.y = 12;
Punto2 = Punto1;
}
La línea Punto2 = Punto1; equivale a Punto2.x = Punto1.x; Punto2.y =
Punto1.y;.
ASIGNACION DE ESTRUCTURAS
Mediante este constructor podemos asignar valores iniciales en la declaración:
complejo c1(10.23, 213.22);
Los números reales se consideran un subconjunto de los imaginarios, en los que la parte
imaginaria vale cero. Esto nos permite crear otro constructor que sólo admita un valor real:
struct complejo {
complejo() { real=0; imaginario = 0; }
complejo(double r, double i) { real=r; imaginario = i; }
complejo(double r) { real=r; imaginario = 0; }
double real;
double imaginario;
};

Este constructor nos permite, como en el caso anterior, inicializar un valor de un complejo en la
declaración, pero también nos permite asignar un valor double a un complejo, y por el sistema
de promoción automático, también podemos asignar valores enteros o en coma flotante:
complejo c1(19.232);
complejo c2 = 1299.212;
int x = 10;
complejo c3 = x;
Este tipo de constructores se comportan como conversores de tipo, nada nos impide crear
constructores con cualquier tipo de parámetro, y tales constructores se podrán usar para
convertir cualquier tipo al de nuestra estructura.
ARRAY DE ESTRUCTURAS
La combinación de las estructuras con los arrays proporciona una potente
herramienta para el almacenamiento y manipulación de datos.
Ejemplo:
struct Persona {
char Nombre[65];
char Direccion[65];
int AnoyNacimiento;
} Plantilla[200];

Vemos en este ejemplo, declarar el array Plantilla que contiene los datos relativos a
doscientas personas.
Podemos acceder a los datos de cada uno de ellos:
cout << Plantilla[43].Direccion;
O asignar los datos de un elemento de la plantilla a otro:
Plantilla[0] = Plantilla[99];
ESTRUCTURAS ANIDADAS
También está permitido anidar estructuras. Ejemplo:
struct stDireccion {
char Calle[64];
int Portal;
int Piso;
char Puerta[3];
char CodigoPostal[6];
char Poblacion[32];
};
struct stPersona {
struct stNombre {
char Nombre[32];
char Apellidos[64];
} NombreCompleto;
stDireccion Direccion;
char Telefono[10];
};
...
Por ejemplo para declarar un objeto del tipo stNombre hay que utilizar el operador de acceso (::):
stPersona::stNombre NombreAuxiliar;
Sin embargo para declarar un objeto de tipo stDireccion basta con declararla:
stDireccion DireccionAuxiliar;
PROBLEMA PROPUESTO

1. Escribir un programa que almacene en un array los nombres y números de


teléfono de 10 personas. El programa debe leer los datos introducidos por el
usuario y guardarlos en memoria (en el array). Después debe ser capaz de buscar
el nombre correspondiente a un número de teléfono y el teléfono correspondiente
a una persona. Ambas opciones deben se accesibles a través de un menú, así
como la opción de salir del programa. El menú debe tener esta forma, más o
menos:
a) Buscar por nombre
b) Buscar por número de teléfono
c) Salir

Pulsa una opción.


GRACIAS

También podría gustarte