C-taal voor beginners - referenties en dankbetuigingen
[rd1 referenties] [rd2 dankbetuigingen] [rd3 personen]
Ik gebruikte veel naslagwerken bij het samenstellen van deze cursus. U kan de meeste ook op het internet vinden (Engels):
Deze staan ook vermeld in het C-gedeelte van ECC: the Programming section.
Vervolgens gebruikte ik stukjes uit de C-cursus van KHK Geel.
Bekijk ook de referenties aan het einde van elk hoofstuk. Ik raad u ook aan om het handboek "The C programming Language", versie 2 aan te schaffen. Dit is het beste en meest verkochte naslagwerk voor C, geschreven door de ontwerper van de C-taal. De originele Engelse versie is waarschijnlijk vollediger, maar de Nederlandstalige zal ongetwijfeld voldoende zijn.
Controleer ook de verschillende nieuwsgroepen over de C-taal.
Vele mensen droegen bij tot dit naslagwerk. Ik zou vooral de mensen van #computer_fixer en #computer_programmer op de irc Microsoftserver (zie inleiding) willen bedanken. Verder haalde ik veel informatie uit de nieuwsgroepen comp.lang.c en comp.lang.c.moderated, waar ontelbare personen hun hulp boden.
Volgende personen droegen direkt bij tot het verbeteren en optimaliseren van de curus, zoals hij vandaag bestaat. Eventuele sectienummers en dergelijke zijn mogelijk niet meer geldig. Ik wil deze personen hierbij bedanken voor hun zeer waardevolle input:
Niels Basjes - 11, 12 en 13 aug 1998
i2 : Voeg referenties naar djgpp (URL: www.delorie.com ofzoiets?) en egcs.cygnus.com toe. Dat zijn GRATIS C/C++ compilers die ook onder DOS/Windows draaien.
i4 deze gids is geschreven voor MS-Windows : WAAROM ??? Een beginners cursus C moet beperkt blijven tot de dingen die je nodig hebt om een console applicatie te schrijven. Deze opmerking deed mij vrezen dat je de Windows API ging behandelen maar er is bijna niets dat Windows specifiek is.
1.4.2 ,1.5.1 en 1.5.2 short,int,long: Je kunt er NIET vanuit gaan dat een "int" 16 of 32 bits is. Dat is helaas niet duidelijk gedefinieerd. Onder DOS is een int meestal 16 bits en een long 32 bits. Onder Win95/98/NT is een int meestal 32 bits en een long 32 bits. Er zijn echter ook systemen waar een long 64 bits is. Dit is een belangrijk punt want daar is die ariane raket een paar jaar geleden door uit de lucht gevallen (dure bug). Kort samengevat: die types zijn niet 100% duidelijk gedefinieerd.
2.1 De bit operatoren ontbreken : "&" en "|"
2.1.1 Dit : g1 = g2 = g3 = g4 = g5 = g6 = g9 = f1 = f2 = f3 = 0; werkt wel maar de volgorde waarin dat gebeurd is niet gedefinieerd. In jouw voorbeeld geen probleem maar dit kan in bepaalde gevallen problemen opleveren, vooral als je overgaat naar C++. Laat het gewoon niet zien: levert minder problemen op.
2.2.4 Dit mag niet in C (wel in C++) : for(int index=0;index<6;index++). Je MOET (moet van je compiler!!)dit herschrijven tot: int index; for(index=0;index<6;index++)
3.1 Wat je zegt over dat strings 0x00 terminated zijn klopt. Wat echter glad ijs is "NULL (0)". In de meeste C compilers is dat inderdaad zo. De nieuwe C++ (niet C maar C++ !!) standaard definieerd echter dat NULL != 0.
3.4 type fout : hooofdstuk 1 (3x o)
3.5.4 strlen voorbeeld: Bij statische strings mag je zeggen: char string[] = "Een voorbeeldstring";
3.5.5 strchr voorbeeld BOEM : You die !
char string[15]; strcpy(string, "Dit is een string");
12345678901234567Dit noemt purify een ABW (Array Bound Write): je schrijf meer elementen dan er voor je array gereserveerd zijn. Dit zal er heel vaak voor zorgen dat je programma crasht.
4.1 : Het is netter om de declaratie van bereken buiten de scope van main() te doen. Er staat geen return 0; aan het eind van main.
Jij zegt: "Beter is natuurlijk: int functie1(), functie2();" Dat is helemaal niet beter want het is moeilijker te onderhouden.
4.2 Jij zegt: b = 14.6847. Belangrijk punt (misschien dat je dat nog ergens anders opmerkt): meestal geldt echter a!=b omdat je floats (of doubles) met elkaar vergelijkt.
Probeer maar
float x = 1.0;
float y = 2.0/2.0;
if (x != y)
etc.4.4.1: "auto" gewoon niet noemen omdat het met de huidige compilers zinloos is.
4.4.2: hier klopt geen hout van. In jouw voorbeeld is x een globale variabele en zeker GEEN externe variabele.
Voorbeeld van extern en static:
test1.c bevat int x;
test2.c bevat extern int x;Als je nu beide sources samen tot 1 programma linkt dan heb je het over dezelfde geheugen lokatie.
Hetzelfde geldt voor functies:
test1.c bevat int Doit(){return5;};
test2.c bevat extern int Doit();als je test1.c nu veranderd in "static int Doit(){return5;};" dan zul je bij het linken een fout melding krijgen dat hij de "Doit()" die test2.o wil hebben niet kan vinden.
4.4.4: weglaten dat werkt namelijk helemaal niet op alle compilers en als helemaal nooit op dezelfde manier.
5: Je bent malloc en free helemaal vergeten.
5.2 : heel leuk dat je pointer rekenkunde gaat uitleggen maar dat is NIET portable naar andere machines. Het probleem is dat jij er vanuit gaat dat het adres van mijn_array[0] lager is dan het adres van mijn_array[1]. Op je PC (en vele andere machines) zal dit best kloppen maar daar kune je NIET 100% zeker van zijn. Er zijn namelijk machines die het precies andersom doen (het zijn er niet veel ,maar toch een reel probleem).
5.3: Je legt uit dat a[3] = 'x'; hetzelfde is als 3[a] = 'x'; Dit klopt maar zou ik er niet in zetten. Als ik je project leider ben mag je het over doen omdat dit onder hacken valt en dus zeker niet geschikt is voor beginners (je doelgroep voor deze tutorial).
6: Algemene opmerking over macros: Gebruik ze zo min mogelijk en het liefst niet voor functies. Een functie die "inline" gedefinieerd is is meestal net zo snel maar VEEL beter te debuggen dan een macro. Ik heb wel eens code afgekeurd omdat iemand complete while lussen in een macro had gepropt.
6.2: #define CUBUS(A) (A)*(A)*(A) is ook fout. Het moet zijn: ((A)*(A)*(A))
Toevoeging: Leg de performance consequenties uit van macros:
Voorbeeld van wat ik bedoel:
#define CUBUS(A) ((A)*(A)*(A)) en float CUBUS(A){return A*A*A;};als je bijvoorbeeld aanroept: CUBUS(sqrt(5.0));
Bij de macro wordt sqrt 3 keer aangeroepen en bij de funtie maar 1 keer. Roep nu eens CUBUS(CUBUS(sqrt(5.0))); Probleem duidelijk ?
6.3
Practische variant op jouw debug macro die beter leesbare code oplevert:
#ifdef DEBUG
#define DEB_MSG(a) printf(a);
#else
#define DEB(a)
#endifDan kun je je programma vol zetten met DEBUG informatie regels als DEB_MSG("We zitten nu in functie XYZ()\n") Als je DEBUG hebt gedefinieerd dan wordt dat afgedrukt en anders niet.
7.1 Altijd expliciet met het goede type je files openen. In dit geval dus "wt". Dit staat helemaal niet genoemd. Altijd controleren of het gelukt is (maak maar eens een TIENLIJNEN.TXT en maak die readonly)
7.4.2: Een ervaren C-programmeur zal de volledige structuur van lijn 1 t.e.m. 2 vervangen door:
while( (c = fscanf(fp1, "%s", eenwoord) != EOF)
{
printf("%s\n", oneword);
}Ik zal structureel een assignment in de conditie van een if of while afkeuren. Ik werk normaal gesproken met standaarden waar dit soort grappen juist vanwegen de leesbaarheid verboden worden.
Beter zou b.v. zijn:
c = fscanf(fp1, "%s", eenwoord);
while(c != EOF)
{
printf("%s\n", oneword);
c = fscanf(fp1, "%s", eenwoord);
}De volgende zijn NIET Windows specifiek (heb ik allemaal op Linux):
8.2 system
8.5 random en randomize
8.6 chmod is zelfs gejat van UNIX
8.7 isalha
8.8 sleep (alleen is het dan een andere header file).Ik denk dat jij "Windows95 specifiek" en "NIET conform de ANSI standaard" gedeeltelijk door elkaar hebt gehaald.
Hoofdstuk 9: two's-complement representatie van signed integers ontbreekt.
10.6
c = fgets(eenwoord, 100, fp1);
/* Controleren of het bestand geopend kon worden. */
if (c != NULL)Je commentaar klopt niet :-)
10.7 Je gebruikt de "," komma-operator. Die mogen ze van mij uit alle compilers slopen : NIET GEBRUIKEN want hij levert alleen maar ellende op.
Toevoegen: Plaatsing van haakjes:
Dit:
if(...)
{
xxxxx;
}
else
{
xxxxx;
}is veel duidelijker dan
if(...) {
xxxxx;
} else {
xxxxx;
}Des ondanks komt je dit laatste nog steeds heel vaak tegen omdat Kernigan en Ritchie het op die manier deden in hun boek.
Toevoegen: Gebruik geen TABs in je source code: als je het afdrukt dan zie je vanzelf waarom: De breedte van een TAB is overal anders gedefinieerd: meestal 8 of 4 maar soms tot de eerst volgende tabstop (dat is dus 1 of 2 of 3 ..... of 8).
Toevoegen: Geen commandos op 1 regel proppen.
Dus NIET: if ((fp=fopen(.....)) != NULL)
Maar wel: fp=fopen(.....);
if (fp == NULL)En al helemaal nooit (dit heb ik werkelijk gezien):
if (fp=fopen(.....))
Jij schrijft:
c = (a == b)
? d + f(A)
: f(b) - d;Probeer die ?: constructie zoveel mogelijk te voorkomen.
s3.6 : waarom is long volgens jou geen elementair type ??
Thomas O. Matthews - 11 aug 1998
Leverde de optimalisatietips in hoofdstuk 4.
Arjan Bokx - 18 aug 1998
- Programma's in de voorbeelden beginnen nog steeds met void main
- Programma's in de tekst zijn numain()
maar de komende ISO C standaard zal dit niet meer toestaan. Het is wellicht beter om de mensen (toekomstige C-programmeurs! de bloem van de natie etc. :-) nu alvast de goede vorm aan te leren:
int main(void)
Ook is er geen return aan het eind van de main. Dit betekent dat het programma een ongedefinieerde waarde teruggeeft aan het OS. Misschien dat sommige OS'en hier tegen kunnen, maar voor andere is het onveilig! Je hebt het n.b. in 4.1.1.1 geschreven maar zelf hou je je er niet aan.
1
programma 1-3.c: Er zijn 365 dagen in 1998, niet 356 (of is dit een 'zoek de fout'-opdracht?2
'gedefinieerd' moet zonder trema op de e3.1
NULL is niet hetzelfde als het null character! Je hebt het n.b. in 5.3 geschreven maar zelf hou je je er niet aan. NULL is de 'NULL pointer' en zou gedefinieerd kunnen zijn als (void *) 0 . Een pointer past waarschijnlijk niet eens in een char. Voor een null character kun je gewoon 0 of '\0' gebruiken.3.2
'definieert' moet zonder trema3.4
'bestudeert' moet met een d op het eind3.5.1
'kopieer', 'kopieert', 'gekopieerd' moeten zonder trema (enz.)3.5.4
Bij een declaratie heet het nog geen index, maar eh, tja... wat vind je van, gewoon, 'lengte' of 'grootte' ?3.5.6
het newline teken is '\n' , geen "\n"3.8
'wa' (typo) 'we' (ik ben nou toch bezig!)4.4.1
'(...)dat u de automatische geheugenklasse helemaal niet moet kennen(...)' Het is juist een heel belangrijke geheugenklasse - alleen het woordje 'auto' hoeft niemand meer te kennen.4.4.2
'komt op de stack' Niet noodzakelijk - het is niet gezegd dat er met een stack wordt gewerkt (meestal wel het geval, maar toch niet altijd).4.4.3
'statsiche' (typo) 'statische'4.5.1 t/m 4.5.3
Dit is allemaal heel systeemspecifiek. Het is overal anders.opdracht 2
'functieoverloading' ??!? Dat lukt dus niet in C. Variable argument lists wel, maar dat is voor een later hoofdstuk...5.1
-De grootte van de diverse typen hangt af van de compiler.
-'gekopieert' moet met een d op het eind, en zonder trema
-'naar welk type de compiler moet wijzen' ... de variabele moet wijzen5.2
'kan niet verandert worden' veranderd5.3
-null character is niet "\0" maar '\0'
-'Ik wijk hier lichtjes af van de vorm die in standaard C gebruikt wordt' Waar schend je de standaard dan? Volgens mij is jouw vorm ook standaard C.
-"X" "T" "U"
Het is verwarrend om karakters telkens tussen dubbele quotes te zetten in de tekst.
-'Ik gebruik hier voor de volledigheid de Engelse benamingen voor "bestemming" en "bron".' Vreemd gebruik van het woord 'volledigheid' vind ik.
-source[i] is hetzelfde als *(source+i) , niet als *(p+i) (waar komt p vandaan?)
-'optellingen meer tijd in beslag dan incrementoperators' Welke incrementoperators?
-'de pointerversie kan een beetje sneller zijn dan de arrayversie'
Ligt eraan. Op sommige platforms is het net andersom en dan werk je jezelf tegen.5-1.c
/* een en twee zijn identiek */
Dat zijn ze pas na de toekenning aan twee, het commentaar staat dus een regel te hoog.
IQnullus - 18 aug 1998
In c_tutor\beginner\vbn\1-3.c staat een fout: het rekent geen seconden uit, maar de minuten van 1998. Verder telt een jaar geen 356, maar 365 dagen.
Verder naar copyright en disclaimer