#Klooienmetcomputers

C

Arnout van Kempen over rommelen in een digitale wereld.

Waar veel programmeertalen werken met 'een programma' waarin eventueel sub-programma's, routines, of functies aangeroepen kunnen worden, is in C het programma zelf een functie. Commando's die je gebruikt? In principe ook functies. En dat betekent dat de basisvorm van zowel je programma als van commando's veelal hetzelfde is: de naam van de functie, argumenten tussen haakjes, en de functie kan een waarde opleveren. Als je zelf functies schrijft, zal je willen coderen wat de functie doet. Deze code plaats je tussen { en }, en functies en eventueel andere opdrachten houd je gescheiden door ;

De naam van de functie die je programma IS, is altijd main. En de uitkomst van main is een waarde die het programma terug zal geven aan het operating system waarop je je programma uitvoert. Gewoonte is hiermee een foutcode mee te geven, waarbij 0 staat voor foutloos, en andere waarden kan je zelf definiëren. Hierdoor kan je bijvoorbeeld een programma “Hallo” aanroepen in de shell, en meteen testen op de correcte werking. Bijvoorbeeld in  de standaard shell van Pi OS, de bash-shell, zou je dan zoiets kunnen doen als

if [Hallo -eq 0] then echo dat ging goed

Hiermee wordt je programma Hallo uitgevoerd, en als het goed ging krijg je te horen dat het goed ging. Maar terug naar C.

Als je programma main heet en een functie is, dan zal je die een type mee moeten geven, bij voorkeur int. Als je geen resultaat wil meegeven, kan void ook, maar dat is dus wat minder netjes. Verder moet je argumenten benoemen in de functiedefinitie. Die argumenten krijgt je programma ook weer mee vanuit het operating system, maar je kan er ook voor kiezen geen argumenten te gebruiken. In dat geval benoem je de argumenten als void, of je benoemt ze helemaal niet. De volgende definities zijn dus allemaal bruikbaar, waarbij de eerste zowel netjes als kort is:

int main(){return 0;}
int main(void){return 0;}
void main(){}
void main(void){}

Let op, bij de eerste twee moet een return waarde worden gegeven, bij de laatste twee niet, aangezien de laatste twee als type void hebben meegekregen.

De accolades gaan nog veel gebruikt worden, en je krijgt accolades binnen accolades, dus het is handig om het overzicht te bewaren. Dat doe je door bij elkaar horende accolades in dezelfde kolom te zetten, en na een accolade altijd in te springen. Geany, Runestone en nano doen dat allemaal automatisch, en zo niet dan kan je dat standaard alsnog aanzetten. Afhankelijk van je code-editor krijg je ook nog handige voorzieningen als code-herkenning (soorten code krijgen automatisch een kleur om de leesbaarheid te vergroten), automatisch sluiten van code-blokken en dergelijke, en code-completion. Gewoon proberen, dat blijkt vanzelf en dan kan je bekijken wat je handig vindt werken.

Als je functies gaat schrijven moet je er in C altijd rekening mee houden dat de variabelen die je gebruikt niet overal dezelfde betekenis hebben. Om te beginnen een simpel voorbeeld waar nog niets spannends aan de hand lijkt:

#include<stdio.h> //header file die je altijd wel nodig hebt

/* eerst een functie die twee integers optelt.

Het resultaat wordt als uitkomst van de functie terug gegeven */

int tel_op(int a, int b)
{
     int r = a+b;
     return r;
}

int main()
{
     int a = 10;
     int b = 20;
     int som;
     som = tel_op(a,b);
     printf("De som van %d en %d is %d\n", a, b, som);
     return 0;
}

Compileer dit, en run het, en je krijgt de correcte uitkomst te zien.

Toch zit hier al een paar bijzonderheden in om op te wijzen. Natuurlijk de manier waarop de printf() functie variabelen in een tekst verwerkt en het feit dat je variabelen zowel met een beginwaarde als zonder beginwaarde kan declareren.

Maar in dit programmaatje is de naamgeving van de variabelen in de functie tel_op onverstandig gekozen. De a en b in tel_op() zijn namelijk NIET de a en b uit main(). Ze mogen dezelfde naam hebben, omdat ze in verschillende functies worden gedeclareerd, maar omdat ze feitelijk verschillend zijn is het handiger ze ook verschillende namen te geven.

Los daarvan is een naam als "som" of "tel_op" net wat duidelijker dan "a" of "b". In een wat groter programma waar je langer aan werkt, is het slimmer namen te kiezen die ook nog betekenis hebben als je er een maand later weer eens naar kijkt.

Verder mag je de volgorde van functies omdraaien, dus beginnen met main() en daarna pas tel_op() schrijven, maar de compiler geeft je dan wel een waarschuwing dat je de functie aanroept voordat je deze definieert. Niet erg, wel net iets slordiger.

Tenslotte nog een ding: C staat extreem compact coderen toe, maar het komt de leesbaarheid niet altijd ten goede. In dit geval zal het niet zoveel uitmaken, maar bedenk dat

int r = a+b;

net iets minder leesbaar is dan

int r;
r = a+b;

maar leesbaarder is dan

return a+b;

Liefhebbers van netjes programmeren zullen sneller kiezen voor de meest leesbare vorm; persoonlijk vind ik het vooral een kwestie van smaak. Het gebruik van toelichting, spaties, inspringen, etcetera: ook allemaal een kwestie van smaak. Maar bedenk dat het hiervoor gegeven programmaatje exact hetzelfde doet als

#include<stdio.h>
int t(int a,int b){return a+b;}
void main(){int a=10,b=20;printf("De som van %d en %d is %d\n",a,b,t(a,b));}

Gebruik GitHub om te klooien met de computer!

Wie mee wil doen met #klooienmetcomputers, maar niet alle teksten van Arnout wil overtypen, of de eigen code wil delen met andere lezers, kan dat doen via GitHub:

1. Maak een account op www.github.com

2. Zoek naar Abmvk/kmc

Het account Abmvk volgen kan ook.

Arnout plaatst daar alle stukjes code voor #klooienmetcomputers, met als naam eerst een volgnummer van het stukje waar de code bij hoort en dan een term uit de titel. Zaken die bijvoorbeeld in een config-bestand thuishoren plaatst hij als .txt bestand, wat overtypen kan besparen. Ook plaatst hij stukjes code waar hij zelf mee rommelt en die niet in een stukje op accountant.nl terechtkomen. Die stukjes zijn te herkennen omdat er geen volgnummer voor staat.

Lezers zijn vrij te gebruiken wat ze willen en om zelf zaken toe te voegen of aan te passen, vragen te stellen of commentaar te leveren. Samen kom je een eind. Om dit goed te laten werken: maak een clone van Arnout’s repo, haal met pull nieuwe bestanden binnen en voeg met push zelf toe. Wie er niet uitkomt: laat het Arnout weten.

Arnout van Kempen di CCO CISA is directeur compliance & risk bij aaff, de fusieorganisatie van Alfa en ABAB. Hij schrijft op persoonlijke titel.

Gerelateerd

6 reacties

Arnout van Kempen

Inderdaad Ron, ChatGPT is een fantastische tool, zeker voor mensen die met hun computer klooien! Ik werk eigenlijk altijd met een scherm waarop ChatGPT aan staat. Tegelijk last je voorbeeld goed de grenzen zien. Want hoewel het commentaar van ChatGPT feitelijk klopt, klopt het toch niet omdat het context mist.

Alexander Vissers

Van de syntax lezen en schrijven wordt je een beetje gestoord, en met dislexie ben je verloren, daarom is chat gpt zoals Ron Heinen aangeeft een goed alternatief. En verder gewoon de libraries plunderen in plaats van zelf het wiel uitvinden.

Ron Heinen

Voor deze discussie is het mogelijk inzichtgevend dat GPT-4 code kan genereren maar ook controleren.

Hieronder de controle van een van je C programma's op github met GPT-4:

Can you verify the following C code on errors, overflows, risks, etc

Sure! Can you please provide me with the code you would like me to verify?

#include
#include

int main()
{
char a=250;

for( int t=250; t<270; t++)
{
++a;
printf("t = %d a= %d\n",t ,a);
}

return 0;
}

The code you provided is a simple C program that declares a char variable a with an initial value of 250. The program then enters a for loop that iterates from 250 to 270, incrementing the value of a by 1 in each iteration and printing the values of t and a.

However, there is an issue with this code. The char data type can only hold values in the range of -128 to 127. When you increment the value of a beyond its maximum value, it will overflow and wrap around to its minimum value. This can cause unexpected behavior in your program.

To avoid this issue, you can change the data type of the variable a to an integer type that can hold larger values, such as int or long.

Here is an example of how you could modify the code to avoid this issue:

#include
#include

int main()
{
int a = 250;

for (int t = 250; t < 270; t++)
{
++a;
printf("t = %d a= %d\n", t, a);
}

return 0;
}

Ron Heinen

@Arnout van Kempen

Een voorbeeld van de snelheid van een C programma kun je vinden in

https://math4optimization.wordpress.com/2020/10/18/python/

Op deze link kun je een wiskundig bewijsbare oplossing vinden van het handelszeizigersprobleem geimplementeerd in C. Als je het programma uitvoerd dan zie je hoe snel dit is.

Merk op dat het handelsreizigersprobleem tot de complexiteitsklasse NP-volledig behoort, zie

https://nl.wikipedia.org/wiki/NP-volledig

Met deze theorie kun je dus met dit C programma elk programma oplossen in deze NP complexiteitsklasse!

Arnout van Kempen

Ik durf wel te beweren dat C, op assembly na, de minst vergevingsgezinde taal is die er is. Maar het is ook een betrekkelijk makkelijke taal, met een eenvoudig te begrijpen programmeerparadigma, en voor rommelaars een buitengewoon goede toegang tot alle mogelijkheden van een computer. En inderdaad, héél veel online hulp :)

Ron Heinen

Voor lezers welke zich meer in C willen verdiepen is https://www.learn-c.org/ een aardige website.

Een voordeel van C is dat het programma erg snel is in de uitvoering.

Een nadeel is de foutgevoeligheid van de taal. Google, Linux en Microsoft zijn daarom overgestapt van de taal C naar de taal Rust, zie ook mijn reactie op

https://www.accountant.nl/achtergrond/2023/4/apache-mysql-en-php/

Reageren op een artikel kan tot drie maanden na plaatsing. Reageren op dit artikel is daarom niet meer mogelijk.

Aanmelden nieuwsbrief

Ontvang elke werkdag (maandag t/m vrijdag) de laatste nieuwsberichten, opinies en artikelen in uw mailbox.

Bent u NBA-lid? Dan kunt u zich ook aanmelden via uw ledenprofiel op MijnNBA.nl.