domingo, 28 de noviembre de 2010

Funcion inline ( puntos extra)

La palabra reservada inline es utilizada en C++ y se aplica a una función. Esta indica al compilador que cada llamado a la función inline deberá ser reemplazado por el cuerpo de esta función. En la practica la función inline es utilizado solo cuando las funciones son pequeñas para evitar generar un ejecutable de tamaño considerable.

La palabra reservada inline tiene la ventaja de acelerar un programa si éste invoca regularmente a la función inline. Permite resumir considerablemente el código, en particular para los accesadores de una clase. Un accesador de clase es típicamente una función de una línea.

ejemplo :

main.cpp
#include <iostream>
inline void mi_funcion(){
  std::cout << "me gustan los tapires" << std::endl;
}

class mi_estructura_t{
  protected:
    int x;
  public:
    mi_estructura_t(int x0):x(x0){}
    inline int get_x() const{
      return x;
    }
};

int main(){
  mi_funcion();
  mi_estructura_t s(7);
  std::cout << s.get_x() << std::endl;
  return 0;

inline y encabezados
 inline permite declarar e implementar funciones directamente en el encabezado (.hpp) sin peligro de multi-definición. El símbolo de la función inline no aparece nunca explícitamente durante el enlazado ya que todos los llamados a esta función han sido reemplazados. Recordemos que si una función no es inline, es implementada en un encabezado, y este encabezado es incluido en varios lugares, el compilador devolverá un error de definición múltiple.

Recordemos que entre las funciones templates, únicamente las funciones inline pueden ser implementadas en el encabezado. Una función template puede perfectamente ser inline.

Si deseamos codificar una función inline declarada en un encabezado (.hpp) pero implementada en un archivo fuente (.cpp) sólo el prototipo del encabezado debe contener la palabra reservada inline.  
archivo.hpp
inline void f();


archivo.cpp:

#include <iostream>
#include "fichier.hpp"

void f(){
  std::cout << "plop !" << std::endl;

codigos javascript (puntos extra)

El código siguiente, muestra un mensaje en el ingreso a nuestra web.

<html>
<head>
<title>Java Script</title>
<script language="JavaScript">
<!--
(!alert("Bienvenido a mi página"))
//-->
</script>
</head>
<body bgcolor="">

<!-- Inserted by miarroba -->
<script type="text/javascript" src="http://hosting.miarroba.info/?d=hosting&h=program.webcindario.com&t=1291003776&k=e457c4794fc9a86fbbfa56b9a852377d"></script>
<!-- Inserted by miarroba -->
</body>
</html>

 

codigos haskell (puntos extra)

Sencillo programa que de una lista de lista de números enteros, muestra en una lista de dos tupla de manera numérica y literal cuantas veces se repiten los elementos de la primera lista. 
rep::Int->[Int]->Int->String
rep _ [] cont="se repite "++show(cont)++" veces"
rep n (x:xs) cont|n==x=rep n xs (cont+1)
     |otherwise=rep n xs cont
conc::[[Int]]->[Int]
conc []=[]
conc (x:xs)=x++conc xs
prin::Int->[[Int]]->(Int,String)
prin n (x:xss)=(n,a)
     where p=conc (x:xss)
     a=rep n p 0
ps::[Int]->[[Int]]->[(Int,String)]
ps [] _=[]
ps (x:xs) xss=k:(ps xs xss)
     where k=prin x xss
pr::[[Int]]->[(Int,String)]
pr (x:xss)=ps x (x:xss) 

Dada una lista de dos tuplas, los cuales serán los elementos "x" e "y" necesarioas para una regresion por mínimos cuadrados la función principal devuelve el número de pares ordenados, la sumatoria de x, sumatoria de y la sumatoria de los cuadrados, la sumatoria del producto de "x" "y", el valor de A y el valor de B de la regresión;

Código fuente

dx::[(Double,Double)]->Double->[Double]
dx [] _=[]
dx (x:xs) n=(k)**n:dx xs n
     where k=fst x
dy::[(Double,Double)]->Double->[Double]
dy [] _=[]
dy (x:xs) n=(k)**n:dy xs n
     where k=snd x
mul::[(Double,Double)]->[Double]
mul []=[]
mul (x:xs)=(k*h):mul xs
     where k=fst x
     h=snd x
regA::Double->Double->Double->Double->Double->Double
regA sx sy sxx sxy n=(sxx*sy-sxy*sx)/(n*sxx-sx*sx)
regB::Double->Double->Double->Double->Double->Double
regB sx sy sxx sxy n=(n*sxy-sx*sy)/(n*sxx-sx*sx)
cn::[(Double,Double)]->Double
cn []=0
cn (x:xs)=1 + cn xs
regresion::[(Double,Double)]->String
regresion (xs)="n="++show(k)++" x="++show(l)++" sum(x)="++show(s)++
     " y="++show(o)++" sum(y)="++show(p)++" x*y="++show(q)++
     " sum(x*y)="++show(r)++" x^2="++show(t)++" sum(x^2)="++show(u)++
      " y^2="++show(v)++" sum(y^2)="++show(w)++" regresion:"++show(a)++
      show(b)
      where k=length xs
      l=dx (xs) 1
      s=sum(l)
      o=dy (xs) 1
      p=sum(o)
      q=mul xs
      r=sum(q)
      t=dx (xs) 2
      u=sum(t)
      v=dy (xs) 2
      w=sum(v)
      z=cn xs
      a=regA s p u r z
      b=regB s p u r z 

Programa que de una lista de listas de números enteros, muestra en una lista de dos tuplas, los coeficientes que se pueden formar, y las raices que se pueden obtener de las ecuaciones cuadrádicas formadas. 
un::[[Float]]->[Float]
un []=[]
un (x:xs)=x++un xs

sep::[Float]->(Float,Float,Float)
sep (x:xs)=(x,y,z)
        where y= head xs
              z= last xs
lis::[Float]->[(Float,Float,Float)]
lis []=[]
lis (xs)|k==3=(sep m):(lis (drop 3 xs))
        |otherwise=[]
           where m=take 3 xs
                 k=length m

sol::(Float,Float,Float)->(Float,Float)
sol (a,b,c)|z>0=(((-b+sqrt(z))/(2*a)),((-b-sqrt(z))/(2*a)))
           |otherwise=(-1999,-1999)
           where z=b^2-4*a*c

prin::[(Float,Float,Float)]->[(Float,Float)]
prin []=[]
prin (x:xs)=(sol x):prin xs

raiz::[[Float]]->String
raiz xs="coeficientes"++show(q)++"resultados"++show(r)
       where p=un xs
             q=lis p
             r=prin q

        


Codigos en Java (puntos extra)

hola compañeros , aki les pondre algunos codigos  de algunos programas que e hecho ,  disculpen que no ponga imagenes pero es que por mas que trato no puedo hacer que compile , pero bueno espero que les sirva y si notan algun error porfavor haganmelo saber.

Este programa devuelve la factorial de un numero introducido por teclado
import java.io.*;
class  archivo
{
  public static void main (String arg []) throws IOException
   {int n,a,x;
BufferedReader dat=new BufferedReader (new InputStreamReader(System.in));
  do{
     System.out.print("Ingrese el numero: ");
    n=Integer.parseInt(dat.readLine());
    a=n;x=n;
    if(n>33){System.out.println("ingrese un numero menor a 34");}
    }while(a>33);
  do {n=n-1;
      a=a*n;
     }while(n>1);
   
    System.out.println("la factorial de "+x+" es: " +a);
   }
}
     


Un número capicua es aquel que representa el mismo valor con los dígitos al revés. Este código invierte los dígitos de un número entero y verifica si este es o no un número capicua.   . 

 import java.io.*;
class capicua
{
    public static void capi (int n)
    {
    int r,s=0,k;
    k=n;
    while(n!=0)
    {  
    r=n%10;
    s=s*10+r;
    n=n/10;
         }
         System.out.print("\nel numero invertido es :"+s);
     if(s==k)
    System.out.println ("\nEs capicua ");
    else
    System.out.println ("\nNo es capicua ");
    }
              
    public static void main (String orgs[])throws IOException
    {
    int n;
    BufferedReader in=new BufferedReader(new InputStreamReader(System.in));
    System.out.print("Introducir numero decimal entero:");
    n=Integer.parseInt(in.readLine());
    capi(n);
    }
}
  

El código siguiente realiza la lectura de un vector, del cual selecciona los elementos pares,para imprimirlos posteriormente.    
import java.io.*;
class vector{
    public static void main(String arg[])throws IOException
      {
    int i,n,pares[];
  BufferedReader mn=new BufferedReader(new InputStreamReader(System.in));
  System.out.print("ingrese limite del vector:");
  n=Integer.parseInt(mn.readLine());
  pares=new int[n];
   
     for(i=0;i<n;i++)
     {System.out.print("a"+"["+i+"]"+"=");
       pares[i]=Integer.parseInt(mn.readLine());
         }
    pares(pares,n);
      }
      public static void pares(int datos[],int m)
      {       
    int i;
    System.out.println("los pares son:");
    for(i=0;i<m;i++)
    {if((datos[i]/2)*2==datos[i])
      System.out.println("a"+"["+i+"]"+"="+datos[i]);
    }
      }
   }
El siguiente codigo lee un archivo de nombre "archivo.txt" , busca las palabras que empiecen con la letra "a" y las imprime. 
import java.io.*;

class LeeFichero {
public static void main(String [] arg)
 {
File archivo = null;
try {
archivo = new File("archivo.txt");//"archivo.txt" es el archivo que va a leer
String linea;
FileReader fr = new FileReader (archivo);
BufferedReader br = new BufferedReader(fr);
int i,j,aux=0;
while((linea=br.readLine())!=null) {
for(i=0;i<linea.length();i++)
{
  if(linea.charAt(i)=='a')
  {
     if(i==0)
      aux=1;
      else
      {if(linea.charAt(i-1)==' ')
        aux=1;
      }
  }
 if(aux==1)
  {if(linea.charAt(i)!=' ')
      System.out.print(linea.charAt(i));
   else
   {aux=0; System.out.println(' ');}
  }
 }
 }
fr.close();
 }
catch(IOException a){
System.out.println(a);
  }
 }
}
      

UML ( puntos extra)

Lenguaje Unificado de Modelado (LUM o UML, por sus siglas en inglés, Unified Modeling Language) es el lenguaje de modelado de sistemas de software más conocido y utilizado en la actualidad; está respaldado por el OMG (Object Management Group). Es un lenguaje gráfico para visualizar, especificar, construir y documentar un sistema. UML ofrece un estándar para describir un "plano" del sistema (modelo), incluyendo aspectos conceptuales tales como procesos de negocio y funciones del sistema, y aspectos concretos como expresiones de lenguajes de programación, esquemas de bases de datos y componentes reutilizables.

Es importante resaltar que UML es un "lenguaje de modelado" para especificar o para describir métodos o procesos. Se utiliza para definir un sistema, para detallar los artefactos en el sistema y para documentar y construir. En otras palabras, es el lenguaje en el que está descrito el modelo.
Se puede aplicar en el desarrollo de software entregando gran variedad de formas para dar soporte a una metodología de desarrollo de software (tal como el Proceso Unificado Racional o RUP), pero no especifica en sí mismo qué metodología o proceso usar.

UML no puede compararse con la programación estructurada, pues UML significa Lenguaje Unificado de Modelado, no es programación, solo se diagrama la realidad de una utilización en un requerimiento. Mientras que, programación estructurada, es una forma de programar como lo es la orientación a objetos, sin embargo, la programación orientada a objetos viene siendo un complemento perfecto de UML, pero no por eso se toma UML sólo para lenguajes orientados a objetos.
UML cuenta con varios tipos de diagramas, los cuales muestran diferentes aspectos de las entidades representadas.

Errores del UML 
A pesar de su status de estándar ampliamente reconocido y utilizado, UML siempre ha sido muy criticado por su carencia de una semántica precisa, lo que ha dado lugar a que la interpretación de un modelo UML no pueda ser objetiva. Otro problema de UML es que no se presta con facilidad al diseño de sistemas distribuidos. En tales sistemas cobran importancia factores como transmisión, serialización, persistencia, etc. UML no cuenta con maneras de describir tales factores. No se puede, por ejemplo, usar UML para señalar que un objeto es persistente o remoto, o que existe en un servidor que corre continuamente y que es compartido entre varias instancias de ejecución del sistema analizado. Sin embargo, UML sí acepta la creación de nuestros propios componentes para este tipo de modelado.

Modelado de Sistemas com UML
 UML se puede usar para modelar distintos tipos de sistemas: sistemas de software, sistemas de hardware, y organizaciones del mundo real. UML ofrece nueve diagramas en los cuales modelar sistemas.
  • Diagramas de Casos de Uso para modelar los procesos 'business'.
  • Diagramas de Secuencia para modelar el paso de mensajes entre objetos.
  • Diagramas de Colaboración para modelar interacciones entre objetos.
  • Diagramas de Estado para modelar el comportamiento de los objetos en el sistema.
  • Diagramas de Actividad para modelar el comportamiento de los Casos de Uso, objetos u operaciones.
  • Diagramas de Clases para modelar la estructura estática de las clases en el sistema.
  • Diagramas de Objetos para modelar la estructura estática de los objetos en el sistema.
  • Diagramas de Componentes para modelar componentes.
  • Diagramas de Implementación para modelar la distribución del sistema.
A su vez indica que UML es una consolidación de muchas de las notaciones y conceptos más usados orientados a objetos. Que empezó como una consolidación del trabajo de Grade Booch, James Rumbaugh, e Ivar Jacobson, creadores de tres de las metodologías orientadas a objetos más populares.

Historia de UML

 Este lenguaje es el primer método en publicar un meta-modelo en su propia notación, incluyendo la notación para la mayoría de la información de requisitos, análisis y diseño. Se trata pues de un meta-modelo auto-referencial (cualquier lenguaje de modelado de propósito general debería ser capaz de modelarse a sí mismo).

Existe actualmente una Revision Task Force (RTF) responsable de la generación de revisiones menores de la especificación UML 1.1. La primera RTF de UML terminó su revisión en Junio de 1999 con el draft final UML 1.3, actualmente ya se publicó la revisión 2.0.


arreglos multidimensionales utilizando apuntadores (puntos extra)

Un arreglo multidimensional puede ser visto en varias formas en C, por ejemplo:
Un arreglo de dos dimensiones es un arreglo de una dimensión, donde cada uno de los elementos es en sí mismo un arreglo.
Por lo tanto, la notación
a[n][m]
nos indica que los elementos del arreglo están guardados renglón por renglón.
Cuando se pasa una arreglo bidimensional a una función se debe especificar el número de columnas -- el número de renglones es irrelevante.
La razón de lo anterior, es nuevamente los apuntadores. C requiere conocer cuantas son las columnas para que pueda brincar de renglón en renglón en la memoria.
Considerando que una función deba recibir int a[5][35], se puede declarar el argumento de la función como:
f( int a[][35] ) { ..... }
o aún
f( int (*a)[35] ) { ..... }
En el último ejemplo se requieren los parénteis (*a) ya que [ ] tiene una precedencia más alta que *.
Por lo tanto:
int (*a)[35]; declara un apuntador a un arreglo de 35 enteros, y por ejemplo si hacemos la siguiente referencia a+2, nos estaremos refiriendo a la dirección del primer elemento que se encuentran en el tercer renglón de la matriz supuesta, mientras que
int *a[35]; declara un arreglo de 35 apuntadores a enteros.
Ahora veamos la diferencia (sutil) entre apuntadores y arreglos. El manejo de cadenas es una aplicación común de esto.
Considera:
char *nomb[10];

char anomb[10][20];
En donde es válido hacer nomb[3][4] y anomb[3][4] en C.
Sin embargo:
-
anomb es un arreglo verdadero de 200 elementos de dos dimensiones tipo char.
-
El acceso de los elementos anomb en memoria se hace bajo la siguiente fórmula 20*renglon + columna + dirección_base
-
En cambio nomb tiene 10 apuntadores a elementos.
NOTA: si cada apuntador en nomb indica un arreglo de 20 elementos entonces y solamente entonces 200 chars estarán disponibles (10 elementos).
Con el primer tipo de declaración se tiene la ventaja de que cada apuntador puede apuntar a arreglos de diferente longitud.
Considerar:
char *nomb[] = { "No mes", "Ene", "Feb", "Mar", .... };

char anomb[][15] = { "No mes", "Ene", "Feb", "Mar", ... };
Se puede indicar que se hace un manejo más eficiente del espacio haciendo uso de un arreglo de apuntadores y usando un arreglo bidimensional.

 Apuntadores
Un apuntador es una variable que contiene la dirección en memoria de otra variable. Se pueden tener apuntadores a cualquier tipo de variable. El operador unario o monádico & devuelve la dirección de memoria de una variable. El operador de indirección o dereferencia * devuelve el ``contenido de un objeto apuntado por un apuntador''. Para declarar un apuntador para una variable entera hacer:
int *apuntador;
Se debe asociar a cada apuntador un tipo particular. Por ejemplo, no se puede asignar la dirección de un short int a un long int. Para tener una mejor idea, considerar el siguiente código:
main()
{
    int x = 1, y = 2;
    int *ap;

    ap = &x;

    y = *ap;

    x = ap;

    *ap = 3;}
Cuando se compile el código se mostrará el siguiente mensaje: warning: assignment makes integer from pointer without a cast. Con el objetivo de entender el comportamiento del código supongamos que la variable x esta en la localidad de la memoria 100, y en 200 y ap en 1000. Nota: un apuntador es una variable, por lo tanto, sus valores necesitan ser guardados en algún lado.
int x = 1, y = 2;
int *ap;

ap = &x;
100 200 1000
x 1 y 2 ap 100
Las variables x e y son declaradas e inicializadas con 1 y 2 respectivamente, ap es declarado como un apuntador a entero y se le asigna la dirección de x (&x). Por lo que ap se carga con el valor 100.

y = *ap;

100 200 1000
x 1 y 1 ap 100
Después y obtiene el contenido de ap. En el ejemplo ap apunta a la localidad de memoria 100 -- la localidad de x. Por lo tanto, y obtiene el valor de x -- el cual es 1.

x = ap;

100 200 1000
x 100 y 1 ap 100
Como se ha visto C no es muy estricto en la asignación de valores de diferente tipo (apuntador a entero). Así que es perfectamente legal (aunque el compilador genera un aviso de cuidado) asigna el valor actual de ap a la variable x. El valor de ap en ese momento es 100.

*ap = 3;

100 200 1000
x 3 y 1 ap 100
Finalmente se asigna un valor al contenido de un apuntador (*ap).
Importante: Cuando un apuntador es declarado apunta a algún lado. Se debe inicializar el apuntador antes de usarlo. Por lo que:

main()
{
    int *ap;
    *ap = 100;
}
puede generar un error en tiempo de ejecución o presentar un comportamiento errático.
El uso correcto será:

main()
{
    int *ap;
    int x;

    ap = &x;
    *ap = 100;
}
Con los apuntadores se puede realizar también aritmética entera, por ejemplo:

main()
{
    float *flp, *flq;

    *flp = *flp + 10;

    ++*flp;

    (*flp)++;

    flq = flp;}
NOTA: Un apuntador a cualquier tipo de variables es una dirección en memoria -- la cual es una dirección entera, pero un apuntador NO es un entero.

La razón por la cual se asocia un apuntador a un tipo de dato, es por que se debe conocer en cuantos bytes esta guardado el dato. De tal forma, que cuando se incrementa un apuntador, se incrementa el apuntador por un ``bloque'' de memoria, en donde el bloque esta en función del tamaño del dato.
Por lo tanto para un apuntador a un char, se agrega un byte a la dirección y para un apuntador a entero o a flotante se agregan 4 bytes. De esta forma si a un apuntador a flotante se le suman 2, el apuntador entonces se mueve dos posiciones float que equivalen a 8 bytes.

A continuación se muestran dos errores comunes que se hacen con los apuntadores.

  • No asignar un apuntador a una dirección de memoria antes de usarlo
    int *x
    
    *x = 100;
    lo adecuado será, tener primeramente una localidad física de memoria, digamos int y;
    int *x, y;
    
    x = &y;
    *x = 100;
  • Indirección no válida Supongamos que se tiene una función llamada malloc() la cual trata de asignar memoria dinámicamente (en tiempo de ejecución), la cual regresa un apuntador al bloque de memoria requerida si se pudo o un apuntador a nulo en otro caso.

    char *malloc() -- una función de la biblioteca estándar que se verá más adelante.
    Supongamos que se tiene un apuntador char *p
    Considerar:
    *p = (char *) malloc(100):    /* pide 100 bytes de la memoria */
    
    *p = 'y';
    Existe un error en el código anterior. ¿Cuál es?
    El * en la primera línea ya que malloc regresa un apuntador y *p no apunta a ninguna dirección.
    El código correcto deberá ser:
    p = (char *) malloc(100);
    Ahora si malloc no puede regresar un bloque de memoria, entonces p es nulo, y por lo tanto no se podrá hacer:
    *p = 'y';
    Un buen programa en C debe revisar lo anterior, por lo que el código anterior puede ser reescrito como:
    p = (char *) malloc(100):    /* pide 100 bytes de la memoria */
    
    if ( p == NULL )
    {
        printf("Error: fuera de memoria\n");
        exit(1);
    }
    
    *p = 'y';
 

viernes, 26 de noviembre de 2010

Punteros (puntos extra)

Punteros

Las variables de C ocupan determinadas posiciones de memoria y las variables de tipo puntero están pensadas para contener estas direcciones de memoria. Veamos cómo se declara un puntero:

int * p;

La variable p se dice que es de tipo "puntero a int".

De igual manera podemos definir un puntero a cualquier otro tipo de dato como, por ejemplo, double:

double * punt;

Un puntero a int puede contener la dirección de memoria donde se aloja una variable de tipo int. Un puntero a char puede contener la dirección de memoria donde se sitúa una variable de tipo char.

Sin embargo, no podemos asignar direcciones directamente a los punteros. Incluso si supiéramos que la variable numero de tipo int ocupa la dirección 904 de memoria, no podríamos hacer la siguiente asignación:

p = 904; /* no es correcto */

Para asignar a p la dirección que ocupa numero tenemos que utilizar el operador &, el mismo que se utiliza en scanf, y no es necesario saber el valor concreto de la posición de memoria:

int numero;
p = &numero;

Se dice que p apunta a numero.

Una vez que tenemos un puntero apuntando a una variable, podemos acceder al contenido de la variable a través del puntero. Por ejemplo, las dos instrucciones siguientes son equivalentes y ambas asignan a numero el valor 3:

numero = 3;
*p = 3;

Dicho de otra forma, la expresión *p es sinónimo de numero.

Dentro de poco veremos que este mecanismo nos permitirá acceder a variables locales de una función desde otras funciones, que es la principal utilidad de los punteros.

Punteros en java
Hay un par de ideas sobre java muy extendidas: java no tiene punteros y en java todo se pasa por referencia.

La realidad, es que java se entiende mucho mejor si lo pensamos exactamente al revés. En java sólo hay punteros (con excepción de los tipos primitivos) y en java todo se pasa por valor (por copia).

Por ejemplo, en C++ hacemos esto

MiClase a;

y ya está todo correcto. La variable a está perfectamente inicializada. Si en java hacemos eso, tenemos una variable a sin inicializar. Es necesario hacerle un new, exactamente igual que un puntero en C++

MiClase a = new MiClase(); // Esto en Java
MiClase *a = new MiClase(); // Esto en C++

// o este otro tipo de inicialización extrañamente parecida ...

MiClase a = null; // en java
MiClase *a=NULL; // en C++

La única diferencia es la notación con el asterisco. Si pensamos que en java TODO son punteros, no es necesario poner el asterisco para distinguir lo que es puntero de lo que no lo es, por lo que símplemente lo han quitado.

Ahora imaginemos un método que recibe una clase y que le hacemos una llamada

// en java...
void medodo (MiClase a)
{
a = new MiClase();
}
...
MiClase b = null;
metodo (b);

Bueno, pues cuando salimos del método b sigue valiendo null, "apuntando a null". Eso quiere decir que a y b son variables disintas, es decir, se ha pasado la variable b por valor al método.

¿Cómo podemos crear una nueva instancia y devolverla?. En java no queda más remedio que hacerlo en el return, es imposible hacerlo a través de parámetros. Sin embargo, en C++ tenemos más posibilidades. Podemos usar un puntero al puntero, es decir, hacer esto

void metodo (MiClase **a)
{
*a = new MiClase();
}
...
MiClase *b=NULL;
metodo (&b);

o bien, incluso usar referencias de verdad

// El & en la declaración es lo que hace que realmente sea una referencia.
void metodo (MiClase * &a)
{
a=new MiClase();
}
...
MiClase *b=NULL;
metodo (b);
// Aqui b apunta al MiClase creado dentro del método.

Punteros en C++

Haciéndolo así, el puntero b de fuera del método y el puntero a del parámetro son exactamente la misma variable. Ojo, no quiero decir que sean dos punteros distintos que apunten al mismo lado, sino que son realmente el mismo puntero. Cuando hacemos que a apunte a otro sitio, b también apuntará al mismo sitio. Esto es lo que yo entiendo realmente por referencia.

El tratamiento de punteros en C++ utiliza su propio vocabulario con el que es aconsejable familiarizarse desde el principio. En general decimos coloquialmente que un puntero "apunta" o "señala" a un objeto determinado cuando su valor (del puntero) es la dirección del objeto (dirección de memoria donde comienza su almacenamiento). El objeto señalado por el puntero se denomina referente. Por esta razón también se dice que el puntero "referencia" al objeto. El operador que permite obtener la dirección de un objeto (para asignarlo a un puntero) se denomina operador de "referencia" y la operación de obtener el referente a partir del puntero se denomina "deferenciar".

Ejemplo :
#include <iostream.h>

void Invertir(char *s) {
  char *t= s;

  while (*t)
    t++;
                                // Pongo t al final de la cadena. En el caracter '\0'
  t--;                         // Me pongo en el anterior

  while(s < t) {                // Hasta que lleguemos al medio
    char Temp= *s;
    *s++= *t;
    *t--= Temp;                 // Intercambiar
  }
}

void main() {
  char *s= "SOLOS NABOS ES AVE Y NADA";

  Invertir(s);
  cout << s;
}
 
aqui les deje la informacion sobre punteros en 3 lenguajes diferentes
aunque son lenguajes tal ves no muy avansados , creo que les dara una idea
de como utilizar los punteros.
cualquier duda por favor diganmela