KB » Computer » C(++) programmeren

C(++) programmeren

    Tweeten

Introductie


Compiler en linker

De meest gebruikte IDE (Integrated Development Environment) is waarschijnlijk Visual Studio (van Microsoft), en daar heb ik een aparte pagina aan gewijd.

Hieronder wat summiere informatie over een Borland compiler en linker. Beide zijn overigens gratis, al heb je dan wel een beperkte versie.

BCC32 (Borland)

Je kan deze compiler downloaden.

Na het installeren voeg je de map met de executables (C:\Borland\BCC55\Bin) toe aan het pad.

De compiler en linker moeten ook include files en library files kunnen vinden. Die info wil je niet steeds meegeven als je de compiler aanroept, dus zet je het in een configuratiebestand.

De config file heeft extensie CFG, en kan je het beste of in de map met executables, of in de map met de te compileren programma's zetten. Als je ook de naam van het configuratiebestand niet steeds wil hoeven opgeven noem je het BCC32.CFG

In dit bestand zet je o.a. de volgende 2 regels:
-Ic:\Borland\BCC32\Include en
-Lc:\Borland\BCC32\Lib

Foutmeldingen

Fatal: Unable to open file 'C0W32.OBJ'
Je moet de map met library files aan de linker opgeven.
Too many types in declaration in function main
Ik gebruikte een type dat blijkbaar niet bestaat. Dat gebeurde in het volgende statement: printf( "\nA long long is %d bytes\n", sizeof( long long));
Unable to open include file 'stdio.h'
Je moet de map met include files aan de linker opgeven.

Declaraties

Variabelen

Bij het declareren van variabelen kan je kijken naar inhoud (teken, geheel getal, etc.) of naar scope (bestaat in het hele programma, alleen binnen een functie, etc.).

Inhoud van een variabele

Van klein naar groot heb je de volgende types voor gehele getallen: (unsigned) char, (unsigned) int, (unsigned) short en (unsigned) long.

Voor niet gehele getallen heb je float en double.

Tenslotte kan je ook getallen van 1, 2 of meer bits definieren, bv. als volgt:

unsigned int a : 1; // getal van 1 bit unsigned int b : 7; // getal van 7 bits

Scope van een variabele (waar is hij geldig)

Deze indeling is ook gerelateerd aan het gebruik van storage. De opties zijn global, static, local of auto en register. En je hebt ook nog thread local storage.

Een local of automatic variabele is een variabele die gedeclareerd is in een functie, en daarbuiten niet zichtbaar is.

Een global variabele is er 1 die buiten functies gedeclareerd wordt.

Om een static variabele te declareren moet je het keyword static voor het type zetten. Een static variabele wordt 1 keer geïnitialiseerd, maar behoudt ook buiten de functie waarin hij gedeclareerd is zijn waarde.

Een register variabele zal indien mogelijk in een CPU register gezet worden. Ook hier moet je het keyword register voor het type zetten.

Voorbeelden

Een declaratie ziet er als volgt uit:
type var [= waarde];

Je kan meerdere variabelen op 1 regel declareren, en ze scheiden door een komma.

Voorbeelden:
int teller=0;
double procent = 0.34;
unsigned int a, b, c;

Constantes

Ik heb het hier alleen over symbolische constantes (i.t.t. tot literal constants, constante waardes die in het programma voorkomen). Deze kan je op 2 manieren declareren.

De 1e is met een #define
De algemene structuur is: #define CONSTNAAM waarde
Voorbeeld: #define PI 3.14

De 2e is met het keyword const
De algemene structuur is: const type constnaam = waarde
Voorbeeld: const int teller = 100


Operators

Complexe operators

Conditional operator

De syntax is exp1 ? exp2 : exp3

Als exp1 waar is heeft het geheel de waarde van exp2, anders die van exp3

Zo zou je bv. kunnen kijken welke van 2 variabelen de laagste waarde heeft, en die dan aan een derde kunnen toekennen: c = (a < b)? a : b;
Dit is equivalent aan if (a < b) c = a; else c = b;

Komma operator


Functies

Definitie en prototype

Voor je een functie definieert, moet je hem declareren. Dat heet een prototype.

Het prototype ziet er precies zo uit als de 1e regel van de functiedefinitie, met 1 verschil: er staan een ';' aan het eind.

Vbd.: int multiply(int x, int y);

Resultaat van een functie

Als je wilt dat de functie een resultaat teruggeeft zet je het type daarvan voor de naam van de functie (zoals de 1e 'int' hierboven).

In de functie moet in dat geval een return expr staan, waarbij de expressie hetzelfde type heeft.

De uitwerking van een functie kan natuurlijk ook zijn dat hij bv. iets afdrukt. In dat geval kan je als resultaattype void gebruiken.

Tip: je kan het beste maar 1 return in een functie gebruiken.

De functie main hoeft niet perse aan het begin van het programma te staan, maar het is wel gebruikelijk.

Parameters en argumenten

Parameters zijn de dingen die tussen haakjes in de functie definitie of prototype staan.

Argumenten zijn de waardes die je daadwerkelijk meegeeft als je een functie aanroept.


Flow of control statements

Je kan een groffe onderverdeling maken in statements om loops mee te maken, en andere statements om de flow of control mee te veranderen.

Loop statements

Het for statement is het beste als je in de loop een teller moet veranderen (of iets soortgelijks) en vooraf iets moet initialiseren.

De do... while is handig als je een statement minimaal 1 keer moet uitvoeren.

Je kan de loops wel binnen elkaar gebruiken, maar niet overlappen. Dus dat je loop1 begint, binnen loop1 ook loop2 begint, dan loop1 eindigt, en dan pas loop2 eindigt.

For statement

for (initialisatie; conditie; increment) { statements; }

Eerst wordt de initialisatie uitgevoerd. Vervolgens wordt de conditie getest. Als ie waar is wordt het statement uitgevoerd, en wordt tenslotte increment uitgevoerd. Daarna wordt de conditie opnieuw getest, etc.

Vbd.: het afdrukken van de getallen 1 t/m 10:

for (cnt = 1; cnt <= 10; cnt++) printf("%d\n", cnt);

While statement

while (conditie) { statements; }

Het while statement is in feit een simpele vorm van het for statement.

Het do... while statement

do { statements; } while (conditie);

In deze constructie wordt in tegenstelling tot de andere 2 het statement minimaal 1 keer uitgevoerd.

Diversen

Continue statement

Als je in een loop een continue-statement plaatst, wordt de rest van de loop overgeslagen voor die ene iteratie van de loop.

Break statement

Met een break verlaat je de loop.

If statement

if (conditie) { statements } else if (conditie) { statements } else { statements }

Switch statement

switch (variable) { case value: statements; break; case value: statements; break; case default: statements; break; }

? : statement

(condition) ? (statement1) : (statement2)

statement1 wordt uitgevoerd als de condition waar is, anders wordt statement2 uitgevoerd.

Goto statement

goto label1 statements1 label1: statements2

De 1e groep statements wordt niet uitgevoerd, omdat het goto-statement zorgt voor een sprong naar label1.


Invoer en uitvoer

De belangrijkste functies zijn printf (print formatted) en puts (put string) voor uitvoer, en scanf (scan formatted) en gets (get string) voor invoer.

Om deze functies te gebruiken moet je aan het begin van je programma de regel
#include stdio.h
opnemen. Dit is het bestand met de prototypes van deze functies.

Format specifiers

In printf en scanf is de 1e parameter een string met format specifiers. In deze string is van elke volgende parameter het type vermeld.

De format specifier begint met %, en je hebt de volgende mogelijkheden:

Puts

Deze functie drukt gewoon een string af, met een newline aan het eind.

Gets

Printf

Voorbeeld: printf("Het product van %d en %d is: %ld\n", x, y, x*y);

Scanf

In tegenstelling tot printf moeten de 2e en volgende parameters niet bestaan uit variabelen (of expressies), maar uit adressen van de variabelen waarin de invoer moet worden opgeslagen.

Voorbeeld: scanf(Voer 2 getallen in om met elkaar te vermenigvuldigen: %d %d", &x, &y);

De scheiding tussen verschillende in te voeren waardes moet bestaan uit "white space". Alles waar geen tekens staan is white space, dus het gaat spaties maar ook newlines.

2 in te voeren waardes kan je dus scheiden door een willekeurig aantal spaties, maar je kan ze ook op verschillende regels zetten, desnoods met een aantal lege ertussen.


Arrays

Declaratie

Een gewone array (met bv. gehele getallen) declareer je als volgt: int array[30];

Deze array heeft 30 elementen, genummerd van 0..29

Een meerdimensionale array declareer je als volgt: int array[12][31];
(als je bv. een element wilt hebben voor elke dag van het jaar, geindexeerd op maand en dag).

Veel compilers staan niet toe dat je in de definitie een constante als index gebruikt, tenminste als die met een const is gedefiniëerd. #define mag wel.

Initialisatie

Een array initialiseren gaat als volgt: int array[4] = {1, 2, 3, 4};

Een meerdimensionale array kan je zo initialiseren:
int array[2][3] = {1, 2, 3, 4, 5, 6};
of zo:
int array[2][3] = {{1, 2, 3}, {4, 5, 6}};
of zo:
int array[2][3] = {{0}};

Als je te weinig waarden opgeeft blijven er gewoon een aantal elementen van de array in ongedefinieerde staat.


Strings (tekenreeksen)

Strings zijn gewoon een speciaal geval van arrays.

Met het statement
char hallo[]="Hallo wereld!";
maak je een array aan, waarvan de waarden worden geïnterpreteerd als tekens. hallo[4] bevat dus het teken 'o'.

Een tekenreeks wordt altijd beëindigd met een 0. In hallo[13] zit dus de waarde 0.

Je kan de hele tekenreeks in 1 keer afdrukken met printf(stringnaam). Als je individuele tekens als teken wilt afdrukken, kan je iets doen als printf(%c, hallo[1]);
In dit geval zou het resultaat 'a' zijn.

Je kan een string als volgt initialiseren met lege waardes:
char hallo[20] = {0};

String functies

Om gebruik te kunnen maken van string functies moet je #include <string.h> bovenaan in je programma opnemen.

Sommige van de oorspronkelijke stringfuncties, zoals scanf, zijn vanwege security redenen vervangen door modernere varianten. Je kan ze nog wel gebruiken, maar dan moet je de controle erop in je IDE uitschakelen.

String copy (strcpy)

Hiermee kopiëer je een constante string naar een string array, bv.:

char testStr[20] = {0}; strcpy (testStr, "hallo");

String concatenation (strcat)

Hiermee plak je de ene string aan de andere vast. De doelstring staat vooraan, bv.:

char testStr1[20] = "hallo "; char testStr2[20] = "wereld!"; char testStr3[20] = {0}; strcat(testStr3, testStr1); strcat(testStr3, testStr2); printf(testStr3);

De uitvoer is nu "hallo wereld!"

String compare (strcmp)

2 strings worden vergeleken.

Het resultaat is 0 als ze gelijk zijn.

Het resultaat is negatief als het 1e ongelijke teken een lagere waarde heeft in de 1e string dan in de 2e, anders positief.


Structures

Een array is een collectie van gelijksoortige elementen (allemaal gehele getallen, of allemaal tekens), een structure is een verzameling ongelijksoortige elementen (vergelijk het met een handtas waar van alles in zit).

Voorbeeld:

struct testStruct { int a; char b; float c; char d[3]; }

Je initialiseert ze net als arrays:
testStruct test1 = {13, 'b', 3.59, "aap"};

Je refereert aan de elementen van een structure m.b.v. de naam van de structure, dan een punt, en dan de naam van het veld waar je iets mee wilt doen. In bovenstaand voorbeeld bv. test.b of test.d[2]


Bestanden (files)

File functies

Openen van een bestand (fopen)

Lezen van een bestand (fread)

Een voorbeeld van lezen:

FILE * pFile; char buffer[20] = {0}; pFile = fopen("testfile.txt", 'r'); pFile = fread("testfile.txt", 1, sizeof(buffer), pFile); fclose(pFile);

Schrijven naar een bestand (fwrite)

Een voorbeeld van schrijven:

FILE * pFile; char buffer[] = "Hello"; pFile = fopen("testfile.txt", 'w'); pFile = fwrite("testfile.txt", 1, sizeof(buffer), pFile); fclose(pFile);

Pointers

Een pointer definieer je als volgt: int *xptr
xptr is de naam van de pointer, int het type.

De compiler is niet erg kieskeurig over waar je de asterisk in de declaratie precies zet, dus bv. de volgende opties zijn ook OK (zij het verwarrend):
int* xptr
int*xptr
int (*xptr)

En zo geef je hem een waarde:
int x;
xptr = &x;

De variabele (pointer) xptr bevat nu het adres van variabele x

In dit stadium refereren &x en xptr beide aan het adres van x, en x en *xptr beide aan de waarde van x

Pointers en arrays

Stel je hebt een 2-dimensionale array
int matrix[10][20];

Je benadert een element bv. met
matrix[4][2]

De naam van een array is een pointer naar het begin van de array.

In het geval van bovenstaande meerdimensionale array is er een verschil tussen int (*matrix)[20] en int *matrix[20]. Het 2e geval is een array van 20 pointers, het 1e van een pointer naar een array van 20 integers.

Als je de geheugenruimte dynamisch wilt alloceren, kan je ook dit doen:
int (*matrix1)[20] = (int (*)[20]) malloc(20*10*sizeof(int))

In C++ typecasting (het stuk tussen haakjes na het '='-teken) is verplicht.

Voorbeeld van invalid address

De volgende code produceert een fout:

int *ptr = 100; *ptr = 7;

Je probeert de waarde 7 te schrijven naar het adres dat in de variable ptr zit, nl. 100. En dat adres is niet gealloceerd.

Alle pointers zijn 32 bits groot in een 32-bit besturingssysteem. Het verschil merk je wanneer je berekeningen gaat uitvoeren met pointers. Afhankelijk van het datatype waar hij naar wijst kan het verhogen van 1 leiden tot het verhogen met 1, 2, 4, etc.

Dus na
int *xptr = 100;
xptr = xptr+1;
is de waarde van xptr met 4 verhoogd (omdat een integer 4 bytes in beslag neemt).

Met xptr + 1 vertel de je de compiler niet dat hij naar het volgende adres moet gaan, maar naar het adres van de volgende integer.


Assembly code in je programma opnemen

Dat doe je als volgt:

__asm { assembly instructies }

In het stuk assembly code kan je gewoon refereren aan bv. een variabele die je ervoor gedeclareerd hebt. Dus met een instructie als:
MOV a,EAX (waarbij a de variabele is).


Aandachtspunten

Expressies hebben een waarde

Een statement als x = 5
kent aan de variabele x de waarde 5 toe.

Maar de expressie als geheel heeft ook de waarde 5. Je kan dus ook het volgende statement maken: y = x = 5
(y krijgt ook de waarde 5)

De expressie x == 5 heeft de waarde 0 of 1

False of true (onwaar of waar)

Elke waarde ongelijk aan 0 is waar, de waarde 0 is onwaar.

Array index

De laagste index in een array is 0.

Als je een array van 500 elementen declareert, loopt de index range van 0 t/m 499.

Calling conventions

In C++ beheert de caller de stack, en moet dus ook de stack weer opruimen na afloop van de call. Vaak voert de callee (de aangeroepen functie) deze taken uit.

Het voordeel van de C-methode is dat je een variabel aantal argumenten mee kan geven aan de call. Het nadeel is de dat de code groter wordt.

Je bepaalt welke conventie gebruikt wordt door te kiezen tussen 1 van deze 2 vormen van declaratie:

int _stdcall funca(...) int _cdecl funcb(...)

Troubleshooting

Syntax

In geval van syntaxfouten, kan je het beste eerst checken of alle haakjes (zowel de gewone, als de curly braces (zoals '}') in balans zijn.

Een andere, veel voorkomende, fout is het ontbreken van een ';' (semicolon, puntkomma) aan het eind van een statement.

Verder kan je checken of alle variabelen gedeclareerd zijn. Daarbij moet je bedenken dat C(++) case sensitive is!!!


Stack frame

Voor debugging doeleinden is het goed te weten hoe de stack in elkaar zit.

Die bestaat uit (van hoge naar lage adressen) achtereenvolgens:


Memory allocation

Global en static variabelen zijn onderdeel van de binary, en worden er tegelijk mee geladen.

Stack variabelen worden tijdens execution time geladen, en bestaan maar kort. Als je een hele grote variabele (array, etc.) lokaal binnen de functie declareert, loopt je kans op een stack overflow. In dat geval kan je hem beter globaal declareren, dan wordt hij gewoon statisch onderdeel van het programma. Op de heap kan ook, zie hieronder.

Dynamic of heap allocation wordt gedaan door C-functies als malloc of calloc. Een heap is een pool of lijst van allocated blokken geheugen. Ze blijven bestaan tot er een free call gedaan wordt.

Memory functies

Malloc

int *xptr = (*int)malloc(2000); int *yptr = (*int)malloc(2000);

stdlib.h is nodig voor deze call.

Memcpy

Kopiëert het geheugen van 1 plek naar een andere.

memcpy(yptr, xptr, 500);

Memset

Geeft bepaalde geheugencellen een waarde.

memset(xptr, 'z', 200);

string.h is nodig voor deze call.


C/C++ Programming Series 1 Part 11 - Strings in C programming
Een uitgebreide serie YouTube-filmpjes (sommige meer dan een half uur) over programmeren in C, waarbij je ook veel leert over Visual Studio.
Retrieving the Last-Error Code
Documentatie op de site van Microsoft over het ophalen van de laatst opgetreden fout.

    Tweeten

© Henk Dalmolen
Reageer via E-mail (dalmolen@xs4all.nl)

Deze pagina is voor het laatst gewijzigd op: 24-01-23 20:37:58