Capitolo – 5
Introduzione alla programmazione in C
Introduzione
"C" è uno dei linguaggi informatici più popolari nel mondo dei computer di oggi. Il linguaggio di programmazione "C" è stato progettato e sviluppato da Brian Kernighan e Dennis Ritchie presso The Bell Research Labs nel 1972.
'C' è un linguaggio creato appositamente per consentire al programmatore di accedere a quasi tutti gli interni della macchina: registri, slot I/O e indirizzi assoluti. Allo stesso tempo, "C" consente la gestione dei dati e la modularizzazione del testo programmato necessarie per consentire la costruzione di progetti multi-programmatore molto complessi in modo organizzato e tempestivo.
Sebbene questo linguaggio fosse originariamente concepito per funzionare sotto UNIX, c'è stato un grande interesse nell'esecuzione sotto il sistema operativo MS-DOS su PC IBM e compatibili. È un linguaggio eccellente per questo ambiente grazie alla semplicità di espressione, alla compattezza del codice e all'ampia gamma di applicabilità.
Inoltre, grazie alla semplicità e facilità di scrittura di un compilatore C, di solito è il primo linguaggio di alto livello disponibile su qualsiasi nuovo computer, inclusi microcomputer, minicomputer e mainframe.
Perché usare C nella programmazione del recupero dati
Nel mondo odierno della programmazione informatica, sono disponibili molti linguaggi di alto livello. Questi linguaggi sono buoni con molte funzionalità adatte per la maggior parte delle attività di programmazione. Tuttavia, ci sono diversi motivi per cui C è la prima scelta dei programmatori che sono disposti a fare programmazione per il recupero dei dati, la programmazione del sistema, la programmazione del dispositivo o la programmazione dell'hardware:
- Il C è un linguaggio popolare preferito dai programmatori professionisti. Di conseguenza, è disponibile un'ampia varietà di compilatori C e utili accessori.
- C è un linguaggio portatile. Un programma C scritto per un sistema informatico può essere compilato ed eseguito su un altro sistema con poche o nessuna modifica. La portabilità è migliorata dallo standard ANSI per C, l'insieme di regole per i compilatori C.
- C consente un ampio utilizzo dei moduli nella programmazione. Il codice C può essere scritto in routine chiamate funzioni. Queste funzioni possono essere riutilizzate in altre applicazioni o programmi. Non è necessario fare sforzi aggiuntivi nella programmazione di una nuova applicazione per creare lo stesso modulo che hai sviluppato in precedenza in un'altra programmazione di applicazione.
Puoi utilizzare questa funzione nel nuovo programma senza alcuna modifica o modifiche minori. In caso di programmazione di recupero dati, troverai questa qualità molto utile quando avrai bisogno di eseguire le stesse funzioni più volte in diverse applicazioni di programmi diversi.
- C è un linguaggio potente e flessibile. Questo è il motivo per cui C viene utilizzato per progetti diversi come sistemi operativi, elaboratori di testi, grafica, fogli di calcolo e persino compilatori per altri linguaggi.
- Il C è un linguaggio di poche parole, contenente solo una manciata di termini, detti parole chiave, che fungono da base su cui si basa la funzionalità del linguaggio. Queste parole chiave, dette anche parole riservate, lo rendono più potente e danno un'ampia area di programmazione e fanno sentire un programmatore a fare qualsiasi tipo di programmazione in C.
Suppongo che tu non sappia nulla in C
Presumo che tu non sappia nulla della programmazione in C e non abbia nemmeno idea di programmazione. Inizierò con i concetti più basilari del C e ti porterò fino all'alto livello della programmazione in C, inclusi i concetti solitamente intimidatori di puntatori, strutture e allocazione dinamica.
Per comprendere appieno questi concetti, ci vorrà un bel po' di tempo e lavorerai da parte tua perché non sono particolarmente facili da afferrare, ma sono strumenti molto potenti.
La programmazione in C è un'enorme risorsa in quelle aree in cui potrebbe essere necessario utilizzare il linguaggio Assembly ma preferiresti mantenerlo un programma semplice da scrivere e di facile manutenzione. Il tempo risparmiato nella codifica di C può essere enorme in questi casi.
Anche se il linguaggio C gode di un buon record quando i programmi vengono trasportati da un'implementazione all'altra, ci sono differenze nei compilatori che troverai ogni volta che proverai a utilizzare un altro compilatore.
La maggior parte delle differenze diventa evidente quando si utilizzano estensioni non standard come le chiamate al BIOS DOS quando si utilizza MS-DOS, ma anche queste differenze possono essere ridotte al minimo da un'attenta scelta dei costrutti di programmazione.
Quando è diventato evidente che il linguaggio di programmazione C stava diventando un linguaggio molto popolare disponibile su un'ampia gamma di computer, un gruppo di persone interessate si è riunito per proporre un insieme standard di regole per l'uso del linguaggio di programmazione C.
Il gruppo ha rappresentato tutti i settori dell'industria del software e dopo molti incontri e molte bozze preliminari, ha finalmente scritto uno standard accettabile per il linguaggio C. È stato accettato dall'American National Standards Institute (ANSI), e dall'International Standards Organisation (ISO).
Non è imposto a nessun gruppo o utente, ma poiché è così ampiamente accettato, sarebbe un suicidio economico per qualsiasi scrittore di compilatori rifiutarsi di conformarsi allo standard.
I programmi scritti in questo libro sono destinati principalmente all'uso su un PC IBM o un computer compatibile, ma possono essere utilizzati con qualsiasi compilatore standard ANSI poiché è strettamente conforme allo standard ANSI.
Iniziamo
Prima di poter fare qualsiasi cosa in qualsiasi lingua e iniziare a programmare, devi sapere come nominare un identificatore. Un identificatore viene utilizzato per qualsiasi variabile, funzione, definizione di dati, ecc. Nel linguaggio di programmazione C, un identificatore è una combinazione di caratteri alfanumerici, il primo è una lettera dell'alfabeto o una sottolineatura e il resto è una lettera dell'alfabeto alfabeto, qualsiasi cifra numerica o la sottolineatura.
È necessario tenere presenti due regole durante la denominazione degli identificatori.
- Il caso dei caratteri alfabetici è significativo. C è un linguaggio con distinzione tra maiuscole e minuscole. Ciò significa che il ripristino è diverso dal ripristino e il ripristino è diverso da entrambi menzionati in precedenza.
- Secondo lo standard ANSI-C, possono essere utilizzati almeno 31 caratteri significativi e saranno considerati significativi da un compilatore ANSI-C conforme. Se vengono utilizzati più di 31, tutti i caratteri oltre il 31° possono essere ignorati da un determinato compilatore.
Parole chiave
Ci sono 32 parole definite come parole chiave in C. Queste hanno usi predefiniti e non possono essere usate per nessun altro scopo in un programma C. Sono usati dal compilatore come ausilio per la compilazione del programma. Sono sempre scritti in minuscolo. Segue un elenco completo:
auto |
break |
case |
char |
const |
continue |
default |
do |
double |
else |
enum |
extern |
float |
for |
goto |
if |
int |
long |
register |
return |
short |
signed |
sizeof |
static |
struct |
switch |
typedef |
union |
unsigned |
void |
volatile |
while |
Qui vediamo la magia di C. La meravigliosa raccolta di sole 32 parole chiave offre un ampio utilizzo in diverse applicazioni. Qualsiasi programma per computer ha due entità da considerare, i dati e il programma. Sono fortemente dipendenti l'uno dall'altro e un'attenta pianificazione di entrambi porta a un programma ben pianificato e ben scritto.
Iniziamo con un semplice programma C:
/* First Program to learn C */
#include <stdio.h>
void main()
{
printf("This is a C program\n"); // printing a message
}
Sebbene il programma sia molto semplice, alcuni punti sono degni di nota. Esaminiamo il programma di cui sopra. Tutto ciò che è all'interno di /* e */ è considerato un commento e verrà ignorato dal compilatore. Non dovresti includere commenti all'interno di altri commenti, quindi qualcosa del genere non è consentito:
/* questo è un /* commento */ all'interno di un commento, che è sbagliato */
C'è anche un modo di documentazione che funziona all'interno di una riga. Usando // possiamo aggiungere una piccola documentazione all'interno di quella riga.
Ogni programma C contiene una funzione chiamata main. Questo è il punto di partenza del programma. Ogni funzione dovrebbe restituire un valore. In questo programma la funzione main non restituisce alcun valore di ritorno quindi abbiamo scritto void main. Potremmo anche scrivere questo programma come:
/* Primo programma per imparare C */
#include <stdio.h>
main()
{
printf("This is a C program\n"); // printing a message
return 0;
}
Entrambi i programmi sono uguali ed eseguono la stessa attività. Il risultato di entrambi i programmi stamperà sullo schermo il seguente output:
Questo è un programma C
#include<stdio.h> consente al programma di interagire con lo schermo, la tastiera e il file system del tuo computer. Lo troverai all'inizio di quasi tutti i programmi C.
main() dichiara l'inizio della funzione, mentre le due parentesi graffe mostrano l'inizio e la fine della funzione. Le parentesi graffe in C vengono utilizzate per raggruppare le istruzioni come in una funzione o nel corpo di un ciclo. Tale raggruppamento è noto come istruzione composta o blocco.
printf("Questo è un programma C\n"); stampa le parole sullo schermo. Il testo da stampare è racchiuso tra virgolette. Il \n alla fine del testo dice al programma di stampare una nuova riga come parte dell'output. La funzione printf() viene utilizzata per monitorare la visualizzazione dell'output.
La maggior parte dei programmi C sono in lettere minuscole. Di solito troverai lettere maiuscole usate nelle definizioni del preprocessore che verranno discusse in seguito, o tra virgolette come parti di stringhe di caratteri.
Compilazione del programma
Lascia che il nome del nostro programma sia CPROG.C. Per inserire e compilare il programma C, segui questi passaggi:
- Crea la directory attiva dei tuoi programmi C e avvia il tuo editor. Per questo è possibile utilizzare qualsiasi editor di testo, ma la maggior parte dei compilatori C come Turbo C++ di Borland ha un ambiente di sviluppo integrato (IDE) che ti consente di inserire, compilare e collegare i tuoi programmi in un'unica comoda impostazione.
- Scrivi e salva il codice sorgente. Dovresti nominare il file CPROG.C.
- Compila e collega CPROG.C. Esegui il comando appropriato specificato dai manuali del tuo compilatore. Dovresti ricevere un messaggio che informa che non ci sono errori o avvisi.
- Controlla i messaggi del compilatore. Se non ricevi errori o avvisi, tutto dovrebbe essere a posto. Se si verifica un errore durante la digitazione del programma, il compilatore lo catturerà e visualizzerà un messaggio di errore. Correggere l'errore, visualizzato nel messaggio di errore.
- Il tuo primo programma C dovrebbe ora essere compilato e pronto per essere eseguito. Se visualizzi un elenco di directory di tutti i file denominati CPROG otterrai i quattro file con estensione diversa descritti come segue:
- CPROG.C, il file del codice sorgente
- CPROG.BAK, il file di backup del file sorgente creato con l'editor
- CPROG.OBJ, contiene il codice oggetto per CPROG.C
- CPROG.EXE, il programma eseguibile creato durante la compilazione e il collegamento di CPROG.C
- Per eseguire, o eseguire, CPROG.EXE, è sufficiente immettere cprog. Sullo schermo viene visualizzato il messaggio Questo è un programma C.
Ora esaminiamo il seguente programma:
/* First Program to learn C */ // 1
// 2
#include <stdio.h> // 3
// 4
main() // 5
{
// 6
printf("This is a C program\n"); // 7
// 8
return 0; // 9
} // 10
Quando si compila questo programma, il compilatore visualizza un messaggio simile al seguente:
cprog.c(8): Errore: `;' previsto
interrompiamo questo messaggio di errore in parti. cprog.c è il nome del file in cui è stato trovato l'errore. (8) è il numero di riga in cui è stato trovato l'errore. Errore: `;' previsto è Una descrizione dell'errore.
Questo messaggio è piuttosto informativo e ti dice che nella riga 8 di CPROG.C il compilatore si aspettava di trovare un punto e virgola ma non l'ha fatto. Tuttavia, sai che il punto e virgola è stato effettivamente omesso dalla riga 7, quindi c'è una discrepanza.
Perché il compilatore segnala un errore nella riga 8 quando, in effetti, un punto e virgola è stato omesso dalla riga 7. La risposta sta nel fatto che a C non interessa cose come le interruzioni tra le righe. Il punto e virgola che appartiene dopo l'istruzione printf() potrebbe essere stato posizionato nella riga successiva, anche se in pratica sarebbe una cattiva programmazione.
Solo dopo aver incontrato il comando successivo (return) nella riga 8 il compilatore è sicuro che manchi il punto e virgola. Pertanto, il compilatore segnala che l'errore è nella riga 8.
Potrebbero esserci diverse possibilità di diversi tipi di errori. Discutiamo di collegamento dei messaggi di errore. Gli errori del linker sono relativamente rari e di solito derivano da errori di ortografia del nome di una funzione della libreria C. In questo caso, viene visualizzato un messaggio di errore: simboli undefined: errore, seguito dal nome errato. Dopo aver corretto l'ortografia, il problema dovrebbe scomparire.
Stampare i numeri
Vediamo il seguente esempio:
// Come stampare i numeri //
#include<stdio.h>
void main()
{
int num = 10;
printf(“ The Number Is %d”, num);
}
L'output del programma verrà visualizzato sullo schermo come segue:
Il numero è 10
Il segno % viene utilizzato per segnalare l'uscita di molti diversi tipi di variabili. Il carattere che segue il segno % è una d, che segnala alla routine di output di ottenere un valore decimale e di emetterlo.
Utilizzo delle variabili
In C, una variabile deve essere dichiarata prima di poter essere utilizzata. Le variabili possono essere dichiarate all'inizio di qualsiasi blocco di codice, ma la maggior parte si trova all'inizio di ogni funzione. La maggior parte delle variabili locali viene creata quando viene chiamata la funzione e viene distrutta al ritorno da quella funzione.
Per utilizzare le variabili nei tuoi programmi C, devi conoscere le seguenti regole quando assegni il nome alle variabili in C:
- Il nome può contenere lettere, cifre e il carattere di sottolineatura (_).
- Il primo carattere del nome deve essere una lettera. Il trattino basso è anche un primo carattere legale, ma il suo utilizzo non è raccomandato.
- C fa distinzione tra maiuscole e minuscole, quindi il nome della variabile num è diverso da Num.
- Le parole chiave C non possono essere utilizzate come nomi di variabili. Una parola chiave è una parola che fa parte del linguaggio C.
L'elenco seguente contiene alcuni esempi di nomi di variabili C legali e illegali:
Variable Name |
Legal or Not |
Num |
Legal |
Ttpt2_t2p |
Legal |
Tt pt |
Illegal: Space is not allowed |
_1990_tax |
Legal but not advised |
Jack_phone# |
Illegal: Contains the illegal character # |
Case |
Illegal: Is a C keyword |
1book |
Illegal: First character is a digit |
La prima novità che spicca è la prima riga del corpo di main():
numero int = 10;
Questa riga definisce una variabile denominata 'num' di tipo int e la inizializza con il valore 10. Questo potrebbe anche essere stato scritto come:
int num; /* define uninitialized variable 'num' */
/* and after all variable definitions: */
num = 10; /* assigns value 10 to variable 'num' */
Le variabili possono essere definite all'inizio di un blocco (tra parentesi graffe {e}), di solito si trova all'inizio del corpo di una funzione, ma può anche trovarsi all'inizio di un altro tipo di blocco.
Per le variabili definite all'inizio di un blocco, lo stato 'auto'. Ciò significa che esistono solo durante l'esecuzione del blocco. Quando inizia l'esecuzione della funzione, le variabili verranno create ma il loro contenuto non sarà definito. Quando la funzione ritorna, le variabili verranno distrutte. La definizione avrebbe potuto anche essere scritta come:
auto int num = 10;
Poiché la definizione con o senza la parola chiave auto è completamente equivalente, la parola chiave auto è ovviamente piuttosto ridondante.
Tuttavia, a volte questo non è quello che vuoi. Supponiamo di volere che una funzione tenga il conto di quante volte viene chiamata. Se la variabile venisse distrutta ogni volta che la funzione ritorna, ciò non sarebbe possibile.
Pertanto è possibile assegnare alla variabile quella che viene chiamata durata statica, il che significa che rimarrà intatta durante l'intera esecuzione del programma. Ad esempio:
statico int num = 10;
Questo inizializza la variabile num su 10 all'inizio dell'esecuzione del programma. Da quel momento in poi il valore rimarrà inalterato; la variabile non verrà reinizializzata se la funzione viene chiamata più volte.
A volte non è sufficiente che la variabile sia accessibile solo da una funzione o potrebbe non essere conveniente passare il valore tramite un parametro a tutte le altre funzioni che ne hanno bisogno.
Ma se hai bisogno di accedere alla variabile da tutte le funzioni nell'intero file sorgente, puoi farlo anche con la parola chiave static, ma mettendo la definizione al di fuori di tutte le funzioni. Ad esempio:
#include <stdio.h>
static int num = 10; /* will be accessible from entire source file */
int main(void)
{
printf("The Number Is: %d\n", num);
return 0;
}
E ci sono anche casi in cui una variabile deve essere accessibile dall'intero programma, che può essere costituito da diversi file sorgente. Questa è chiamata variabile globale e dovrebbe essere evitata quando non è richiesta.
Questo viene fatto anche mettendo la definizione al di fuori di tutte le funzioni, ma senza usare la parola chiave static:
#include <stdio.h>
int num = 10; /* will be accessible from entire program! */
int main(void)
{
printf("The Number Is: %d\n", num);
return 0;
}
C'è anche la parola chiave extern, che viene utilizzata per accedere alle variabili globali in altri moduli. Ci sono anche alcuni qualificatori che puoi aggiungere alle definizioni delle variabili. Il più importante di loro è const. Una variabile definita come const non può essere modificata.
Ci sono altri due modificatori che sono usati meno comunemente. Il volatile e il modificatore di registro. Il modificatore volatile richiede che il compilatore acceda effettivamente alla variabile ogni volta che viene letta. Potrebbe non ottimizzare la variabile inserendola in un registro o giù di lì. Viene utilizzato principalmente per scopi di multithreading e di elaborazione degli interrupt, ecc.
Il modificatore di registro richiede al compilatore di ottimizzare la variabile in un registro. Questo è possibile solo con le variabili auto e in molti casi il compilatore può selezionare meglio le variabili da ottimizzare nei registri, quindi questa parola chiave è obsoleta. L'unica conseguenza diretta della creazione di un registro variabile è che il suo indirizzo non può essere preso.
La tabella delle variabili, fornita nella pagina successiva, descrive la classe di archiviazione di cinque tipi di classi di archiviazione.
Nella tabella vediamo che la parola chiave extern è posizionata su due righe. La parola chiave extern viene utilizzata nelle funzioni per dichiarare una variabile esterna statica definita altrove.
Tipi di variabili numeriche
C fornisce diversi tipi di variabili numeriche perché valori numerici diversi hanno requisiti di archiviazione di memoria variabili. Questi tipi numerici differiscono per la facilità con cui è possibile eseguire determinate operazioni matematiche su di essi.
I numeri interi piccoli richiedono meno memoria per essere archiviati e il tuo computer può eseguire operazioni matematiche con tali numeri molto rapidamente. Interi grandi e valori a virgola mobile richiedono più spazio di archiviazione e più tempo per le operazioni matematiche. Utilizzando i tipi di variabili appropriati, ti assicuri che il tuo programma venga eseguito nel modo più efficiente possibile.
Le variabili numeriche di C rientrano nelle seguenti due categorie principali:
- Variabili intere
- Variabili in virgola mobile
All'interno di ciascuna di queste categorie ci sono due o più tipi di variabili specifici. La tabella riportata di seguito mostra la quantità di memoria, in byte, richiesta per contenere una singola variabile di ogni tipo.
Il tipo char può essere equivalente a char firmato o char non firmato, ma è sempre un tipo separato da uno di questi.
In C non c'è differenza tra la memorizzazione di caratteri oi valori numerici corrispondenti in una variabile, quindi non c'è nemmeno bisogno di una funzione per convertire tra un carattere e il suo valore numerico o viceversa. Per gli altri tipi di numeri interi, se ometti firmato o non firmato il valore predefinito sarà firmato, quindi ad es. int e firmato int sono equivalenti.
Il tipo int deve essere maggiore o uguale al tipo short e minore o uguale al tipo long. Se hai semplicemente bisogno di memorizzare alcuni valori che non sono enormemente grandi, spesso è una buona idea usare il tipo int; di solito è la dimensione con cui il processore può gestire il più semplice, e quindi il più veloce.
Con diversi compilatori double e long double sono equivalenti. Questo, combinato con il fatto che la maggior parte delle funzioni matematiche standard funzionano con il tipo double, è un buon motivo per utilizzare sempre il tipo double se devi lavorare con numeri frazionari.
La tabella seguente descrive meglio i tipi di variabili:
Tipi per scopi speciali comunemente usati:
Variable Type |
Description |
size_t |
unsigned type used for storing the sizes of objects in bytes |
time_t |
used to store results of the time() function |
clock_t |
used to store results of the clock() function |
FILE |
used for accessing a stream (usually a file or device) |
ptrdiff_t |
signed type of the difference between 2 pointers |
div_t |
used to store results of the div() function |
ldiv_t |
used to store results of ldiv() function |
fpos_t |
used to hold file position information |
va_list |
used in variable argument handling |
wchar_t |
wide character type (used for extended character sets) |
sig_atomic_t |
used in signal handlers |
Jmp_buf |
used for non-local jumps |
Per comprendere meglio queste variabili facciamo un esempio:
/* Programma per indicare l'intervallo e la dimensione in byte della variabile C */
#include <stdio.h>
int main()
{
int a; /* simple integer type */
long int b; /* long integer type */
short int c; /* short integer type */
unsigned int d; /* unsigned integer type */
char e; /* character type */
float f; /* floating point type */
double g; /* double precision floating point */
a = 1023;
b = 2222;
c = 123;
d = 1234;
e = 'X';
f = 3.14159;
g = 3.1415926535898;
printf( "\nA char is %d bytes", sizeof( char ));
printf( "\nAn int is %d bytes", sizeof( int ));
printf( "\nA short is %d bytes", sizeof( short ));
printf( "\nA long is %d bytes", sizeof( long ));
printf( "\nAn unsigned char is %d bytes",
sizeof( unsigned char ));
printf( "\nAn unsigned int is %d bytes",
sizeof( unsigned int ));
printf( "\nAn unsigned short is %d bytes",
sizeof( unsigned short ));
printf( "\nAn unsigned long is %d bytes",
sizeof( unsigned long ));
printf( "\nA float is %d bytes", sizeof( float ));
printf( "\nA double is %d bytes\n", sizeof( double ));
printf("a = %d\n", a); /* decimal output */
printf("a = %o\n", a); /* octal output */
printf("a = %x\n", a); /* hexadecimal output */
printf("b = %ld\n", b); /* decimal long output */
printf("c = %d\n", c); /* decimal short output */
printf("d = %u\n", d); /* unsigned output */
printf("e = %c\n", e); /* character output */
printf("f = %f\n", f); /* floating output */
printf("g = %f\n", g); /* double float output */
printf("\n");
printf("a = %d\n", a); /* simple int output */
printf("a = %7d\n", a); /* use a field width of 7 */
printf("a = %-7d\n", a); /* left justify in
field of 7 */
c = 5;
d = 8;
printf("a = %*d\n", c, a); /* use a field width of 5*/
printf("a = %*d\n", d, a); /* use a field width of 8 */
printf("\n");
printf("f = %f\n", f); /* simple float output */
printf("f = %12f\n", f); /* use field width of 12 */
printf("f = %12.3f\n", f); /* use 3 decimal places */
printf("f = %12.5f\n", f); /* use 5 decimal places */
printf("f = %-12.5f\n", f); /* left justify in field */
return 0;
}
Il risultato del programma dopo l'esecuzione verrà visualizzato come:
A char is 1 bytes
An int is 2 bytes
A short is 2 bytes
A long is 4 bytes
An unsigned char is 1 bytes
An unsigned int is 2 bytes
An unsigned short is 2 bytes
An unsigned long is 4 bytes
A float is 4 bytes
A double is 8 bytes
a = 1023
a = 1777
a = 3ff
b = 2222
c = 123
d = 1234
e = X
f = 3.141590
g = 3.141593
a = 1023
a = 1023
a = 1023
a = 1023
a = 1023
f = 3.141590
f = 3.141590
f = 3.142
f = 3.14159
f = 3.14159 |
Prima del suo utilizzo, una variabile in un programma C deve essere dichiarata. Una dichiarazione di variabile indica al compilatore il nome e il tipo di una variabile e, facoltativamente, inizializza la variabile su un valore specifico.
Se il tuo programma tenta di utilizzare una variabile che non è stata dichiarata, il compilatore genera un messaggio di errore. Una dichiarazione di variabile ha la forma seguente:
typename nomevar;
typename specifica il tipo di variabile e deve essere una delle parole chiave. nomevar è il nome della variabile. Puoi dichiarare più variabili dello stesso tipo su una riga separando i nomi delle variabili con virgole:
int count, number, start; /* three integer variables */
float percent, total; /* two float variables */
La parola chiave typedef
La parola chiave typedef viene utilizzata per creare un nuovo nome per un tipo di dati esistente. In effetti, typedef crea un sinonimo. Ad esempio, l'affermazione
numero intero typedef;
qui vediamo typedef crea intero come sinonimo di int. È quindi possibile utilizzare intero per definire variabili di tipo int, come in questo esempio:
conteggio intero;
Quindi typedef non crea un nuovo tipo di dati, ti consente solo di utilizzare un nome diverso per un tipo di dati predefinito.
Inizializzazione delle variabili numeriche
Quando viene dichiarata una variabile, al compilatore viene richiesto di riservare spazio di archiviazione per la variabile. Tuttavia, il valore memorizzato in quello spazio, il valore della variabile, non è definito. Potrebbe essere zero o potrebbe trattarsi di una "spazzatura" casuale; valore. Prima di utilizzare una variabile, dovresti sempre inizializzarla su un valore noto. Prendiamo questo esempio:
int count; /* Set aside storage space for count */
count = 0; /* Store 0 in count */
Questa istruzione usa il segno di uguale (=), che è l'operatore di assegnazione di C. Puoi anche inizializzare una variabile quando viene dichiarata. Per fare ciò, segui il nome della variabile nella dichiarazione di dichiarazione con un segno di uguale e il valore iniziale desiderato:
int count = 0;
double rate = 0.01, complexity = 28.5;
Fare attenzione a non inizializzare una variabile con un valore al di fuori dell'intervallo consentito. Di seguito sono riportati due esempi di inizializzazioni fuori gamma:
int amount = 100000;
unsigned int length = -2500;
Il compilatore C non rileva tali errori. Il tuo programma potrebbe essere compilato e collegato, ma potresti ottenere risultati imprevisti quando il programma viene eseguito.
Facciamo il seguente esempio per calcolare il numero totale di settori in un disco:
// Programma modello per calcolare i settori in un disco //
#include<stdio.h>
#define SECTOR_PER_SIDE 63
#define SIDE_PER_CYLINDER 254
void main()
{
int cylinder=0;
clrscr();
printf("Enter The No. of Cylinders in the Disk \n\n\t");
scanf("%d",&cylinder); // Get the value from the user //
printf("\n\n\t Total Number of Sectors in the disk = %ld", (long)SECTOR_PER_SIDE*SIDE_PER_CYLINDER* cylinder);
getch();
}
L'output del programma è il seguente:
Enter The No. of Cylinders in the Disk
1024
Total Number of Sectors in the disk = 16386048
In questo esempio vediamo tre nuove cose da imparare. #define viene utilizzato per utilizzare costanti simboliche nel programma o in alcuni casi per risparmiare tempo definendo parole lunghe in piccoli simboli.
Qui abbiamo definito il numero di settori per lato che è 63 come SECTOR_PER_SIDE per rendere il programma facile da capire. Lo stesso caso vale per #define SIDE_PER_CYLINDER 254. scanf() viene utilizzato per ottenere l'input dall'utente.
Qui prendiamo il numero di cilindri come input dall'utente. * viene utilizzato per moltiplicare due o più valori come mostrato nell'esempio.
La funzione getch() fondamentalmente ottiene un input di un singolo carattere dalla tastiera. Digitando getch(); qui fermiamo lo schermo fino a quando non viene premuto un tasto qualsiasi dalla tastiera.
Operatori
Un operatore è un simbolo che indica a C di eseguire qualche operazione, o azione, su uno o più operandi. Un operando è qualcosa su cui agisce un operatore. In C, tutti gli operandi sono espressioni. Gli operatori C sono delle seguenti quattro categorie:
- L'operatore di assegnazione
- Operatori matematici
- Operatori relazionali
- Operatori logici
Operatore di assegnazione
L'operatore di assegnazione è il segno di uguale (=). L'uso del segno di uguale nella programmazione è diverso dal suo uso nelle normali relazioni algebriche matematiche. Se scrivi
x = y;
In un programma C, non significa "x è uguale a y". Significa invece "assegna il valore di y a x". In un'istruzione di assegnazione C, il lato destro può essere qualsiasi espressione e il lato sinistro deve essere un nome di variabile. Pertanto, il modulo è il seguente:
variabile = espressione;
Durante l'esecuzione, l'espressione viene valutata e il valore risultante viene assegnato alla variabile.
Operatori matematici
Gli operatori matematici di C eseguono operazioni matematiche come addizioni e sottrazioni. C ha due operatori matematici unari e cinque operatori matematici binari. Gli operatori matematici unari sono così chiamati perché accettano un singolo operando. C ha due operatori matematici unari.
Gli operatori di incremento e decremento possono essere utilizzati solo con variabili, non con costanti. L'operazione eseguita consiste nell'aggiungerne uno o sottrarne uno dall'operando. In altre parole, le affermazioni ++x; e --y; sono gli equivalenti di queste affermazioni:
x = x + 1;
y = y - 1;
gli operatori matematici binari accettano due operandi. I primi quattro operatori binari, che includono le operazioni matematiche comuni che si trovano su una calcolatrice (+, -, *, /), ti sono familiari. Il quinto operatore Modulo restituisce il resto quando il primo operando viene diviso per il secondo operando. Ad esempio, 11 modulo 4 è uguale a 3 (11 è diviso per 4, due volte e 3 rimanenti).
Operatori relazionali
Gli operatori relazionali di C sono usati per confrontare le espressioni. Un'espressione contenente un operatore relazionale restituisce true (1) o false (0). C ha sei operatori relazionali.
Operatori logici
Gli operatori logici di C consentono di combinare due o più espressioni relazionali in un'unica espressione che restituisce true o false. Gli operatori logici restituiscono true o false, a seconda del valore true o false dei loro operandi.
Se x è una variabile intera, le espressioni che utilizzano operatori logici possono essere scritte nei seguenti modi:
(x > 1) && (x < 5)
(x >= 2) && (x <= 4)
Operator |
Symbol |
Description |
Example |
Assignment operators |
equal |
= |
assign the value of y to x |
x = y |
Mathematical operators |
Increment |
++ |
Increments the operand by one |
++x, x++ |
Decrement |
-- |
Decrements the operand by one |
--x, x-- |
Addition |
+ |
Adds two operands |
x + y |
Subtraction |
- |
Subtracts the second operand from the first |
x - y |
Multiplication |
* |
Multiplies two operands |
x * y |
Division |
/ |
Divides the first operand by the second operand |
x / y |
Modulus |
% |
Gives the remainder when the first operand is divided by the second operand |
x % y |
Relational operators |
Equal |
= = |
Equality |
x = = y |
Greater than |
> |
Greater than |
x > y |
Less than |
< |
Less than |
x < y |
Greater than or equal to |
>= |
Greater than or equal to |
x >= y |
Less than or equal to |
<= |
Less than or equal to |
x <= y |
Not equal |
!= |
Not equal to |
x != y |
Logical operators |
AND |
&& |
True (1) only if both exp1 and exp2 are true; false (0) otherwise |
exp1 && exp2 |
OR |
|| |
True (1) if either exp1 or exp2 is true; false (0) only if both are false |
exp1 || exp2 |
NOT |
! |
False (0) if exp1 is true; true (1) if exp1 is false |
!exp1 |
Cose da ricordare sulle espressioni logiche
x * = y |
is same as |
x = x * y |
y - = z + 1 |
is same as |
y = y - z + 1 |
a / = b |
is same as |
a = a / b |
x + = y / 8 |
is same as |
x = x + y / 8 |
y % = 3 |
is same as |
y = y % 3 |
L'operatore di virgola
La virgola viene spesso utilizzata in C come un semplice segno di punteggiatura, per separare dichiarazioni di variabili, argomenti di funzioni, ecc. In determinate situazioni, la virgola funge da operatore.
Puoi formare un'espressione separando due sottoespressioni con una virgola. Il risultato è il seguente:
- Vengono valutate entrambe le espressioni, con l'espressione di sinistra valutata per prima.
- L'intera espressione restituisce il valore dell'espressione corretta.
Ad esempio, la seguente istruzione assegna il valore di b a x, quindi incrementa a e quindi incrementa b:
x = (a++, b++);
Precedenza dell'operatore C (Riepilogo degli operatori C)
Rank and Associativity |
Operators |
1(left to right) |
() [] -> . |
2(right to left) |
! ~ ++ -- * (indirection) & (address-of) (type)
sizeof + (unary) - (unary) |
3(left to right) |
* (multiplication) / % |
4(left to right) |
+ - |
5(left to right) |
<< >> |
6(left to right) |
< <= > >= |
7(left to right) |
= = != |
8(left to right) |
& (bitwise AND) |
9(left to right) |
^ |
10(left to right) |
| |
11(left to right) |
&& |
12(left to right) |
|| |
13(right to left) |
?: |
14(right to left) |
= += -= *= /= %= &= ^= |= <<= >>= |
15(left to right) |
, |
() is the function operator; [] is the array operator. |
|
Facciamo un esempio di utilizzo degli operatori:
/* Utilizzo di operatori */
int main()
{
int x = 0, y = 2, z = 1025;
float a = 0.0, b = 3.14159, c = -37.234;
/* incrementing */
x = x + 1; /* This increments x */
x++; /* This increments x */
++x; /* This increments x */
z = y++; /* z = 2, y = 3 */
z = ++y; /* z = 4, y = 4 */
/* decrementing */
y = y - 1; /* This decrements y */
y--; /* This decrements y */
--y; /* This decrements y */
y = 3;
z = y--; /* z = 3, y = 2 */
z = --y; /* z = 1, y = 1 */
/* arithmetic op */
a = a + 12; /* This adds 12 to a */
a += 12; /* This adds 12 more to a */
a *= 3.2; /* This multiplies a by 3.2 */
a -= b; /* This subtracts b from a */
a /= 10.0; /* This divides a by 10.0 */
/* conditional expression */
a = (b >= 3.0 ? 2.0 : 10.5 ); /* This expression */
if (b >= 3.0) /* And this expression */
a = 2.0; /* are identical, both */
else /* will cause the same */
a = 10.5; /* result. */
c = (a > b ? a : b); /* c will have the max of a or b */
c = (a > b ? b : a); /* c will have the min of a or b */
printf("x=%d, y=%d, z= %d\n", x, y, z);
printf("a=%f, b=%f, c= %f", a, b, c);
return 0;
}
e il risultato di questo programma verrà visualizzato sullo schermo come:
x=3, y=1, z=1
a=2.000000, b=3.141590, c=2.000000
Qualcosa di più su printf() e Scanf()
Considera le seguenti due affermazioni printf
printf(“\t %d\n”, num);
printf(“%5.2f”, fract);
nella prima istruzione printf \t richiede lo spostamento di tabulazione sullo schermo l'argomento %d dice al compilatore che il valore di num deve essere stampato come intero decimale. \n fa iniziare il nuovo output da una nuova riga.
Nella seconda istruzione printf %5.2f dice al compilatore che l'output deve essere in virgola mobile, con cinque cifre in tutto e due a destra della virgola decimale. Ulteriori informazioni sul carattere della barra rovesciata sono state mostrate nella tabella seguente:
Constant |
Meaning |
‘\a’ |
Audible alert (bell) |
‘\b’ |
Backspace |
‘\f’ |
Form feed |
‘\n’ |
New line |
‘\r’ |
Carriage return |
‘\t’ |
Horizontal tab |
‘\v’ |
Vertical tab |
‘\’’ |
Single quote |
‘\”’ |
Double quote |
‘\?’ |
Question mark |
‘\\’ |
Backslash |
‘\0’ |
Null |
Consideriamo la seguente istruzione scanf
scanf(“%d”, &num);
I dati dalla tastiera vengono ricevuti dalla funzione scanf. Nel formato sopra, il & Il simbolo (e commerciale) prima di ogni nome di variabile è un operatore che specifica l'indirizzo del nome di variabile.
In questo modo l'esecuzione si interrompe e attende la digitazione del valore della variabile num. Quando si immette il valore intero e si preme il tasto Invio, il computer procede all'istruzione successiva. I codici di formato scanf e printf sono elencati nella tabella seguente:
Code |
Reads... |
%c |
Single character |
%d |
Decimal integer |
%e |
Floating point value |
%f |
Floating point value |
%g |
Floating point value |
%h |
Short integer |
%i |
Decimal, hexadecimal or octal integer |
%o |
Octal integer |
%s |
String |
%u |
Unsigned decimal integer |
%x |
Hexadecimal integer |
Dichiarazioni di controllo
Un programma è costituito da un numero di istruzioni che di solito vengono eseguite in sequenza. I programmi possono essere molto più potenti se riusciamo a controllare l'ordine in cui vengono eseguite le istruzioni.
Le dichiarazioni si dividono in tre tipi generali:
- Assegnazione, in cui i valori, solitamente i risultati dei calcoli, sono memorizzati nelle variabili.
- Input / Output, i dati vengono letti o stampati.
- Controllo, il programma decide cosa fare dopo.
Questa sezione discuterà l'uso delle istruzioni di controllo in C. Mostreremo come possono essere utilizzate per scrivere programmi potenti da;
- Ripetizione di sezioni importanti del programma.
- Selezione tra le sezioni opzionali di un programma.
Dichiarazione if else
Viene utilizzato per decidere se fare qualcosa in un punto speciale o per decidere tra due linee di azione.
Il seguente test decide se uno studente ha superato un esame con un punteggio di 45
if (result >= 45)
printf("Pass\n");
else
printf("Fail\n");
It is possible to use the if part without the else.
if (temperature < 0)
print("Frozen\n");
Ogni versione consiste in un test, nella dichiarazione tra parentesi dopo l'if. Se il test è vero, allora si obbedisce all'affermazione successiva. Se è falso, l'affermazione successiva all'altro viene obbedita, se presente. Dopodiché, il resto del programma continua normalmente.
Se desideriamo avere più di un'istruzione dopo l'if o l'altro, dovrebbero essere raggruppate tra parentesi graffe. Tale raggruppamento è chiamato istruzione composta o blocco.
if (result >= 45)
{ printf("Passed\n");
printf("Congratulations\n");
}
else
{ printf("Failed\n");
printf("Better Luck Next Time\n");
}
A volte desideriamo prendere una decisione a più vie in base a diverse condizioni. Il modo più generale per farlo è usare la variante else if sull'istruzione if.
Funziona sovrapponendo a cascata diversi confronti. Non appena uno di questi fornisce un risultato vero, viene eseguita l'istruzione o il blocco seguente e non vengono eseguiti ulteriori confronti. Nell'esempio seguente assegniamo voti in base al risultato dell'esame.
if (result <=100 && result >= 75)
printf("Passed: Grade A\n");
else if (result >= 60)
printf("Passed: Grade B\n");
else if (result >= 45)
printf("Passed: Grade C\n");
else
printf("Failed\n");
In questo esempio, tutti i confronti testano una singola variabile chiamata risultato. In altri casi, ogni test può comportare una variabile diversa o una combinazione di test. Lo stesso schema può essere utilizzato con più o meno se altri e l'ultimo solo altro può essere tralasciato.
Spetta al programmatore ideare la struttura corretta per ogni problema di programmazione. Per capire meglio l'uso di if else vediamo l'esempio
#include <stdio.h>
int main()
{
int num;
for(num = 0 ; num < 10 ; num = num + 1)
{
if (num == 2)
printf("num is now equal to %d\n", num);
if (num < 5)
printf("num is now %d, which is less than 5\n", num);
else
printf("num is now %d, which is greater than 4\n", num);
} /* end of for loop */
return 0;
}
Risultato del programma
num is now 0, which is less than 5
num is now 1, which is less than 5
num is now equal to 2
num is now 2, which is less than 5
num is now 3, which is less than 5
num is now 4, which is less than 5
num is now 5, which is greater than 4
num is now 6, which is greater than 4
num is now 7, which is greater than 4
num is now 8, which is greater than 4
num is now 9, which is greater than 4
La dichiarazione sul passaggio
Questa è un'altra forma di decisione a più vie. È ben strutturato, ma può essere utilizzato solo in alcuni casi in cui;
- Viene testata solo una variabile, tutti i rami devono dipendere dal valore di quella variabile. La variabile deve essere di tipo integrale. (int, long, short o char).
- Ogni possibile valore della variabile può controllare un singolo ramo. È possibile utilizzare un ramo predefinito finale, catch all, facoltativamente per intercettare tutti i casi non specificati.
L'esempio riportato di seguito chiarirà le cose. Questa è una funzione che converte un numero intero in una descrizione vaga. È utile quando ci occupiamo di misurare una quantità solo quando è piuttosto piccola.
estimate(number)
int number;
/* Estimate a number as none, one, two, several, many */
{ switch(number) {
case 0 :
printf("None\n");
break;
case 1 :
printf("One\n");
break;
case 2 :
printf("Two\n");
break;
case 3 :
case 4 :
case 5 :
printf("Several\n");
break;
default :
printf("Many\n");
break;
}
}
Ogni caso interessante è elencato con un'azione corrispondente. L'istruzione break impedisce l'esecuzione di ulteriori istruzioni lasciando l'opzione. Poiché il caso 3 e il caso 4 non hanno interruzione successiva, continuano a consentire la stessa azione per diversi valori di numero.
Sia i costrutti if che switch consentono al programmatore di effettuare una selezione da una serie di possibili azioni. Vediamo un esempio:
#include <stdio.h>
int main()
{
int num;
for (num = 3 ; num < 13 ; num = num + 1)
{
switch (num)
{
case 3 :
printf("The value is three\n");
break;
case 4 :
printf("The value is four\n");
break;
case 5 :
case 6 :
case 7 :
case 8 :
printf("The value is between 5 and 8\n");
break;
case 11 :
printf("The value is eleven\n");
break;
default :
printf("It is one of the undefined values\n");
break;
} /* end of switch */
} /* end of for loop */
return 0;
}
L'output del programma sarà
The value is three
The value is four
The value is between 5 and 8
The value is between 5 and 8
The value is between 5 and 8
The value is between 5 and 8
It is one of the undefined values
It is one of the undefined values
The value is eleven
It is one of the undefined values
La dichiarazione di rottura
Abbiamo già incontrato un'interruzione nella discussione dell'istruzione switch. Viene utilizzato per uscire da un ciclo o da uno switch, il controllo passa alla prima istruzione oltre il ciclo o uno switch.
Con i loop, l'interruzione può essere utilizzata per forzare un'uscita anticipata dal loop o per implementare un loop con un test per uscire nel mezzo del corpo del loop. Un'interruzione all'interno di un ciclo dovrebbe sempre essere protetta all'interno di un'istruzione if che fornisce il test per controllare la condizione di uscita.
La dichiarazione continua
È simile all'interruzione ma si verifica meno frequentemente. Funziona solo all'interno di loop in cui il suo effetto è quello di forzare un salto immediato all'istruzione di controllo del ciclo.
- In un ciclo while, passa all'istruzione di test.
- In un ciclo do while, passa all'istruzione di test.
- In un ciclo for, passa al test ed esegui l'iterazione.
Come una pausa, continue dovrebbe essere protetto da un'istruzione if. È improbabile che lo usi molto spesso. Per comprendere meglio l'uso di break e continue esaminiamo il seguente programma:
#include <stdio.h>
int main()
{
int value;
for(value = 5 ; value < 15 ; value = value + 1)
{
if (value == 8)
break;
printf("In the break loop, value is now %d\n", value);
}
for(value = 5 ; value < 15 ; value = value + 1)
{
if (value == 8)
continue;
printf("In the continue loop, value is now %d\n", value);
}
return 0;
}
L'output del programma sarà il seguente:
In the break loop, value is now 5
In the break loop, value is now 6
In the break loop, value is now 7
In the continue loop, value is now 5
In the continue loop, value is now 6
In the continue loop, value is now 7
In the continue loop, value is now 9
In the continue loop, value is now 10
In the continue loop, value is now 11
In the continue loop, value is now 12
In the continue loop, value is now 13
In the continue loop, value is now 14
Cicli
L'altro tipo principale di istruzione di controllo è il ciclo. I loop consentono di ripetere un'istruzione o un blocco di istruzioni. I computer sono molto bravi a ripetere compiti semplici molte volte. Il ciclo è il modo in cui C per raggiungere questo obiettivo.
C ti offre la possibilità di scegliere tra tre tipi di loop, while, do-while e for.
- Il ciclo while continua a ripetere un'azione finché un test associato non restituisce false. Questo è utile quando il programmatore non sa in anticipo quante volte il ciclo verrà attraversato.
- Il ciclo do while è simile, ma il test si verifica dopo l'esecuzione del corpo del ciclo. Ciò garantisce che il corpo del ciclo venga eseguito almeno una volta.
- Il ciclo for è usato frequentemente, di solito dove il ciclo verrà attraversato un numero fisso di volte. È molto flessibile e i programmatori inesperti dovrebbero fare attenzione a non abusare della potenza che offre.
Il ciclo del tempo
Il ciclo while ripete un'istruzione finché il test in alto non si rivela falso. Ad esempio, ecco una funzione per restituire la lunghezza di una stringa. Ricorda che la stringa è rappresentata come una matrice di caratteri terminata da un carattere nullo '\0'.
int string_length(char string[])
{ int i = 0;
while (string[i] != '\0')
i++;
return(i);
}
La stringa viene passata alla funzione come argomento. La dimensione dell'array non è specificata, la funzione funzionerà per una stringa di qualsiasi dimensione.
Il ciclo while viene utilizzato per esaminare i caratteri nella stringa uno alla volta finché non viene trovato il carattere nullo. Quindi si esce dal ciclo e viene restituito l'indice del null.
Mentre il carattere non è nullo, l'indice viene incrementato e il test viene ripetuto. Approfondiremo gli array in seguito. Vediamo un esempio per il ciclo while:
#include <stdio.h>
int main()
{
int count;
count = 0;
while (count < 6)
{
printf("The value of count is %d\n", count);
count = count + 1;
}
return 0;
}
e il risultato viene visualizzato come segue:
The value of count is 0
The value of count is 1
The value of count is 2
The value of count is 3
The value of count is 4
The value of count is 5
Il ciclo continuo
Questo è molto simile al ciclo while tranne per il fatto che il test si verifica alla fine del corpo del ciclo. Ciò garantisce che il ciclo venga eseguito almeno una volta prima di continuare.
Tale configurazione viene spesso utilizzata per la lettura dei dati. Il test verifica quindi i dati e torna indietro per leggerli di nuovo se non erano accettabili.
do
{
printf("Enter 1 for yes, 0 for no :");
scanf("%d", &input_value);
} while (input_value != 1 && input_value != 0)
Per comprendere meglio il ciclo do while, vediamo il seguente esempio:
#include <stdio.h>
int main()
{
int i;
i = 0;
do
{
printf("The value of i is now %d\n", i);
i = i + 1;
} while (i < 5);
return 0;
}
Il risultato del programma viene visualizzato come segue:
The value of i is now 0
The value of i is now 1
The value of i is now 2
The value of i is now 3
The value of i is now 4
Il ciclo for
Il ciclo for funziona bene dove il numero di iterazioni del ciclo è noto prima che venga inserito il ciclo. La testa del ciclo è composta da tre parti separate da punto e virgola.
- Il primo viene eseguito prima che venga inserito il ciclo. Questa è solitamente l'inizializzazione della variabile di ciclo.
- Il secondo è un test, il ciclo viene chiuso quando restituisce false.
- La terza è un'istruzione da eseguire ogni volta che il corpo del ciclo viene completato. Questo è solitamente un incremento del contatore di loop.
L'esempio è una funzione che calcola la media dei numeri memorizzati in un array. La funzione prende l'array e il numero di elementi come argomenti.
float average(float array[], int count)
{
float total = 0.0;
int i;
for(i = 0; i < count; i++)
total += array[i];
return(total / count);
}
Il ciclo for assicura che venga sommato il numero corretto di elementi dell'array prima di calcolare la media.
Le tre istruzioni all'inizio di un ciclo for di solito fanno solo una cosa ciascuna, tuttavia ognuna di esse può essere lasciata vuota. Una prima o l'ultima istruzione vuota significherà nessuna inizializzazione o incremento in esecuzione. Una dichiarazione di confronto vuota sarà sempre considerata vera. Ciò farà sì che il ciclo venga eseguito indefinitamente a meno che non venga interrotto con altri mezzi. Potrebbe trattarsi di una dichiarazione di ritorno o di interruzione.
È anche possibile comprimere più affermazioni nella prima o terza posizione, separandole con virgole. Ciò consente un ciclo con più di una variabile di controllo. L'esempio seguente illustra la definizione di tale ciclo, con le variabili hi e lo che iniziano rispettivamente da 100 e 0 e convergenti.
Il ciclo for fornisce una varietà di scorciatoie da utilizzare al suo interno. Fai attenzione alla seguente espressione, in questa espressione il ciclo singolo contiene due cicli for. Qui hi-- è uguale a hi = hi - 1 e lo++ è uguale a lo = lo + 1,
for(hi = 100, lo = 0; hi >= lo; hi--, lo++)
Il ciclo for è estremamente flessibile e consente di specificare in modo semplice e rapido molti tipi di comportamento del programma. Vediamo un esempio di ciclo for
#include <stdio.h>
int main()
{
int index;
for(index = 0 ; index < 6 ; index = index + 1)
printf("The value of the index is %d\n", index);
return 0;
}
Il risultato del programma viene visualizzato come segue:
The value of the index is 0
The value of the index is 1
The value of the index is 2
The value of the index is 3
The value of the index is 4
The value of the index is 5
La dichiarazione di goto
C ha un'istruzione goto che consente di eseguire salti non strutturati. Per utilizzare un'istruzione goto, è sufficiente utilizzare la parola riservata goto seguita dal nome simbolico a cui si desidera saltare. Il nome viene quindi inserito in un punto qualsiasi del programma seguito da due punti. Puoi saltare quasi ovunque all'interno di una funzione, ma non ti è permesso saltare in un loop, anche se ti è permesso saltare fuori da un loop.
Questo particolare programma è davvero un disastro, ma è un buon esempio del motivo per cui gli autori di software stanno cercando di eliminare il più possibile l'uso dell'istruzione goto. L'unico punto in questo programma in cui è ragionevole usare goto è, dove il programma salta fuori dai tre loop nidificati in un salto. In questo caso sarebbe piuttosto complicato impostare una variabile e saltare successivamente da ciascuno dei tre cicli nidificati, ma un'istruzione goto ti fa uscire da tutti e tre in modo molto conciso.
Alcuni dicono che l'istruzione goto non dovrebbe mai essere usata in nessuna circostanza, ma questo è un pensiero ristretto. Se c'è un punto in cui un goto eseguirà chiaramente un flusso di controllo più ordinato rispetto a qualche altro costrutto, sentiti libero di usarlo, tuttavia, come è nel resto del programma sul tuo monitor. Vediamo l'esempio:
#include <stdio.h>
int main()
{
int dog, cat, pig;
goto real_start;
some_where:
printf("This is another line of the mess.\n");
goto stop_it;
/* the following section is the only section with a useable goto */
real_start:
for(dog = 1 ; dog < 6 ; dog = dog + 1)
{
for(cat = 1 ; cat < 6 ; cat = cat + 1)
{
for(pig = 1 ; pig < 4 ; pig = pig + 1)
{
printf("Dog = %d Cat = %d Pig = %d\n", dog, cat, pig);
if ((dog + cat + pig) > 8 ) goto enough;
}
}
}
enough: printf("Those are enough animals for now.\n");
/* this is the end of the section with a useable goto statement */
printf("\nThis is the first line of the code.\n");
goto there;
where:
printf("This is the third line of the code.\n");
goto some_where;
there:
printf("This is the second line of the code.\n");
goto where;
stop_it:
printf("This is the last line of this mess.\n");
return 0;
}
Fammi vedere i risultati visualizzati
Dog = 1 Cat = 1 Pig = 1
Dog = 1 Cat = 1 Pig = 2
Dog = 1 Cat = 1 Pig = 3
Dog = 1 Cat = 2 Pig = 1
Dog = 1 Cat = 2 Pig = 2
Dog = 1 Cat = 2 Pig = 3
Dog = 1 Cat = 3 Pig = 1
Dog = 1 Cat = 3 Pig = 2
Dog = 1 Cat = 3 Pig = 3
Dog = 1 Cat = 4 Pig = 1
Dog = 1 Cat = 4 Pig = 2
Dog = 1 Cat = 4 Pig = 3
Dog = 1 Cat = 5 Pig = 1
Dog = 1 Cat = 5 Pig = 2
Dog = 1 Cat = 5 Pig = 3
Those are enough animals for now.
This is the first line of the code.
This is the second line of the code.
This is the third line of the code.
This is another line of the mess.
This is the last line of this mess.
Puntatori
A volte vogliamo sapere dove risiede una variabile in memoria. Un puntatore contiene l'indirizzo di una variabile che ha un valore specifico. Quando si dichiara un puntatore, viene posizionato un asterisco immediatamente prima del nome del puntatore .
L'indirizzo della posizione di memoria in cui è memorizzata la variabile può essere trovato mettendo una e commerciale davanti al nome della variabile.
int num; /* Normal integer variable */
int *numPtr; /* Pointer to an integer variable */
L'esempio seguente stampa il valore della variabile e l'indirizzo in memoria di quella variabile.
printf("Il valore %d è memorizzato all'indirizzo %X\n", num, &num);
Per assegnare l'indirizzo della variabile num al puntatore numPtr, si assegna l'indirizzo della variabile, num, come nell'esempio riportato di seguito:
numPtr = #
Per scoprire cosa è memorizzato all'indirizzo indicato da numPtr, è necessario dereferenziare la variabile. La dereferenziazione si ottiene con l'asterisco con cui è stato dichiarato il puntatore.
printf("Il valore %d è memorizzato all'indirizzo %X\n", *numPtr, numPtr);
Tutte le variabili in un programma risiedono in memoria. Le istruzioni fornite di seguito richiedono che il compilatore riservi 4 byte di memoria su un computer a 32 bit per la variabile a virgola mobile x, quindi inserisca il valore 6,5 in essa.
float x;
x = 6.5;
Poiché la posizione dell'indirizzo in memoria di qualsiasi variabile si ottiene posizionando l'operatore & prima del suo nome quindi &x è l'indirizzo di x. C ci permette di andare oltre e definire una variabile, chiamata puntatore, che contiene l'indirizzo di altre variabili. Piuttosto possiamo dire che il puntatore punta ad un'altra variabile. Per esempio:
float x;
float* px;
x = 6.5;
px = &x;
definisce px come un puntatore a oggetti di tipo float e lo imposta uguale all'indirizzo di x. Pertanto, *px si riferisce al valore di x:
Esaminiamo le seguenti affermazioni:
int var_x;
int* ptrX;
var_x = 6;
ptrX = &var_x;
*ptrX = 12;
printf("value of x : %d", var_x);
La prima riga fa sì che il compilatore riservi uno spazio in memoria per un intero. La seconda riga dice al compilatore di riservare spazio per memorizzare un puntatore.
Un puntatore è una posizione di archiviazione per un indirizzo. La terza riga dovrebbe ricordarti le istruzioni scanf. L'indirizzo "&" l'operatore dice al compilatore di andare nella posizione in cui ha memorizzato var_x, quindi fornire l'indirizzo della posizione di archiviazione a ptrX.
L'asterisco * davanti a una variabile dice al compilatore di dereferenziare il puntatore e andare in memoria. Quindi puoi assegnare assegnazioni a variabili memorizzate in quella posizione. Puoi fare riferimento a una variabile e accedere ai suoi dati tramite un puntatore. Vediamo un esempio di puntatori:
/* illustration of pointer use */
#include <stdio.h>
int main()
{
int index, *pt1, *pt2;
index = 39; /* any numerical value */
pt1 = &index; /* the address of index */
pt2 = pt1;
printf("The value is %d %d %d\n", index, *pt1, *pt2);
*pt1 = 13; /* this changes the value of index */
printf("The value is %d %d %d\n", index, *pt1, *pt2);
return 0;
}
L'output del programma verrà visualizzato come segue:
The value is 39 39 39
The value is 13 13 13
Vediamo un altro esempio per comprendere meglio l'uso dei puntatori:
#include <stdio.h>
#include <string.h>
int main()
{
char strg[40], *there, one, two;
int *pt, list[100], index;
strcpy(strg, "This is a character string.");
/* the function strcpy() is to copy one string to another. we’ll read about strcpy() function in String Section later */
one = strg[0]; /* one and two are identical */
two = *strg;
printf("The first output is %c %c\n", one, two);
one = strg[8]; /* one and two are identical */
two = *(strg+8);
printf("The second output is %c %c\n", one, two);
there = strg+10; /* strg+10 is identical to &strg[10] */
printf("The third output is %c\n", strg[10]);
printf("The fourth output is %c\n", *there);
for (index = 0 ; index < 100 ; index++)
list[index] = index + 100;
pt = list + 27;
printf("The fifth output is %d\n", list[27]);
printf("The sixth output is %d\n", *pt);
return 0;
}
L'output del programma sarà così:
The first output is T T
The second output is a a
The third output is c
The fourth output is c
The fifth output is 127
The sixth output is 127
Array
Un array è una raccolta di variabili dello stesso tipo. I singoli elementi dell'array sono identificati da un indice intero. In C l'indice inizia da zero e si scrive sempre tra parentesi quadre.
Abbiamo già incontrato array a dimensione singola dichiarati in questo modo
risultati int[20];
Gli array possono avere più dimensioni, nel qual caso potrebbero essere dichiarati come
int results_2d[20][5];
int results_3d[20][5][3];
Ogni indice ha il proprio set di parentesi quadre. Un array è dichiarato nella funzione principale, di solito include i dettagli delle dimensioni. È possibile utilizzare un altro tipo chiamato puntatore al posto di un array. Ciò significa che le dimensioni non vengono fissate immediatamente, ma lo spazio può essere allocato secondo necessità. Questa è una tecnica avanzata richiesta solo in alcuni programmi specializzati.
Ad esempio, ecco una semplice funzione per sommare tutti i numeri interi in un array a dimensione singola.
int add_array(int array[], int size)
{
int i;
int total = 0;
for(i = 0; i < size; i++)
total += array[i];
return(total);
}
Il programma indicato di seguito creerà una stringa, accederà ad alcuni dati in essa contenuti, la stamperà. Accedi di nuovo utilizzando i puntatori, quindi stampa la stringa. Dovrebbe stampare "Ciao!" e “012345678” su righe diverse. Vediamo la codifica del programma:
#include <stdio.h>
#define STR_LENGTH 10
void main()
{
char Str[STR_LENGTH];
char* pStr;
int i;
Str[0] = 'H';
Str[1] = 'i';
Str[2] = '!';
Str[3] = '\0'; // special end string character NULL
printf("The string in Str is : %s\n", Str);
pStr = &Str[0];
for (i = 0; i < STR_LENGTH; i++)
{
*pStr = '0'+i;
pStr++;
}
Str[STR_LENGTH-1] = '\0';
printf("The string in Str is : %s\n", Str);
}
[] (parentesi quadre) sono usati per dichiarare l'array. La riga del programma char Str[STR_LENGTH]; dichiara una matrice di dieci caratteri. Questi sono dieci personaggi individuali, che sono tutti riuniti nella memoria nello stesso posto. Sono tutti accessibili tramite il nostro nome variabile Str insieme a [n] dove n è il numero dell'elemento.
Va sempre tenuto presente quando si parla di array che quando C dichiara un array di dieci, gli elementi a cui si può accedere sono numerati da 0 a 9. L'accesso al primo elemento corrisponde all'accesso allo 0° elemento. Quindi, in caso di Array, contare sempre da 0 alla dimensione dell'array - 1.
Prossimo avviso che mettiamo le lettere "Ciao!" nell'array, ma poi inseriamo un '\0' probabilmente ti starai chiedendo di cosa si tratta. "\0" sta per NULL e rappresenta la fine della stringa. Tutte le stringhe di caratteri devono terminare con questo carattere speciale '\0'. Se non lo fanno, e poi qualcuno chiama printf sulla stringa, allora printf partirebbe dalla posizione di memoria della tua stringa e continua a stampare dire che incontra '\0' e quindi alla fine ti ritroverai con un mucchio di spazzatura della tua corda. Quindi assicurati di terminare correttamente le tue stringhe.
Matrici di caratteri
Una costante stringa , come
"Io sono una stringa"
è un array di caratteri. È rappresentato internamente in C dai caratteri ASCII nella stringa, ovvero "I", spazio vuoto, "a", "m",... o dalla stringa sopra, e terminato dal carattere nullo speciale "\0" in modo che i programmi possano trova la fine della stringa.
Le costanti stringa vengono spesso utilizzate per rendere intelligibile l'output del codice utilizzando printf:
printf("Hello, world\n");
printf("The value of a is: %f\n", a);
Le costanti stringa possono essere associate a variabili. C fornisce la variabile del tipo di carattere, che può contenere un carattere (1 byte) alla volta. Una stringa di caratteri viene memorizzata in una matrice di tipo di carattere, un carattere ASCII per posizione.
Non dimenticare mai che, poiché le stringhe sono convenzionalmente terminate con il carattere nullo "\0", è necessaria una posizione di archiviazione aggiuntiva nell'array.
C non fornisce alcun operatore che manipola intere stringhe contemporaneamente. Le stringhe vengono manipolate tramite puntatori o tramite routine speciali disponibili dalla libreria di stringhe standard string.h.
L'uso dei puntatori a caratteri è relativamente facile poiché il nome di un array è solo un puntatore al suo primo elemento. Considera il programma indicato di seguito:
#include<stdio.h>
void main()
{
char text_1[100], text_2[100], text_3[100];
char *ta, *tb;
int i;
/* set message to be an arrray */
/* of characters; initialize it */
/* to the constant string "..." */
/* let the compiler decide on */
/* its size by using [] */
char message[] = "Hello, I am a string; what are
you?";
printf("Original message: %s\n", message);
/* copy the message to text_1 */
i=0;
while ( (text_1[i] = message[i]) != '\0' )
i++;
printf("Text_1: %s\n", text_1);
/* use explicit pointer arithmetic */
ta=message;
tb=text_2;
while ( ( *tb++ = *ta++ ) != '\0' )
;
printf("Text_2: %s\n", text_2);
}
L'output del programma sarà il seguente:
Original message: Hello, I am a string; what are you?
Text_1: Hello, I am a string; what are you?
Text_2: Hello, I am a string; what are you?
La libreria "stringa" standard contiene molte funzioni utili per manipolare le stringhe, che impareremo più avanti nella sezione delle stringhe.
Accesso agli elementi
Per accedere a un singolo elemento nell'array, il numero di indice segue il nome della variabile tra parentesi quadre. La variabile può quindi essere trattata come qualsiasi altra variabile in C. L'esempio seguente assegna un valore al primo elemento dell'array.
x[0] = 16;
L'esempio seguente stampa il valore del terzo elemento in una matrice.
printf("%d\n", x[2]);
L'esempio seguente utilizza la funzione scanf per leggere un valore dalla tastiera nell'ultimo elemento di un array con dieci elementi.
scanf("%d", &x[9]);
Inizializzazione degli elementi dell'array
Gli array possono essere inizializzati come qualsiasi altra variabile tramite assegnazione. Poiché una matrice contiene più di un valore, i singoli valori vengono inseriti tra parentesi graffe e separati da virgole. L'esempio seguente inizializza una matrice a dieci dimensioni con i primi dieci valori della tabellina a tre tempi.
int x[10] = {3, 6, 9, 12, 15, 18, 21, 24, 27, 30};
Questo salva l'assegnazione dei valori individualmente come nell'esempio seguente.
int x[10];
x[0] = 3;
x[1] = 6;
x[2] = 9;
x[3] = 12;
x[4] = 15;
x[5] = 18;
x[6] = 21;
x[7] = 24;
x[8] = 27;
x[9] = 30;
Ciclo attraverso un array
Poiché l'array è indicizzato in sequenza, possiamo utilizzare il ciclo for per visualizzare tutti i valori di un array. L'esempio seguente mostra tutti i valori di una matrice:
#include <stdio.h>
int main()
{
int x[10];
int counter;
/* Randomise the random number generator */
srand((unsigned)time(NULL));
/* Assign random values to the variable */
for (counter=0; counter<10; counter++)
x[counter] = rand();
/* Display the contents of the array */
for (counter=0; counter<10; counter++)
printf("element %d has the value %d\n", counter, x[counter]);
return 0;
}
sebbene l'output stamperà ogni volta valori diversi, il risultato verrà visualizzato in questo modo:
element 0 has the value 17132
element 1 has the value 24904
element 2 has the value 13466
element 3 has the value 3147
element 4 has the value 22006
element 5 has the value 10397
element 6 has the value 28114
element 7 has the value 19817
element 8 has the value 27430
element 9 has the value 22136
Matrici multidimensionali
Un array può avere più di una dimensione. Consentendo all'array di avere più di una dimensione, si ottiene una maggiore flessibilità. Ad esempio, i fogli di calcolo sono costruiti su una matrice bidimensionale; un array per le righe e un array per le colonne.
L'esempio seguente utilizza una matrice bidimensionale con due righe, ciascuna contenente cinque colonne:
#include <stdio.h>
int main()
{
/* Declare a 2 x 5 multidimensional array */
int x[2][5] = { {1, 2, 3, 4, 5},
{2, 4, 6, 8, 10} };
int row, column;
/* Display the rows */
for (row=0; row<2; row++)
{
/* Display the columns */
for (column=0; column<5; column++)
printf("%d\t", x[row][column]);
putchar('\n');
}
return 0;
}
L'output di questo programma verrà visualizzato come segue:
1 2 3 4 5
2 4 6 8 10
Stringhe
Una stringa è un gruppo di caratteri, solitamente lettere dell'alfabeto, per formattare il display di stampa in modo che appaia piacevole, abbia nomi e titoli significativi e sia esteticamente gradevole a te e alle persone che utilizzano il output del tuo programma.
In effetti, hai già utilizzato le stringhe negli esempi degli argomenti precedenti. Ma non è l'introduzione completa delle stringhe. Ci sono molti casi possibili nella programmazione, in cui l'uso di stringhe formattate aiuta il programmatore ad evitare le troppe complicazioni del programma e ovviamente troppi bug.
Una definizione completa di una stringa è una serie di dati di tipo carattere terminati da un carattere nullo ('\0').
Quando C utilizzerà una stringa di dati in qualche modo, per confrontarla con un'altra stringa, emetterla, copiarla in un'altra stringa o qualsiasi altra cosa, le funzioni sono impostate per fare ciò per cui sono chiamate fino a quando non viene rilevato un null.
Non esiste invece un tipo di dati di base per una stringa in C; le stringhe in C sono implementate come una matrice di caratteri. Ad esempio, per memorizzare un nome è possibile dichiarare un array di caratteri abbastanza grande da memorizzare il nome, quindi utilizzare le funzioni di libreria appropriate per manipolare il nome.
L'esempio seguente mostra la stringa sullo schermo, immessa dall'utente:
#include <stdio.h>
int main()
{
char name[80]; /* Create a character array
called name */
printf("Enter your name: ");
gets(name);
printf("The name you entered was %s\n", name);
return 0;
}
L'esecuzione del programma sarà:
Enter your name: Tarun Tyagi
The name you entered was Tarun Tyagi
Alcune funzioni comuni per le stringhe
La libreria standard string.h contiene molte utili funzioni per manipolare le stringhe. Alcune delle funzioni più utili sono state illustrate qui.
La funzione strlen
La funzione strlen viene utilizzata per determinare la lunghezza di una stringa. Impariamo l'uso di strlen con l'esempio:
#include <stdio.h>
#include <string.h>
int main()
{
char name[80];
int length;
printf("Enter your name: ");
gets(name);
length = strlen(name);
printf("Your name has %d characters\n", length);
return 0;
}
E l'esecuzione del programma sarà la seguente:
Enter your name: Tarun Subhash Tyagi
Your name has 19 characters
Enter your name: Preeti Tarun
Your name has 12 characters
La funzione strcpy
La funzione strcpy viene utilizzata per copiare una stringa in un'altra. Impariamo l'uso di questa funzione con un esempio:
#include <stdio.h>
#include <string.h>
int main()
{
char first[80];
char second[80];
printf("Enter first string: ");
gets(first);
printf("Enter second string: ");
gets(second);
printf("first: %s, and second: %s Before strcpy()\n "
, first, second);
strcpy(second, first);
printf("first: %s, and second: %s After strcpy()\n",
first, second);
return 0;
}
e l'output del programma sarà il seguente:
Enter first string: Tarun
Enter second string: Tyagi
first: Tarun, and second: Tyagi Before strcpy()
first: Tarun, and second: Tarun After strcpy()
La funzione strcmp
La funzione strcmp viene utilizzata per confrontare due stringhe insieme. Il nome della variabile di una matrice punta all'indirizzo di base di quella matrice. Pertanto, se proviamo a confrontare due stringhe utilizzando quanto segue, confronteremo due indirizzi, che ovviamente non sarebbero mai gli stessi in quanto non è possibile memorizzare due valori nella stessa posizione.
if (first == secondo) /* Non è mai possibile confrontare le stringhe */
L'esempio seguente utilizza la funzione strcmp per confrontare due stringhe:
#include <string.h>
int main()
{
char first[80], second[80];
int t;
for(t=1;t<=2;t++)
{
printf("\nEnter a string: ");
gets(first);
printf("Enter another string: ");
gets(second);
if (strcmp(first, second) == 0)
puts("The two strings are equal");
else
puts("The two strings are not equal");
}
return 0;
}
E l'esecuzione del programma sarà la seguente:
Enter a string: Tarun
Enter another string: tarun
The two strings are not equal
Enter a string: Tarun
Enter another string: Tarun
The two strings are equal
La funzione strcat
La funzione strcat viene utilizzata per unire una stringa all'altra. Vediamo come? Con l'aiuto dell'esempio:
#include <string.h>
int main()
{
char first[80], second[80];
printf("Enter a string: ");
gets(first);
printf("Enter another string: ");
gets(second);
strcat(first, second);
printf("The two strings joined together: %s\n",
first);
return 0;
}
E l'esecuzione del programma sarà la seguente:
Enter a string: Data
Enter another string: Recovery
The two strings joined together: DataRecovery
La funzione strtok
La funzione strtok viene utilizzata per trovare il token successivo in una stringa. Il token è specificato da un elenco di possibili delimitatori.
L'esempio seguente legge una riga di testo da un file e determina una parola utilizzando i delimitatori, lo spazio, la tabulazione e la nuova riga. Ogni parola viene quindi visualizzata su una riga separata:
#include <stdio.h>
#include <string.h>
int main()
{
FILE *in;
char line[80];
char *delimiters = " \t\n";
char *token;
if ((in = fopen("C:\\text.txt", "r")) == NULL)
{
puts("Unable to open the input file");
return 0;
}
/* Read each line one at a time */
while(!feof(in))
{
/* Get one line */
fgets(line, 80, in);
if (!feof(in))
{
/* Break the line up into words */
token = strtok(line, delimiters);
while (token != NULL)
{
puts(token);
/* Get the next word */
token = strtok(NULL, delimiters);
}
}
}
fclose(in);
return 0;
}
Esso sopra il programma, in = fopen("C:\\text.txt", "r"), apre e il file esistente C:\\text.txt. Se non esiste nel percorso specificato o per qualsiasi motivo, non è stato possibile aprire il file, sullo schermo viene visualizzato un messaggio di errore.
Considera il seguente esempio, che utilizza alcune di queste funzioni:
#include <stdio.h>
#include <string.h>
void main()
{
char line[100], *sub_text;
/* initialize string */
strcpy(line,"hello, I am a string;");
printf("Line: %s\n", line);
/* add to end of string */
strcat(line," what are you?");
printf("Line: %s\n", line);
/* find length of string */
/* strlen brings back */
/* length as type size_t */
printf("Length of line: %d\n", (int)strlen(line));
/* find occurence of substrings */
if ( (sub_text = strchr ( line, 'W' ) )!= NULL )
printf("String starting with \"W\" ->%s\n",
sub_text);
if ( ( sub_text = strchr ( line, 'w' ) )!= NULL )
printf("String starting with \"w\" ->%s\n",
sub_text);
if ( ( sub_text = strchr ( sub_text, 'u' ) )!= NULL )
printf("String starting with \"w\" ->%s\n",
sub_text);
}
L'output del programma verrà visualizzato come segue:
Line: hello, I am a string;
Line: hello, I am a string; what are you?
Length of line: 35
String starting with "w" ->what are you?
String starting with "w" ->u?
Funzioni
Il modo migliore per sviluppare e mantenere un programma di grandi dimensioni è costruirlo a partire da pezzi più piccoli, ciascuno dei quali è più facile da gestire (una tecnica a volte chiamata Divide et impera). Le funzioni consentono al programmatore di modularizzare il programma.
Le funzioni consentono di suddividere programmi complicati in piccoli blocchi, ognuno dei quali è più facile da scrivere, leggere e mantenere. Abbiamo già incontrato la funzione main e fatto uso di printf dalla libreria standard. Ovviamente possiamo creare le nostre funzioni e file di intestazione. Una funzione ha il seguente layout:
return-type function-name ( argument list if necessary )
{
local-declarations;
statements ;
return return-value;
}
Se il tipo di ritorno viene omesso, per impostazione predefinita C è int. Il valore restituito deve essere del tipo dichiarato. Tutte le variabili dichiarate all'interno delle funzioni sono dette variabili locali, in quanto sono note solo nella funzione a cui sono state definite.
Alcune funzioni hanno un elenco di parametri che fornisce un metodo di comunicazione tra la funzione e il modulo che ha chiamato la funzione. I parametri sono anche variabili locali, in quanto non sono disponibili al di fuori della funzione. I programmi finora trattati hanno tutti main, che è una funzione.
Una funzione può semplicemente eseguire un'attività senza restituire alcun valore, nel qual caso ha il seguente layout:
void function-name ( argument list if necessary )
{
local-declarations ;
statements;
}
Gli argomenti vengono sempre passati per valore nelle chiamate di funzione C. Ciò significa che le copie locali dei valori degli argomenti vengono passate alle routine. Qualsiasi modifica apportata agli argomenti all'interno della funzione viene apportata solo alle copie locali degli argomenti.
Per modificare o definire un argomento nell'elenco degli argomenti, questo argomento deve essere passato come indirizzo. Si utilizzano variabili regolari se la funzione non modifica i valori di tali argomenti. DEVI usare i puntatori se la funzione cambia i valori di quegli argomenti.
Impariamo con esempi:
#include <stdio.h>
void exchange ( int *a, int *b )
{
int temp;
temp = *a;
*a = *b;
*b = temp;
printf(" From function exchange: ");
printf("a = %d, b = %d\n", *a, *b);
}
void main()
{
int a, b;
a = 5;
b = 7;
printf("From main: a = %d, b = %d\n", a, b);
exchange(&a, &b);
printf("Back in main: ");
printf("a = %d, b = %d\n", a, b);
}
E l'output di questo programma verrà visualizzato come segue:
From main: a = 5, b = 7
From function exchange: a = 7, b = 5
Back in main: a = 7, b = 5
Vediamo un altro esempio. L'esempio seguente utilizza una funzione chiamata quadrato che scrive il quadrato dei numeri compresi tra 1 e 10.
#include <stdio.h>
int square(int x); /* Function prototype */
int main()
{
int counter;
for (counter=1; counter<=10; counter++)
printf("Square of %d is %d\n", counter, square(counter));
return 0;
}
/* Define the function 'square' */
int square(int x)
{
return x * x;
}
L'output di questo programma verrà visualizzato come segue:
Square of 1 is 1
Square of 2 is 4
Square of 3 is 9
Square of 4 is 16
Square of 5 is 25
Square of 6 is 36
Square of 7 is 49
Square of 8 is 64
Square of 9 is 81
Square of 10 is 100
La funzione prototipo quadrato dichiara una funzione che accetta un parametro intero e restituisce un intero. Quando il compilatore raggiunge la chiamata di funzione a square nel programma principale, è in grado di confrontare la chiamata di funzione con la definizione della funzione.
Quando il programma raggiunge la riga che chiama la funzione square, il programma salta alla funzione ed esegue quella funzione prima di riprendere il suo percorso attraverso il programma principale. I programmi che non hanno un tipo restituito devono essere dichiarati utilizzando void. Pertanto i parametri della funzione possono essere Passa per valore o Passa per riferimento.
Una funzione ricorsiva è una funzione che chiama se stessa. E questo processo è chiamato ricorsione.
Funzioni Pass By Value
I parametri della funzione quadrato nell'esempio precedente vengono passati per valore. Ciò significa che alla funzione è stata passata solo una copia della variabile. Eventuali modifiche al valore non verranno riflesse nella funzione chiamante.
L'esempio seguente utilizza pass-by-value e modifica il valore del parametro passato, che non ha alcun effetto sulla funzione chiamante. La funzione count_down è stata dichiarata nulla in quanto non esiste un tipo restituito.
#include <stdio.h>
void count_down(int x);
int main()
{
int counter;
for (counter=1; counter<=10; counter++)
count_down(counter);
return 0;
}
void count_down(int x)
{
int counter;
for (counter = x; counter > 0; counter--)
{
printf("%d ", x);
x--;
}
putchar('\n');
}
L'output del programma verrà visualizzato come segue:
1
2 1
3 2 1
4 3 2 1
5 4 3 2 1
6 5 4 3 2 1
7 6 5 4 3 2 1
8 7 6 5 4 3 2 1
9 8 7 6 5 4 3 2 1
10 9 8 7 6 5 4 3 2 1
Vediamo un altro esempio di valore C Pass By per capirlo meglio. L'esempio seguente converte in parole un numero compreso tra 1 e 30.000 digitato dall'utente.
#include <stdio.h>
void do_units(int num);
void do_tens(int num);
void do_teens(int num);
int main()
{
int num, residue;
do
{
printf("Enter a number between 1 and 30,000: ");
scanf("%d", &num);
} while (num < 1 || num > 30000);
residue = num;
printf("%d in words = ", num);
do_tens(residue/1000);
if (num >= 1000)
printf("thousand ");
residue %= 1000;
do_units(residue/100);
if (residue >= 100)
{
printf("hundred ");
}
if (num > 100 && num%100 > 0)
printf("and ");
residue %=100;
do_tens(residue);
putchar('\n');
return 0;
}
void do_units(int num)
{
switch(num)
{
case 1:
printf("one ");
break;
case 2:
printf("two ");
break;
case 3:
printf("three ");
break;
case 4:
printf("four ");
break;
case 5:
printf("five ");
break;
case 6:
printf("six ");
break;
case 7:
printf("seven ");
break;
case 8:
printf("eight ");
break;
case 9:
printf("nine ");
}
}
void do_tens(int num)
{
switch(num/10)
{
case 1:
do_teens(num);
break;
case 2:
printf("twenty ");
break;
case 3:
printf("thirty ");
break;
case 4:
printf("forty ");
break;
case 5:
printf("fifty ");
break;
case 6:
printf("sixty ");
break;
case 7:
printf("seventy ");
break;
case 8:
printf("eighty ");
break;
case 9:
printf("ninety ");
}
if (num/10 != 1)
do_units(num%10);
}
void do_teens(int num)
{
switch(num)
{
case 10:
printf("ten ");
break;
case 11:
printf("eleven ");
break;
case 12:
printf("twelve ");
break;
case 13:
printf("thirteen ");
break;
case 14:
printf("fourteen ");
break;
case 15:
printf("fifteen ");
break;
case 16:
printf("sixteen ");
break;
case 17:
printf("seventeen ");
break;
case 18:
printf("eighteen ");
break;
case 19:
printf("nineteen ");
}
}
e l'output del programma sarà il seguente:
Enter a number between 1 and 30,000: 12345
12345 in words = twelve thousand three hundred and forty five
Chiamata per riferimento
Per fare una funzione call-by-reference, invece di passare la variabile stessa, passa l'indirizzo della variabile. L'indirizzo della variabile può essere preso usando il & operatore. Quanto segue chiama una funzione di scambio che passa l'indirizzo delle variabili invece dei valori effettivi.
scambia(&x, &y);
Dereferenziazione
Il problema che abbiamo ora è che alla funzione swap è stato passato l'indirizzo anziché la variabile, quindi dobbiamo dereferenziare le variabili in modo da guardare i valori effettivi piuttosto che gli indirizzi delle variabili per scambiare loro.
Il dereferenziamento si ottiene in C utilizzando la notazione puntatore (*). In parole povere, questo significa mettere un * prima di ogni variabile prima di usarla in modo che si riferisca al valore della variabile piuttosto che al suo indirizzo. Il programma seguente illustra il passaggio per riferimento per scambiare due valori.
#include <stdio.h>
void swap(int *x, int *y);
int main()
{
int x=6, y=10;
printf("Before the function swap, x = %d and y =
%d\n\n", x, y);
swap(&x, &y);
printf("After the function swap, x = %d and y =
%d\n\n", x, y);
return 0;
}
void swap(int *x, int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
Vediamo l'output del programma:
Before the function swap, x = 6 and y = 10
After the function swap, x = 10 and y = 6
Le funzioni possono essere ricorsive che è una funzione può chiamare se stessa. Ogni chiamata a se stessa richiede che lo stato corrente della funzione venga inserito nello stack. È importante ricordare questo fatto poiché è facile creare un overflow dello stack, ovvero lo stack ha esaurito lo spazio per posizionare altri dati.
L'esempio seguente calcola il fattoriale di un numero utilizzando la ricorsione. Un fattoriale è un numero moltiplicato per ogni altro intero inferiore a se stesso, fino a 1. Ad esempio, il fattoriale del numero 6 è:
Fattoriale 6 = 6 * 5 * 4 * 3 * 2 * 1
Quindi il fattoriale di 6 è 720. Dall'esempio sopra si può vedere che fattoriale 6 = 6 * fattoriale 5. Allo stesso modo, fattoriale 5 = 5 * fattoriale 4, e così via.
Quella che segue è la regola generale per il calcolo dei numeri fattoriali.
fattoriale(n) = n * fattoriale(n-1)
La regola di cui sopra termina quando n = 1, poiché il fattoriale di 1 è 1. Cerchiamo di capirlo meglio con l'aiuto dell'esempio:
#include <stdio.h>
long int factorial(int num);
int main()
{
int num;
long int f;
printf("Enter a number: ");
scanf("%d", &num);
f = factorial(num);
printf("factorial of %d is %ld\n", num, f);
return 0;
}
long int factorial(int num)
{
if (num == 1)
return 1;
else
return num * factorial(num-1);
}
Vediamo l'output dell'esecuzione di questo programma:
Enter a number: 7
factorial of 7 is 5040
allocazione della memoria in C
Il compilatore C ha una libreria di allocazione della memoria, definita in malloc.h. La memoria viene riservata utilizzando la funzione malloc e restituisce un puntatore all'indirizzo. Richiede un parametro, la dimensione della memoria richiesta in byte.
L'esempio seguente alloca spazio per la stringa "hello world".
ptr = (char *)maloc(strlen("Hello world") + 1);
È necessario un byte in più per tenere conto del carattere di terminazione della stringa, '\0'. Il (char *) è chiamato cast e forza il tipo restituito a essere char *.
Poiché i tipi di dati hanno dimensioni diverse e malloc restituisce lo spazio in byte, è buona norma per motivi di portabilità utilizzare l'operatore sizeof quando si specifica una dimensione da allocare.
L'esempio seguente legge una stringa nel buffer dell'array di caratteri, quindi alloca la quantità esatta di memoria richiesta e la copia in una variabile denominata "ptr".
#include <string.h>
#include <malloc.h>
int main()
{
char *ptr, buffer[80];
printf("Enter a string: ");
gets(buffer);
ptr = (char *)malloc((strlen(buffer) + 1) *
sizeof(char));
strcpy(ptr, buffer);
printf("You entered: %s\n", ptr);
return 0;
}
L'output del programma sarà il seguente:
Enter a string: India is the best
You entered: India is the best
Riallocazione della memoria
È possibile molte volte durante la programmazione di voler riallocare la memoria. Questo viene fatto con la funzione di riallocazione. La funzione realloc prende due parametri, l'indirizzo di base della memoria che vuoi ridimensionare e la quantità di spazio che vuoi riservare e restituisce un puntatore all'indirizzo di base.
Supponiamo di avere spazio riservato per un puntatore chiamato msg e di voler riallocare lo spazio alla quantità di spazio che occupa già, più la lunghezza di un'altra stringa, quindi potremmo usare quanto segue.
msg = (carattere *)realloc(msg, (strlen(msg) + strlen(buffer) + 1)*sizeof(carattere));
Il seguente programma illustra l'uso di malloc, realloc e free. L'utente immette una serie di stringhe unite tra loro. Il programma interrompe la lettura delle stringhe quando viene immessa una stringa vuota.
#include <string.h>
#include <malloc.h>
int main()
{
char buffer[80], *msg;
int firstTime=0;
do
{
printf("\nEnter a sentence: ");
gets(buffer);
if (!firstTime)
{
msg = (char *)malloc((strlen(buffer) + 1) *
sizeof(char));
strcpy(msg, buffer);
firstTime = 1;
}
else
{
msg = (char *)realloc(msg, (strlen(msg) +
strlen(buffer) + 1) * sizeof(char));
strcat(msg, buffer);
}
puts(msg);
} while(strcmp(buffer, ""));
free(msg);
return 0;
}
L'output del programma sarà il seguente:
Enter a sentence: Once upon a time
Once upon a time
Enter a sentence: there was a king
Once upon a timethere was a king
Enter a sentence: the king was
Once upon a timethere was a kingthe king was
Enter a sentence:
Once upon a timethere was a kingthe king was
Rilascio della memoria
Quando hai finito con la memoria che è stata allocata, non dovresti mai dimenticare di liberare la memoria poiché libererà risorse e migliorerà la velocità. Per liberare la memoria allocata, usa la funzione libera.
gratuito(ptr);
Strutture
Oltre ai tipi di dati di base, C dispone di un meccanismo di struttura che consente di raggruppare elementi di dati correlati tra loro sotto un nome comune. Questo è comunemente indicato come un tipo definito dall'utente.
La parola chiave struct avvia la definizione della struttura e un tag assegna il nome univoco alla struttura. I tipi di dati ei nomi delle variabili aggiunti alla struttura sono membri della struttura. Il risultato è un modello di struttura che può essere utilizzato come identificatore di tipo. Quella che segue è una struttura con un tag di mese.
struct month
{
char name[10];
char abbrev[4];
int days;
};
Un tipo di struttura viene solitamente definito vicino all'inizio di un file utilizzando un'istruzione typedef. typedef definisce e nomina un nuovo tipo, consentendone l'uso in tutto il programma. typedef di solito si verifica subito dopo le istruzioni #define e #include in un file.
La parola chiave typedef può essere usata per definire una parola per fare riferimento alla struttura invece di specificare la parola chiave struct con il nome della struttura. È normale nominare il typedef in lettere maiuscole. Ecco gli esempi di definizione della struttura.
typedef struct {
char name[64];
char course[128];
int age;
int year;
} student;
Questo definisce un nuovo tipo di variabili studente di tipo studente può essere dichiarato come segue.
studente st_rec;
Nota quanto sia simile alla dichiarazione di un int o float. Il nome della variabile è st_rec, ha membri chiamati nome, corso, età e anno. Allo stesso modo,
typedef struct element
{
char data;
struct element *next;
} STACKELEMENT;
A variable of the user defined type struct element may now be declared as follows.
STACKELEMENT *stack;
Considera la seguente struttura:
struct student
{
char *name;
int grade;
};
Un puntatore a struct student può essere definito come segue.
struct student *hnc;
When accessing a pointer to a structure, the member pointer operator, -> is used instead of the dot operator. To add a grade to a structure,
s.grade = 50;
Puoi assegnare un voto alla struttura come segue.
s->voto = 50;
Come per i tipi di dati di base, se vuoi che le modifiche apportate in una funzione ai parametri passati siano persistenti, devi passare per riferimento (passare l'indirizzo). Il meccanismo è esattamente lo stesso dei tipi di dati di base. Passare l'indirizzo e fare riferimento alla variabile utilizzando la notazione del puntatore.
Dopo aver definito la struttura, puoi dichiararne un'istanza e assegnare valori ai membri utilizzando la notazione del punto. L'esempio seguente illustra l'uso della struttura del mese.
#include <stdio.h>
#include <string.h>
struct month
{
char name[10];
char abbreviation[4];
int days;
};
int main()
{
struct month m;
strcpy(m.name, "January");
strcpy(m.abbreviation, "Jan");
m.days = 31;
printf("%s is abbreviated as %s and has %d days\n", m.name, m.abbreviation, m.days);
return 0;
}
L'output del programma sarà il seguente:
January is abbreviated as Jan and has 31 days
Tutti i compilatori ANSI C consentono di assegnare una struttura a un'altra, eseguendo una copia a livello di membro. Se avessimo strutture mensili chiamate m1 e m2, potremmo assegnare i valori da m1 a m2 con quanto segue:
- Struttura con membri puntatore.
- Inizializzazione della struttura.
- Passaggio di una struttura a una funzione.
- Puntatori e strutture.
Strutture con membri puntatore in C
Tenere le stringhe in un array di dimensioni fisse è un uso inefficiente della memoria. Un approccio più efficiente sarebbe quello di utilizzare i puntatori. I puntatori vengono utilizzati nelle strutture esattamente nello stesso modo in cui vengono utilizzati nelle normali definizioni dei puntatori. Vediamo un esempio:
#include <string.h>
#include <malloc.h>
struct month
{
char *name;
char *abbreviation;
int days;
};
int main()
{
struct month m;
m.name = (char *)malloc((strlen("January")+1) *
sizeof(char));
strcpy(m.name, "January");
m.abbreviation = (char *)malloc((strlen("Jan")+1) *
sizeof(char));
strcpy(m.abbreviation, "Jan");
m.days = 31;
printf("%s is abbreviated as %s and has %d days\n",
m.name, m.abbreviation, m.days);
return 0;
}
L'output del programma sarà il seguente:
Gennaio è abbreviato in gennaio e ha 31 giorni
Inizializzatori di struttura in C
Per fornire una serie di valori iniziali per la struttura, è possibile aggiungere inizializzatori all'istruzione di dichiarazione. Poiché i mesi iniziano da 1, ma gli array iniziano da zero in C, nell'esempio seguente è stato utilizzato un elemento aggiuntivo in posizione zero chiamato spazzatura.
#include <stdio.h>
#include <string.h>
struct month
{
char *name;
char *abbreviation;
int days;
} month_details[] =
{
"Junk", "Junk", 0,
"January", "Jan", 31,
"February", "Feb", 28,
"March", "Mar", 31,
"April", "Apr", 30,
"May", "May", 31,
"June", "Jun", 30,
"July", "Jul", 31,
"August", "Aug", 31,
"September", "Sep", 30,
"October", "Oct", 31,
"November", "Nov", 30,
"December", "Dec", 31
};
int main()
{
int counter;
for (counter=1; counter<=12; counter++)
printf("%s is abbreviated as %s and has %d days\n",
month_details[counter].name,
month_details[counter].abbreviation,
month_details[counter].days);
return 0;
}
E l'output verrà visualizzato come segue:
January is abbreviated as Jan and has 31 days
February is abbreviated as Feb and has 28 days
March is abbreviated as Mar and has 31 days
April is abbreviated as Apr and has 30 days
May is abbreviated as May and has 31 days
June is abbreviated as Jun and has 30 days
July is abbreviated as Jul and has 31 days
August is abbreviated as Aug and has 31 days
September is abbreviated as Sep and has 30 days
October is abbreviated as Oct and has 31 days
November is abbreviated as Nov and has 30 days
December is abbreviated as Dec and has 31 days
Passaggio di strutture a funzioni in C
Le strutture possono essere passate come parametro a una funzione, proprio come qualsiasi tipo di dati di base. L'esempio seguente utilizza una struttura denominata date che è stata passata a una funzione isLeapYear per determinare se l'anno è bisestile.
Normalmente passeresti solo il valore del giorno, ma l'intera struttura viene passata per illustrare il passaggio delle strutture alle funzioni.
#include <stdio.h>
#include <string.h>
struct month
{
char *name;
char *abbreviation;
int days;
} month_details[] =
{
"Junk", "Junk", 0,
"January", "Jan", 31,
"February", "Feb", 28,
"March", "Mar", 31,
"April", "Apr", 30,
"May", "May", 31,
"June", "Jun", 30,
"July", "Jul", 31,
"August", "Aug", 31,
"September", "Sep", 30,
"October", "Oct", 31,
"November", "Nov", 30,
"December", "Dec", 31
};
struct date
{
int day;
int month;
int year;
};
int isLeapYear(struct date d);
int main()
{
struct date d;
printf("Enter the date (eg: 11/11/1980): ");
scanf("%d/%d/%d", &d.day, &d.month, &d.year);
printf("The date %d %s %d is ", d.day,
month_details[d.month].name, d.year);
if (isLeapYear(d) == 0)
printf("not ");
puts("a leap year");
return 0;
}
int isLeapYear(struct date d)
{
if ((d.year % 4 == 0 && d.year % 100 != 0) ||
d.year % 400 == 0)
return 1;
return 0;
}
E l'esecuzione del programma sarà la seguente:
Enter the date (eg: 11/11/1980): 9/12/1980
The date 9 December 1980 is a leap year
L'esempio seguente assegna dinamicamente una matrice di strutture per memorizzare i nomi e il voto degli studenti. I voti vengono quindi mostrati all'utente in ordine crescente.
#include <string.h>
#include <malloc.h>
struct student
{
char *name;
int grade;
};
void swap(struct student *x, struct student *y);
int main()
{
struct student *group;
char buffer[80];
int spurious;
int inner, outer;
int counter, numStudents;
printf("How many students are there in the group: ");
scanf("%d", &numStudents);
group = (struct student *)malloc(numStudents *
sizeof(struct student));
for (counter=0; counter<numStudents; counter++)
{
spurious = getchar();
printf("Enter the name of the student: ");
gets(buffer);
group[counter].name = (char *)malloc((strlen(buffer)+1) * sizeof(char));
strcpy(group[counter].name, buffer);
printf("Enter grade: ");
scanf("%d", &group[counter].grade);
}
for (outer=0; outer<numStudents; outer++)
for (inner=0; inner<outer; inner++)
if (group[outer].grade <
group[inner].grade)
swap(&group[outer], &group[inner]);
puts("The group in ascending order of grades ...");
for (counter=0; counter<numStudents; counter++)
printf("%s achieved Grade %d \n”,
group[counter].name,
group[counter].grade);
return 0;
}
void swap(struct student *x, struct student *y)
{
struct student temp;
temp.name = (char *)malloc((strlen(x->name)+1) *
sizeof(char));
strcpy(temp.name, x->name);
temp.grade = x->grade;
x->grade = y->grade;
x->name = (char *)malloc((strlen(y->name)+1) *
sizeof(char));
strcpy(x->name, y->name);
y->grade = temp.grade;
y->name = (char *)malloc((strlen(temp.name)+1) *
sizeof(char));
strcpy(y->name, temp.name);
}
L'esecuzione dell'output sarà la seguente:
How many students are there in the group: 4
Enter the name of the student: Anuraaj
Enter grade: 7
Enter the name of the student: Honey
Enter grade: 2
Enter the name of the student: Meetushi
Enter grade: 1
Enter the name of the student: Deepti
Enter grade: 4
The group in ascending order of grades ...
Meetushi achieved Grade 1
Honey achieved Grade 2
Deepti achieved Grade 4
Anuraaj achieved Grade 7
Unione
Un'unione ti consente di esaminare gli stessi dati con tipi diversi o di utilizzare gli stessi dati con nomi diversi. I sindacati sono simili alle strutture. Un'unione viene dichiarata e utilizzata allo stesso modo di una struttura.
Un'unione differisce da una struttura in quanto solo uno dei suoi membri può essere utilizzato alla volta. Il motivo è semplice. Tutti i membri di un sindacato occupano la stessa area di memoria. Sono posti uno sopra l'altro.
Le unioni sono definite e dichiarate allo stesso modo delle strutture. L'unica differenza nelle dichiarazioni è che viene utilizzata la parola chiave union invece di struct. Per definire una semplice unione di una variabile char e una variabile intera, dovresti scrivere quanto segue:
union shared {
char c;
int i;
};
Questa unione, condivisa, può essere utilizzata per creare istanze di un'unione che può contenere un valore di carattere c o un valore intero i. Questa è una condizione OR. A differenza di una struttura che conterrebbe entrambi i valori, l'unione può contenere un solo valore alla volta.
Un'unione può essere inizializzata sulla sua dichiarazione. Perché è possibile utilizzare un solo membro alla volta e solo uno può essere inizializzato. Per evitare confusione, è possibile inizializzare solo il primo membro dell'unione. Il codice seguente mostra un'istanza dell'unione condivisa dichiarata e inizializzata:
unione condivisa generic_variable = {`@'};
Si noti che l'unione generic_variable è stata inizializzata proprio come sarebbe stato inizializzato il primo membro di una struttura.
I singoli membri dell'unione possono essere utilizzati nello stesso modo in cui i membri della struttura possono essere utilizzati utilizzando l'operatore membro (.). Tuttavia, c'è una differenza importante nell'accesso ai membri del sindacato.
È necessario accedere a un solo membro del sindacato alla volta. Poiché un sindacato conserva i propri membri uno sopra l'altro, è importante accedere a un solo membro alla volta.
La parola chiave dell'unione
union tag {
union_member(s);
/* additional statements may go here */
}instance;
La parola chiave union viene utilizzata per dichiarare le unioni. Un'unione è una raccolta di una o più variabili (union_members) che sono state raggruppate sotto un unico nome. Inoltre, ciascuno di questi membri del sindacato occupa la stessa area di memoria.
La parola chiave union identifica l'inizio di una definizione di unione. È seguito da un tag che è il nome dato al sindacato. Dopo il tag ci sono i membri dell'unione racchiusi tra parentesi graffe.
Si può anche definire un'istanza, la dichiarazione vera e propria di un'unione. Se definisci la struttura senza l'istanza, è solo un modello che può essere utilizzato successivamente in un programma per dichiarare le strutture. Quello che segue è il formato di un modello:
union tag {
union_member(s);
/* additional statements may go here */
};
Per utilizzare il modello, dovresti utilizzare il seguente formato:
istanza tag union;
Per utilizzare questo formato, devi aver precedentemente dichiarato un'unione con il tag specificato.
/* Declare a union template called tag */
union tag {
int num;
char alps;
}
/* Use the union template */
union tag mixed_variable;
/* Declare a union and instance together */
union generic_type_tag {
char c;
int i;
float f;
double d;
} generic;
/* Initialize a union. */
union date_tag {
char full_date[9];
struct part_date_tag {
char month[2];
char break_value1;
char day[2];
char break_value2;
char year[2];
} part_date;
}date = {"09/12/80"};
Comprendiamolo meglio con l'aiuto di esempi:
#include <stdio.h>
int main()
{
union
{
int value; /* This is the first part of the union */
struct
{
char first; /* These two values are the second part of it */
char second;
} half;
} number;
long index;
for (index = 12 ; index < 300000L ; index += 35231L)
{
number.value = index;
printf("%8x %6x %6x\n", number.value,
number.half.first,
number.half.second);
}
return 0;
}
E l'output del programma verrà visualizzato come segue:
c c 0
89ab ffab ff89
134a 4a 13
9ce9 ffe9 ff9c
2688 ff88 26
b027 27 ffb0
39c6 ffc6 39
c365 65 ffc3
4d04 4 4d
Uso pratico di un'unione nel recupero dei dati
Ora vediamo un uso pratico di union è la programmazione del recupero dati. Facciamo un piccolo esempio. Il seguente programma è il piccolo modello di programma di scansione di settori danneggiati per un'unità floppy disk (a: ) tuttavia non è il modello completo di software di scansione di settori danneggiati.
Esaminiamo il programma:
#include<dos.h>
#include<conio.h>
int main()
{
int rp, head, track, sector, status;
char *buf;
union REGS in, out;
struct SREGS s;
clrscr();
/* Reset the disk system to initialize to disk */
printf("\n Resetting the disk system....");
for(rp=0;rp<=2;rp++)
{
in.h.ah = 0;
in.h.dl = 0x00;
int86(0x13,&in,&out);
}
printf("\n\n\n Now Testing the Disk for Bad Sectors....");
/* scan for bad sectors */
for(track=0;track<=79;track++)
{
for(head=0;head<=1;head++)
{
for(sector=1;sector<=18;sector++)
{
in.h.ah = 0x04;
in.h.al = 1;
in.h.dl = 0x00;
in.h.ch = track;
in.h.dh = head;
in.h.cl = sector;
in.x.bx = FP_OFF(buf);
s.es = FP_SEG(buf);
int86x(0x13,&in,&out,&s);
if(out.x.cflag)
{
status=out.h.ah;
printf("\n track:%d Head:%d Sector:%d Status ==0x%X",track,head,sector,status);
}
}
}
}
printf("\n\n\nDone");
return 0;
}
Ora vediamo come apparirà il suo output se ci sono settori danneggiati nel floppy disk:
Ripristino del sistema del disco....
Ora testando il disco per i settori danneggiati....
track:0 Head:0 Sector:4 Status ==0xA
track:0 Head:0 Sector:5 Status ==0xA
track:1 Head:0 Sector:4 Status ==0xA
track:1 Head:0 Sector:5 Status ==0xA
track:1 Head:0 Sector:6 Status ==0xA
track:1 Head:0 Sector:7 Status ==0xA
track:1 Head:0 Sector:8 Status ==0xA
track:1 Head:0 Sector:11 Status ==0xA
track:1 Head:0 Sector:12 Status ==0xA
track:1 Head:0 Sector:13 Status ==0xA
track:1 Head:0 Sector:14 Status ==0xA
track:1 Head:0 Sector:15 Status ==0xA
track:1 Head:0 Sector:16 Status ==0xA
track:1 Head:0 Sector:17 Status ==0xA
track:1 Head:0 Sector:18 Status ==0xA
track:1 Head:1 Sector:5 Status ==0xA
track:1 Head:1 Sector:6 Status ==0xA
track:1 Head:1 Sector:7 Status ==0xA
track:1 Head:1 Sector:8 Status ==0xA
track:1 Head:1 Sector:9 Status ==0xA
track:1 Head:1 Sector:10 Status ==0xA
track:1 Head:1 Sector:11 Status ==0xA
track:1 Head:1 Sector:12 Status ==0xA
track:1 Head:1 Sector:13 Status ==0xA
track:1 Head:1 Sector:14 Status ==0xA
track:1 Head:1 Sector:15 Status ==0xA
track:1 Head:1 Sector:16 Status ==0xA
track:1 Head:1 Sector:17 Status ==0xA
track:1 Head:1 Sector:18 Status ==0xA
track:2 Head:0 Sector:4 Status ==0xA
track:2 Head:0 Sector:5 Status ==0xA
track:14 Head:0 Sector:6 Status ==0xA
Done
Potrebbe essere un po' difficile capire le funzioni e gli interrupt utilizzati in questo programma per verificare la presenza di settori danneggiati nel disco e ripristinare il sistema del disco ecc. ma non devi preoccuparti, impareremo tutte queste cose nel BIOS e interrompere le sezioni di programmazione più avanti nei prossimi capitoli.
Gestione dei file in C
L'accesso ai file in C si ottiene associando un flusso a un file. C comunica con i file utilizzando un nuovo tipo di dati chiamato puntatore a file. Questo tipo è definito all'interno di stdio.h e scritto come FILE *. Un puntatore a file chiamato output_file viene dichiarato in un'istruzione come
FILE *file_output;
Le modalità file della funzione fopen
Il tuo programma deve aprire un file prima di potervi accedere. Questo viene fatto usando la funzione fopen, che restituisce il puntatore al file richiesto. Se il file non può essere aperto per qualsiasi motivo, verrà restituito il valore NULL. Di solito utilizzerai fopen come segue
if ((output_file = fopen("output_file", "w")) == NULL)
fprintf(stderr, "Cannot open %s\n",
"output_file");
fopen accetta due argomenti, entrambi sono stringhe, il primo è il nome del file da aprire, il secondo è un carattere di accesso, che di solito è uno tra r, a o w ecc. I file possono essere aperti in un numero di modalità, come mostrato nella tabella seguente.
File Modes |
r |
Open a text file for reading. |
w |
Create a text file for writing. If the file exists, it is overwritten. |
a |
Open a text file in append mode. Text is added to the end of the file. |
rb |
Open a binary file for reading. |
wb |
Create a binary file for writing. If the file exists, it is overwritten. |
ab |
Open a binary file in append mode. Data is added to the end of the file. |
r+ |
Open a text file for reading and writing. |
w+ |
Create a text file for reading and writing. If the file exists, it is overwritten. |
a+ |
Open a text file for reading and writing at the end. |
r+b or rb+ |
Open binary file for reading and writing. |
w+b or wb+ |
Create a binary file for reading and writing. If the file exists, it is overwritten. |
a+b or ab+ |
Open a text file for reading and writing at the end. |
Le modalità di aggiornamento vengono utilizzate con le funzioni fseek, fsetpos e rewind. La funzione fopen restituisce un puntatore a un file o NULL se si verifica un errore.
L'esempio seguente apre un file, tarun.txt in modalità di sola lettura. È buona pratica di programmazione verificare l'esistenza del file.
if ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Unable to open the file");
return 0;
}
File di chiusura
I file vengono chiusi utilizzando la funzione fclose. La sintassi è la seguente:
fclose(in);
Lettura di file
La funzione feof viene utilizzata per verificare la fine del file. Le funzioni fgetc, fscanf e fgets vengono utilizzate per leggere i dati dal file.
L'esempio seguente elenca il contenuto di un file sullo schermo, utilizzando fgetc per leggere il file un carattere alla volta.
#include <stdio.h>
int main()
{
FILE *in;
int key;
if ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Unable to open the file");
return 0;
}
while (!feof(in))
{
key = fgetc(in);
/* The last character read is the end of file marker so don't print it */
if (!feof(in))
putchar(key);
}
fclose(in);
return 0;
}
La funzione fscanf può essere utilizzata per leggere diversi tipi di dati dal file come nell'esempio seguente, a condizione che i dati nel file siano nel formato della stringa di formato utilizzata con fscanf.
fscanf(in, "%d/%d/%d", &giorno, &mese, &anno);
La funzione fgets viene utilizzata per leggere un numero di caratteri da un file. stdin è il flusso di file di input standard e la funzione fgets può essere utilizzata per controllare l'input.
Scrittura su file
I dati possono essere scritti nel file usando fputc e fprintf. L'esempio seguente usa le funzioni fgetc e fputc per creare una copia di un file di testo.
#include <stdio.h>
int main()
{
FILE *in, *out;
int key;
if ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Unable to open the file");
return 0;
}
out = fopen("copy.txt", "w");
while (!feof(in))
{
key = fgetc(in);
if (!feof(in))
fputc(key, out);
}
fclose(in);
fclose(out);
return 0;
}
La funzione fprintf può essere utilizzata per scrivere dati formattati su un file.
fprintf(out, "Date: %02d/%02d/%02d\n",
day, month, year);
Argomenti della riga di comando con C
La definizione ANSI C per dichiarare la funzione main() è:
int main() o int main(int argc, char **argv)
La seconda versione consente di passare argomenti dalla riga di comando. Il parametro argc è un contatore di argomenti e contiene il numero di parametri passati dalla riga di comando. Il parametro argv è l'argomento vettore che è un array di puntatori a stringhe che rappresentano i parametri effettivamente passati.
L'esempio seguente consente di passare un numero qualsiasi di argomenti dalla riga di comando e di stamparli. argv[0] è il programma effettivo. Il programma deve essere eseguito da un prompt dei comandi.
#include <stdio.h>
int main(int argc, char **argv)
{
int counter;
puts("The arguments to the program are:");
for (counter=0; counter<argc; counter++)
puts(argv[counter]);
return 0;
}
If the program name was count.c, it could be called as follows from the command line.
count 3
or
count 7
or
count 192 etc.
L'esempio successivo utilizza le routine di gestione dei file per copiare un file di testo in un nuovo file. Ad esempio, l'argomento della riga di comando potrebbe essere chiamato come:
txtcpy uno.txt due.txt
#include <stdio.h>
int main(int argc, char **argv)
{
FILE *in, *out;
int key;
if (argc < 3)
{
puts("Usage: txtcpy source destination\n");
puts("The source must be an existing file");
puts("If the destination file exists, it will be
overwritten");
return 0;
}
if ((in = fopen(argv[1], "r")) == NULL)
{
puts("Unable to open the file to be copied");
return 0;
}
if ((out = fopen(argv[2], "w")) == NULL)
{
puts("Unable to open the output file");
return 0;
}
while (!feof(in))
{
key = fgetc(in);
if (!feof(in))
fputc(key, out);
}
fclose(in);
fclose(out);
return 0;
}
manipolatori bit a bit
A livello hardware, i dati sono rappresentati come numeri binari. La rappresentazione binaria del numero 59 è 111011. Il bit 0 è il bit meno significativo e in questo caso il bit 5 è il bit più significativo.
Ogni bit impostato viene calcolato come 2 rispetto alla potenza del bit impostato. Gli operatori bit per bit consentono di manipolare variabili intere a livello di bit. Quanto segue mostra la rappresentazione binaria del numero 59.
binary representation of the number 59 |
bit 5 4 3 2 1 0 |
2 power n 32 16 8 4 2 1 |
set 1 1 1 0 1 1 |
Con tre bit è possibile rappresentare i numeri da 0 a 7. La tabella seguente mostra i numeri da 0 a 7 nella loro forma binaria.
Binary Digits |
000 |
0 |
001 |
1 |
010 |
2 |
011 |
3 |
100 |
4 |
101 |
5 |
110 |
6 |
111 |
7 |
La tabella seguente elenca gli operatori bit per bit che possono essere utilizzati per manipolare i numeri binari.
Binary Digits |
& |
Bitwise AND |
| |
Bitwise OR |
^ |
Bitwise Exclusive OR |
~ |
Bitwise Complement |
<< |
Bitwise Shift Left |
>> |
Bitwise Shift Right |
E bit per bit
L'AND bit per bit è True solo se sono impostati entrambi i bit. L'esempio seguente mostra il risultato di un AND bit per bit sui numeri 23 e 12.
10111 (23)
01100 (12) AND
____________________
00100 (result = 4) |
Puoi usare un valore di maschera per verificare se alcuni bit sono stati impostati. Se volessimo verificare se i bit 1 e 3 sono stati impostati, potremmo mascherare il numero con 10 (il valore se i bit 1 e 3) e testare il risultato rispetto alla maschera.
#include <stdio.h>
int main()
{
int num, mask = 10;
printf("Enter a number: ");
scanf("%d", &num);
if ((num & mask) == mask)
puts("Bits 1 and 3 are set");
else
puts("Bits 1 and 3 are not set");
return 0;
}
OR bit a bit
L'OR bit per bit è vero se sono impostati entrambi i bit. Di seguito viene mostrato il risultato di un OR bit per bit sui numeri 23 e 12.
10111 (23)
01100 (12) OR
______________________
11111 (result = 31) |
Puoi usare una maschera per assicurarti che uno o più bit siano stati impostati. L'esempio seguente assicura che il bit 2 sia impostato.
#include <stdio.h>
int main()
{
int num, mask = 4;
printf("Enter a number: ");
scanf("%d", &num);
num |= mask;
printf("After ensuring bit 2 is set: %d\n", num);
return 0;
}
Esclusivo bit a bit O
L'OR esclusivo bit per bit è True se sono impostati entrambi i bit, ma non entrambi. Quanto segue mostra il risultato di un OR esclusivo bit per bit sui numeri 23 e 12.
10111 (23)
01100 (12) Exclusive OR (XOR)
_____________________________
11011 (result = 27) |
L'Exclusive OR ha alcune proprietà interessanti. Se esclusivi o un numero da solo, si imposta su zero poiché gli zeri rimarranno zero e gli uno non possono essere impostati entrambi, quindi sono impostati su zero.
Di conseguenza, se esclusivi OR un numero con un altro numero, quindi Exclusive OR il risultato di nuovo con l'altro numero, il risultato è il numero originale. Puoi provare con i numeri usati nell'esempio sopra.
23 XOR 12 = 27
27 XOR 12 = 23
27 XOR 23 = 12
Questa funzione può essere utilizzata per la crittografia. Il seguente programma utilizza una chiave di crittografia di 23 per illustrare la proprietà su un numero inserito dall'utente.
#include <stdio.h>
int main()
{
int num, key = 23;
printf("Enter a number: ");
scanf("%d", &num);
num ^= key;
printf("Exclusive OR with %d gives %d\n", key, num);
num ^= key;
printf("Exclusive OR with %d gives %d\n", key, num);
return 0;
}
Complimenti a bit
Il complimento bit a bit è un operatore di complimento che attiva o disattiva il bit. Se è 1 verrà impostato a 0, se è 0 verrà impostato a 1.
#include <stdio.h>
int main()
{
int num = 0xFFFF;
printf("The compliment of %X is %X\n", num, ~num);
return 0;
}
Sposta bit per bit a sinistra
L'operatore Bitwise Shift Left sposta il numero a sinistra. I bit più significativi vengono persi man mano che il numero si sposta a sinistra e i bit meno significativi lasciati liberi sono zero. Quanto segue mostra la rappresentazione binaria di 43.
0101011 (decimale 43)
Spostando i bit a sinistra, perdiamo il bit più significativo (in questo caso, uno zero) e il numero viene riempito con uno zero al bit meno significativo. Quello che segue è il numero risultante.
1010110 (decimale 86)
Sposta bit a destra a destra
L'operatore Bitwise Shift Right sposta il numero a destra. Zero viene introdotto nei bit più significativi lasciati liberi e i bit meno significativi lasciati liberi vengono persi. Quanto segue mostra la rappresentazione binaria del numero 43.
0101011 (decimale 43)
Spostando i bit a destra, perdiamo il bit meno significativo (in questo caso, uno) e il numero viene riempito con uno zero nel bit più significativo. Quello che segue è il numero risultante.
0010101 (decimale 21)
Il programma seguente utilizza lo spostamento a destra bit per bit e AND bit per bit per visualizzare un numero come numero binario a 16 bit. Il numero viene spostato a destra in successione da 16 a zero e bit a bit AND con 1 per vedere se il bit è impostato. Un metodo alternativo sarebbe quello di utilizzare maschere successive con l'operatore Bitwise OR.
#include <stdio.h>
int main()
{
int counter, num;
printf("Enter a number: ");
scanf("%d", &num);
printf("%d is binary: ", num);
for (counter=15; counter>=0; counter--)
printf("%d", (num >> counter) & 1);
putchar('\n');
return 0;
}
Funzioni per conversioni binarie e decimali
Le due funzioni fornite di seguito sono per la conversione da Binario a Decimale e da Decimale a Binario. La funzione data accanto per convertire un numero decimale nel numero binario corrispondente supporta un numero binario fino a 32 – Bit. Puoi utilizzare questo o il programma fornito in precedenza per la conversione secondo le tue esigenze.
Funzione per la conversione da decimale a binario:
void Decimal_to_Binary(void)
{
int input =0;
int i;
int count = 0;
int binary [32]; /* 32 Bit, MAXIMUM 32 elements */
printf ("Enter Decimal number to convert into
Binary :");
scanf ("%d", &input);
do
{
i = input%2; /* MOD 2 to get 1 or a 0*/
binary[count] = i; /* Load Elements into the Binary Array */
input = input/2; /* Divide input by 2 to decrement via binary */
count++; /* Count how many elements are needed*/
}while (input > 0);
/* Reverse and output binary digits */
printf ("Binary representation is: ");
do
{
printf ("%d", binary[count - 1]);
count--;
} while (count > 0);
printf ("\n");
}
Funzione per la conversione da binario a decimale:
La seguente funzione consente di convertire qualsiasi numero binario nel numero decimale corrispondente:
void Binary_to_Decimal(void)
{
char binaryhold[512];
char *binary;
int i=0;
int dec = 0;
int z;
printf ("Please enter the Binary Digits.\n");
printf ("Binary digits are either 0 or 1 Only ");
printf ("Binary Entry : ");
binary = gets(binaryhold);
i=strlen(binary);
for (z=0; z<i; ++z)
{
dec=dec*2+(binary[z]=='1'? 1:0); /* if Binary[z] is
equal to 1,
then 1 else 0 */
}
printf ("\n");
printf ("Decimal value of %s is %d",
binary, dec);
printf ("\n");
}
Debug e test
Errori di sintassi
La sintassi si riferisce alla grammatica, alla struttura e all'ordine degli elementi in un'istruzione. Si verifica un errore di sintassi quando infrangiamo le regole, ad esempio dimenticando di terminare un'istruzione con un punto e virgola. Quando compili il programma, il compilatore produrrà un elenco di eventuali errori di sintassi che potrebbe incontrare.
Un buon compilatore visualizzerà l'elenco con una descrizione dell'errore e potrebbe fornire una possibile soluzione. La correzione degli errori può comportare la visualizzazione di ulteriori errori durante la ricompilazione. Il motivo è che gli errori precedenti hanno modificato la struttura del programma, il che significa che ulteriori errori sono stati eliminati durante la compilazione originale.
Allo stesso modo, un singolo errore può causare diversi errori. Prova a mettere un punto e virgola alla fine della funzione principale di un programma che viene compilato ed eseguito correttamente. Quando lo ricompili, otterrai un enorme elenco di errori, eppure è solo un punto e virgola fuori posto.
Oltre agli errori di sintassi, i compilatori possono anche emettere avvisi. Un avviso non è un errore, ma può causare problemi durante l'esecuzione del programma. Ad esempio, l'assegnazione di un numero a virgola mobile a precisione doppia a un numero a virgola mobile a precisione singola può comportare una perdita di precisione. Non è un errore di sintassi, ma potrebbe causare problemi. In questo esempio particolare, potresti mostrare l'intento eseguendo il cast della variabile nel tipo di dati appropriato.
Considera l'esempio seguente in cui x è un numero a virgola mobile a precisione singola e y è un numero a virgola mobile a precisione doppia. y viene convertito in modo esplicito su un float durante l'assegnazione, il che eliminerebbe qualsiasi avviso del compilatore.
x = (mobile)y;
Errori logici
Gli errori logici si verificano quando c'è un errore nella logica. Ad esempio, potresti verificare che un numero sia minore di 4 e maggiore di 8. Ciò non potrebbe mai essere vero, ma se è sintatticamente corretto il programma verrà compilato correttamente. Considera il seguente esempio:
if (x < 4 && x > 8)
puts("Will never happen!");
La sintassi è corretta, quindi il programma verrà compilato, ma l'istruzione puts non verrà mai stampata poiché il valore di x non potrebbe essere inferiore a quattro e maggiore di otto contemporaneamente.
La maggior parte degli errori logici viene rilevata durante il test iniziale del programma. Quando non si comporta come ti aspettavi, ispezioni più da vicino le affermazioni logiche e le correggi. Questo è vero solo per evidenti errori logici. Più grande è il programma, più percorsi ci saranno, più difficile diventa verificare che il programma si comporti come previsto.
Test
Nel processo di sviluppo del software, gli errori possono essere inseriti in qualsiasi fase durante lo sviluppo. Ciò è dovuto al fatto che i metodi di verifica delle prime fasi di sviluppo del software sono manuali. Quindi è probabile che il codice sviluppato durante l'attività di codifica contenga alcuni errori di requisito ed errori di progettazione, oltre agli errori introdotti durante l'attività di codifica. Durante il test, il programma da testare viene eseguito con una serie di test case e l'output del programma per i test case viene valutato per determinare se la programmazione sta funzionando.
Quindi, il test è il processo di analisi di un elemento software per rilevare la differenza tra le condizioni esistenti e richieste (ad esempio, bug) e per valutare le caratteristiche degli elementi software. Quindi, il test è il processo di analisi di un programma con l'intento di trovare errori.
Alcuni principi di verifica
- I test non possono mostrare l'assenza di difetti, solo la loro presenza.
- Quanto prima viene commesso un errore, tanto più costoso è.
- Più tardi viene rilevato un errore, più è costoso.
Ora discutiamo alcune tecniche di test:
Test della scatola bianca
Il white box testing è una tecnica in base alla quale tutti i percorsi del programma vengono testati con ogni valore possibile. Questo approccio richiede una certa conoscenza di come dovrebbe comportarsi il programma. Ad esempio, se il tuo programma accetta un valore intero compreso tra 1 e 50, un test della casella bianca verificherà il programma con tutti i 50 valori per assicurarsi che sia corretto per ciascuno, quindi verificherà ogni altro valore possibile che un numero intero può assumere e verifica quello si è comportato come previsto. Considerando il numero di elementi di dati che può avere un programma tipico, le possibili permutazioni rendono estremamente difficile il test della white box per i programmi di grandi dimensioni.
Il test della scatola bianca può essere applicato alle funzioni critiche per la sicurezza di un programma di grandi dimensioni e gran parte del resto è stato testato utilizzando il test della scatola nera, discusso di seguito. A causa del numero di permutazioni, il test della scatola bianca viene solitamente eseguito utilizzando un cablaggio di test, in cui gli intervalli di valori vengono inviati rapidamente al programma tramite un programma speciale, registrando le eccezioni al comportamento previsto. Il test della scatola bianca viene talvolta definito test strutturale, trasparente o aperto.
Test della scatola nera
Il test della scatola nera è simile al test della scatola bianca, tranne per il fatto che, anziché testare ogni possibile valore, vengono testati i valori selezionati. In questo tipo di test, il tester conosce gli input e quali dovrebbero essere i risultati attesi, ma non necessariamente come il programma li ha raggiunti. Il test della scatola nera viene talvolta definito test funzionale.
I test case per il test della scatola nera vengono normalmente elaborati non appena le specifiche del programma sono complete. I casi di test si basano su classi di equivalenza.
Classi di equivalenza
Per ogni input, una classe di equivalenza identifica gli stati validi e non validi. Ci sono generalmente tre scenari da pianificare quando si definiscono le classi di equivalenza.
Se l'input specifica un intervallo o un valore specifico, ci sarà uno stato valido e due stati non validi definiti. Ad esempio, se un numero deve essere compreso tra 1 e 20, lo stato valido è compreso tra 1 e 20, ci sarà uno stato non valido inferiore a 1 e uno stato non valido maggiore di 20.
Se l'input esclude un intervallo o un valore specifico, saranno definiti due stati validi e uno stato non valido. Ad esempio, se un numero non deve essere compreso tra 1 e 20, gli stati validi sono minori di uno e maggiori di 20 e lo stato non valido è compreso tra 1 e 20.
Se l'input specifica un valore booleano, ci saranno solo due stati, uno valido e uno non valido.
Analisi del valore limite
L'analisi del valore limite considera solo i valori al limite degli input. Ad esempio, nel caso in cui un numero sia compreso tra 1 e 20, i casi di test possono essere 1, 20, 0 e 21. L'idea alla base è che se il programma funziona come previsto con questi valori, anche gli altri valori saranno funziona come previsto.
La tabella seguente fornisce una panoramica dei limiti tipici che potresti voler identificare.
Testing Ranges |
Input type |
Test Values |
Range |
- x[lower_bound]-1
- x[lower_bound]
- x[upper_bound]
- x[upper_bound]+1
|
Boolean |
|
Elaborazione di un piano di test
Identifica le classi di equivalenza e per ciascuna classe identifica i confini. Dopo aver identificato i limiti per la classe, scrivi un elenco di valori validi e non validi sul limite e quale dovrebbe essere il comportamento previsto. Il tester può quindi eseguire il programma con i valori limite e indicare cosa è successo quando il valore limite è stato testato rispetto al risultato richiesto.
Di seguito potrebbe essere un tipico piano di test utilizzato per verificare l'inserimento di un'età in cui i valori accettabili siano compresi tra 10 e 110.
Equivalence Class |
Valid |
Invalid |
Between 10 and 110 |
> 110 |
|
< 10 |
Dopo aver definito la nostra classe di equivalenza, ora possiamo elaborare un piano di test per, età.
Test Plan |
Value |
State |
Expected Result |
Actual Result |
10 |
Valid |
Continue execution to get name |
|
110 |
Valid |
Continue execution to get name |
|
9 |
Invalid |
Ask for age again |
|
111 |
Invalid |
Ask for age again |
|
Il "Risultato effettivo" la colonna viene lasciata vuota, poiché verrà completata durante il test. Se il risultato è quello previsto, la colonna verrà spuntata. In caso contrario, è necessario inserire un commento che indichi cosa è successo.
Pagina modificata il: 10/03/2022