Barninga Z
a- a+

Le funzioni: main() parametri e valore restituito

main(): parametri e valore restituito

La funzione main() è presente in tutti i programmi C ed è sempre eseguita per prima, tuttavia non è necessario chiamarla dall'interno del programma[20]. La chiamata a main() è contenuta in un object file, fornito con il compilatore, che il linker collega automaticamente in testa al modulo oggetto prodotto dalla compilazione del sorgente. Si tratta dello startup module (o startup code)[21]: è questa, in realtà, la parte di codice eseguita per prima; lo startup module effettua alcune operazioni preliminari ed al termine di queste chiama main() dopo avere copiato sullo stack tre parametri, che essa può, opzionalmente, referenziare.

La tabella che segue elenca e descrive detti parametri, indicandone anche il nome convenzionalmente loro attribuito[22]:

PARAMETRI DI main()

 

Nome Tipo Descrizione
argc int Numero degli argomenti della riga di comando, compreso il nome del programma.
argv char ** Indirizzo dell'array di stringhe rappresentanti ciascuna un parametro della riga di comando. La prima stringa è il nome del programma completo di pathname se l'esecuzione avviene in una versione di DOS uguale o successiva alla 3.0, altrimenti contiene la stringa "C". L'ultimo elemento dell'array è un puntatore nullo.
envp char ** Indirizzo dell'array di stringhe copiate dall'environment (variabili d'ambiente) che il DOS ha reso disponibile al programma. L'ultimo elemento dell'array è un puntatore nullo.

La funzione main() può referenziare tutti i tre argomenti o solo alcuni di essi; tuttavia deve referenziare tutti i parametri che precedono l'ultimo nell'ordine in cui sono elencati nella tabella. Vediamo:

void main(void);

 

Quello appena presentato è il prototipo di una main() che non referenzia alcuno dei tre parametri. Perché main() li possa referenziare tutti, il prototipo deve essere:

void main(int argc,char **argv,char **envp);

 

Se, ad esempio, nel programma è necessario accedere solo alle stringhe dell'environment attraverso l'array envp, devono essere comunque dichiarati nel prototipo anche argc e argv.

La forma di main() più comunemente utilizzata è quella che referenzia argv al fine di accedere ai parametri della riga di comando[23]. Perché possa essere utilizzato argv deve essere referenziato anche argc (il quale, da solo, in genere non è di grande utilità):

void main(int argc,char **argv);

 

Ecco una semplice applicazione pratica:

#include 

void main(int argc,char **argv);

void main(int argc,char **argv)
{
    register i;

    printf("%s ha ricevuto %d argomenti:
" ,argv[0],argc-1);
    for(i = 1; argv[i]; i++)
        printf("%d) %s
" ,i,argv[i]);
}

 

Se il programma eseguibile si chiama PRINTARG.EXE, si trova nella directory C:PROVEEXEC e viene lanciato al prompt del DOS con la seguente riga di comando:

printarg Pippo Pluto & Paperino "Nonna Papera" 33 21

 

l'output prodotto è:

C:PROVEEXECPRINTARG.EXE ha ricevuto 7 argomenti:
Pippo
Pluto
&
Paperino
Nonna Papera
33
21

 

E' facile notare che viene isolata come parametro ogni sequenza di caratteri compresa tra spazi; le due parole Nonna Papera sono considerate un unico parametro in quanto racchiuse tra virgolette. Anche i numeri 33 e 21 sono referenziati come stringhe: per poterli utilizzare come interi è necessario convertire le stringhe in numeri, mediante le apposite funzioni di libreria[24].

Come ogni altra funzione, inoltre, main() può restituire un valore tramite l'istruzione return; in deroga, però, alla regola generale, per la quale è possibile la restituzione di un valore di qualsiasi tipo, main() può restituire unicamente un valore di tipo int.

Vediamo, con riferimento all'esempio precedente, quali sono i cambiamenti necessari perché main() possa restituire il numero di argomenti ricevuti dal programma:

#include 

int main(int argc,char **argv);

int main(int argc,char **argv)
{
    register i;

    printf("%s ha ricevuto %d argomenti:
" ,argv[0],argc-1);
    for(i = 1; argv[i]; i++)
        printf("%d) %s
" ,i,argv[i]);
    return(argc-1);
}

 

E' stato sufficiente modificare la definizione ed il prototipo di main(), sostituendo il dichiaratore di tipo void con int ed inserire un'istruzione return, seguita dall'espressione che produce il valore da restituire.

E' cosa arcinota, ormai, che l'esecuzione di un programma C ha inizio con la prima istruzione di main(); è, del resto, facilmente intuibile che l'esecuzione del programma, dopo avere eseguito l'ultima istruzione di main(), ha termine[25]. Ma allora, quale significato ha la restituzione di un valore da parte di main(), dal momento che nessuna altra funzione del programma lo può conoscere? In quale modo lo si può utilizzare? La risposta è semplice: il valore viene restituito direttamente al DOS, che lo rende disponibile attraverso il registro ERRORLEVEL. L'utilizzo più comune è rappresentato dall'effettuazione di opportuni tests all'interno di programmi batch che sono così in grado di condizionare il flusso esecutivo in dipendenza dal valore restituito proprio da main(). Di seguito è presentato un esempio di programma batch utilizzante il valore restituito dalla seconda versione di PRINTARG:

@echo off
printarg %1 %2 %3 %4 %5 %6 %7 %8 %9
if errorlevel 2 goto Molti
if errorlevel 1 goto Uno
echo PRINTARG lanciato senza argomenti (ERRORLEVEL = 0)
goto Fine
:Molti
echo PRINTARG lanciato con 2 o piu' 
argomenti (ERRORLEVEL >= 2)
goto Fine
:Uno
echo PRINTARG lanciato con un solo 
argomento (ERRORLEVEL = 1)
:Fine

 

Occorre prestare attenzione ad un particolare: il valore restituito da main() è un int (16 bit) e poiché, per contro, il registro ERRORLEVEL dispone di soli 8 bit (equivale ad un unsigned char) ed il valore in esso contenuto può variare da 0255, gli 8 bit più significativi del valore restituito da main() sono ignorati. Ciò significa, in altre parole, che l'istruzione

    return(256);

 

in main() restituisce, in realtà, 0 (la rappresentazione binaria di 256 è, infatti, 0000000100000000), mentre

    return(257);

 

restituisce 1 (257 in binario è 0000000100000001).

Va ancora precisato che le regole del C standard richiedono che main() sia sempre dichiarata int e precisano che una main() dichiarata void determina un undefined behaviour: non è cioè possibile a priori prevedere quale sarà il comportamento del programma. Del resto, numerosi esperimenti condotti non solo in ambiente DOS consentono di affermare che dichiarare main() con return type void non comporta alcun problema (ecco perché, per semplicità, detto tipo di dichiarazione ricorre più volte nel testo): ovviamente non è possibile utilizzare il valore restituito dal programma, perché questo è sicuramente indefinito (non si può cioè prevedere a priori quale valore contiene ERRORLEVEL in uscita dal programma). Qualora si intenda utilizzare il sorgente in ambienti o con compilatori diversi, può essere prudente dichiarare main() secondo le regole canoniche o verificare che il return type void non sia causa di problemi.

Se utilizzata con accortezza, la descritta tecnica di utilizzo dei parametri della riga di comando e di restituzione di valori al DOS consente di realizzare, con piccolo sforzo, procedure in grado di lavorare senza alcuna interazione con l'utilizzatore, cioè in modo completamente automatizzato.

Vale infine la pena di ricordare che dichiarare il parametro envp di main() non è l'unico modo per accedere alle stringhe dell'environment: allo scopo possono essere utilizzate le funzione di libreria getenv() e putenv(): la prima legge dall'environment il valore di una variabile, mentre la seconda lo modifica.

Chi ama le cose complicate può accedere all'environment leggendone la parte segmento dell'indirizzo nel PSP del programma, e costruendo un puntatore far con l'aiuto della macro MK_FP(), definita in DOS.H.

 



Ti potrebbe interessare anche

commenta la notizia

C'è 1 commento
Francesco
Hai dubbi su questo articolo?