Argumentos del main

Argumentos del main

¿Por qué?


Como hacemos:


ls -l pepe*

Que es "ls" y que es "-l pepe*"


$ which ls
      /bin/ls

O sea que "ls" es un programa

y "-l pepe*"... ¡Argumentos!

Argumentos del main: Opciones


  • La función main:
    • Sin parámetros:
      
      void main(void)
      
    • Usando argumentos:
      
      void main(int argc, char *argv[])
      
    • Usando argumentos y las variables de entorno:
      
      void main(int argc, char *argv[], char *envp[])
      

Argumentos del main


  • Argumentos:
    • argc: Cantidad de argumentos pasados incluyendo el nombre del ejecutable
    • argv: Puntero a un arreglo de punteros con un strings en cada posición. Por cada espacio encontrado se genera un string.
    • envp: Variables del sistema operativo también divididas en strings.

Ejemplo



#include <stdio.h>

void main(int argc,char *argv[]) {
	char *str1="Hola ";
	char *str2=", que queres hacer hoy?\n";

	if(argc>1) {
		printf("%s%s%s", str1, argv[1], str2);
	}
}

Estructura envp


Argumentos del main: Ejemplos

Recursividad


(¿no mas parte del programa?)


Tail Call Optimization: The Musical

Recursividad


  • Se dice que algo es recursivo si se define en función de sí mismo o a sí mismo.

  • También se dice que nunca se debe incluir la misma palabra en la definición de ésta.

  • El caso es que las definiciones recursivas aparecen con frecuencia en matemáticas, e incluso en la vida real.

Recursividad


Ejemplo: basta con apuntar una cámara al monitor que muestra la imagen que muestra esa cámara. El efecto es verdaderamente curioso, en especial cuando se mueve la cámara alrededor del monitor.


Imagen recursiva


El Triángulo de Sierpinski es un fractal, y los fractales
los podemos ver como imagenes recursivas.

Más información didáctica sobre fractales (en ingles).

Llamados a funciones no recursivos


Recursividad


Un ejemplo sencillo de un algoritmo recursivo es el factorial:



Ejemplo factorial recursivo (llamados)


Recursividad (condición de cierre)

¿Qué pasa si se hace una llamada recursiva que no termina?

  • Cada llamada recursiva almacena los parámetros que se pasaron al procedimiento, y otras variables necesarias para el correcto funcionamiento del programa.
  • Por tanto si se produce una llamada recursiva infinita (que no termina nunca) llega un momento en el que no quedará memoria para almacenar más datos
  • En ese momento se abortará la ejecución del programa.
    • Para probar esto se puede intentar hacer esta llamada en el programa factorial definido anteriormente: factorial(-1);
  • A pesar de ser un buen ejemplo del problema computacional, este es un ejemplo engañoso. El valor -1 esta fuera del dominio de definición de la función matemática y no fue tampoco comprendido en el algoritmo (factorial está definido solamente para números naturales).

Recursividad


  • Puede definirse un programa en términos recursivos, como una serie de pasos básicos, o paso base (también conocido como condición de parada), y un paso recursivo, donde vuelve a llamarse al programa.
  • En un computador, esta serie de pasos recursivos debe ser finita, terminando con un paso base. Es decir, a cada paso recursivo se reduce el número de pasos que hay que dar para terminar, llegando un momento en el que no se verifica la condición de paso a la recursividad.
  • Por otra parte, la recursividad también puede ser indirecta, si tenemos un procedimiento P que llama a otro Q y éste a su vez llama a P. También en estos casos debe haber una condición de parada.

Memoria dinámica


  • Que hacemos: Tomamos areas de memoria.

  • Como la usamos: Con punteros podemos accederlas
    similar a como hacemos con variables.

  • Por que: Nos va a dar una mayor flexibilidad para
    manejar grandes cantidades de datos.

  • Desventajas: Los algoritmos de manejo son mas
    complicados.

Memoria dinámica


  • Libreria: stdlib.h
  • Funciones mas usadas:
    • void *malloc(size_t size);
    • void *calloc(size_t cant, size_t size);
    • void *realloc(void *ptr, size_t size);
    • void free(void *ptr);

Memoria dinámica


#include <stdlib.h>
#define MAX_STR 20

int *pi;
char *pc;
char *str;

pi = (int *) malloc( sizeof(int) );
pc = (char *) malloc( sizeof(char) );
str = (char *) malloc( sizeof(char) * MAX_STR );

  • Si se puede alojar la memoria malloc() retorna el puntero a la memoria reservada.
  • Si no se puede alojar la memoria malloc() retornara un puntero a NULL.


Bajar el ejemplo para correr en consola.

Estructuras dinámicas de datos


Por ejemplo, puedo crear un vector en forma dinámica:


#include <stdlib.h>
#include <stdio.h>

void main(void) {
	char *str, tam;
	printf("Ingrese el tamaño de cadena deseado: ");
	scanf("%d",&tam);
	setbuf(stdin, NULL); // Limpio el buffer de teclado
	str = (char *) malloc ( sizeof(char) * tam );
	printf("Ingrese su cadena: ");
	fgets(str, tam, stdin);
	printf("\nSu cadena es: %s",str);
}


Bajar el ejemplo para correr en consola.

void *calloc(size_t cant, size_t size);


Idem a malloc pero toda la memoria es
borrada (clear) "con ceros" antes de asignarnosla.

void *realloc(void *ptr, size_t size);


Intenta redimensionar el area de memoria asignada
y trabaja como malloc (no inicializa la memoria).

void free(void *ptr);


Para liberar la memoria que pedimos
(¡Seamos "ecológicos"!)

Vectores dinámicos



void fun(int n) {
	int arr[n];
	// ......
}

Esto da error si compilamos con:

gcc -Wall -std=c89 -pedantic-errors -o prog mi_variable_array.c



Los arreglos de tamaño variable fueron introducidos en el C99.

Y ahora volvemos a strings...


¿Como evitamos el problema del espacio en origen?

¡Usamos memoria dinámica!

Strings dinámicos



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

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

   string = (char *) malloc(strlen(str1)+1);

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

   free(string);

   return 0;
}