I parametri di main()






Nelle operazioni di lettura da file e scrittura su file sarà comodo avere la possibilità di memorizzare le parole lette usando un costrutto che ci permetta di gestire tali parole (o stringhe di caratteri) in modo compatto. Una struttura che ci permette di implementare questo tipo di gestione è l'array di stringhe.

Ogni stringa, di norma, si gestisce mediante un puntatore a char, quindi un array di stringhe verrà implementato usando un vettore di elementi di tipo char *.






char *array_str[30];






Gli elementi del vettore ci permettono di gestire le stringhe, ma la loro dichiarazione non ci fornisce automaticamente l'area di memoria necessaria per poter memorizzare in una stringa dei caratteri. Oltre ai puntatori, quindi, è necessaria la loro inizializzazione, in modo da allocare lo spazio necessario per la memorizzazione dei caratteri relativi a ciascuna stringa.






L'inizializzazione degli elementi dell'array di char * deve essere effettuata assegnando a ciascuno di essi l'indirizzo di un'area di memoria a cui sia legalmente possibile accedere.




for(i = 0; i < 30 ; i++)
  array_str[i] = (char *) malloc( sizeof(char) * 40 );





Non è necessario che le stringhe possiedano tutte la medesima lunghezza ... l'allocazione viene effettuata in modo dinamico, possiamo quindi scegliere in modo del tutto arbitrario la dimensione delle stringhe.



int dim,i=0;
srand(34432);
for(i = 0; i < 30 ; i++)
  {dim = 10 + rand() % 31 ; // genero numeri casuali tra 10 e 40
    array_str[i] = (char *) malloc( sizeof(char) * dim ); }




Questa possibilità è tanto interessante quanto difficile da gestire, in quanto diventa difficile ricordarsi le lunghezze di tutte le stringhe.



















Torniamo al caso di prima e supponiamo di aver dichiarato un array di char * e di aver allocato per tutte le variabili la stessa quantità di memoria



int i;
char *array_str[5];

// inizializziamo gli elementi dell'array

for(i = 0; i < 5 ; i++ )
  array_str[i] = (char *) malloc(10 * sizeof(char));

// inizializziamo le stringhe

for(i = 0; i < 5 ; i++)
  {printf("inserisci dei caratteri (max 9, senza spazi)"); // lasciamo lo spazio per lo '\0'
    
    scanf("%s",array_str[i]);

   }







Supponiamo che l'utente abbia inserito queste stringhe



array_str[0]
   
p
r
o
v
a
'\0'
array_str[1]
   
1
2
3
4
'\0'
array_str[2]
   
3
.
1
4
1
5
9
2
'\0'
array_str[3]
   
t
e
s
t
'\0'
array_str[4]
   
u
l
t
i
m
a
'\0'












Per visualizzarne il contenuto è sufficiente usare un ciclo:

for( i = 0 ; i < 5 ; i ++ )
   printf("Ecco la stringa numero %d : %s",i + 1, array_str[i]);



La seconda e la terza stringa contengono dei caratteri che possono essere interpretati come un intero ed un double, rispettivamente: esiste un modo per ricavare tali valori numerici a partire dalle stringhe ?



La libreria standard ci mette a disposizione due funzioni per convertire una stringa contenente dei caratteri numerici in un numero di tipo int o double. I prototipi delle due funzioni sono:



int atoi (char *) ; /* converte la stringa che riceve come argomento nel corrispondente valore di tipo int*/

float atof(char *) ; /* converte la stringa che riceve come argomento nel corrispondente valore di tipo float*/


Queste funzioni vengono dichiarate in stdlib.h



















array_str[0]
   
p
r
o
v
a
'\0'
array_str[1]
   
1
2
3
4
'\0'
array_str[2]
   
3
.
1
4
1
5
9
2
'\0'
array_str[3]
   
t
e
s
t
'\0'
array_str[4]
   
u
l
t
i
m
a
'\0'





int valore_intero;
float valore_float;

valore_intero = atoi ( array_str[1] );

printf("\n confronto %s e %d ",array_str[1],valore_intero); // i due valori coincidono !


valore_float = atof ( array_str[2] );

printf("\n confronto %s e %f",array_str[2],valore_float); // i due valori coincidono !



























Ora possiamo passare alla descrizione di come debba essere implementata la funzione main() in modo tale che sia possibile, dall'interno del codice, accedere ai parametri della funzione stessa.

Per passare parametri a main() è sufficiente inserire del testo dopo il nome del programma: se il programma si chiama myprog.exe, allora


myprog.exe  param1  param2  param3


param1, param2 e param3 sono i parametri di main() [ovvero del programma]


Come li gestisce il codice ?














Vincoli da rispettare (per avere la massima flessibilità da parte del codice):

(1) l'utente deve poter inserire un numero arbitrario di parametri (ovvero non ci devono essere limitazioni a priori sul numero di parametri inseribili sulla linea di comando)

(2) la lunghezza dei parametri deve essere anch'essa arbitraria (ovvero l'utente deve poter scrivere stringhe di qualsiasi dimensione)

(3) la gestione dei parametri deve essere effettuata in modo tale da permettere all'utente di passare al programma un qualsiasi tipo di dato (numeri interi, caratteri, numeri decimali)
























Il vincolo (3) viene soddisfatto gestendo i parametri come delle stringhe, in quanto una stringa può essere sia trattata come una stringa vera e propria (e quindi come del testo) sia convertita in un valore numerico di tipo intero sia convertita in un valore numerico di tipo decimale.

Per ora possiamo quindi immaginare di gestire i parametri mediante un array di stringhe


Le stringhe vengono gestite mediante puntatori a char; visto che l'area di memoria utile a contenere i caratteri di cui è formata una stringa può essere scelta in fase di esecuzione, questo tipo di implementazione soddisfa al vincolo (2).

Gli array di stringhe visti fino ad ora, però, non soddisfano al vincolo (1), in quanto il numero di elementi di cui è composto l'array deve essere fissato in fase di compilazione. Nel caso specifico,


OVVERO SOLO NEL CASO IN CUI SI STIA IMPLEMENTANTO QUESTA FUNZIONALITA' DI MAIN()
,


si può omettere il numero degli elementi dell'array.


L'implementazione di main(), nel caso in cui ci si aspetta che essa accetti parametri diventa:






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


... istruzioni


}



In fase di ESECUZIONE le variabili vengono inizializzate AUTOMATICAMENTE in questo modo:

alla variabile di tipo intero argc viene assegnato un valore pari al NUMERO di parametri

il numero di elementi dell'array di char diventa pari al numero di parametri ricevuti ed ogni elemento dell'array fa riferimento alla stringa contenente il corrispondente parametro


Dire che le variabili vengono inizializzate automaticamente significa che NON E' IL PROGRAMMATORE A DOVERGLI ASSEGNARE UN VALORE




Esempio: mandiamo in esecuzione il programma myprog.exe


myprog.exe Carlo Rossi 1980



I parametri sono 4: myprog.exe / Carlo / Rossi /1984 (il primo è il nome del programma, gli altri rappresentano il nome, cognome ed anno di nascita di una persona)


Scriviamo un codice che visualizzi le informazioni inserite dall'utente



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



int main(int argc, char *argv[])
{int data;
 printf("l'utente ha inserito %d parametri",argc);
 
 if(argc == 1)
   {printf("\n inserisci nome, cognome e data di nascita !\n");
     exit(0);}

 printf("l'utente ha inserito %d parametri",argc);
 
 printf("\n L'utente si chiama %s %s",argv[1],argv[2]);

 data = atoi (argv[3]);

 printf("ed e' nato nell'anno %d", data );



 /* argv[1] e argv[2] sono dei puntatori a char e permettono di entrare nelle aree di memoria in cui sono stati salvati i caratteri corrispondenti al secondo e terzo parametro, rispettivamente */

  printf("\n le iniziali dell'utente sono : %c %c", *(argv[1]) , *( argv[2] ) );


/* in questo modo visualizzo i primi elementi dei due array di char, ovvero le iniziali. In alternativa avrei potuto scrivere : */

  printf("\n le iniziali dell'utente sono : %c %c", argv[1][0] , argv[2][0] );


 return 0; }














Ultime note:

(0) un array di elementi di tipo TIPO (int, float, char, double) si gestisce con un puntatore a TIPO


(1) un array di elementi di tipo char si gestisce mediante un puntatore a char; allocando dinamicamente la memoria non è necessario che la dimensione dell'array sia nota in fase di compilazione ma la possiamo fissare in fase di esecuzione.


(2) un array di elementi di tipo char * , equivalentemente, si gestisce con un puntatore a char *, ovvero un puntatore a puntatore a char, cioè un puntatore doppio. Gestendolo in questo modo non serve specificare la dimensione dell'array di char * in fase di compilazione; essa può essere gestita tranquillamente in fase di esecuzione


(3) da questo segue che



char * argv[]


altri non è che un puntatore doppio a char


char ** argv