Punteros (repaso)

Punteros y arreglos

  • El nombre de una variable de tipo arreglo es un puntero a la primera posición del arreglo
    
    double a[20];
             /* a == &a[0]  */
    

  • Podemos acceder al valor almacenado en un elemento de un arreglo de dos formas:
    • Usando el subíndice: a[3]
    • Usando aritmética de punteros para
      la dirección de memoria *(a+3)

Relación entre índices y punteros




a[0] ==   *a   == 3
a[1] == *(a+1) == 4
a[2] == *(a+2) == 7
a[3] == *(a+3) == 2
...
a[9] == *(a+9) == 6

Ejemplo: ¿Qué hacen estas sentencias?



int i, *pA;

int arreglo[10] = {3, 2, 4, 6, 8, 1, 3, 5, 7, 9};

pA = arreglo;

printf("%d,%d\n", *pA, arreglo[0]);
            /* Al contenido de *pA (arreglo[0]) le suma i */

for(i=0; i < 10; i++)
      printf("%d, %d\n", arreglo[i], *pA+i);

for(i=0; i < 10; i++)
      printf("%d, %d\n", arreglo[i], *(pA+i));
                                      /* Equivalente a arreglo[i] */

Puntero NULL

  • p = NULL;
    • Puntero que no apunta a ninguna dirección
    • Valor común para todos los tipos puntero definido en <stdlib.h>
  • Asignación:
    
    pc = pv = NULL;
    
  • No se puede desreferenciar, por que *pc no existe



Nota1: Es importante inicializar los punteros para evitar los "punteros descontrolados". ¡Las variables en C no se inicializan solas!

Nota2: En realidad la dirección de memoria 0x0 existe, pero siempre esta reservada para fines específicos de la arquitectura. Por esto el lenguaje C lo considera como un puntero especial a nada.

Resumiendo...

  • Punteros
    • Variables que guardan direcciones de memoria de variables de un tipo base
    • Apunta a una variable de tipo base
  • Permiten el paso por referencia en funciones
    • Permite devolver valores a través de parámetros
  • Aritmética de punteros:
    • Como moverse entre variables del mismo tipo...

Imprimiendo punteros



#include <stdio.h>
// Headers de C99 para enteros
#include <inttypes.h>
#include <stdint.h>

void main(void) {
	char c='a';
	char *pc=&c;

	// Forma normal (no imprime los ceros del principio)
	printf("Address: %p\n",pc);
	printf("Address: %#lx\n",pc);

	// Usando tipos enteros de ancho fijo (C99)
	printf("Address: 0x%016" PRIxPTR "\n", (uintptr_t)pc);
}

Nota: En cppreference.com se puede leer más sobre
tipos de enteros de ancho fijo (C99)

Conversiones de tipos (casting)

  • Para los ejemplos:
  • 
    float a;
    int b;
    

  • Conversiones automáticas
    • De entero a flotante:
      
      a = 4 + 4.0 -> 4.0 + 4.0 -> 8.0
      
    • De flotante a entero:
      
      b = 3.5 + 3 -> 3.5 + 3.0 -> 6.5 -> 6
      
  • Conversiones manuales o explicitas (casting de tipos)
    
    (float) 1 -> 1.0
    (int) 1.5 -> 1
    

Ruptura de reglas de tipado

Aritmética de punteros

  • A los punteros se les puede sumar y restar enteros
    • El incremento subyacente es multiplicado por el tamaño del tipo base
    • Se entiende que el puntero puede apuntar a un array indefinido de elementos del tipo base, no se comprueban limites
  • Se pueden restar entre sí
    • Deben ser del mismo tipo y apuntar al mismo array, en ese caso nos da la distancia entre elementos del array
  • No se pueden sumar entre sí (ni otras operaciones), no esta definido (resultados impredecibles)

El operador sizeof


  • sizeof es un operador de C (como ! o ~) que retorna un "constant int" que nos dice el tamaño de cualquier dato.

  • El elemento a examinar puede ser un dato, un porción de un dato o un tipo de dato.

  • sizeof retorna el tamaño del elemento en chars (bytes/size_t)

Tamaño de las variables puntero


  • El tamaño de las variables puntero es siempre el mismo. No importa si apuntan a un char, un int, float, etc...
  • Si estamos en una arquitectura de:
    • 32 bits la variable puntero tendrá 4 bytes.
    • 64 bits la variable puntero tendrá 8 bytes.



#include <stdio.h>

void main(void) {
char *pc=NULL;
int *pi=NULL;
printf("Mi puntero a char ocupa: %d bytes\n",
                                (int)(sizeof(pc)));
printf("Mi puntero a int ocupa: %d bytes\n",
                                (int)(sizeof(pi)));
}

sizeof


sizeof también puede averiguar el tamaño de un array:


#include <stdio.h>

void main(void) {
char cadena[] = "Mi string de prueba";
int vector[] = {0, 2, 4, 6, 8, 1, 3, 5, 7, 9};
printf("Mi \"cadena\" tiene %d caracteres\n",
                 (int)(sizeof(cadena)/sizeof(char)));
printf("Mi \"vector\" tiene %d posiciones\n",
                 (int)(sizeof(vector)/sizeof(int)));
}


Bajar ejemplo para correr con más printf().

sizeof

Como el sizeof lo hace el compilador el lo sabe por que también maneja la declaración. Pero la variable debe haber sido declarada explicitamente como un vector:


char string[32] = "hello, world";
char *ptr = string;


  • sizeof (string)
    • Devuelve: 32
  • sizeof (ptr)

      Si estamos en una máquina de 64bits (con punteros de 8 bytes)

    • Devuelve: 8


http://www.gnu.org/software/libc/manual/html_node/String-Length.html

Arreglos multidimensionales (matrices)

  • Declaración:
    
    {tipo-base}  {identificador}[{NumElem1}][{NumElem2}];
    
  • Declaración y asignación:
    
    tipo-base identif[M][N] = {
             {valor0_0, valor0_1, ..., valor0_N-1},
             {valor1_0, valor1_1, ..., valor1_N-1},
             {valor2_0, valor2_1, ..., valor2_N-1},
             ...,
             {valorM-1_0, valorM-1_1, ..., valorM-1_N-1}
    };
    
  • Asignación:
    
    matriz[indice1][indice2] = valor;
    

Ejemplos de declaración y uso de Matrices (I)



  • Declaración de una matriz de 50 filas de 20 enteros:
    
    int matriz[50][20]; 
           /* 50 filas x 20 columnas */
    

  • Declaración e inicialización de una matriz de 2x3:
    
    int matriz[2][3] = {
                { 2, 5, 8 },
                { 9, 1, 2 }
                };
    

Ejemplos de declaración y uso de Matrices (II)



  • Asignando un valor a la primera posición de la segunda fila de una matriz de enteros:
    
        matriz[1][0] = 50;
    

  • Imprimiendo una matriz de 100 x 50 enteros mediante ciclos for:
    
    int i, j;
    for (i=0; i < 100; i++)
        for (j=0; j < 50; j++)
            printf("%d\n", matriz[i][j]);

¿Y si usamos 3 dimensiones?


    • Declaración:
      
      int v[2][2][2]={
      	{{1,2},{3,4}},
      	{{5,6},{7,8}}
      	};
      

    • Recorriendo la matriz:
      
      for(i=0;i<2;i++)
          for(j=0;j<2;j++)
              for(k=0;k<2;k++)
                  printf("%d",v[i][j][k]);
      
    • Ejemplo: matrices.c


Funciones de manejo de strings

Librerias


  • STDIO.H
    • char *fgets(char *s, int size, stdin);
    • int fputs(const char *s, stdout);

  • STRING.H
    • strcat(), strcpy(), strlen()
    • strchr(), strcmp(), strncmp()
    • strcasecmp(), strncasecmp() (solo p/Linux, no Windows)
    • strstr(), strtok()


Nota: El stdin es el teclado, y el stdout es la pantalla.

size_t strlen(const char *s);



#include <stdio.h>
#include <string.h>

int main(void) {
    char *string = "Curso Informatica 1";
    printf("%d\n", strlen(string));
    return 0;
}

size_t: Es un tipo que permite representar el tamaño de cualquier objeto en bytes.
Es el tipo de dato que retorna el operador sizeof y es muy usado
por la librería estándar para representar tamaños y cuentas.

char *strcpy(char *dest, const char *src);



#include <stdio.h>
#include <string.h>

int main(void) {
   char string[10];
   char *str1 = "abcdefghi";

   strcpy(string, str1);
   printf("%s\n", string);
   return 0;
}

Nota: Esta función siempre asume que en destino hay suficiente lugar para copiar todos los elementos de origen. Si este no fuera el caso el programa fallará!

Bajarse ejemplo extendido que muestra el error de no usar strcpy().

char *strcat(char *dest, const char *src);



#include <string.h>
#include <stdio.h>

int main(void) {
   char destination[25];
   char *espacio = " ", *s1 = "Curso", *s2 = "Info 1";

   strcpy(destination, s1);
   strcat(destination, espacio);
   strcat(destination, s2);

   printf("%s\n", destination);
   return 0;
}

Nota: Esta función siempre asume que en string1 hay suficiente lugar para copiar todos los elementos de string2. Si este no fuera el caso el programa fallará!

¿Como evitamos problemas por no tener espacio suficiente en el string de destino?

Para srtcpy() y strcat()


Podemos usar : strncpy() y strncat()



char *strncpy(char *dest, const char *src, size_t n);
char *strncat(char *dest, const char *src, size_t n);

int strcmp(const char *s1, const char *s2);


#include <string.h>
#include <stdio.h>

void main(void) {
   char *buf1 = "aaa", *buf2 = "bbb", *buf3 = "ccc";
   int ptr;
   ptr = strcmp(buf2, buf1);
   if (ptr > 0)
      printf("buffer 2 es mayor que buffer 1\n");
   else
      printf("buffer 2 es menor que buffer 1\n");
   ptr = strcmp(buf2, buf3);
   if (ptr > 0)
      printf("buffer 2 es mayor que buffer 3\n");
   else
      printf("buffer 2 es menor que buffer 3\n");
}

Bajarse ejemplo extendido que incluye una comparación buscando si son iguales.

Librerías y Funciones

http://www.gnu.org/software/libc/manual/

  • STDIO.H
    • Int getchar(void) & int putchar(int c)

  • CTYPE.H (ver ejemplo)
    • isalnum(). isalpha(), isascii(), iscntrl(), isdigit()
    • islower(), isupper(), isprint(), ispunct(), isspace()
    • tolower(), toupper(), isxdigit()... (ver más en el manual)

  • STDLIB.H

Código

NCURSES


Instalando y copilando:


$ sudo apt-get install libncurses5-dev
$ gcc -o ncurses ncurses1_helloworld.c -lncurses 

Usando curses.h:


sudo apt-get install libncurses-dev
gcc -lcurses -o ej1 mi_ejercicio.c

Mas sobre ncurses (y kbhit):



Mas ejemplos y informacion sobre como usar ncurses.

¿Unicode en C?




Ejemplo de código unicode.

Clase practica

Ejercicios

Realizar el estructograma y la codificación en C

  1. Extraer un substring
    
    int substring(char st_salida[], char st_entrada[], int pos, int n)
    
    • Ejemplo:
      • substring(str,"Curso de informatica1", 9, 4);
      • Imprime "info"

  2. Función que hace preguntas
    
    char *pregunta(char texto[], char opc1[], char opc2[])
    

  • Ejemplo:
    • pregunta("Quiere estudiar hoy? ", "si", "no");
    • Imprime: "Quiere estudiar hoy? (si/no)"
    • y retorna "si" o "no"

Ejercicios:

  • Extraer un substring: De la clase anterior...
  • Organizar vector: Leer un conjunto de 100 valores a razon de uno por vez. Formar el vector PAR con los valores pares del conjunto y el vector POS con los valores positivos. Imprimir ambos vectores. Si alguno de ellos o ambos no contienen ningun elemento, informar con una leyenda.

Ejercicio 4.27


  • IP: 4 numeros de 0 a 255 separados por puntos.
    • Primer llamado: strtok(str,'.');
    • Siguientes llamados: strtok(NULL,'.');
  • Email: 2 string de caracteres separado por '@'
    • Los strings pueden tener numeros y caracteres,
      usar isalphanum()
    • Ambos string pueden tener '.' , '_' o '-'
  • CC: Ver algoritmo de Luhn


Ejemplo de uso de la función strtok

Uso de memoria de la función strtok