Hoofdstuk – 5
Inleiding tot C-programmering
Invoering
'C' is een van de populairste programmeertalen in de moderne computerwereld. De programmeertaal C werd in 1972 ontworpen en ontwikkeld door Brian Kernighan en Dennis Ritchie bij Bell Research Labs.
'C' is een taal die speciaal is ontworpen om de programmeur toegang te geven tot bijna alle interne componenten van een machine: registers, I/O-slots en absolute adressen. Tegelijkertijd maakt 'C' zoveel gegevensverwerking en modularisatie van geprogrammeerde tekst mogelijk als nodig is, zodat zeer complexe multiprogrammeringsprojecten op een georganiseerde en tijdige manier kunnen worden gebouwd.
Hoewel de taal oorspronkelijk bedoeld was om onder UNIX te draaien, was er veel belangstelling om het te draaien onder het MS-DOS besturingssysteem op de IBM PC en compatibele computers. Het is een uitstekende taal voor deze omgeving vanwege de eenvoud van expressie, de compacte code en het brede scala aan toepasbaarheid.
Bovendien is C, vanwege de eenvoud en het gemak waarmee het geschreven kan worden, doorgaans de eerste hogere programmeertaal die beschikbaar is op een nieuwe computer, waaronder microcomputers, minicomputers en mainframes.
Waarom C gebruiken bij het programmeren van gegevensherstel
In de huidige wereld van computerprogrammering zijn er veel geavanceerde talen beschikbaar. Deze talen zijn goed omdat ze over veel functies beschikken die geschikt zijn voor de meeste programmeertaken. Er zijn echter verschillende redenen waarom C de eerste keuze is voor programmeurs die willen programmeren voor dataherstel, systeemprogrammering, apparaatprogrammering of hardwareprogrammering:
- C is een populaire taal die de voorkeur geniet van professionele programmeurs. Hierdoor is er een breed scala aan C-compilers en nuttige accessoires beschikbaar.
- C is een draagbare taal . Een C-programma dat voor het ene computersysteem is geschreven, kan met weinig of geen aanpassingen op een ander systeem worden gecompileerd en uitgevoerd. De overdraagbaarheid wordt verbeterd door de ANSI-standaard voor C, een reeks regels voor C-compilers.
- C maakt uitgebreid gebruik van modules bij het programmeren mogelijk. C-code kan worden geschreven in subroutines die functies worden genoemd. Deze functies kunnen opnieuw worden gebruikt in andere toepassingen of programma's. U hoeft geen extra moeite te doen bij het programmeren van een nieuwe toepassing om dezelfde module te maken die u eerder in een andere toepassing hebt ontwikkeld.
U kunt deze functionaliteit in het nieuwe programma zonder enige wijzigingen of met enkele kleine wijzigingen gebruiken. In het geval van programmering voor gegevensherstel zult u deze eigenschap zeer nuttig vinden wanneer u dezelfde functies meerdere malen in verschillende toepassingen van verschillende programma's moet uitvoeren.
- C is een krachtige en flexibele taal. Daarom wordt C gebruikt voor uiteenlopende projecten, zoals besturingssystemen, tekstverwerkers, grafische toepassingen, spreadsheets en zelfs compilers voor andere talen.
- C is een taal met weinig woorden, die slechts een paar termen bevat (trefwoorden). Deze termen vormen de basis waarop de functionaliteit van de taal is gebouwd. Deze trefwoorden, ook wel gereserveerde woorden genoemd, maken het programma krachtiger en bieden een breed scala aan programmeermogelijkheden. Ze geven de programmeur het gevoel dat hij/zij elk type programmering in C kan uitvoeren.
Laten we aannemen dat je niets over C weet.
Ik neem aan dat je niets weet over C-programmering en geen idee hebt van programmeren. Ik begin met de meest elementaire concepten van de C-taal en neem je mee naar C-programmering op hoog niveau, inclusief de doorgaans intimiderende concepten van pointers, structuren en dynamische geheugentoewijzing.
Het kost veel tijd en moeite om deze concepten volledig te begrijpen, omdat ze niet gemakkelijk te begrijpen zijn, maar het zijn wel zeer krachtige hulpmiddelen.
C-programmering is een enorm voordeel in gebieden waar u wellicht assembler moet gebruiken, maar de voorkeur geeft aan een eenvoudige schrijf- en onderhoudsomgeving. De tijdsbesparing die in dergelijke gevallen ontstaat door te coderen in C, kan enorm zijn.
Hoewel de programmeertaal C een goede reputatie heeft als het gaat om het overbrengen van programma's van de ene implementatie naar de andere, zijn er verschillen tussen compilers die u zult tegenkomen wanneer u een andere compiler probeert te gebruiken.
De meeste verschillen worden duidelijk wanneer u niet-standaard extensies gebruikt, zoals aanroepen naar het DOS BIOS bij MS-DOS. Maar zelfs deze verschillen kunnen worden geminimaliseerd door zorgvuldige keuze van programmeerconstructies.
Toen het duidelijk werd dat de programmeertaal C een zeer populaire taal werd die beschikbaar was op een breed scala aan computers, kwam een groep betrokken personen bijeen om een standaardset regels voor het gebruik van de programmeertaal C voor te stellen.
De groep vertegenwoordigde alle sectoren van de software-industrie en na vele vergaderingen en vele voorlopige ontwerpen schreven ze uiteindelijk een acceptabele standaard voor de C-taal. Deze is geaccepteerd door het American National Standards Institute (ANSI) en door de International Standards Organization (ISO) .
Het wordt niet aan een bepaalde groep of gebruiker opgedrongen, maar omdat het zo breed geaccepteerd is, zou het voor een compilerschrijver economische zelfmoord zijn om te weigeren zich aan de standaard te conformeren.
De programma's die in dit boek zijn geschreven, zijn in de eerste plaats bedoeld voor gebruik op een IBM-PC of compatibele computer, maar kunnen met elke ANSI-standaardcompiler worden gebruikt, omdat deze nauw aansluit bij de ANSI-standaard.
Laten we beginnen
Voordat u iets in welke taal dan ook kunt doen en kunt beginnen met programmeren, moet u weten hoe u een identifier benoemt. Een identifier wordt gebruikt voor elke variabele, functie, datadefinitie, etc. In de programmeertaal C is een identifier een combinatie van alfanumerieke tekens, waarbij de eerste een letter van het alfabet of een onderstrepingsteken is, en de rest een willekeurige letter van het alfabet, een willekeurig numeriek cijfer of de onderstrepingsteken.
Bij het benoemen van identificatoren moeten twee regels in gedachten worden gehouden.
- De hoofdlettergevoeligheid van alfabetische tekens is belangrijk. C is een hoofdlettergevoelige taal. Dat betekent dat Recovery verschilt van recovery en rEcOveRY verschilt van beide eerder genoemde.
- Volgens de ANSI-C-standaard kunnen er ten minste 31 significante tekens worden gebruikt en worden deze door een conforme ANSI-C-compiler als significant beschouwd. Als er meer dan 31 worden gebruikt, kunnen alle tekens na de 31e door een willekeurige compiler worden genegeerd.
Trefwoorden
Er zijn 32 woorden gedefinieerd als trefwoorden in C. Deze hebben vooraf gedefinieerde toepassingen en kunnen niet voor andere doeleinden in een C-programma worden gebruikt. Ze worden door de compiler gebruikt als hulpmiddel bij het compileren van het programma. Ze worden altijd in kleine letters geschreven. Hieronder volgt een volledige lijst:
auto |
pauze |
geval |
char |
constant |
doorgaan |
standaard |
Doen |
dubbele |
anders |
opsomming |
extern |
vlot |
voor |
ga naar |
als |
int |
lang |
register |
opbrengst |
kort |
ondertekend |
grootte van |
statisch |
structuur |
schakelaar |
typedef |
unie |
niet ondertekend |
leegte |
vluchtig |
terwijl |
Hier zien we de magie van C. De prachtige verzameling van slechts 32 trefwoorden biedt een breed gebruik in verschillende toepassingen. Elk computerprogramma heeft twee entiteiten om te overwegen, de data en het programma. Ze zijn sterk afhankelijk van elkaar en zorgvuldige planning van beide leidt tot een goed gepland en goed geschreven programma.
Laten we beginnen met een eenvoudig C-programma:
/* Eerste programma om C te leren */
#include <stdio.h>
leeg hoofd()
{
printf("Dit is een C-programma\n"); // een bericht afdrukken
}
Hoewel het programma heel eenvoudig is, zijn er een paar punten die het vermelden waard zijn. Laten we het bovenstaande programma eens bekijken. Alles wat zich binnen /* en */ bevindt, wordt beschouwd als een opmerking en wordt door de compiler genegeerd. U mag geen opmerkingen in andere opmerkingen opnemen, dus zoiets als dit is niet toegestaan:
/* dit is een /* opmerking */ binnen een opmerking, wat fout is */
Er is ook een manier van documentatie die binnen een regel werkt. Door // te gebruiken kunnen we kleine documentatie binnen die regel toevoegen.
Elk C-programma bevat een functie genaamd main. Dit is het startpunt van het programma. Elke functie moet een waarde retourneren. In dit programma retourneert de functie main geen retourwaarde, daarom hebben we void main geschreven. We kunnen dit programma ook schrijven als:
/* Eerste programma om C te leren */
#include <stdio.h>
voornaamst()
{
printf("Dit is een C-programma\n"); // een bericht afdrukken
retourneer 0;
}
Beide programma's zijn hetzelfde en voeren dezelfde taak uit. Het resultaat van beide programma's zal de volgende output op het scherm afdrukken:
Dit is een C-programma
#include<stdio.h> laat het programma communiceren met het scherm, toetsenbord en bestandssysteem van uw computer. U vindt het aan het begin van bijna elk C-programma.
main() declareert het begin van de functie, terwijl de twee accolades het begin en einde van de functie aangeven. Accolades in C worden gebruikt om statements te groeperen zoals in een functie, of in de body van een lus. Een dergelijke groepering staat bekend als een samengestelde statement of een blok.
printf("Dit is een C-programma\n"); drukt de woorden af op het scherm. De af te drukken tekst staat tussen dubbele aanhalingstekens. De \n aan het einde van de tekst vertelt het programma om een nieuwe regel af te drukken als onderdeel van de uitvoer. De printf()-functie wordt gebruikt voor weergave van de uitvoer op de monitor.
De meeste C-programma's zijn in kleine letters. U zult meestal hoofdletters vinden in preprocessordefinities die later worden besproken, of binnen aanhalingstekens als onderdelen van tekenreeksen.
Het programma samenstellen
Laat de naam van ons programma CPROG.C zijn. Om het C-programma in te voeren en te compileren, volgt u deze stappen:
- Maak de actieve directory van uw C-programma's en start uw editor. Hiervoor kan elke teksteditor worden gebruikt, maar de meeste C-compilers zoals Borland's Turbo C++ hebben een geïntegreerde ontwikkelomgeving (IDE) waarmee u uw programma's in één handige instelling kunt invoeren, compileren en koppelen.
- Schrijf en sla de broncode op. Je zou het bestand CPROG.C moeten noemen.
- Compileer en link CPROG.C. Voer de juiste opdracht uit die is opgegeven in de handleidingen van uw compiler. U zou een bericht moeten krijgen dat er geen fouten of waarschuwingen waren.
- Controleer de compilerberichten. Als u geen fouten of waarschuwingen ontvangt, zou alles in orde moeten zijn. Als er een fout is in het typen van het programma, zal de compiler dit opvangen en een foutmelding weergeven. Corrigeer de fout die in de foutmelding wordt weergegeven.
- Uw eerste C-programma zou nu gecompileerd moeten zijn en klaar om te draaien. Als u een directorylijst van alle bestanden met de naam CPROG weergeeft, krijgt u de vier bestanden met verschillende extensies, zoals hieronder beschreven:
- CPROG.C, het broncodebestand
- CPROG.BAK, het back-upbestand van het bronbestand dat u met de editor hebt gemaakt
- CPROG.OBJ, bevat de objectcode voor CPROG.C
- CPROG.EXE, het uitvoerbare programma dat werd gemaakt toen u CPROG.C compileerde en koppelde
- Om CPROG.EXE uit te voeren, of te runnen, typt u gewoon cprog. Het bericht, Dit is een C-programma wordt op het scherm weergegeven.
Laten we nu het volgende programma eens bekijken:
/* Eerste programma om C te leren */ // 1
// 2
#include <stdio.h> // 3
// 4
hoofd() // 5
{
// 6
printf("Dit is een C-programma\n"); // 7
// 8
retourneer 0; // 9
} // 10
Wanneer u dit programma compileert, geeft de compiler een bericht weer dat lijkt op het volgende:
cprog.c(8) : Fout: `;' verwacht
laten we deze foutmelding in delen opsplitsen. cprog.c is de naam van het bestand waar de fout is gevonden. (8) is het regelnummer waar de fout is gevonden. Error: `;' expected is Een beschrijving van de fout.
Dit bericht is behoorlijk informatief en vertelt u dat de compiler in regel 8 van CPROG.C een puntkomma verwachtte te vinden, maar dat niet deed. U weet echter dat de puntkomma in regel 7 is weggelaten, dus er is een discrepantie.
Waarom de compiler een fout meldt in regel 8, terwijl er in feite een puntkomma is weggelaten in regel 7. Het antwoord ligt in het feit dat C zich niets aantrekt van zaken als onderbrekingen tussen regels. De puntkomma die na de printf()-statement hoort, had op de volgende regel kunnen worden geplaatst, maar dat zou in de praktijk slecht programmeren zijn.
Pas nadat de volgende opdracht (return) in regel 8 is aangetroffen, weet de compiler zeker dat de puntkomma ontbreekt. Daarom meldt de compiler dat de fout in regel 8 staat.
Er kunnen verschillende soorten fouten voorkomen. Laten we linking error Messages bespreken. Linker errors zijn relatief zeldzaam en zijn meestal het gevolg van een verkeerde spelling van de naam van een C-bibliotheekfunctie. In dit geval krijgt u een Error: undefined symbols: foutmelding, gevolgd door de verkeerd gespelde naam. Zodra u de spelling corrigeert, zou het probleem moeten verdwijnen.
Afdrukken van getallen
Laten we eens naar het volgende voorbeeld kijken:
// Hoe de getallen af te drukken //
#include<stdio.h>
leeg hoofd()
{
int getal = 10;
printf(“Het getal is %d”, num);
}
De uitvoer van het programma wordt als volgt op het scherm weergegeven:
Het getal is 10
Het %-teken wordt gebruikt om de uitvoer van veel verschillende typen variabelen te signaleren. Het teken na het %-teken is ad, wat de uitvoerroutine signaleert om een decimale waarde te krijgen en deze uit te voeren.
Variabelen gebruiken
In C moet een variabele worden gedeclareerd voordat deze kan worden gebruikt. Variabelen kunnen worden gedeclareerd aan het begin van elk codeblok, maar de meeste bevinden zich aan het begin van elke functie. De meeste lokale variabelen worden gemaakt wanneer de functie wordt aangeroepen en worden vernietigd bij terugkeer van die functie.
Om variabelen in uw C-programma's te kunnen gebruiken, moet u de volgende regels kennen bij het geven van namen aan variabelen in C:
- De naam kan letters, cijfers en het onderstrepingsteken (_) bevatten.
- Het eerste teken van de naam moet een letter zijn. De underscore is ook een legaal eerste teken, maar het gebruik ervan wordt afgeraden.
- C is hoofdlettergevoelig, daarom is de variabelenaam num anders dan Num.
- C-trefwoorden kunnen niet worden gebruikt als variabelenamen. Een trefwoord is een woord dat deel uitmaakt van de C-taal.
De volgende lijst bevat enkele voorbeelden van legale en illegale namen van C-variabelen:
Variabele naam |
Legaal of niet |
In een |
Juridisch |
Ttpt2_t2p |
Juridisch |
Tt-pt |
Illegaal: Ruimte is niet toegestaan |
_1990_belasting |
Wettelijk maar niet aan te raden |
Jack_telefoon# |
Illegaal: Bevat het illegale teken # |
Geval |
Illegaal: Is een C-trefwoord |
1boek |
Illegaal: Eerste teken is een cijfer |
Het eerste nieuwe dat opvalt, is de eerste regel van de body van main():
int getal = 10;
Deze regel definieert een variabele met de naam 'num' van het type int en initialiseert deze met de waarde 10. Dit had ook als volgt geschreven kunnen worden:
int num; /* definieer niet-geïnitialiseerde variabele 'num' */
/* en na alle variabele definities: */
num = 10; /* wijst waarde 10 toe aan variabele 'num' */
Variabelen kunnen aan het begin van een blok worden gedefinieerd (tussen de accolades {en}). Meestal is dit aan het begin van een functiebody, maar het kan ook aan het begin van een ander type blok staan.
Variabelen die aan het begin van een blok worden gedefinieerd, hebben standaard de status 'auto'. Dit betekent dat ze alleen bestaan tijdens de uitvoering van het blok. Wanneer de uitvoering van de functie begint, worden de variabelen gemaakt, maar hun inhoud is ongedefinieerd. Wanneer de functie terugkeert, worden de variabelen vernietigd. De definitie had ook als volgt kunnen worden geschreven:
automatisch int num = 10;
Omdat de definitie met of zonder het trefwoord auto volledig gelijkwaardig is, is het trefwoord auto uiteraard nogal overbodig.
Soms is dit echter niet wat u wilt. Stel dat u wilt dat een functie bijhoudt hoe vaak deze wordt aangeroepen. Als de variabele elke keer dat de functie terugkeert, zou worden vernietigd, zou dit niet mogelijk zijn.
Daarom is het mogelijk om de variabele wat static-duration wordt genoemd te geven, wat betekent dat deze intact blijft gedurende de gehele uitvoering van het programma. Bijvoorbeeld:
statisch int num = 10;
Dit initialiseert de variabele num naar 10 aan het begin van de uitvoering van het programma. Vanaf dat moment blijft de waarde onaangeroerd; de variabele wordt niet opnieuw geïnitialiseerd als de functie meerdere keren wordt aangeroepen.
Soms is het niet voldoende dat de variabele alleen vanuit één functie toegankelijk is, of is het niet handig om de waarde via een parameter door te geven aan alle andere functies die de waarde nodig hebben.
Maar als u toegang nodig hebt tot de variabele van alle functies in het gehele bronbestand, kan dit ook worden gedaan met het static-trefwoord, maar door de definitie buiten alle functies te plaatsen. Bijvoorbeeld:
#include <stdio.h>
static int num = 10; /* zal toegankelijk zijn vanuit het gehele bronbestand */
int hoofd(leeg)
{
printf("Het getal is: %d\n", num);
retourneer 0;
}
En er zijn ook gevallen waarin een variabele toegankelijk moet zijn vanuit het hele programma, dat kan bestaan uit meerdere bronbestanden. Dit wordt een globale variabele genoemd en moet worden vermeden wanneer het niet nodig is.
Dit wordt ook gedaan door de definitie buiten alle functies te plaatsen, maar zonder het static trefwoord te gebruiken:
#include <stdio.h>
int num = 10; /* zal toegankelijk zijn vanuit het hele programma! */
int hoofd(leeg)
{
printf("Het getal is: %d\n", num);
retourneer 0;
}
Er is ook het extern-trefwoord, dat wordt gebruikt voor toegang tot globale variabelen in andere modules. Er zijn ook een paar qualifiers die u kunt toevoegen aan variabeledefinities. De belangrijkste daarvan is const. Een variabele die is gedefinieerd als const mag niet worden gewijzigd.
Er zijn nog twee modifiers die minder vaak worden gebruikt. De volatile en register modifier. De volatile modifier vereist dat de compiler daadwerkelijk toegang heeft tot de variabele elke keer dat deze wordt gelezen. Het kan zijn dat de variabele niet wordt geoptimaliseerd door deze in een register of zo te plaatsen. Dit wordt voornamelijk gebruikt voor multithreading en interrupt processing doeleinden etc.
De registermodifier vraagt de compiler om de variabele te optimaliseren in een register. Dit is alleen mogelijk met autovariabelen en in veel gevallen kan de compiler de variabelen die geoptimaliseerd moeten worden in registers beter selecteren, dus dit trefwoord is verouderd. Het enige directe gevolg van het maken van een variabel register is dat het adres ervan niet kan worden overgenomen.
De variabelentabel op de volgende pagina beschrijft de opslagklasse van vijf typen opslagklassen.
In de tabel zien we dat het sleutelwoord extern in twee rijen is geplaatst. Het sleutelwoord extern wordt in functies gebruikt om een statische externe variabele te declareren die elders is gedefinieerd.
Numerieke variabelentypen
C biedt verschillende typen numerieke variabelen omdat verschillende numerieke waarden verschillende geheugenopslagvereisten hebben. Deze numerieke typen verschillen in het gemak waarmee bepaalde wiskundige bewerkingen erop kunnen worden uitgevoerd.
Kleine gehele getallen vereisen minder geheugen om op te slaan en uw computer kan wiskundige bewerkingen met dergelijke getallen heel snel uitvoeren. Grote gehele getallen en drijvende-kommawaarden vereisen meer opslagruimte en meer tijd voor wiskundige bewerkingen. Door de juiste variabeletypen te gebruiken, zorgt u ervoor dat uw programma zo efficiënt mogelijk draait.
De numerieke variabelen van C vallen in de volgende twee hoofdcategorieën:
- Gehele getallen variabelen
- Variabelen met drijvende komma
Binnen elk van deze categorieën zijn er twee of meer specifieke variabelentypen. De volgende tabel toont de hoeveelheid geheugen, in bytes, die nodig is om één variabele van elk type te bevatten.
Het type char kan gelijkwaardig zijn aan signed char of unsigned char, maar het is altijd een ander type.
In C is er geen verschil tussen het opslaan van tekens of hun corresponderende numerieke waarden in een variabele, dus is er ook geen behoefte aan een functie om te converteren tussen een teken en zijn numerieke waarde of vice versa. Voor de andere integer-typen, als u signed of unsigned weglaat, zal de standaard signed zijn, dus bijvoorbeeld int en signed int zijn equivalent.
Het type int moet groter zijn dan of gelijk aan het type short, en kleiner dan of gelijk aan het type long. Als u gewoon wat waarden wilt opslaan die niet enorm groot zijn, is het vaak een goed idee om het type int te gebruiken; het is meestal de grootte die de processor het makkelijkst aankan, en daarom het snelst.
Bij meerdere compilers zijn double en long double equivalent. Dat gecombineerd met het feit dat de meeste standaard wiskundige functies met type double werken, is een goede reden om altijd het type double te gebruiken als je met fractionele getallen moet werken.
De volgende tabel beschrijft de variabelentypen beter:
Veelgebruikte speciale typen:
Variabel type |
Beschrijving |
grootte_t |
ongetekend type dat wordt gebruikt voor het opslaan van de grootte van objecten in bytes |
tijd_t |
wordt gebruikt om de resultaten van de time()-functie op te slaan |
klok_t |
wordt gebruikt om de resultaten van de clock()-functie op te slaan |
BESTAND |
wordt gebruikt voor toegang tot een stream (meestal een bestand of apparaat) |
ptrdiff_t |
getekend type van het verschil tussen 2 pointers |
div_t |
wordt gebruikt om de resultaten van de div()-functie op te slaan |
ldiv_t |
wordt gebruikt om de resultaten van de ldiv()-functie op te slaan |
fpos_t |
wordt gebruikt om informatie over de positie van een bestand vast te houden |
zal_lijst |
gebruikt bij het verwerken van variabele argumenten |
wchar_t |
breed karaktertype (gebruikt voor uitgebreide tekensets) |
sig_atomisch_t |
gebruikt in signaalhandlers |
Jmp_buf |
gebruikt voor niet-lokale sprongen |
Om deze variabelen beter te begrijpen, nemen we een voorbeeld:
/* Programma om het bereik en de grootte in bytes van de C-variabele te vertellen */
#include <stdio.h>
int hoofd()
{
int a; /* eenvoudig geheel getal */
lange int b; /* lang geheel getal type */
korte int c; /* kort geheel getal type */
ongetekende int d; /* ongetekend geheel getal type */
char e; /* tekentype */
float f; /* drijvende komma type */
dubbele g; /* drijvende komma met dubbele precisie */
een = 1023;
b = 2222;
c = 123;
d = 1234;
e = 'X';
f = 3,14159;
g = 3,1415926535898;
printf( "\nEen char is %d bytes", sizeof( char ));
printf( "\nEen int is %d bytes", sizeof( int ));
printf( "\nEen korte is %d bytes", sizeof( korte ));
printf( "\nEen long is %d bytes", sizeof( long ));
printf( "\nEen ongetekende char is %d bytes",
grootte van( ongetekende char ));
printf( "\nEen ongetekende int is %d bytes",
grootte van( unsigned int ));
printf( "\nEen ongetekende short is %d bytes",
grootte(niet-ondertekende korte));
printf( "\nEen ongetekende long is %d bytes",
grootte(unsigned long));
printf( "\nEen float is %d bytes", sizeof( float ));
printf( "\nEen double is %d bytes\n", sizeof( double ));
printf("a = %d\n", a); /* decimale uitvoer */
printf("a = %o\n", a); /* octale uitvoer */
printf("a = %x\n", a); /* hexadecimale uitvoer */
printf("b = %ld\n", b); /* decimale lange uitvoer */
printf("c = %d\n", c); /* decimale korte uitvoer */
printf("d = %u\n", d); /* ongetekende uitvoer */
printf("e = %c\n", e); /* tekenuitvoer */
printf("f = %f\n", f); /* zwevende uitvoer */
printf("g = %f\n", g); /* dubbele float-uitvoer */
afdrukkenf("\n");
printf("a = %d\n", a); /* eenvoudige int-uitvoer */
printf("a = %7d\n", a); /* gebruik een veldbreedte van 7 */
printf("a = %-7d\n", a); /* links uitlijnen in
veld van 7 */
c = 5;
d = 8;
printf("a = %*d\n", c, a); /* gebruik een veldbreedte van 5*/
printf("a = %*d\n", d, a); /* gebruik een veldbreedte van 8 */
afdrukkenf("\n");
printf("f = %f\n", f); /* eenvoudige float-uitvoer */
printf("f = %12f\n", f); /* gebruik veldbreedte van 12 */
printf("f = %12.3f\n", f); /* gebruik 3 decimalen */
printf("f = %12.5f\n", f); /* gebruik 5 decimalen */
printf("f = %-12.5f\n", f); /* links uitlijnen in veld */
retourneer 0;
}
Het resultaat van het programma wordt na uitvoering als volgt weergegeven:
Een char is 1 byte
Een int is 2 bytes
Een short is 2 bytes
Een long is 4 bytes
Een unsigned char is 1 byte
Een unsigned int is 2 bytes lang
Een unsigned short is 2 bytes
Een unsigned long is 4 bytes
Een float is 4 bytes
Een double is 8 bytes
een = 1023
een = 1777
een = 3ff
b = 2222
c = 123
d = 1234
e = X
f = 3,141590
g = 3,141593
een = 1023
een = 1023
een = 1023
een = 1023
een = 1023
f = 3,141590
f = 3,141590
v = 3,142
f = 3,14159
f = 3,14159 |
Voordat een variabele in een C-programma wordt gebruikt, moet deze worden gedeclareerd. Een variabeledeclaratie vertelt de compiler de naam en het type van een variabele en initialiseert de variabele optioneel naar een specifieke waarde.
Als uw programma probeert een variabele te gebruiken die niet is gedeclareerd, genereert de compiler een foutmelding. Een variabele declaratie heeft de volgende vorm:
typenaam varnaam;
typename specificeert het variabeletype en moet een van de trefwoorden zijn. varname is de variabelenaam. U kunt meerdere variabelen van hetzelfde type op één regel declareren door de variabelenamen te scheiden met komma's:
int count, number, start; /* drie gehele variabelen */
float percentage, totaal; /* twee float variabelen */
Het typedef-trefwoord
Het typedef-trefwoord wordt gebruikt om een nieuwe naam te maken voor een bestaand gegevenstype. In feite maakt typedef een synoniem. Bijvoorbeeld, de verklaring
typedef int geheel getal;
hier zien we dat typedef integer aanmaakt als synoniem voor int. Vervolgens kunt u integer gebruiken om variabelen van het type int te definiëren, zoals in dit voorbeeld:
geheel getal;
typedef maakt dus geen nieuw gegevenstype aan, het staat u alleen toe om een andere naam te gebruiken voor een vooraf gedefinieerd gegevenstype.
Numerieke variabelen initialiseren
Wanneer een variabele wordt gedeclareerd, krijgt de compiler de opdracht om opslagruimte voor de variabele te reserveren. De waarde die in die ruimte wordt opgeslagen, de waarde van de variabele, is echter niet gedefinieerd. Het kan nul zijn, of een willekeurige "garbage"-waarde. Voordat u een variabele gebruikt, moet u deze altijd initialiseren naar een bekende waarde. Laten we dit voorbeeld nemen:
int count; /* Reserveer opslagruimte voor count */
count = 0; /* Sla 0 op in count */
Deze statement gebruikt het gelijkteken (=), wat de toewijzingsoperator van C is. U kunt een variabele ook initialiseren wanneer deze wordt gedeclareerd. Om dit te doen, volgt u de variabelenaam in de declaratie-statement met een gelijkteken en de gewenste beginwaarde:
int aantal = 0;
dubbele snelheid = 0,01, complexiteit = 28,5;
Wees voorzichtig om geen variabele te initialiseren met een waarde buiten het toegestane bereik. Hier zijn twee voorbeelden van out-of-range initialisaties:
int bedrag = 100000;
ongetekende int lengte = -2500;
De C-compiler vangt dergelijke fouten niet op. Uw programma kan compileren en linken, maar u kunt onverwachte resultaten krijgen wanneer het programma wordt uitgevoerd.
Laten we het volgende voorbeeld nemen om het totale aantal sectoren op een schijf te berekenen:
// Modelprogramma om sectoren op een schijf te berekenen //
#include<stdio.h>
#define SECTOR_PER_ZIJDE 63
#define ZIJDE_PER_CILINDER 254
leeg hoofd()
{
int cilinder=0;
clrscr();
printf("Voer het aantal cilinders in de schijf in \n\n\t");
scanf("%d",&cylinder); // Haal de waarde op van de gebruiker //
printf("\n\n\t Totaal aantal sectoren op de schijf = %ld", (long)SECTOR_PER_SIDE*SIDE_PER_CYLINDER* cylinder);
halen();
}
De uitvoer van het programma is als volgt:
Voer het aantal cilinders in de schijf in
1024
Totaal aantal sectoren op de schijf = 16386048
In dit voorbeeld zien we drie nieuwe dingen om te leren. #define wordt gebruikt om symbolische constanten in het programma te gebruiken of in sommige gevallen om tijd te besparen door lange woorden in kleine symbolen te definiëren.
Hier hebben we het aantal sectoren per zijde gedefinieerd, namelijk 63, als SECTOR_PER_SIDE om het programma gemakkelijk te begrijpen te maken. Hetzelfde geldt voor #define SIDE_PER_CYLINDER 254. scanf() wordt gebruikt om de invoer van de gebruiker te krijgen.
Hier nemen we het aantal cilinders als invoer van de gebruiker. * wordt gebruikt om twee of meer waarden te vermenigvuldigen, zoals in het voorbeeld wordt getoond.
De functie getch() haalt in principe één enkel teken op van het toetsenbord. Door getch(); te typen stoppen we het scherm totdat er een toets op het toetsenbord wordt ingedrukt.
Exploitanten
Een operator is een symbool dat C instrueert om een bewerking of actie uit te voeren op een of meer operanden. Een operand is iets waarop een operator inwerkt. In C zijn alle operanden expressies. C-operatoren zijn van de volgende vier categorieën:
- De toewijzingsoperator
- Wiskundige operatoren
- Relationele operatoren
- Logische operatoren
Toewijzingsoperator
De toewijzingsoperator is het gelijkteken (=). Het gebruik van het gelijkteken in programmeren verschilt van het gebruik ervan in reguliere wiskundige algebraïsche relaties. Als u schrijft
x = y;
In een C-programma betekent het niet "x is gelijk aan y". In plaats daarvan betekent het "wijs de waarde van y toe aan x". In een C-toewijzingsstatement kan de rechterkant een willekeurige expressie zijn en moet de linkerkant een variabelenaam zijn. De vorm is dus als volgt:
variabele = expressie;
Tijdens de uitvoering wordt de expressie geëvalueerd en de resulterende waarde wordt toegewezen aan een variabele.
Wiskundige operatoren
De wiskundige operatoren van C voeren wiskundige bewerkingen uit zoals optellen en aftrekken. C heeft twee unaire wiskundige operatoren en vijf binaire wiskundige operatoren. De unaire wiskundige operatoren worden zo genoemd omdat ze één operand nemen. C heeft twee unaire wiskundige operatoren.
De increment- en decrement-operatoren kunnen alleen worden gebruikt met variabelen, niet met constanten. De uitgevoerde bewerking is om één toe te voegen aan of één af te trekken van de operand. Met andere woorden, de statements ++x; en --y; zijn de equivalenten van deze statements:
x = x + 1;
y = y - 1;
binaire wiskundige operatoren nemen twee operanden. De eerste vier binaire operatoren, die de gebruikelijke wiskundige bewerkingen op een rekenmachine bevatten (+, -, *, /), zijn u bekend. De vijfde operator Modulus retourneert de rest wanneer de eerste operand wordt gedeeld door de tweede operand. Bijvoorbeeld, 11 modulus 4 is gelijk aan 3 (11 wordt gedeeld door 4, twee keer en 3 blijft over).
Relationele operatoren
De relationele operatoren van C worden gebruikt om expressies te vergelijken. Een expressie die een relationele operator bevat, wordt geëvalueerd naar true (1) of false (0). C heeft zes relationele operatoren.
Logische operatoren
Met logische operatoren van C kunt u twee of meer relationele expressies combineren tot één expressie die wordt geëvalueerd als true of false. Logische operatoren evalueren naar true of false, afhankelijk van de true of false-waarde van hun operanden.
Als x een gehele variabele is, kunnen expressies die gebruikmaken van logische operatoren op de volgende manieren worden geschreven:
(x > 1) en (x < 5)
(x >= 2) en (x <= 4)
Bediende |
Symbool |
Beschrijving |
Voorbeeld |
Toewijzingsoperatoren |
gelijkwaardig |
= |
de waarde van y aan x toewijzen |
x = j |
Wiskundige operatoren |
Toename |
++ |
Verhoogt de operand met één |
++x, x++ |
Afname |
-- |
Vermindert de operand met één |
--x, x-- |
Toevoeging |
+ |
Voegt twee operanden toe |
x + j |
Aftrekken |
- |
Trekt de tweede operand af van de eerste |
x-j |
Vermenigvuldiging |
* |
Vermenigvuldigt twee operanden |
x * j |
Divisie |
/ |
Deelt de eerste operand door de tweede operand |
x / j |
Modulus |
% |
Geeft de rest wanneer de eerste operand wordt gedeeld door de tweede operand |
x % en |
Relationele operatoren |
Gelijkwaardig |
= = |
Gelijkwaardigheid |
x = = y |
Groter dan |
> |
Groter dan |
x > j |
Minder dan |
< |
Minder dan |
x < y |
Groter dan of gelijk aan |
>= |
Groter dan of gelijk aan |
x >= y |
Kleiner dan of gelijk aan |
<= |
Kleiner dan of gelijk aan |
x <= y |
Niet gelijk |
!= |
Niet gelijk aan |
x != y |
Logische operatoren |
EN |
&& |
Alleen waar (1) als zowel exp1 als exp2 waar zijn; anders onwaar (0) |
exp1 en exp2 |
OF |
|| |
Waar (1) als exp1 of exp2 waar is; onwaar (0) alleen als beide onwaar zijn |
exp1 || exp2 |
NIET |
! |
Onwaar (0) als exp1 waar is; waar (1) als exp1 onwaar is |
!exp1 |
Dingen om te onthouden over logische uitdrukkingen
x * = j |
is hetzelfde als |
x = x * y |
y - = z + 1 |
is hetzelfde als |
y = y - z + 1 |
een / = b |
is hetzelfde als |
een = een / b |
x + = y / 8 |
is hetzelfde als |
x = x + y / 8 |
en % = 3 |
is hetzelfde als |
j = j % 3 |
De komma-operator
De komma wordt in C vaak gebruikt als een eenvoudig leesteken, om variabelendeclaraties, functieargumenten, etc. te scheiden. In bepaalde situaties fungeert de komma als een operator.
U kunt een expressie vormen door twee subexpressies te scheiden met een komma. Het resultaat is als volgt:
- Beide expressies worden geëvalueerd, waarbij de linkerexpressie als eerste wordt geëvalueerd.
- De gehele expressie wordt geëvalueerd naar de waarde van de juiste expressie.
De volgende instructie wijst bijvoorbeeld de waarde van b toe aan x, verhoogt vervolgens a en verhoogt vervolgens b: x = (a++, b++);
C-operatorprecedentie (Samenvatting van C-operatoren)
Rang en associativiteit |
Exploitanten |
1 (van links naar rechts) |
() [] -> . |
2 (van rechts naar links) |
! ~ ++ -- * (indirectie) & (adres-van) (type) sizeof + (unair) - (unair) |
3 (van links naar rechts) |
* (vermenigvuldiging) / % |
4 (van links naar rechts) |
+ - |
5 (van links naar rechts) |
<< >> |
6 (van links naar rechts) |
< <= > >= |
7 (van links naar rechts) |
= = != |
8 (van links naar rechts) |
& (bitgewijs EN ) |
9 (van links naar rechts) |
^ |
10 (van links naar rechts) |
| |
11 (van links naar rechts) |
&& |
12 (van links naar rechts) |
|| |
13 (van rechts naar links) |
?: |
14 (van rechts naar links) |
= += -= *= /= %= &= ^= |= <<= >>= |
15 (van links naar rechts) |
, |
() is de functie-operator; [] is de array-operator. |
|
Laten we een voorbeeld nemen van het gebruik van operatoren:
/* Gebruik van operatoren */
int hoofd()
{
int x = 0, y = 2, z = 1025;
vlotter a = 0,0, b = 3,14159, c = -37,234;
/* verhogen */
x = x + 1; /* Dit verhoogt x */
x++; /* Dit verhoogt x */
++x; /* Dit verhoogt x */
z = y++; /* z = 2, y = 3 */
z = ++y; /* z = 4, y = 4 */
/* afnemend */
y = y - 1; /* Dit verlaagt y */
y--; /* Dit verlaagt y */
--y; /* Dit verlaagt y */
y = 3;
z = y--; /* z = 3, y = 2 */
z = --y; /* z = 1, y = 1 */
/* rekenkundige op */
a = a + 12; /* Dit telt 12 op bij a */
a += 12; /* Dit voegt 12 toe aan a */
a *= 3,2; /* Dit vermenigvuldigt a met 3,2 */
a -= b; /* Dit trekt b van a af */
a /= 10.0; /* Dit deelt a door 10.0 */
/* voorwaardelijke expressie */
a = (b >= 3,0 ? 2,0 : 10,5 ); /* Deze expressie */
als (b >= 3.0) /* En deze expressie */
a = 2.0; /* zijn identiek, beide */
anders zal /* hetzelfde veroorzaken */
a = 10,5; /* resultaat. */
c = (a > b ? a : b); /* c zal de max van a of b hebben */
c = (a > b ? b : a); /* c zal de min van a of b hebben */
printf("x=%d, y=%d, z= %d\n", x, y, z);
printf("a=%f, b=%f, c= %f", a, b, c);
retourneer 0;
}
en het resultaat van dit programma wordt op het scherm weergegeven als:
x=3, y=1, z=1
a=2,000000, b=3,141590, c=2,000000
Nog iets over printf() en Scanf()
Beschouw de volgende twee printf-statements
printf(“\t %d\n”, num);
printf(“%5.2f”, fract);
in de eerste printf-instructie vraagt \t om de tabverplaatsing op het scherm. Het argument %d vertelt de compiler dat de waarde van num moet worden afgedrukt als decimaal geheel getal. \n zorgt ervoor dat de nieuwe uitvoer begint vanaf een nieuwe regel.
In de tweede printf-instructie vertelt %5.2f de compiler dat de uitvoer in floating point moet zijn, met vijf plaatsen in totaal en twee plaatsen rechts van de decimale punt. Meer over het backslash-teken is weergegeven in de volgende tabel:
Constante |
Betekenis |
'\A' |
Hoorbaar alarm (bel) |
'\B' |
Backspace |
'\F' |
Formulier feed |
'\N' |
Nieuwe lijn |
'\R' |
Wagenretour |
'\T' |
Horizontale tab |
'\v' |
Verticaal tabblad |
'\'' |
Enkele aanhalingstekens |
'\”' |
Dubbele aanhalingstekens |
'\?' |
Vraagteken |
'\\' |
Schuine streep |
'\0' |
Nul |
Laten we de volgende scanf-verklaring beschouwen
scanf(“%d”, &num);
De gegevens van het toetsenbord worden ontvangen door de scanf-functie. In het bovenstaande formaat is het & (ampersand)-symbool voor elke variabelenaam een operator die het adres van de variabelenaam specificeert.
Door dit te doen stopt de uitvoering en wacht tot de waarde van de variabele num is getypt. Wanneer de gehele waarde is ingevoerd en de return-toets is ingedrukt, gaat de computer door naar de volgende instructie. De scanf- en printf-formaatcodes worden in de volgende tabel weergegeven:
Code |
Leest... |
%C |
Enkel teken |
%D |
Decimaal geheel getal |
%En |
Vlottende-kommawaarde |
%F |
Vlottende-kommawaarde |
%G |
Vlottende-kommawaarde |
%H |
Kort geheel getal |
%i |
Decimaal, hexadecimaal of octaal geheel getal |
%de |
Octaal geheel getal |
%S |
Snaar |
%in |
Ongetekend decimaal geheel getal |
%X |
Hexadecimaal geheel getal |
Controleverklaringen
Een programma bestaat uit een aantal statements die meestal in volgorde worden uitgevoerd. Programma's kunnen veel krachtiger zijn als we de volgorde waarin statements worden uitgevoerd, kunnen bepalen.
Er zijn drie algemene typen uitspraken:
- Toewijzing, waarbij waarden, meestal de resultaten van berekeningen, in variabelen worden opgeslagen.
- Input/Output, gegevens worden ingelezen of afgedrukt.
- Controle, het programma beslist wat er vervolgens moet gebeuren.
In deze sectie wordt het gebruik van control statements in C besproken. We laten zien hoe ze gebruikt kunnen worden om krachtige programma's te schrijven door;
- Belangrijke delen van het programma herhalen.
- Kiezen tussen optionele onderdelen van een programma.
De if else-instructie
Dit wordt gebruikt om te beslissen of iets op een bepaald moment gedaan moet worden, of om te kiezen tussen twee mogelijke acties.
De volgende test bepaalt of een student geslaagd is voor een examen met een voldoende van 45
als (resultaat >= 45)
printf("Pass\n");
anders
printf("Mislukt\n");
Het is mogelijk om het if-gedeelte te gebruiken zonder het else-gedeelte.
als (temperatuur < 0)
print("Bevroren\n");
Elke versie bestaat uit een test, in de tussen haakjes geplaatste statement na de if. Als de test waar is, wordt de volgende statement uitgevoerd. Als deze onwaar is, wordt de statement na de else uitgevoerd als deze aanwezig is. Hierna gaat de rest van het programma gewoon door.
Als we meer dan één statement na de if of de else willen hebben, moeten ze gegroepeerd worden tussen accolades. Zo'n groepering wordt een samengesteld statement of een blok genoemd.
als (resultaat >= 45)
{ printf("Geslaagd\n");
printf("Gefeliciteerd\n");
}
anders
{ printf("Mislukt\n");
printf("Volgende keer meer geluk\n");
}
Soms willen we een multi-way beslissing nemen op basis van meerdere voorwaarden. De meest algemene manier om dit te doen is door de else if-variant op de if-statement te gebruiken.
Dit werkt door meerdere vergelijkingen te cascaderen. Zodra een van deze een waar resultaat geeft, wordt de volgende verklaring of blok uitgevoerd en worden er geen verdere vergelijkingen uitgevoerd. In het volgende voorbeeld geven we cijfers op basis van het examenresultaat.
als (resultaat <=100 en resultaat >= 75)
printf("Geslaagd: Cijfer A\n");
anders als (resultaat >= 60)
printf("Geslaagd: Cijfer B\n");
anders als (resultaat >= 45)
printf("Geslaagd: Cijfer C\n");
anders
printf("Mislukt\n");
In dit voorbeeld testen alle vergelijkingen één variabele, genaamd result. In andere gevallen kan elke test een andere variabele of een combinatie van tests omvatten. Hetzelfde patroon kan worden gebruikt met meer of minder else if's, en de laatste else kan alleen worden weggelaten.
Het is aan de programmeur om de juiste structuur voor elk programmeerprobleem te bedenken. Om het gebruik van if else beter te begrijpen, laten we het voorbeeld bekijken
#include <stdio.h>
int hoofd()
{
int getal;
voor(num = 0 ; num < 10 ; num = num + 1)
{
als (num == 2)
printf("num is nu gelijk aan %d\n", num);
als (num < 5)
printf("num is nu %d, wat kleiner is dan 5\n", num);
anders
printf("num is nu %d, wat groter is dan 4\n", num);
} /* einde van for-lus */
retourneer 0;
}
Resultaat van het programma
num is nu 0, wat kleiner is dan 5
num is nu 1, wat minder is dan 5
num is nu gelijk aan 2
num is nu 2, wat minder is dan 5
num is nu 3, wat minder is dan 5
num is nu 4, wat minder is dan 5
num is nu 5, wat groter is dan 4
num is nu 6, wat groter is dan 4
num is nu 7, wat groter is dan 4
num is nu 8, wat groter is dan 4
num is nu 9, wat groter is dan 4
De schakelaar Verklaring
Dit is een andere vorm van de multi-way decision. Het is goed gestructureerd, maar kan alleen worden gebruikt in bepaalde gevallen waar;
- Er wordt slechts één variabele getest, alle branches moeten afhankelijk zijn van de waarde van die variabele. De variabele moet een integraal type zijn. (int, long, short of char).
- Elke mogelijke waarde van de variabele kan één enkele branch besturen. Een laatste, catch all, standaardbranch kan optioneel worden gebruikt om alle niet-gespecificeerde gevallen te vangen.
Het onderstaande voorbeeld zal de zaken verduidelijken. Dit is een functie die een geheel getal omzet in een vage beschrijving. Het is nuttig wanneer we alleen een hoeveelheid willen meten wanneer deze vrij klein is.
schatting(nummer)
int nummer;
/* Schat een getal als geen, één, twee, meerdere, veel */
{ schakelaar(nummer) {
geval 0 :
printf("Geen\n");
pauze;
geval 1:
printf("Een\n");
pauze;
geval 2:
printf("Twee\n");
pauze;
geval 3:
geval 4:
geval 5:
printf("Meerdere\n");
pauze;
standaard :
printf("Veel\n");
pauze;
}
}
Elk interessant geval wordt vermeld met een bijbehorende actie. De break-instructie voorkomt dat er verdere statements worden uitgevoerd door de switch te verlaten. Omdat geval 3 en geval 4 geen volgende break hebben, blijven ze dezelfde actie toestaan voor verschillende waarden van number.
Zowel if- als switch-constructies stellen de programmeur in staat om een selectie te maken uit een aantal mogelijke acties. Laten we een voorbeeld bekijken:
#include <stdio.h>
int hoofd()
{
int getal;
voor (num = 3 ; num < 13 ; num = num + 1)
{
schakelaar (num)
{
geval 3:
printf("De waarde is drie\n");
pauze;
geval 4:
printf("De waarde is vier\n");
pauze;
geval 5:
geval 6:
geval 7:
geval 8 :
printf("De waarde ligt tussen 5 en 8\n");
pauze;
geval 11 :
printf("De waarde is elf\n");
pauze;
standaard :
printf("Het is een van de ongedefinieerde waarden\n");
pauze;
} /* einde van de schakelaar */
} /* einde van for-lus */
retourneer 0;
}
De uitvoer van het programma zal zijn:
De waarde is drie
De waarde is vier
De waarde ligt tussen 5 en 8
De waarde ligt tussen 5 en 8
De waarde ligt tussen 5 en 8
De waarde ligt tussen 5 en 8
Het is een van de ongedefinieerde waarden
Het is een van de ongedefinieerde waarden
De waarde is elf
Het is een van de ongedefinieerde waarden
De pauzeverklaring
We hebben break al ontmoet in de discussie over de switch-statement. Het wordt gebruikt om een lus of een switch te verlaten, waarbij de controle wordt doorgegeven aan de eerste statement voorbij de lus of een switch.
Met loops kan break worden gebruikt om een vroege exit uit de loop te forceren, of om een loop te implementeren met een test om in het midden van de loop body te exiten. Een break binnen een loop moet altijd worden beschermd binnen een if-statement die de test biedt om de exit-conditie te controleren.
De continue verklaring
Dit is vergelijkbaar met break, maar wordt minder vaak aangetroffen. Het werkt alleen binnen lussen waarbij het effect is dat er direct naar de loop control statement wordt geforceerd.
- Spring in een while-lus naar de testinstructie.
- Spring in een do while-lus naar de test-instructie.
- Ga in een for-lus naar de test en voer de iteratie uit.
Net als een break moet continue worden beschermd door een if-statement. U zult het waarschijnlijk niet vaak gebruiken. Om het gebruik van break en continue beter te begrijpen, bekijken we het volgende programma:
#include <stdio.h>
int hoofd()
{
int-waarde;
voor(waarde = 5 ; waarde < 15 ; waarde = waarde + 1)
{
als (waarde == 8)
pauze;
printf("In de break-lus is de waarde nu %d\n", value);
}
voor(waarde = 5 ; waarde < 15 ; waarde = waarde + 1)
{
als (waarde == 8)
doorgaan;
printf("In de continue lus is de waarde nu %d\n", value);
}
retourneer 0;
}
De uitvoer van het programma is als volgt:
In de break-loop is de waarde nu 5
In de break-loop is de waarde nu 6
In de break-loop is de waarde nu 7
In de continue lus is de waarde nu 5
In de continue lus is de waarde nu 6
In de continue lus is de waarde nu 7
In de continue lus is de waarde nu 9
In de continue lus is de waarde nu 10
In de continue lus is de waarde nu 11
In de continue lus is de waarde nu 12
In de continue lus is de waarde nu 13
In de continue lus is de waarde nu 14
Lussen
Het andere hoofdtype van een control statement is de loop. Loops zorgen ervoor dat een statement, of blok statements, herhaald kan worden. Computers zijn erg goed in het herhalen van simpele taken. De loop is C's manier om dit te bereiken.
Met C kunt u kiezen uit drie soorten lussen: while, do-while en for.
- De while-lus blijft een actie herhalen totdat een bijbehorende test false retourneert. Dit is handig als de programmeur niet van tevoren weet hoe vaak de lus zal worden doorlopen.
- De do while loops zijn vergelijkbaar, maar de test vindt plaats nadat de loop body is uitgevoerd. Dit zorgt ervoor dat de loop body ten minste één keer wordt uitgevoerd.
- De for-lus wordt vaak gebruikt, meestal als de lus een vast aantal keer wordt doorlopen. Het is erg flexibel en beginnende programmeurs moeten oppassen dat ze de kracht die het biedt niet misbruiken.
De while-lus
De while-lus herhaalt een statement totdat de test bovenaan false bewijst. Als voorbeeld is hier een functie om de lengte van een string te retourneren. Vergeet niet dat de string wordt weergegeven als een array van tekens die wordt beëindigd door een null-teken '\0'.
int string_lengte(char string[])
{ int i = 0;
terwijl (string[i] != '\0')
ik++;
terugkeer(i);
}
De string wordt als argument aan de functie doorgegeven. De grootte van de array is niet gespecificeerd, de functie werkt voor een string van elke grootte.
De while-lus wordt gebruikt om de tekens in de string één voor één te bekijken totdat het null-teken is gevonden. Vervolgens wordt de lus verlaten en wordt de index van de null geretourneerd.
Terwijl het karakter niet null is, wordt de index verhoogd en wordt de test herhaald. We gaan later dieper in op arrays. Laten we een voorbeeld bekijken voor een while-lus:
#include <stdio.h>
int hoofd()
{
int aantal;
aantal = 0;
terwijl (aantal < 6)
{
printf("De waarde van count is %d\n", count);
tellen = tellen + 1;
}
retourneer 0;
}
en het resultaat wordt als volgt weergegeven:
De waarde van count is 0
De waarde van count is 1
De waarde van count is 2
De waarde van count is 3
De waarde van count is 4
De waarde van count is 5
De do while-lus
Dit lijkt erg op de while-lus, behalve dat de test aan het einde van de lusbody plaatsvindt. Dit garandeert dat de lus ten minste één keer wordt uitgevoerd voordat deze verdergaat.
Een dergelijke opstelling wordt vaak gebruikt als gegevens moeten worden gelezen. De test verifieert vervolgens de gegevens en gaat terug om opnieuw te lezen als deze onacceptabel waren.
Doen
{
printf("Voer 1 in voor ja, 0 voor nee :");
scanf("%d", &invoerwaarde);
} terwijl (invoerwaarde != 1 && invoerwaarde != 0)
Om de do while-lus beter te begrijpen, bekijken we het volgende voorbeeld:
#include <stdio.h>
int hoofd()
{
int ik;
ik = 0;
Doen
{
printf("De waarde van i is nu %d\n", i);
ik = ik + 1;
} terwijl (i < 5);
retourneer 0;
}
Het resultaat van het programma wordt als volgt weergegeven:
De waarde van i is nu 0
De waarde van i is nu 1
De waarde van i is nu 2
De waarde van i is nu 3
De waarde van i is nu 4
De for-lus
De for-lus werkt goed als het aantal iteraties van de lus bekend is voordat de lus wordt ingevoerd. De kop van de lus bestaat uit drie delen gescheiden door puntkomma's.
- De eerste wordt uitgevoerd voordat de lus wordt ingevoerd. Dit is meestal de initialisatie van de lusvariabele.
- De tweede is een test, de lus wordt verlaten als dit false retourneert.
- De derde is een statement die elke keer moet worden uitgevoerd als de loop body is voltooid. Dit is meestal een verhoging van de lusteller.
Het voorbeeld is een functie die het gemiddelde berekent van de getallen die in een array zijn opgeslagen. De functie neemt de array en het aantal elementen als argumenten.
float gemiddelde(float array[], int aantal)
{
float totaal = 0,0;
int ik;
voor(i = 0; i < aantal; i++)
totaal += array[i];
return(totaal / aantal);
}
De for-lus zorgt ervoor dat het juiste aantal elementen in de matrix wordt opgeteld voordat het gemiddelde wordt berekend.
De drie statements aan het hoofd van een for-lus doen meestal maar één ding, maar elk van hen kan leeg worden gelaten. Een lege first of last statement betekent geen initialisatie of running increment. Een lege comparison statement wordt altijd als true behandeld. Dit zorgt ervoor dat de lus oneindig doorloopt, tenzij deze op een andere manier wordt onderbroken. Dit kan een return of break statement zijn.
Het is ook mogelijk om meerdere statements in de eerste of derde positie te proppen, gescheiden door komma's. Dit maakt een lus met meer dan één controlerende variabele mogelijk. Het onderstaande voorbeeld illustreert de definitie van zo'n lus, met variabelen hi en lo die respectievelijk beginnen bij 100 en 0 en convergeren.
De for-lus geeft een verscheidenheid aan afkortingen die erin gebruikt kunnen worden. Let op de volgende expressie, in deze expressie bevat de enkele lus twee for-lussen. Hier is hi-- hetzelfde als hi = hi - 1 en lo++ is hetzelfde als lo = lo + 1,
voor(hi = 100, lo = 0; hi >= lo; hi--, lo++)
De for-lus is extreem flexibel en maakt het mogelijk om veel soorten programmagedrag eenvoudig en snel te specificeren. Laten we een voorbeeld van de for-lus bekijken
#include <stdio.h>
int hoofd()
{
int-index;
voor(index = 0 ; index < 6 ; index = index + 1)
printf("De waarde van de index is %d\n", index);
retourneer 0;
}
Het resultaat van het programma wordt als volgt weergegeven:
De waarde van de index is 0
De waarde van de index is 1
De waarde van de index is 2
De waarde van de index is 3
De waarde van de index is 4
De waarde van de index is 5
De goto-verklaring
C heeft een goto-statement waarmee ongestructureerde sprongen kunnen worden gemaakt. Om een goto-statement te gebruiken, gebruikt u eenvoudigweg het gereserveerde woord goto gevolgd door de symbolische naam waarnaar u wilt springen. De naam wordt vervolgens ergens in het programma geplaatst, gevolgd door een dubbele punt. U kunt bijna overal binnen een functie springen, maar u mag niet in een lus springen, hoewel u wel uit een lus mag springen.
Dit specifieke programma is echt een puinhoop, maar het is een goed voorbeeld van waarom softwareschrijvers proberen het gebruik van de goto-instructie zoveel mogelijk te elimineren. De enige plek in dit programma waar het redelijk is om de goto te gebruiken, is waar het programma in één sprong uit de drie geneste lussen springt. In dit geval zou het nogal rommelig zijn om een variabele in te stellen en achtereenvolgens uit elk van de drie geneste lussen te springen, maar één goto-instructie haalt je op een zeer beknopte manier uit alle drie.
Sommige mensen zeggen dat de goto-statement nooit gebruikt mag worden, onder geen enkele omstandigheid, maar dat is bekrompen denken. Als er een plek is waar een goto duidelijk een nettere controlestroom zal hebben dan een andere constructie, voel je dan vrij om het te gebruiken, zoals het ook in de rest van het programma op je monitor is. Laten we eens naar het voorbeeld kijken:
#include <stdio.h>
int hoofd()
{
int hond, kat, varken;
ga naar real_start;
ergens:
printf("Dit is een andere regel van de puinhoop.\n");
ga naar stop_it;
/* de volgende sectie is de enige sectie met een bruikbare goto */
echte_start:
voor(hond = 1 ; hond < 6 ; hond = hond + 1)
{
voor(kat = 1 ; kat < 6 ; kat = kat + 1)
{
voor(varken = 1 ; varken < 4 ; varken = varken + 1)
{
printf("Hond = %d Kat = %d Varken = %d\n", hond, kat, varken);
als ((hond + kat + varken) > 8 ) goto genoeg;
}
}
}
genoeg: printf("Dat zijn voorlopig genoeg dieren.\n");
/* dit is het einde van de sectie met een bruikbare goto-instructie */
printf("\nDit is de eerste regel van de code.\n");
ga daarheen;
waar:
printf("Dit is de derde regel van de code.\n");
ga ergens heen;
daar:
printf("Dit is de tweede regel van de code.\n");
ga naar waar;
stop_het:
printf("Dit is de laatste regel van deze puinhoop.\n");
retourneer 0;
}
Laten we de weergegeven resultaten bekijken
Hond = 1 Kat = 1 Varken = 1
Hond = 1 Kat = 1 Varken = 2
Hond = 1 Kat = 1 Varken = 3
Hond = 1 Kat = 2 Varken = 1
Hond = 1 Kat = 2 Varken = 2
Hond = 1 Kat = 2 Varken = 3
Hond = 1 Kat = 3 Varken = 1
Hond = 1 Kat = 3 Varken = 2
Hond = 1 Kat = 3 Varken = 3
Hond = 1 Kat = 4 Varken = 1
Hond = 1 Kat = 4 Varken = 2
Hond = 1 Kat = 4 Varken = 3
Hond = 1 Kat = 5 Varken = 1
Hond = 1 Kat = 5 Varken = 2
Hond = 1 Kat = 5 Varken = 3
Dat zijn voorlopig wel even genoeg dieren.
Dit is de eerste regel van de code.
Dit is de tweede regel van de code.
Dit is de derde regel van de code.
Dit is nog een regel uit de chaos.
Dit is de laatste regel van deze puinhoop.
Wijzers
Soms willen we weten waar een variabele zich in het geheugen bevindt. Een pointer bevat het adres van een variabele met een specifieke waarde. Bij het declareren van een pointer wordt er direct voor de pointernaam een asterisk geplaatst.
Het adres van de geheugenlocatie waar de variabele is opgeslagen, kunt u vinden door een ampersand vóór de variabelenaam te plaatsen.
int num; /* Normale gehele variabele */
int *numPtr; /* Wijzer naar een gehele variabele */
In het volgende voorbeeld worden de variabelewaarde en het adres in het geheugen van die variabele afgedrukt.
printf("De waarde %d wordt opgeslagen op adres %X\n", num, &num);
Om het adres van de variabele num aan de aanwijzer numPtr toe te wijzen, wijst u het adres van de variabele num toe zoals in het volgende voorbeeld:
numPtr = #
Om erachter te komen wat er is opgeslagen op het adres waarnaar numPtr verwijst, moet de variabele worden gederefereerd. Dereferencen gebeurt met de asterisk waarmee de pointer is gedeclareerd.
printf("De waarde %d wordt opgeslagen op adres %X\n", *numPtr, numPtr);
Alle variabelen in een programma bevinden zich in het geheugen. De onderstaande statements vragen de compiler om 4 bytes geheugen op een 32-bits computer te reserveren voor de floating-point variabele x, en vervolgens de waarde 6.5 erin te zetten.
zwevende x;
x = 6,5;
Omdat de adreslocatie in het geheugen van een variabele wordt verkregen door de operator & voor de naam te plaatsen, is &x het adres van x. Met C kunnen we nog een stap verder gaan en een variabele definiëren, een pointer genaamd, die het adres van andere variabelen bevat. We kunnen eerder zeggen dat de pointer naar een andere variabele wijst. Bijvoorbeeld:
zwevende x;
zwevend* px;
x = 6,5;
px = &x;
definieert px als een pointer naar objecten van het type float, en stelt het gelijk aan het adres van x. Dus, *px verwijst naar de waarde van x:

Laten we de volgende beweringen eens onderzoeken:
int var_x;
int* ptrX;
waarbij_x = 6;
ptrX = &var_x;
*ptrX = 12;
printf("waarde van x : %d", var_x);
De eerste regel zorgt ervoor dat de compiler een ruimte in het geheugen reserveert voor een integer. De tweede regel vertelt de compiler om ruimte te reserveren om een pointer op te slaan.
Een pointer is een opslaglocatie voor een adres. De derde regel zou u de scanf statements moeten herinneren. De adres "&" operator vertelt de compiler om naar de plek te gaan waar var_x is opgeslagen, en vervolgens het adres van de opslaglocatie aan ptrX te geven.
De asterisk * voor een variabele vertelt de compiler om de pointer te derefereren en naar het geheugen te gaan. Vervolgens kunt u toewijzingen maken aan variabelen die op die locatie zijn opgeslagen. U kunt een variabele refereren en toegang krijgen tot de data via een pointer. Laten we een voorbeeld van pointers bekijken:
/* illustratie van het gebruik van de aanwijzer */
#include <stdio.h>
int hoofd()
{
int-index, *pt1, *pt2;
index = 39; /* elke numerieke waarde */
pt1 = &index; /* het adres van de index */
pt2 = pt1;
printf("De waarde is %d %d %d\n", index, *pt1, *pt2);
*pt1 = 13; /* dit verandert de waarde van index */
printf("De waarde is %d %d %d\n", index, *pt1, *pt2);
retourneer 0;
}
De uitvoer van het programma wordt als volgt weergegeven:
De waarde is 39 39 39
De waarde is 13 13 13
Laten we eens naar een ander voorbeeld kijken om het gebruik van pointers beter te begrijpen:
#include <stdio.h>
#include <tekenreeks.h>
int hoofd()
{
char strg[40], *daar, een, twee;
int *pt, lijst[100], index;
strcpy(strg, "Dit is een tekenreeks.");
/* de functie strcpy() is om één string naar een andere te kopiëren. we zullen later in de String-sectie meer lezen over de strcpy()-functie */
one = strg[0]; /* een en twee zijn identiek */
twee = *ctrl;
printf("De eerste uitvoer is %c %c\n", one, two);
one = strg[8]; /* one en two zijn identiek */
twee = *(ctrl+8);
printf("De tweede uitvoer is %c %c\n", one, two);
daar = ctrl+10; /* strg+10 is identiek aan &strg[10] */
printf("De derde uitvoer is %c\n", strg[10]);
printf("De vierde uitvoer is %c\n", *there);
voor (index = 0 ; index < 100 ; index++)
lijst[index] = index + 100;
pt = lijst + 27;
printf("De vijfde uitvoer is %d\n", list[27]);
printf("De zesde uitvoer is %d\n", *pt);
retourneer 0;
}
De uitvoer van het programma ziet er als volgt uit:
De eerste uitgang is TT
De tweede uitgang is aa
De derde uitgang is c
De vierde uitgang is c
De vijfde uitgang is 127
De zesde uitgang is 127
Arrays
Een array is een verzameling variabelen van hetzelfde type. Individuele array-elementen worden geïdentificeerd door een integer-index. In C begint de index bij nul en wordt altijd tussen vierkante haken geschreven.
We zijn al eendimensionale arrays tegengekomen die als volgt zijn gedeclareerd
int resultaten[20];
Arrays kunnen meer dimensies hebben, in welk geval ze kunnen worden gedeclareerd als
int resultaten_2d[20][5];
int resultaten_3d[20][5][3];
Elke index heeft zijn eigen set vierkante haken. Een array wordt gedeclareerd in de hoofdfunctie, bevat meestal details van dimensies. Het is mogelijk om een ander type te gebruiken, een pointer genaamd, in plaats van een array. Dit betekent dat dimensies niet direct worden vastgelegd, maar dat er ruimte kan worden toegewezen indien nodig. Dit is een geavanceerde techniek die alleen vereist is in bepaalde gespecialiseerde programma's.
Hieronder ziet u bijvoorbeeld een eenvoudige functie waarmee u alle gehele getallen in een eendimensionale matrix bij elkaar optelt.
int add_array(int array[], int grootte)
{
int ik;
int totaal = 0;
voor(i = 0; i < grootte; i++)
totaal += array[i];
return(totaal);
}
Het volgende programma zal een string maken, wat data erin benaderen, het afdrukken. Het opnieuw benaderen met behulp van pointers, en dan de string afdrukken. Het zou “Hi!” en “012345678” op verschillende regels moeten afdrukken. Laten we de codering van het programma bekijken:
#include <stdio.h>
#definieer STR_LENGTH 10
leeg hoofd()
{
teken Str[STR_LENGTH];
teken* pStr;
int ik;
Str[0] = 'H';
Str[1] = 'i';
Str[2] = '!';
Str[3] = '\0'; // speciaal eindtekenreeksteken NULL
printf("De string in Str is: %s\n", Str);
pStr = &Str[0];
voor (i = 0; i < STR_LENGTH; i++)
{
*pStr = '0'+i;
pStr++;
}
Str[STR_LENGTH-1] = '\0';
printf("De string in Str is: %s\n", Str);
}
[] (vierkante haakjes) worden gebruikt om de array te declareren. De regel van het programma char Str[STR_LENGTH]; declareert een array van tien tekens. Dit zijn tien individuele tekens, die allemaal in het geheugen op dezelfde plaats worden samengevoegd. Ze zijn allemaal toegankelijk via onze variabelenaam Str, samen met een [n] waarbij n het elementnummer is.
Houd er bij het praten over arrays altijd rekening mee dat wanneer C een array van tien declareert, de elementen die u kunt benaderen genummerd zijn van 0 tot 9. Het benaderen van het eerste element komt overeen met het benaderen van het 0e element. Dus in het geval van arrays telt u altijd van 0 tot de grootte van de array - 1.
Let er vervolgens op dat we de letters "Hi!" in de array hebben gezet, maar dat we er dan een '\0' in hebben gezet. U vraagt zich waarschijnlijk af wat dit is. "\0" staat voor NULL en vertegenwoordigt het einde van de string. Alle tekenreeksen moeten eindigen met dit speciale teken '\0'. Als dat niet het geval is en iemand roept printf aan op de string, dan zou printf beginnen op de geheugenlocatie van uw string en doorgaan met printen en vertellen dat het '\0' tegenkomt en dat u dus met een hoop rommel aan het einde van uw string zult eindigen. Zorg er dus voor dat u uw strings op de juiste manier afsluit.
Karakterreeksen
Een tekenreeksconstante, zoals
"Ik ben een snaar"
is een array van tekens. Het wordt intern in C gerepresenteerd door de ASCII-tekens in de string, d.w.z. "I", blanco, "a", "m", ... of de bovenstaande string, en beëindigd door het speciale null-teken "\0" zodat programma's het einde van de string kunnen vinden.
Stringconstanten worden vaak gebruikt om de uitvoer van code begrijpelijk te maken met behulp van printf:
printf("Hallo, wereld\n");
printf("De waarde van a is: %f\n", a);
Stringconstanten kunnen worden gekoppeld aan variabelen. C biedt de karaktertypevariabele, die één teken (1 byte) per keer kan bevatten. Een tekenreeks wordt opgeslagen in een array van karaktertype, één ASCII-teken per locatie.
Vergeet niet dat, omdat strings gewoonlijk worden beëindigd met het null-teken “\0”, we een extra opslaglocatie in de array nodig hebben.
C biedt geen operator die hele strings in één keer manipuleert. Strings worden gemanipuleerd via pointers of via speciale routines die beschikbaar zijn in de standaard stringbibliotheek string.h.
Het gebruik van karakterpointers is relatief eenvoudig, aangezien de naam van een array slechts een pointer is naar het eerste element. Bekijk het volgende programma:
#include<stdio.h>
leeg hoofd()
{
teken tekst_1[100], tekst_2[100], tekst_3[100];
teken *ta, *tb;
int ik;
/* bericht instellen als een array */
/* van tekens; initialiseren */
/* naar de constante string "..." */
/* laat de compiler beslissen over */
/* de grootte ervan met behulp van [] */
char message[] = "Hallo, ik ben een string; wat zijn
Jij?";
printf("Origineel bericht: %s\n", bericht);
/* kopieer het bericht naar text_1 */
ik=0;
terwijl ( (tekst_1[i] = bericht[i]) != '\0' )
ik++;
printf("Tekst_1: %s\n", tekst_1);
/* gebruik expliciete pointer-rekenkunde */
jouw=bericht;
tb=tekst_2;
terwijl ( ( *tb++ = *ta++ ) != '\0' )
;
printf("Tekst_2: %s\n", tekst_2);
}
De uitvoer van het programma is als volgt:
Oorspronkelijk bericht: Hallo, ik ben een snaar. Wat ben jij?
Text_1: Hallo, ik ben een string. Wat ben jij?
Tekst_2: Hallo, ik ben een string. Wat ben jij?
De standaard “string”-bibliotheek bevat veel nuttige functies om strings te manipuleren, die we later in de stringsectie zullen leren.
Toegang tot de elementen
Om toegang te krijgen tot een afzonderlijk element in de array, volgt het indexnummer de variabelenaam in vierkante haken. De variabele kan dan worden behandeld als elke andere variabele in C. Het volgende voorbeeld wijst een waarde toe aan het eerste element in de array.
x[0] = 16;
In het volgende voorbeeld wordt de waarde van het derde element in een array afgedrukt.
afdrukkenf("%d\n", x[2]);
In het volgende voorbeeld wordt de functie scanf gebruikt om een waarde van het toetsenbord te lezen in het laatste element van een matrix met tien elementen.
scanf("%d", &x[9]);
Array-elementen initialiseren
Arrays kunnen net als andere variabelen worden geïnitialiseerd door toewijzing. Omdat een array meer dan één waarde bevat, worden de afzonderlijke waarden tussen accolades geplaatst en gescheiden door komma's. Het volgende voorbeeld initialiseert een tiendimensionale array met de eerste tien waarden van de drievoudige tabel.
int x[10] = {3, 6, 9, 12, 15, 18, 21, 24, 27, 30};
Hierdoor hoeft u de waarden niet individueel toe te wijzen, zoals in het volgende voorbeeld.
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;
Door een array heen lussen
Omdat de array sequentieel is geïndexeerd, kunnen we de for-lus gebruiken om alle waarden van een array weer te geven. Het volgende voorbeeld geeft alle waarden van een array weer:
#include <stdio.h>
int hoofd()
{
int x[10];
int-teller;
/* Randomiseer de willekeurige nummergenerator */
srand((unsigned)tijd(NULL));
/* Wijs willekeurige waarden toe aan de variabele */
voor (teller=0; teller<10; teller++)
x[teller] = rand();
/* De inhoud van de array weergeven */
voor (teller=0; teller<10; teller++)
printf("element %d heeft de waarde %d\n", counter, x[counter]);
retourneer 0;
}
Hoewel de uitvoer elke keer andere waarden zal weergeven, zal het resultaat er ongeveer zo uitzien:
element 0 heeft de waarde 17132
element 1 heeft de waarde 24904
element 2 heeft de waarde 13466
element 3 heeft de waarde 3147
element 4 heeft de waarde 22006
element 5 heeft de waarde 10397
element 6 heeft de waarde 28114
element 7 heeft de waarde 19817
element 8 heeft de waarde 27430
element 9 heeft de waarde 22136
Multidimensionale arrays
Een array kan meer dan één dimensie hebben. Door de array meer dan één dimensie te laten hebben, ontstaat er meer flexibiliteit. Spreadsheets worden bijvoorbeeld gebouwd op een tweedimensionale array; een array voor de rijen en een array voor de kolommen.
In het volgende voorbeeld wordt een tweedimensionale matrix met twee rijen gebruikt, die elk vijf kolommen bevatten:
#include <stdio.h>
int hoofd()
{
/* Declareer een 2 x 5 multidimensionale array */
int x[2][5] = { {1, 2, 3, 4, 5},
{2, 4, 6, 8, 10} };
int rij, kolom;
/* Toon de rijen */
voor (rij=0; rij<2; rij++)
{
/* Kolommen weergeven */
voor (kolom=0; kolom<5; kolom++)
printf("%d\t", x[rij][kolom]);
putchar('\n');
}
retourneer 0;
}
De uitvoer van dit programma wordt als volgt weergegeven:
1 2 3 4 5
2 4 6 8 10
Snaren
Een string is een groep tekens, meestal letters van het alfabet. Om uw afdrukweergave zo op te maken dat deze er mooi uitziet, betekenisvolle namen en titels heeft en esthetisch aantrekkelijk is voor u en de mensen die de uitvoer van uw programma gebruiken.
Eigenlijk heb je strings al gebruikt in de voorbeelden van de vorige topics. Maar het is niet de complete introductie van strings. Er zijn veel mogelijke gevallen in de programmering, waarbij het gebruik van geformatteerde strings de programmeur helpt om te veel complicaties in het programma en natuurlijk te veel bugs te vermijden.
Een volledige definitie van een string is een reeks gegevens van het tekentype, afgesloten met een nulteken ('\0').
Wanneer C een reeks gegevens op een bepaalde manier gaat gebruiken, bijvoorbeeld om deze te vergelijken met een andere reeks, uit te voeren, te kopiëren naar een andere reeks of wat dan ook, worden de functies zo ingesteld dat ze doen waarvoor ze zijn aangeroepen, totdat er een nul wordt gedetecteerd.
Er is geen basisgegevenstype voor een string in C. Strings in C worden daarentegen geïmplementeerd als een array van tekens. Om bijvoorbeeld een naam op te slaan, kunt u een tekenarray declareren die groot genoeg is om de naam op te slaan, en vervolgens de juiste bibliotheekfuncties gebruiken om de naam te manipuleren.
Het volgende voorbeeld toont de door de gebruiker ingevoerde tekenreeks op het scherm:
#include <stdio.h>
int hoofd()
{
char name[80]; /* Maak een tekenreeks
genaamd naam */
printf("Vul uw naam in: ");
krijgt(naam);
printf("De naam die u hebt ingevoerd was %s\n", name);
retourneer 0;
}
De uitvoering van het programma zal zijn:
Voer uw naam in: Tarun Tyagi
De naam die u hebt ingevoerd was Tarun Tyagi
Enkele veelvoorkomende tekenreeksfuncties
De standaard string.h-bibliotheek bevat veel nuttige functies om strings te manipuleren. Enkele van de meest nuttige functies zijn hier als voorbeeld gegeven.
De strlen-functie
De strlen-functie wordt gebruikt om de lengte van een string te bepalen. Laten we het gebruik van strlen leren met een voorbeeld:
#include <stdio.h>
#include <tekenreeks.h>
int hoofd()
{
karakternaam[80];
int lengte;
printf("Vul uw naam in: ");
krijgt(naam);
lengte = strlen(naam);
printf("Uw naam heeft %d tekens\n", length);
retourneer 0;
}
De uitvoering van het programma zal als volgt zijn:
Voer uw naam in: Tarun Subhash Tyagi
Uw naam heeft 19 tekens
Voer uw naam in: Preeti Tarun
Uw naam heeft 12 tekens
De strcpy-functie
De strcpy-functie wordt gebruikt om een string naar een andere te kopiëren. Laten we het gebruik van deze functie leren met een voorbeeld:
#include <stdio.h>
#include <tekenreeks.h>
int hoofd()
{
char eerst[80];
char seconde[80];
printf("Voer de eerste string in: ");
krijgt(eerste);
printf("Voer tweede string in: ");
krijgt(tweede);
printf("eerste: %s, en tweede: %s Voor strcpy()\n "
, eerste, tweede);
strcpy(tweede, eerste);
printf("eerste: %s, en tweede: %s Na strcpy()\n",
eerste, tweede);
retourneer 0;
}
en de uitvoer van het programma zal er als volgt uitzien:
Voer de eerste string in: Tarun
Voer de tweede string in: Tyagi
eerst: Tarun , en als tweede: Tyagi Voor strcpy()
eerste: Tarun , en tweede: Tarun Na strcpy()
De strcmp-functie
De strcmp-functie wordt gebruikt om twee strings met elkaar te vergelijken. De variabelenaam van een array wijst naar het basisadres van die array. Als we dus proberen om twee strings te vergelijken met behulp van het volgende, vergelijken we twee adressen, die uiteraard nooit hetzelfde zijn, aangezien het niet mogelijk is om twee waarden op dezelfde locatie op te slaan.
if (first == second) /* Het is nooit mogelijk om strings te vergelijken */
In het volgende voorbeeld wordt de strcmp-functie gebruikt om twee strings te vergelijken:
#include <tekenreeks.h>
int hoofd()
{
char eerste[80], tweede[80];
int t;
voor(t=1;t<=2;t++)
{
printf("\nVoer een string in: ");
krijgt(eerste);
printf("Voer een andere string in: ");
krijgt(tweede);
als (strcmp(eerste, tweede) == 0)
puts("De twee strings zijn gelijk");
anders
puts("De twee strings zijn niet gelijk");
}
retourneer 0;
}
De uitvoering van het programma zal als volgt zijn:
Voer een tekenreeks in: Tarun
Voer een andere tekenreeks in: tarun
De twee snaren zijn niet gelijk
Voer een string in: Tarun
Voer een andere string in: Tarun
De twee strings zijn gelijk
De strcat-functie
De strcat-functie wordt gebruikt om een string aan een andere te koppelen. Laten we eens kijken hoe? Met behulp van een voorbeeld:
#include <tekenreeks.h>
int hoofd()
{
char eerste[80], tweede[80];
printf("Voer een string in: ");
krijgt(eerste);
printf("Voer een andere string in: ");
krijgt(tweede);
strcat(eerste, tweede);
printf("De twee strings samengevoegd: %s\n",
Eerst);
retourneer 0;
}
De uitvoering van het programma zal als volgt zijn:
Voer een tekenreeks in: Data
Voer een andere tekenreeks in: Recovery
De twee tekenreeksen samengevoegd: DataRecovery
De strtok-functie
De strtok-functie wordt gebruikt om het volgende token in een string te vinden. Het token wordt gespecificeerd door een lijst met mogelijke scheidingstekens.
Het volgende voorbeeld leest een tekstregel uit een bestand en bepaalt een woord met behulp van de scheidingstekens, spatie, tab en nieuwe regel. Elk woord wordt vervolgens op een aparte regel weergegeven:
#include <stdio.h>
#include <tekenreeks.h>
int hoofd()
{
BESTAND *in;
char-regel[80];
char *delimiters = " \t\n";
teken *token;
als ((in = fopen("C:\\text.txt", "r")) == NULL)
{
puts("Kan het invoerbestand niet openen");
retourneer 0;
}
/* Lees elke regel één voor één */
terwijl(!feof(in))
{
/* Haal één regel op */
fgets(lijn, 80, in);
als (!feof(in))
{
/* Verdeel de regel in woorden */
token = strtok(regel, scheidingstekens);
terwijl (token != NULL)
{
zet(token);
/* Haal het volgende woord op */
token = strtok(NULL, scheidingstekens);
}
}
}
fclose(in);
retourneer 0;
}
Het bovenstaande programma, in = fopen("C:\\text.txt", "r"), opent een bestaand bestand C:\\text.txt. Als het niet bestaat in het opgegeven pad of om welke reden dan ook, het bestand kon niet worden geopend, wordt er een foutmelding op het scherm weergegeven.
Overweeg het volgende voorbeeld, dat enkele van deze functies gebruikt:
#include <stdio.h>
#include <tekenreeks.h>
leeg hoofd()
{
char regel[100], *sub_tekst;
/* initialiseer string */
strcpy(regel,"Hallo, ik ben een string;");
printf("Regel: %s\n", regel);
/* toevoegen aan einde van string */
strcat(regel," wat ben je?");
printf("Regel: %s\n", regel);
/* lengte van string vinden */
/* strlen brengt terug */
/* lengte als type size_t */
printf("Lengte van de regel: %d\n", (int)strlen(regel));
/* vind voorkomen van substrings */
als ( (sub_tekst = strchr ( regel, 'W' ) )!= NULL )
printf("String beginnend met \"W\" ->%s\n",
sub_tekst);
als ( ( sub_tekst = strchr ( regel, 'w' ) )!= NULL )
printf("String beginnend met \"w\" ->%s\n",
sub_tekst);
als ( ( sub_tekst = strchr ( sub_tekst, 'u' ) )!= NULL )
printf("String beginnend met \"w\" ->%s\n",
sub_tekst);
}
De uitvoer van het programma wordt als volgt weergegeven:
Regel: Hallo, ik ben een snaar;
Regel: Hallo, ik ben een touwtje. Wat ben jij?
Lengte van de lijn: 35
String beginnend met "w" -> wat ben jij?
String beginnend met "w" ->u?
Functies
De beste manier om een groot programma te ontwikkelen en onderhouden is om het te construeren uit kleinere delen die elk gemakkelijker te beheren zijn (een techniek die soms Divide and Conquer wordt genoemd). Functies stellen de programmeur in staat om het programma te modulariseren.
Functies maken het mogelijk om ingewikkelde programma's op te delen in kleine blokken, die elk gemakkelijker te schrijven, lezen en onderhouden zijn. We zijn de functie main al tegengekomen en hebben printf uit de standaardbibliotheek gebruikt. We kunnen natuurlijk onze eigen functies en headerbestanden maken. Een functie heeft de volgende indeling:
return-type functie-naam (argumentenlijst indien nodig)
{
lokale-verklaringen;
verklaringen;
retour retourwaarde;
}
Als return-type wordt weggelaten, gebruikt C standaard int. De return-value moet van het gedeclareerde type zijn. Alle variabelen die binnen functies worden gedeclareerd, worden lokale variabelen genoemd, in die zin dat ze alleen bekend zijn in de functie waarvoor ze zijn gedefinieerd.
Sommige functies hebben een parameterlijst die een communicatiemethode biedt tussen de functie en de module die de functie aanriep. De parameters zijn ook lokale variabelen, in die zin dat ze niet beschikbaar zijn buiten de functie. De tot nu toe behandelde programma's hebben allemaal main, wat een functie is.
Een functie kan eenvoudigweg een taak uitvoeren zonder een waarde te retourneren. In dat geval heeft de functie de volgende indeling:
void functienaam (argumentenlijst indien nodig)
{
lokale-declaraties;
verklaringen;
}
Argumenten worden altijd doorgegeven op waarde in C-functieaanroepen. Dit betekent dat lokale kopieën van de waarden van de argumenten worden doorgegeven aan de routines. Elke wijziging die intern in de functie aan de argumenten wordt aangebracht, wordt alleen doorgevoerd in de lokale kopieën van de argumenten.
Om een argument in de argumentenlijst te wijzigen of te definiëren, moet dit argument als een adres worden doorgegeven. U gebruikt reguliere variabelen als de functie de waarden van die argumenten niet wijzigt. U MOET pointers gebruiken als de functie de waarden van die argumenten wijzigt.
Laten we leren aan de hand van voorbeelden:
#include <stdio.h>
ongeldige uitwisseling (int *a, int *b)
{
int-tijd;
temperatuur = *a;
*a = *b;
*b = temperatuur;
printf("Van functie exchange: ");
printf("a = %d, b = %d\n", *a, *b);
}
leeg hoofd()
{
int a, b;
een = 5;
b = 7;
printf("Van hoofd: a = %d, b = %d\n", a, b);
uitwisselen(&a, &b);
printf("Terug in hoofd: ");
printf("a = %d, b = %d\n", a, b);
}
De uitvoer van dit programma wordt als volgt weergegeven:
Van hoofd: a = 5, b = 7
Uit functie-uitwisseling: a = 7, b = 5
Terug naar hoofd: a = 7, b = 5
Laten we een ander voorbeeld bekijken. Het volgende voorbeeld gebruikt een functie genaamd square die het kwadraat van de getallen tussen 1 en 10 schrijft.
#include <stdio.h>
int square(int x); /* Functie prototype */
int hoofd()
{
int-teller;
voor (teller=1; teller<=10; teller++)
printf("Kwadraat van %d is %d\n", teller, square(teller));
retourneer 0;
}
/* Definieer de functie 'square' */
int kwadraat(int x)
{
retourneer x * x;
}
De uitvoer van dit programma wordt als volgt weergegeven:
Kwadraat van 1 is 1
Kwadraat van 2 is 4
Kwadraat van 3 is 9
Vierkant van 4 is 16
Kwadraat van 5 is 25
Kwadraat van 6 is 36
Kwadraat van 7 is 49
Kwadraat van 8 is 64
Kwadraat van 9 is 81
Het kwadraat van 10 is 100
De functie prototype square declareert een functie die een integer parameter neemt en een integer retourneert. Wanneer de compiler de functieaanroep naar square in het hoofdprogramma bereikt, kan deze de functieaanroep controleren tegen de definitie van de functie.
Wanneer het programma de regel bereikt die de functie square aanroept, springt het programma naar de functie en voert die functie uit voordat het zijn pad door het hoofdprogramma hervat. Programma's die geen returntype hebben, moeten worden gedeclareerd met void. Parameters voor de functie kunnen dus Pass By Value of Pass By Reference zijn.
Een recursieve functie is een functie die zichzelf aanroept. En dit proces heet recursie.
Pass-by-waardefuncties
De parameters van de vierkante functie in het vorige voorbeeld worden doorgegeven op waarde. Dit betekent dat er alleen een kopie van de variabele is doorgegeven aan de functie. Wijzigingen in de waarde worden niet teruggekaatst naar de aanroepende functie.
Het volgende voorbeeld gebruikt pass-by-value en verandert de waarde van de doorgegeven parameter, wat geen effect heeft op de aanroepende functie. De functie count_down is gedeclareerd als void omdat er geen return type is.
#include <stdio.h>
leeg aftellen(int x);
int hoofd()
{
int-teller;
voor (teller=1; teller<=10; teller++)
count_down(teller);
retourneer 0;
}
leeg aftellen(int x)
{
int-teller;
voor (teller = x; teller > 0; teller--)
{
afdrukkenf("%d ", x);
X--;
}
putchar('\n');
}
De uitvoer van het programma wordt als volgt weergegeven:
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
Laten we een ander C Pass By Value-voorbeeld bekijken om het beter te begrijpen. Het volgende voorbeeld converteert een getal tussen 1 en 30.000 dat door de gebruiker is getypt naar woorden.
#include <stdio.h>
void do_units(int num);
void do_tens(int num);
void do_teens(int num);
int hoofd()
{
int num, rest;
Doen
{
printf("Voer een getal in tussen 1 en 30.000: ");
scanf("%d", &num);
} terwijl (num < 1 || num > 30000);
rest = getal;
printf("%d in woorden = ", num);
do_tens(residu/1000);
als (num >= 1000)
printf("duizend ");
residu %= 1000;
do_units(residu/100);
als (residu >= 100)
{
printf("honderd ");
}
als (num > 100 en num%100 > 0)
printf("en ");
residu %=100;
do_tens(residu);
putchar('\n');
retourneer 0;
}
void do_units(int num)
{
schakelaar(num)
{
geval 1:
printf("één ");
pauze;
geval 2:
printf("twee ");
pauze;
geval 3:
printf("drie ");
pauze;
geval 4:
printf("vier ");
pauze;
geval 5:
printf("vijf ");
pauze;
geval 6:
printf("zes ");
pauze;
geval 7:
printf("zeven ");
pauze;
geval 8:
printf("acht ");
pauze;
geval 9:
printf("negen ");
}
}
leeg do_tens(int num)
{
schakelaar(num/10)
{
geval 1:
do_teens(num);
pauze;
geval 2:
printf("twintig ");
pauze;
geval 3:
printf("dertig ");
pauze;
geval 4:
printf("veertig ");
pauze;
geval 5:
printf("vijftig ");
pauze;
geval 6:
printf("zestig ");
pauze;
geval 7:
printf("zeventig ");
pauze;
geval 8:
printf("tachtig ");
pauze;
geval 9:
printf("negentig ");
}
als (num/10 != 1)
do_eenheden(num%10);
}
leeg do_teens(int num)
{
schakelaar(num)
{
geval 10:
printf("tien ");
pauze;
geval 11:
printf("elf ");
pauze;
geval 12:
printf("twaalf ");
pauze;
geval 13:
printf("dertien ");
pauze;
geval 14:
printf("veertien ");
pauze;
geval 15:
printf("vijftien ");
pauze;
geval 16:
printf("zestien ");
pauze;
geval 17:
printf("zeventien ");
pauze;
geval 18:
printf("achttien ");
pauze;
geval 19:
printf("negentien ");
}
}
en de uitvoer van het programma zal als volgt zijn:
Vul een getal in tussen 1 en 30.000: 12345
12345 in woorden = twaalfduizend driehonderdvijfenveertig
Oproep via referentie
Om een functie-aanroep-bij-referentie te maken, geeft u in plaats van de variabele zelf het adres van de variabele door. Het adres van de variabele kan worden genomen door de operator & te gebruiken. Het volgende roept een swap-functie aan die het adres van variabelen doorgeeft in plaats van de werkelijke waarden.
wisselen(&x, &y);
Dereferentie
Het probleem dat we nu hebben, is dat aan de functie swap het adres is doorgegeven in plaats van de variabele. We moeten de variabelen dus derefereren, zodat we naar de werkelijke waarden kijken in plaats van naar de adressen van de variabelen, zodat we ze kunnen swappen.
Dereferencen wordt in C bereikt door de pointer (*) notatie te gebruiken. Simpel gezegd betekent dit dat er een * voor elke variabele wordt geplaatst voordat deze wordt gebruikt, zodat deze verwijst naar de waarde van de variabele in plaats van naar het adres. Het volgende programma illustreert passing-by-reference om twee waarden te verwisselen.
#include <stdio.h>
void swap(int *x, int *y);
int hoofd()
{
int x=6, y=10;
printf("Voordat de functie swap, x = %d en y =
%d\n\n", x, y);
wisselen(&x, &y);
printf("Na de functie swap, x = %d en y =
%d\n\n", x, y);
retourneer 0;
}
void swap(int *x, int *y)
{
int temperatuur = *x;
*x = *y;
*j = temperatuur;
}
Laten we eens kijken naar de uitvoer van het programma:
Vóór de functiewissel was x = 6 en y = 10
Na de functiewissel is x = 10 en y = 6
Functies kunnen recursief zijn, dat wil zeggen dat een functie zichzelf kan aanroepen. Elke aanroep van zichzelf vereist dat de huidige status van de functie op de stack wordt gepusht. Het is belangrijk om dit feit te onthouden, omdat het gemakkelijk is om een stack overflow te creëren, d.w.z. dat de stack geen ruimte meer heeft om meer data te plaatsen.
Het volgende voorbeeld berekent de faculteit van een getal met behulp van recursie. Een faculteit is een getal vermenigvuldigd met elk ander geheel getal onder zichzelf, tot 1. De faculteit van het getal 6 is bijvoorbeeld:
Faculteit 6 = 6 * 5 * 4 * 3 * 2 * 1
De faculteit van 6 is dus 720. Uit het bovenstaande voorbeeld blijkt dat faculteit 6 = 6 * faculteit 5. Op dezelfde manier is faculteit 5 = 5 * faculteit 4, enzovoort.
Hieronder volgt de algemene regel voor het berekenen van faculteitsgetallen.
faculteit(n) = n * faculteit(n-1)
De bovenstaande regel eindigt wanneer n = 1, aangezien de faculteit van 1 1 is. Laten we proberen deze beter te begrijpen met behulp van een voorbeeld:
#include <stdio.h>
lange int faculteit(int num);
int hoofd()
{
int getal;
lange int f;
printf("Voer een getal in: ");
scanf("%d", &num);
f = faculteit(num);
printf("faculteit van %d is %ld\n", num, f);
retourneer 0;
}
lange int faculteit(int num)
{
als (num == 1)
retour 1;
anders
return num * faculteit(num-1);
}
Laten we eens kijken naar de uitvoer van dit programma:
Voer een getal in: 7
faculteit van 7 is 5040
Geheugentoewijzing in C
De C-compiler heeft een geheugentoewijzingsbibliotheek, gedefinieerd in malloc.h. Geheugen wordt gereserveerd met behulp van de malloc-functie en retourneert een pointer naar het adres. Het neemt één parameter, de grootte van het vereiste geheugen in bytes.
In het volgende voorbeeld wordt ruimte toegewezen aan de tekenreeks "hello world".
ptr = (char *)malloc(strlen("Hallo wereld") + 1);
De extra byte is nodig om rekening te houden met het tekenreeksbeëindigingsteken, '\0'. De (char *) wordt een cast genoemd en dwingt het retourtype om char * te zijn.
Omdat gegevenstypen verschillende groottes hebben en malloc de ruimte in bytes retourneert, is het omwille van de portabiliteit een goede gewoonte om de operator sizeof te gebruiken bij het opgeven van een toe te wijzen grootte.
In het volgende voorbeeld wordt een tekenreeks in de tekenreeksbuffer gelezen, wordt de exacte hoeveelheid geheugen toegewezen en wordt deze gekopieerd naar een variabele met de naam 'ptr'.
#include <tekenreeks.h>
#include <malloc.h>
int hoofd()
{
char *ptr, buffer[80];
printf("Voer een string in: ");
krijgt(buffer);
ptr = (char *)malloc((strlen(buffer) + 1) *
groottevan(char));
strcpy(ptr, buffer);
printf("U hebt ingevoerd: %s\n", ptr);
retourneer 0;
}
De uitvoer van het programma is als volgt:
Voer een tekenreeks in: India is de beste
U hebt ingevoerd: India is de beste
Herverdelen van geheugen
Het is mogelijk dat u tijdens het programmeren meerdere keren geheugen wilt heralloceren. Dit doet u met de realloc-functie. De realloc-functie neemt twee parameters, het basisadres van het geheugen dat u wilt herschalen en de hoeveelheid ruimte die u wilt reserveren, en retourneert een pointer naar het basisadres.
Stel dat we ruimte hebben gereserveerd voor een aanwijzer met de naam msg en we willen de ruimte opnieuw toewijzen aan de hoeveelheid ruimte die deze al inneemt, plus de lengte van een andere tekenreeks. Dan kunnen we het volgende gebruiken.
msg = (char *)realloc(msg, (strlen(msg) + strlen(buffer) + 1)*groottevan(char));
Het volgende programma illustreert het gebruik van malloc, realloc en free. De gebruiker voert een reeks strings in die aan elkaar worden gekoppeld. Het programma stopt met het lezen van strings wanneer een lege string wordt ingevoerd.
#include <tekenreeks.h>
#include <malloc.h>
int hoofd()
{
char-buffer[80], *msg;
int eersteTijd=0;
Doen
{
printf("\nVoer een zin in: ");
krijgt(buffer);
als (!eersteTijd)
{
bericht = (char *)malloc((strlen(buffer) + 1) *
groottevan(char));
strcpy(bericht, buffer);
eersteTijd = 1;
}
anders
{
bericht = (char *)realloc(bericht, (strlen(bericht) +
strlen(buffer) + 1) * grootte van(char));
strcat(bericht, buffer);
}
zet(msg);
} terwijl(strcmp(buffer, ""));
gratis(bericht);
retourneer 0;
}
De uitvoer van het programma is als volgt:
Voer een zin in: Er was eens
Er was eens
Voer een zin in: er was eens een koning Er
was eenser was eens een koning
Voer een zin in: de koning was Er was
eenser was eens een koningde koning was
Voer een zin in:
Er was eens een koning, de koning was
Het vrijgeven van geheugen
Wanneer u klaar bent met toegewezen geheugen, mag u nooit vergeten het geheugen vrij te geven, omdat dit resources vrijmaakt en de snelheid verbetert. Om toegewezen geheugen vrij te geven, gebruikt u de functie vrijgeven.
vrij(ptr);
Structuren
Naast de basisgegevenstypen heeft C een structuurmechanisme waarmee u gegevensitems die aan elkaar gerelateerd zijn, kunt groeperen onder een algemene naam. Dit wordt doorgaans een User Defined Type genoemd.
Het trefwoord struct start de structuurdefinitie en een tag geeft de unieke naam aan de structuur. De gegevenstypen en variabelenamen die aan de structuur worden toegevoegd, zijn leden van de structuur. Het resultaat is een structuursjabloon dat kan worden gebruikt als typespecificatie. Het volgende is een structuur met een tag van maand.
structuur maand
{
karakternaam[10];
teken afgekort[4];
int dagen;
};
Een structuurtype wordt gewoonlijk aan het begin van een bestand gedefinieerd met behulp van een typedef-instructie. typedef definieert en benoemt een nieuw type, zodat het in het hele programma kan worden gebruikt. typedef komt gewoonlijk direct na de #define- en #include-instructies in een bestand voor.
Het typedef-trefwoord kan worden gebruikt om een woord te definiëren dat verwijst naar de structuur in plaats van het struct-trefwoord te specificeren met de naam van de structuur. Het is gebruikelijk om de typedef in hoofdletters te noemen. Hier zijn de voorbeelden van structuurdefinities.
typedef-structuur {
karakternaam[64];
char cursus[128];
int leeftijd;
int jaar;
} student;
Hiermee wordt een nieuw type student gedefinieerd. Variabelen van het type student kunnen als volgt worden gedeclareerd.
student st_rec;
Let op hoe vergelijkbaar dit is met het declareren van een int of float. De variabelenaam is st_rec, het heeft leden genaamd name, course, age en year. Op dezelfde manier,
typedef struct-element
{
char-gegevens;
struct-element *volgende;
} STAPELELEMENT;
Een variabele van het door de gebruiker gedefinieerde type struct element kan nu als volgt worden gedeclareerd.
STAPELKELEMENT *stapel;
Beschouw de volgende structuur:
structuur student
{
char *naam;
int-cijfer;
};
Een aanwijzer naar struct student kan als volgt worden gedefinieerd.
structuur student *hnc;
Bij het benaderen van een pointer naar een structuur wordt de member pointer operator, -> gebruikt in plaats van de dot operator. Om een cijfer toe te voegen aan een structuur,
s.cijfer = 50;
U kunt de structuur als volgt een cijfer geven.
s->cijfer = 50;
Net als bij de basisgegevenstypen moet u, als u wilt dat de wijzigingen die in een functie zijn aangebracht aan doorgegeven parameters, persistent zijn, by reference doorgeven (het adres doorgeven). Het mechanisme is precies hetzelfde als bij de basisgegevenstypen. Geef het adres door en verwijs naar de variabele met behulp van pointernotatie.
Nadat u de structuur hebt gedefinieerd, kunt u een instantie ervan declareren en waarden toewijzen aan de leden met behulp van de puntnotatie. Het volgende voorbeeld illustreert het gebruik van de maandstructuur.
#include <stdio.h>
#include <tekenreeks.h>
structuur maand
{
karakternaam[10];
char afkorting[4];
int dagen;
};
int hoofd()
{
structuur maand m;
strcpy(m.name, "januari");
strcpy(m.afkorting, "Jan");
m.dagen = 31;
printf("%s wordt afgekort als %s en heeft %d dagen\n", m.name, m.abbreviation, m.days);
retourneer 0;
}
De uitvoer van het programma is als volgt:
Januari wordt afgekort als Jan en heeft 31 dagen
Alle ANSI C-compilers staan toe dat u één structuur aan een andere toewijst, door een member-wise copy uit te voeren. Als we maandstructuren hadden die m1 en m2 heten, dan zouden we de waarden van m1 aan m2 kunnen toewijzen met het volgende:
- Structuur met pointerleden.
- Structuur initialiseert.
- Een structuur doorgeven aan een functie.
- Aanwijzers en structuren.
Structuren met pointerleden in C
Strings in een array met vaste grootte houden is inefficiënt geheugengebruik. Een efficiëntere aanpak zou zijn om pointers te gebruiken. Pointers worden in structuren op exact dezelfde manier gebruikt als in normale pointerdefinities. Laten we een voorbeeld bekijken:
#include <tekenreeks.h>
#include <malloc.h>
structuur maand
{
char *naam;
char *afkorting;
int dagen;
};
int hoofd()
{
structuur maand m;
m.name = (char *)malloc((strlen("januari")+1) *
groottevan(char));
strcpy(m.name, "januari");
m.afkorting = (char *)malloc((strlen("Jan")+1) *
groottevan(char));
strcpy(m.afkorting, "Jan");
m.dagen = 31;
printf("%s wordt afgekort als %s en heeft %d dagen\n",
m.naam, m.afkorting, m.dagen);
retourneer 0;
}
De uitvoer van het programma is als volgt:
Januari wordt afgekort als Jan en heeft 31 dagen
Structuurinitialisatoren in C
Om een set beginwaarden voor de structuur te bieden, kunnen Initialisers worden toegevoegd aan de declaratie-instructie. Omdat maanden beginnen bij 1, maar arrays beginnen bij nul in C, is in het volgende voorbeeld een extra element op positie nul gebruikt, genaamd junk.
#include <stdio.h>
#include <tekenreeks.h>
structuur maand
{
char *naam;
char *afkorting;
int dagen;
} maand_details[] =
{
"Rots", "Rots", 0,
"Januari", "Jan", 31,
"Februari", "februari", 28,
"Maart", "Mar", 31,
"april", "april", 30,
"Mei", "Mei", 31,
"Juni", "Jun", 30,
"Juli", "Jul", 31,
"Augustus", "Aug", 31,
"september", "september", 30,
"Oktober", "Okt", 31,
"November", "november", 30,
"december", "december", 31
};
int hoofd()
{
int-teller;
voor (teller=1; teller<=12; teller++)
printf("%s wordt afgekort als %s en heeft %d dagen\n",
maand_details[teller].naam,
maand_details[teller].afkorting,
maand_details[teller].dagen);
retourneer 0;
}
De uitvoer wordt als volgt weergegeven:
Januari wordt afgekort als Jan en heeft 31 dagen
Februari wordt afgekort als Feb en heeft 28 dagen
Maart wordt afgekort als Mar en heeft 31 dagen
April wordt afgekort als Apr en heeft 30 dagen
Mei wordt afgekort als mei en heeft 31 dagen
Juni wordt afgekort als Jun en heeft 30 dagen
Juli wordt afgekort als Jul en heeft 31 dagen
Augustus wordt afgekort als Aug en heeft 31 dagen
September wordt afgekort als Sep en heeft 30 dagen
Oktober wordt afgekort als okt en heeft 31 dagen
November wordt afgekort als Nov en heeft 30 dagen
December wordt afgekort als Dec en heeft 31 dagen
Structuren doorgeven aan functies in C
Structuren kunnen als parameter aan een functie worden doorgegeven, net als elk van de basisgegevenstypen. Het volgende voorbeeld gebruikt een structuur genaamd date die is doorgegeven aan een isLeapYear-functie om te bepalen of het jaar een schrikkeljaar is.
Normaal gesproken zou u alleen de dagwaarde doorgeven, maar de volledige structuur wordt doorgegeven om te illustreren hoe structuren aan functies kunnen worden doorgegeven.
#include <stdio.h>
#include <tekenreeks.h>
structuur maand
{
char *naam;
char *afkorting;
int dagen;
} maand_details[] =
{
"Rots", "Rots", 0,
"Januari", "Jan", 31,
"Februari", "februari", 28,
"Maart", "Mar", 31,
"april", "april", 30,
"Mei", "Mei", 31,
"Juni", "Jun", 30,
"Juli", "Jul", 31,
"Augustus", "Aug", 31,
"september", "september", 30,
"Oktober", "Okt", 31,
"November", "november", 30,
"december", "december", 31
};
structuur datum
{
int dag;
int maand;
int jaar;
};
int isLeapYear(struct datum d);
int hoofd()
{
structuur datum d;
printf("Voer de datum in (bijv.: 11/11/1980): ");
scanf("%d/%d/%d", &d.dag, &d.maand, &d.jaar);
printf("De datum %d %s %d is ", d.day,
maand_details[d.maand].naam, d.jaar);
als (Schrikkeljaar(d) == 0)
printf("niet ");
puts("een schrikkeljaar");
retourneer 0;
}
int isLeapYear(struct datum d)
{
als ((d.jaar % 4 == 0 && d.jaar % 100 != 0) ||
d.jaar % 400 == 0)
retour 1;
retourneer 0;
}
De uitvoering van het programma zal als volgt zijn:
Voer de datum in (bijv.: 11/11/1980): 9/12/1980
De datum 9 december 1980 is een schrikkeljaar
Het volgende voorbeeld wijst dynamisch een array van structuren toe om namen en cijfers van studenten op te slaan. De cijfers worden vervolgens in oplopende volgorde aan de gebruiker weergegeven.
#include <tekenreeks.h>
#include <malloc.h>
structuur student
{
char *naam;
int-cijfer;
};
void swap(struct student *x, struct student *y);
int hoofd()
{
struct student *groep;
char-buffer[80];
int onecht;
int binnen, buiten;
int teller, numStudenten;
printf("Hoeveel studenten zitten er in de groep: ");
scanf("%d", &numStudenten);
groep = (struct student *)malloc(numStudents *
grootte van(struct student));
voor (teller=0; teller<numStudenten; teller++)
{
onecht = getchar();
printf("Voer de naam van de student in: ");
krijgt(buffer);
groep[teller].naam = (char *)malloc((strlen(buffer)+1) * sizeof(char));
strcpy(groep[teller].naam, buffer);
printf("Voer cijfer in: ");
scanf("%d", &groep[teller].graad);
}
voor (outer=0; outer<numStudents; outer++)
voor (inner=0; inner<outer; inner++)
als (groep[buiten].graad <
groep[innerlijk].graad)
swap(&groep[buiten], &groep[binnen]);
puts("De groep in oplopende volgorde van cijfers ...");
voor (teller=0; teller<numStudenten; teller++)
printf("%s behaalde cijfer %d \n”,
groep[teller].naam,
groep[teller].graad);
retourneer 0;
}
void swap(struct student *x, struct student *y)
{
structuur student temp;
temp.naam = (char *)malloc((strlen(x->naam)+1) *
groottevan(char));
strcpy(temp.naam, x->naam);
temp.graad = x->graad;
x->graad = y->graad;
x->naam = (char *)malloc((strlen(y->naam)+1) *
groottevan(char));
strcpy(x->naam, y->naam);
y->graad = temp.graad;
y->naam = (char *)malloc((strlen(temp.naam)+1) *
groottevan(char));
strcpy(y->naam, temp.naam);
}
De uitvoering van de uitvoer zal als volgt zijn:
Hoeveel studenten zitten er in de groep: 4
Vul de naam van de student in: Anuraaj
Vul het cijfer in: 7
Vul de naam van de student in: Honey
Vul het cijfer in: 2
Vul de naam van de student in: Meetushi
Vul het cijfer in: 1
Vul de naam van de student in: Deepti
Vul het cijfer in: 4
De groep in oplopende volgorde van graad ...
Meetushi behaalde Graad 1
Honey behaalde Graad 2
Deepti behaalde Graad 4
Anuraaj behaalde Graad 7
Unie
Met een union kunt u dezelfde gegevens met verschillende typen bekijken of dezelfde gegevens met verschillende namen gebruiken. Unions lijken op structuren. Een union wordt op dezelfde manier gedeclareerd en gebruikt als een structuur.
Een unie verschilt van een structuur doordat slechts één van de leden tegelijk kan worden gebruikt. De reden hiervoor is simpel. Alle leden van een unie bezetten hetzelfde geheugengebied. Ze worden op elkaar gelegd.
Unions worden op dezelfde manier gedefinieerd en gedeclareerd als structuren. Het enige verschil in de declaraties is dat het sleutelwoord union wordt gebruikt in plaats van struct. Om een eenvoudige union van een char-variabele en een integer-variabele te definiëren, zou u het volgende schrijven:
vakbond gedeeld {
teken c;
int ik;
};
Deze union, shared, kan worden gebruikt om instanties van een union te maken die een tekenwaarde c of een integerwaarde i kan bevatten. Dit is een OF-voorwaarde. In tegenstelling tot een structuur die beide waarden zou bevatten, kan de union slechts één waarde tegelijk bevatten.
Een union kan worden geïnitialiseerd op zijn declaratie. Omdat er maar één lid tegelijk kan worden gebruikt en er maar één kan worden geïnitialiseerd. Om verwarring te voorkomen, kan alleen het eerste lid van de union worden geïnitialiseerd. De volgende code toont een instantie van de gedeelde union die wordt gedeclareerd en geïnitialiseerd:
unie gedeelde generieke_variabele = {`@'};
Merk op dat de generic_variable unie op dezelfde manier werd geïnitialiseerd als het eerste lid van een structuur.
Individuele union-leden kunnen op dezelfde manier worden gebruikt als structuurleden, door de member-operator (.) te gebruiken. Er is echter een belangrijk verschil in de toegang tot union-leden.
Er mag maar één vakbondslid tegelijk worden benaderd. Omdat een vakbond zijn leden boven elkaar opslaat, is het belangrijk om maar één lid tegelijk te benaderen.
De vakbond Trefwoord
vakbond tag {
vakbondslid(en);
/* hier kunnen aanvullende statements geplaatst worden */
}aanleg;
Het trefwoord union wordt gebruikt voor het declareren van unions. Een union is een verzameling van een of meer variabelen (union_members) die onder één naam zijn gegroepeerd. Bovendien neemt elk van deze union-leden hetzelfde geheugengebied in beslag.
Het sleutelwoord union identificeert het begin van een union-definitie. Het wordt gevolgd door een tag die de naam is die aan de union is gegeven. Na de tag staan de union-leden tussen accolades.
Een instance, de feitelijke declaratie van een union, kan ook worden gedefinieerd. Als u de structuur definieert zonder de instance, is het gewoon een template die later in een programma kan worden gebruikt om structuren te declareren. Het volgende is de opmaak van een template:
vakbond tag {
vakbondslid(en);
/* hier kunnen aanvullende statements geplaatst worden */
};
Om de sjabloon te gebruiken, gebruikt u de volgende indeling: union tag instance;
Om deze opmaak te kunnen gebruiken, moet u vooraf een unie met de opgegeven tag hebben gedeclareerd.
/* Declareer een uniesjabloon met de naam tag */
vakbond tag {
int getal;
char alpen;
}
/* Gebruik de uniesjabloon */
verenigingstag gemengde_variabele;
/* Verklaar een unie en instantie samen */
unie generieke_type_tag {
teken c;
int ik;
vlotter f;
dubbele d;
} algemeen;
/* Initialiseer een unie. */
vakbond datum_tag {
char volledige_datum[9];
struct onderdeel_datum_tag {
char maand[2];
char break_waarde1;
char dag[2];
char breekwaarde2;
char jaar[2];
} deel_datum;
}datum = {"09/12/80"};
Laten we het beter begrijpen met behulp van voorbeelden:
#include <stdio.h>
int hoofd()
{
unie
{
int waarde; /* Dit is het eerste deel van de unie */
structuur
{
char first; /* Deze twee waarden vormen het tweede deel ervan */
char seconde;
} half;
} nummer;
lange index;
voor (index = 12 ; index < 300000L ; index += 35231L)
{
getal.waarde = index;
printf("%8x %6x %6x\n", getal.waarde,
nummer.half.eerste,
getal.halve.seconde);
}
retourneer 0;
}
De uitvoer van het programma wordt als volgt weergegeven:
cc0
89ab fantastisch ff89
134a 4a 13
9ce9 ffe9 ff9c
2688 ff88 26
b027 27 ffb0
39c6 ffc6 39
c365 65 ffc3
4d04 4 4d
Een praktisch gebruik van een unie bij dataherstel
Laten we nu eens kijken naar een praktisch gebruik van union is data recovery programming. Laten we een klein voorbeeld nemen. Het volgende programma is het kleine model van bad sector scanning programma voor een floppy disk drive (a: ), maar het is niet het complete model van bad sector scanning software.
Laten we het programma eens bekijken:
#include<dos.h>
#include<conio.h>
int hoofd()
{
int rp, hoofd, spoor, sector, status;
char *buf;
vakbond REGS in, uit;
structuur SREGS s;
clrscr();
/* Reset het schijfsysteem om te initialiseren naar schijf */
printf("\n Het schijfsysteem resetten....");
voor(rp=0;rp<=2;rp++)
{
in.h.ah = 0;
in.h.dl = 0x00;
int86(0x13,&in,&uit);
}
printf("\n\n\n De schijf wordt nu getest op slechte sectoren....");
/* scannen op slechte sectoren */
voor(track=0;track<=79;track++)
{
voor(hoofd=0;hoofd<=1;hoofd++)
{
voor(sector=1;sector<=18;sector++)
{
in.h.ah = 0x04;
in.h.al = 1;
in.h.dl = 0x00;
in.h.ch = spoor;
in.h.dh = hoofd;
in.h.cl = sector;
in.x.bx = FP_OFF(buf);
s.es = FP_SEG(buf);
int86x(0x13,&in,&uit,&s);
als(uit.x.cvlag)
{
status=uit.h.ah;
printf("\n spoor:%d Hoofd:%d Sector:%d Status ==0x%X",spoor,hoofd,sector,status);
}
}
}
}
printf("\n\n\nKlaar");
retourneer 0;
}
Laten we nu eens kijken hoe de uitvoer eruit zou zien als er slechte sectoren op de floppydisk zitten:
Het schijfsysteem resetten....
De schijf wordt nu getest op slechte sectoren....
spoor:0 Hoofd:0 Sector:4 Status ==0xA
spoor:0 Hoofd:0 Sector:5 Status ==0xA
spoor:1 Hoofd:0 Sector:4 Status ==0xA
spoor:1 Hoofd:0 Sector:5 Status ==0xA
spoor:1 Hoofd:0 Sector:6 Status ==0xA
spoor:1 Hoofd:0 Sector:7 Status ==0xA
spoor:1 Hoofd:0 Sector:8 Status ==0xA
spoor:1 Hoofd:0 Sector:11 Status ==0xA
spoor:1 Hoofd:0 Sector:12 Status ==0xA
spoor:1 Hoofd:0 Sector:13 Status ==0xA
spoor:1 Hoofd:0 Sector:14 Status ==0xA
spoor:1 Hoofd:0 Sector:15 Status ==0xA
spoor:1 Hoofd:0 Sector:16 Status ==0xA
spoor:1 Hoofd:0 Sector:17 Status ==0xA
spoor:1 Hoofd:0 Sector:18 Status ==0xA
spoor:1 Hoofd:1 Sector:5 Status ==0xA
spoor:1 Hoofd:1 Sector:6 Status ==0xA
spoor:1 Hoofd:1 Sector:7 Status ==0xA
spoor:1 Hoofd:1 Sector:8 Status ==0xA
spoor:1 Hoofd:1 Sector:9 Status ==0xA
spoor:1 Hoofd:1 Sector:10 Status ==0xA
spoor:1 Hoofd:1 Sector:11 Status ==0xA
spoor:1 Hoofd:1 Sector:12 Status ==0xA
spoor:1 Hoofd:1 Sector:13 Status ==0xA
spoor:1 Hoofd:1 Sector:14 Status ==0xA
spoor:1 Hoofd:1 Sector:15 Status ==0xA
spoor:1 Hoofd:1 Sector:16 Status ==0xA
spoor:1 Hoofd:1 Sector:17 Status ==0xA
spoor:1 Hoofd:1 Sector:18 Status ==0xA
spoor:2 Hoofd:0 Sector:4 Status ==0xA
spoor:2 Hoofd:0 Sector:5 Status ==0xA
spoor:14 Hoofd:0 Sector:6 Status ==0xA
Klaar
Het kan lastig zijn om de functies en interrupts te begrijpen die in dit programma worden gebruikt om de schijf te controleren op slechte sectoren en om het schijfsysteem te resetten, maar u hoeft zich geen zorgen te maken. We gaan dit allemaal leren in de secties BIOS en interruptprogrammering die later in de komende hoofdstukken worden behandeld.
Bestandsbeheer in C
Bestandstoegang in C wordt bereikt door een stream te associëren met een bestand. C communiceert met bestanden met behulp van een nieuw gegevenstype, een bestandspointer genaamd. Dit type is gedefinieerd in stdio.h en geschreven als FILE *. Een bestandspointer genaamd output_file wordt gedeclareerd in een statement zoals
BESTAND *output_file;
De bestandsmodi van de fopen-functie
Uw programma moet een bestand openen voordat het er toegang toe heeft. Dit wordt gedaan met de fopen-functie, die de vereiste bestandspointer retourneert. Als het bestand om welke reden dan ook niet kan worden geopend, wordt de waarde NULL geretourneerd. U gebruikt fopen meestal als volgt
als ((uitvoerbestand = fopen("uitvoerbestand", "w")) == NULL)
fprintf(stderr, "Kan %s\n niet openen",
"uitvoerbestand");
fopen heeft twee argumenten, beide zijn strings. De eerste is de naam van het te openen bestand, de tweede is een toegangskarakter, dat meestal r, a of w etc. is. Bestanden kunnen op verschillende manieren worden geopend, zoals in de volgende tabel wordt getoond.
Bestandsmodi |
R |
Open een tekstbestand om te lezen. |
In |
Maak een tekstbestand om te schrijven. Als het bestand bestaat, wordt het overschreven. |
A |
Open een tekstbestand in de append-modus. Tekst wordt aan het einde van het bestand toegevoegd. |
rb |
Open een binair bestand om te lezen. |
gemeenschappelijk |
Maak een binair bestand om te schrijven. Als het bestand bestaat, wordt het overschreven. |
ik |
Open een binair bestand in de append-modus. Gegevens worden aan het einde van het bestand toegevoegd. |
r+ |
Open een tekstbestand om te lezen en schrijven. |
in+ |
Maak een tekstbestand om te lezen en te schrijven. Als het bestand bestaat, wordt het overschreven. |
een+ |
Open een tekstbestand om aan het einde te lezen en te schrijven. |
r+b of rb+ |
Open binair bestand om te lezen en schrijven. |
w+b of wb+ |
Maak een binair bestand voor lezen en schrijven. Als het bestand bestaat, wordt het overschreven. |
a+b of ab+ |
Open een tekstbestand om aan het einde te lezen en te schrijven. |
De update-modi worden gebruikt met fseek-, fsetpos- en rewind-functies. De fopen-functie retourneert een bestandspointer of NULL als er een fout optreedt.
Het volgende voorbeeld opent een bestand, tarun.txt, in read-only-modus. Het is een goede programmeerpraktijk om te testen of het bestand bestaat.
als ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Kan het bestand niet openen");
retourneer 0;
}
Bestanden sluiten
Bestanden worden gesloten met de fclose-functie. De syntaxis is als volgt:
fclose(in);
Bestanden lezen
De feof-functie wordt gebruikt om te testen op het einde van het bestand. De functies fgetc, fscanf en fgets worden gebruikt om gegevens uit het bestand te lezen.
In het volgende voorbeeld wordt de inhoud van een bestand op het scherm weergegeven, waarbij fgetc wordt gebruikt om het bestand teken voor teken te lezen.
#include <stdio.h>
int hoofd()
{
BESTAND *in;
int-toets;
als ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Kan het bestand niet openen");
retourneer 0;
}
terwijl (!feof(in))
{
sleutel = fgetc(in);
/* Het laatste gelezen teken is de eindmarkering van het bestand, dus druk deze niet af */
als (!feof(in))
putchar(sleutel);
}
fclose(in);
retourneer 0;
}
De functie fscanf kan worden gebruikt om verschillende gegevenstypen uit het bestand te lezen, zoals in het volgende voorbeeld. Hiervoor geldt wel dat de gegevens in het bestand de indeling hebben van de opmaakreeks die wordt gebruikt met fscanf.
fscanf(in, "%d/%d/%d", &dag, &maand, &jaar);
Met de functie fgets kunt u een aantal tekens uit een bestand lezen. stdin is de standaardinvoerbestandsstroom en de functie fgets kan worden gebruikt om de invoer te regelen.
Schrijven naar bestanden
Gegevens kunnen naar het bestand worden geschreven met behulp van fputc en fprintf. Het volgende voorbeeld gebruikt de functies fgetc en fputc om een kopie van een tekstbestand te maken.
#include <stdio.h>
int hoofd()
{
BESTAND *in, *uit;
int-toets;
als ((in = fopen("tarun.txt", "r")) == NULL)
{
puts("Kan het bestand niet openen");
retourneer 0;
}
uit = fopen("kopie.txt", "w");
terwijl (!feof(in))
{
sleutel = fgetc(in);
als (!feof(in))
fputc(sleutel, uit);
}
fclose(in);
fclose(uit);
retourneer 0;
}
Met de functie fprintf kunt u geformatteerde gegevens naar een bestand schrijven.
fprintf(out, "Datum: %02d/%02d/%02d\n",
dag, maand, jaar);
Opdrachtregelargumenten met C
De ANSI C-definitie voor het declareren van de main()-functie is:
int main() of int main(int argc, char **argv)
De tweede versie staat toe dat argumenten worden doorgegeven vanaf de opdrachtregel. De parameter argc is een argumententeller en bevat het aantal parameters dat is doorgegeven vanaf de opdrachtregel. De parameter argv is de argumentvector die een array is van pointers naar strings die de daadwerkelijk doorgegeven parameters vertegenwoordigen.
Het volgende voorbeeld maakt het mogelijk om een willekeurig aantal argumenten door te geven vanaf de opdrachtregel en deze af te drukken. argv[0] is het eigenlijke programma. Het programma moet worden uitgevoerd vanaf een opdrachtprompt.
#include <stdio.h>
int hoofd(int argc, char **argv)
{
int-teller;
puts("De argumenten voor het programma zijn:");
voor (teller=0; teller<argc; teller++)
zet(argv[teller]);
retourneer 0;
}
Als de programmanaam count.c was, kon het als volgt vanaf de opdrachtregel worden aangeroepen.
tellen 3
of
tellen 7
of
tellen 192 etc.
Het volgende voorbeeld gebruikt de bestandsverwerkingsroutines om een tekstbestand naar een nieuw bestand te kopiëren. Het opdrachtregelargument kan bijvoorbeeld worden aangeroepen als:
txtcpy een.txt twee.txt
#include <stdio.h>
int hoofd(int argc, char **argv)
{
BESTAND *in, *uit;
int-toets;
als (argc < 3)
{
puts("Gebruik: txtcpy bron bestemming\n");
puts("De bron moet een bestaand bestand zijn");
puts("Als het doelbestand bestaat, wordt het
overwritten");
retourneer 0;
}
als ((in = fopen(argv[1], "r")) == NULL)
{
puts("Kan het te kopiëren bestand niet openen");
retourneer 0;
}
als ((uit = fopen(argv[2], "w")) == NULL)
{
puts("Kan het uitvoerbestand niet openen");
retourneer 0;
}
terwijl (!feof(in))
{
sleutel = fgetc(in);
als (!feof(in))
fputc(sleutel, uit);
}
fclose(in);
fclose(uit);
retourneer 0;
}
Bitgewijze manipulatoren
Op hardwareniveau worden gegevens weergegeven als binaire getallen. De binaire weergave van het getal 59 is 111011. Bit 0 is de minst significante bit en in dit geval is bit 5 de meest significante bit.
Elke bitset wordt berekend als 2 tot de macht van de bitset. Met bitwise-operatoren kunt u gehele variabelen op bitniveau manipuleren. Hieronder ziet u de binaire representatie van het getal 59.
binaire representatie van het getal 59 |
beetje 5 4 3 2 1 0 |
2 macht n 32 16 8 4 2 1 |
stel 1 1 1 0 1 1 |
Met drie bits is het mogelijk om de getallen 0 tot en met 7 weer te geven. De volgende tabel toont de getallen 0 tot en met 7 in hun binaire vorm.
Binaire cijfers |
000 |
0 |
001 |
1 |
010 |
2 |
011 |
3 |
100 |
4 |
101 |
5 |
110 |
6 |
111 |
7 |
In de onderstaande tabel staan de bitgewijze operatoren die gebruikt kunnen worden om binaire getallen te manipuleren.
Binaire cijfers |
& |
Bitgewijs EN |
| |
Bitgewijs OF |
^ |
Bitwise Exclusief OF |
~ |
Bitgewijs complement |
<< |
Bitgewijze verschuiving naar links |
>> |
Bitgewijze verschuiving naar rechts |
Bitgewijs EN
De bitwise AND is alleen True als beide bits zijn ingesteld. Het volgende voorbeeld toont het resultaat van een bitwise AND op de getallen 23 en 12.
10111 (23)
01100 (12) EN
____________________
00100 (resultaat = 4) |
U kunt een maskerwaarde gebruiken om te controleren of bepaalde bits zijn ingesteld. Als we willen controleren of bits 1 en 3 zijn ingesteld, kunnen we het getal maskeren met 10 (de waarde van bits 1 en 3) en het resultaat testen tegen het masker.
#include <stdio.h>
int hoofd()
{
int num, masker = 10;
printf("Voer een getal in: ");
scanf("%d", &num);
als ((num & masker) == masker)
puts("Bits 1 en 3 zijn ingesteld");
anders
puts("Bits 1 en 3 zijn niet ingesteld");
retourneer 0;
}
Bitgewijs OF
De bitwise OR is true als een van beide bits is ingesteld. Hieronder ziet u het resultaat van een bitwise OR op de getallen 23 en 12.
10111 (23)
01100 (12) OF
______________________
11111 (resultaat = 31) |
U kunt een masker gebruiken om ervoor te zorgen dat een bit of bits zijn ingesteld. Het volgende voorbeeld zorgt ervoor dat bit 2 is ingesteld.
#include <stdio.h>
int hoofd()
{
int num, masker = 4;
printf("Voer een getal in: ");
scanf("%d", &num);
num |= masker;
printf("Nadat bit 2 is ingesteld: %d\n", num);
retourneer 0;
}
Bitwise Exclusief OF
De bitwise Exclusive OR is True als een van de bits is ingesteld, maar niet beide. Hieronder ziet u het resultaat van een bitwise Exclusive OR op de getallen 23 en 12.
10111 (23)
01100 (12) Exclusief OF (XOR)
_____________________________
11011 (resultaat = 27) |
De Exclusive OR heeft een aantal interessante eigenschappen. Als je een getal op zichzelf Exclusive OR, zet het zichzelf op nul omdat de nullen nul blijven en de enen niet allebei gezet kunnen worden en dus op nul gezet worden.
Als gevolg hiervan, als u een getal exclusief OF met een ander getal maakt, en vervolgens het resultaat exclusief OF met het andere getal maakt, is het resultaat het oorspronkelijke getal. U kunt dit proberen met de getallen die in het bovenstaande voorbeeld zijn gebruikt.
23 OF 12 = 27
27 OF 12 = 23
27 OF 23 = 12
Deze functie kan worden gebruikt voor encryptie. Het volgende programma gebruikt een encryptiesleutel van 23 om de eigenschap van een door de gebruiker ingevoerd nummer te illustreren.
#include <stdio.h>
int hoofd()
{
int num, sleutel = 23;
printf("Voer een getal in: ");
scanf("%d", &num);
num ^= sleutel;
printf("Exclusief OF met %d geeft %d\n", key, num);
num ^= sleutel;
printf("Exclusief OF met %d geeft %d\n", key, num);
retourneer 0;
}
Bitgewijs compliment
Het bitwise Compliment is een one's compliment operator die de bit aan of uit zet. Als het 1 is, wordt het op 0 gezet, als het 0 is, wordt het op 1 gezet.
#include <stdio.h>
int hoofd()
{
int num = 0xFFFF;
printf("Het complement van %X is %X\n", num, ~num);
retourneer 0;
}
Bitgewijze verschuiving naar links
De Bitwise Shift Left operator verschuift het getal naar links. De meest significante bits gaan verloren als het getal naar links beweegt, en de vrijgekomen minst significante bits zijn nul. Het volgende toont de binaire representatie van 43.
0101011 (decimaal 43)
Door de bits naar links te verschuiven, verliezen we de meest significante bit (in dit geval een nul) en wordt het getal aangevuld met een nul bij de minst significante bit. Het volgende is het resulterende getal.
1010110 (decimaal 86)
Bitgewijze verschuiving naar rechts
De Bitwise Shift Right operator verschuift het getal naar rechts. Nul wordt geïntroduceerd in de vrijgekomen meest significante bits, en de vrijgekomen minst significante bits gaan verloren. Het volgende toont de binaire representatie van het getal 43.
0101011 (decimaal 43)
Door de bits naar rechts te verschuiven, verliezen we de minst significante bit (in dit geval een één) en wordt het getal aangevuld met een nul bij de meest significante bit. Het volgende is het resulterende getal.
0010101 (decimaal 21)
Het volgende programma gebruikt Bitwise Shift Right en Bitwise AND om een getal weer te geven als een 16-bits binair getal. Het getal wordt achtereenvolgens naar rechts verschoven van 16 naar beneden naar nul en Bitwise ANDed met 1 om te zien of de bit is ingesteld. Een alternatieve methode zou zijn om opeenvolgende maskers te gebruiken met de Bitwise OR-operator.
#include <stdio.h>
int hoofd()
{
int teller, num;
printf("Voer een getal in: ");
scanf("%d", &num);
printf("%d is binair: ", num);
voor (teller=15; teller>=0; teller--)
printf("%d", (num >> teller) & 1);
putchar('\n');
retourneer 0;
}
Functies voor binaire – decimale conversies
De twee functies die hierna worden gegeven zijn voor Binair naar Decimaal en Decimaal naar Binair conversie. De functie die hierna wordt gegeven om een decimaal getal naar een overeenkomstig binair getal te converteren ondersteunt tot 32-bits binaire getallen. U kunt dit of het eerder gegeven programma gebruiken voor conversie volgens uw vereisten.
Functie voor decimaal naar binair conversie:
leeg Decimaal_naar_binair(leeg)
{
int invoer = 0;
int ik;
int aantal = 0;
int binair [32]; /* 32 Bit, MAXIMAAL 32 elementen */
printf ("Voer een decimaal getal in om te converteren naar
Binair :");
scanf ("%d", &invoer);
Doen
{
i = input%2; /* MOD 2 om 1 of een 0*/ te krijgen
binary[count] = i; /* Elementen laden in de binaire array */
input = input/2; /* Deel input door 2 om te decrementeren via binair */
count++; /* Tel hoeveel elementen er nodig zijn*/
}terwijl (invoer > 0);
/* Binaire cijfers omkeren en uitgeven */
printf ("Binaire representatie is: ");
Doen
{
printf ("%d", binair[aantal - 1]);
graaf--;
} terwijl (aantal > 0);
afdrukkenf ("\n");
}
Functie voor conversie van binair naar decimaal:
De volgende functie is om elk binair getal om te zetten naar het overeenkomstige decimale getal:
void Binair_naar_decimaal(void)
{
char binairhold[512];
char *binair;
int i=0;
int dec = 0;
int z;
printf ("Voer de binaire cijfers in.\n");
printf ("Binaire cijfers zijn alleen 0 of 1 ");
printf ("Binaire invoer: ");
binair = krijgt(binairvasthouden);
i=strlen(binair);
voor (z=0; z<i; ++z)
{
dec=dec*2+(binair[z]=='1'? 1:0); /* als Binair[z] is
gelijk aan 1,
dan 1 anders 0 */
}
afdrukkenf ("\n");
printf ("Decimale waarde van %s is %d",
binair, dec);
afdrukkenf ("\n");
}
Debuggen en testen
Syntaxisfouten
Syntaxis verwijst naar de grammatica, structuur en volgorde van de elementen in een statement. Een syntaxisfout treedt op wanneer we de regels overtreden, zoals vergeten een statement te beëindigen met een puntkomma. Wanneer u het programma compileert, zal de compiler een lijst produceren met alle syntaxisfouten die hij kan tegenkomen.
Een goede compiler zal de lijst met een beschrijving van de fout uitgeven en kan een mogelijke oplossing bieden. Het oplossen van de fouten kan ertoe leiden dat er meer fouten worden weergegeven bij het opnieuw compileren. De reden hiervoor is dat de vorige fouten de structuur van het programma hebben gewijzigd, wat betekent dat er meer fouten werden onderdrukt tijdens de oorspronkelijke compilatie.
Op dezelfde manier kan één enkele fout resulteren in meerdere fouten. Probeer een puntkomma aan het einde van de hoofdfunctie van een programma te zetten dat correct compileert en draait. Wanneer u het opnieuw compileert, krijgt u een enorme lijst met fouten, en toch is het slechts een verkeerd geplaatste puntkomma.
Naast syntaxisfouten kunnen compilers ook waarschuwingen geven. Een waarschuwing is geen fout, maar kan problemen veroorzaken tijdens de uitvoering van uw programma. Bijvoorbeeld, het toewijzen van een floating point-getal met dubbele precisie aan een floating point-getal met enkele precisie kan resulteren in een verlies van precisie. Het is geen syntaxisfout, maar kan leiden tot problemen. In dit specifieke voorbeeld kunt u intentie tonen door de variabele naar het juiste gegevenstype te casten.
Bekijk het volgende voorbeeld, waarbij x een drijvendekommagetal met enkele precisie is en y een drijvendekommagetal met dubbele precisie. y wordt tijdens de toewijzing expliciet naar een float gecast, waardoor eventuele compilerwaarschuwingen worden geëlimineerd.
x = (float)y;
Logische fouten
Logische fouten treden op wanneer er een fout in de logica zit. U kunt bijvoorbeeld testen of een getal kleiner is dan 4 en groter dan 8. Dat kan onmogelijk waar zijn, maar als het syntactisch correct is, zal het programma succesvol compileren. Bekijk het volgende voorbeeld:
als (x < 4 en x > 8)
puts("Zal nooit gebeuren!");
De syntaxis is correct, dus het programma kan worden gecompileerd, maar de puts-instructie wordt nooit afgedrukt, omdat de waarde van x onmogelijk kleiner dan vier en groter dan acht tegelijk kan zijn.
De meeste logische fouten worden ontdekt tijdens de eerste tests van het programma. Wanneer het zich niet gedraagt zoals u verwachtte, inspecteert u de logische statements nauwkeuriger en corrigeert u ze. Dit geldt alleen voor voor de hand liggende logische fouten. Hoe groter het programma, hoe meer paden er doorheen zullen zijn, hoe moeilijker het wordt om te verifiëren dat het programma zich gedraagt zoals verwacht.
Testen
In het softwareontwikkelingsproces kunnen fouten in elke fase van de ontwikkeling worden geïnjecteerd. Dit komt doordat verificatiemethoden van eerdere fasen van de softwareontwikkeling handmatig zijn. Daarom zal de code die tijdens de coderingsactiviteit wordt ontwikkeld, waarschijnlijk enkele vereistefouten en ontwerpfouten bevatten, naast fouten die tijdens de coderingsactiviteit zijn geïntroduceerd. Tijdens het testen wordt het te testen programma uitgevoerd met een set testcases en wordt de uitvoer van het programma voor de testcases geëvalueerd om te bepalen of de programmering naar verwachting presteert.
Testen is dus het proces van het analyseren van een software-item om het verschil te detecteren tussen bestaande en vereiste condities (d.w.z. bugs) en om de features van de software-items te evalueren. Testen is dus het proces van het analyseren van een programma met de bedoeling om fouten te vinden.
Enkele testprincipes
- Met testen kan niet worden aangetoond dat er geen gebreken zijn, alleen dat ze aanwezig zijn.
- Hoe eerder een fout wordt gemaakt, hoe duurder deze is.
- Hoe later een fout wordt ontdekt, hoe duurder deze is.
Laten we nu enkele testtechnieken bespreken:
Whitebox-testen
White box testing is een techniek waarbij alle paden door het programma worden getest met elke mogelijke waarde. Deze aanpak vereist enige kennis van hoe het programma zich zou moeten gedragen. Als uw programma bijvoorbeeld een gehele waarde tussen 1 en 50 accepteert, test een white box test het programma met alle 50 waarden om te verzekeren dat het voor elk correct is, en test vervolgens elke andere mogelijke waarde die een gehele waarde kan aannemen en test of het zich gedraagt zoals verwacht. Gezien het aantal data-items dat een typisch programma kan hebben, maken de mogelijke permutaties white box testing extreem moeilijk voor grote programma's.
White box testing kan worden toegepast op veiligheidskritieke functies van een groot programma, en veel van de rest wordt getest met behulp van black box testing, hieronder besproken. Vanwege het aantal permutaties wordt white box testing meestal uitgevoerd met behulp van een test harness, waarbij bereiken van waarden snel aan het programma worden doorgegeven via een speciaal programma, waarbij uitzonderingen op het verwachte gedrag worden vastgelegd. White box testing wordt soms ook wel structural, clear of open box testing genoemd.
Blackbox-testen
Black box testing is vergelijkbaar met white box testing, behalve dat in plaats van het testen van elke mogelijke waarde, geselecteerde waarden worden getest. Bij dit type test weet de tester de invoer en wat de verwachte uitkomsten zouden moeten zijn, maar niet noodzakelijkerwijs hoe het programma tot die invoer is gekomen. Black box testing wordt soms functioneel testen genoemd.
Testcases voor black-box-testen worden doorgaans direct ontwikkeld nadat de programmaspecificaties zijn voltooid. Testgevallen zijn gebaseerd op equivalentieklassen.
Equivalentieklassen
Voor elke invoer definieert een equivalentieklasse geldige en ongeldige toestanden. Bij het definiëren van equivalentieklassen moet u doorgaans rekening houden met drie scenario's.
Als de invoergegevens een bereik of een specifieke waarde specificeren, worden er één geldige status en twee ongeldige statussen gedefinieerd. Als een getal bijvoorbeeld tussen 1 en 20 moet liggen, ligt de geldige status tussen 1 en 20. Er is een ongeldige status voor waarden kleiner dan 1 en een ongeldige status groter dan 20.
Als de invoergegevens een bereik of een specifieke waarde uitsluiten, worden er twee geldige toestanden en één ongeldige toestand gedefinieerd. Als een getal bijvoorbeeld niet tussen 1 en 20 mag liggen, zijn geldige toestanden kleiner dan 1 en groter dan 20, en ongeldige toestanden tussen 1 en 20.
Als de invoer een Booleaanse waarde specificeert, zijn er slechts twee toestanden: één geldig en één ongeldig.
Grenswaardeanalyse
Bij grenswaardeanalyse worden alleen de waarden op de grens van de invoergegevens in aanmerking genomen. Bijvoorbeeld, in het geval van een getal van 1 tot 20, kunnen de testgevallen 1, 20, 0 en 21 zijn. Het idee is dat als het programma werkt zoals verwacht met deze waarden, andere waarden ook zullen werken zoals verwacht.
De onderstaande tabel geeft een overzicht van de typische grenzen die u wellicht wilt definiëren.
Testbereiken |
Invoertype |
Testwaarden |
Bereik |
- x[ondergrens]-1
- x[ondergrens]
- x[bovengrens]
- x[bovengrens]+1
|
Booleaans |
|
Een testplan ontwikkelen
Definieer equivalentieklassen en bepaal de grenzen voor elke klasse. Zodra u de grenzen voor de klasse hebt gedefinieerd, schrijft u een lijst met acceptabele en onacceptabele waarden bij de grens en wat het verwachte gedrag zou moeten zijn. De tester kan vervolgens het programma met de grenswaarden uitvoeren en aangeven wat er gebeurde toen de grenswaarde werd getoetst aan het gewenste resultaat.
Hieronder vindt u een typisch testplan dat wordt gebruikt om invoerleeftijden te valideren, waarbij de acceptabele waarden variëren van 10 tot 110.
Equivalentieklasse |
Geldig |
Onjuist |
Tussen 10 en 110 |
> 110 |
|
< 10 |
Nu we onze equivalentieklasse hebben bepaald, kunnen we een testplan voor leeftijd ontwikkelen.
Testplan |
Waarde |
Staat |
Verwacht resultaat |
Werkelijke resultaat |
10 |
Geldig |
Ga door met uitvoeren om de naam te krijgen |
|
110 |
Geldig |
Ga door met uitvoeren om de naam te krijgen |
|
9 |
Onjuist |
Vraag nogmaals naar de leeftijd |
|
111 |
Onjuist |
Vraag nogmaals naar de leeftijd |
|
De kolom 'Werkelijke resultaten' blijft leeg, omdat deze tijdens het testen wordt ingevuld. Als het resultaat overeenkomt met de verwachting, wordt de kolom gecontroleerd. Als dat niet het geval is, moet u een opmerking plaatsen waarin u aangeeft wat er is gebeurd.