C Rekenmachine Bouwer – Stapsgewijze Calculator
Hoe Maak Ik een Rekenmachine in C: Complete Gids
Het bouwen van een rekenmachine in C is een uitstekende manier om je programmeervaardigheden te verbeteren. Deze gids behandelt alles van basisrekenkunde tot geavanceerde wetenschappelijke functies, met praktische voorbeelden en beste praktijken.
1. Basisconcepten voor een C Rekenmachine
1.1 Vereiste Kennis
- Basis C-syntaxis (variabelen, lussen, voorwaarden)
- Functies en hun parameters
- Pointers en geheugenbeheer
- Basiswiskundige operaties
1.2 Benodigde Bibliotheken
Voor een basisrekenmachine heb je alleen stdio.h nodig. Voor geavanceerde functies:
#include <stdio.h> #include <math.h> // Voor sin, cos, log, etc. #include <stdlib.h> // Voor exit(), atof()
2. Stapsgewijze Implementatie
2.1 Eenvoudige Command-Line Rekenmachine
Begin met een programma dat twee getallen accept en een bewerking uitvoert:
#include <stdio.h>
int main() {
char operator;
double num1, num2;
printf("Voer operator in (+, -, *, /): ");
scanf("%c", &operator);
printf("Voer twee getallen in: ");
scanf("%lf %lf", &num1, &num2);
switch(operator) {
case '+':
printf("%.2lf + %.2lf = %.2lf", num1, num2, num1 + num2);
break;
case '-':
printf("%.2lf - %.2lf = %.2lf", num1, num2, num1 - num2);
break;
case '*':
printf("%.2lf * %.2lf = %.2lf", num1, num2, num1 * num2);
break;
case '/':
if(num2 != 0)
printf("%.2lf / %.2lf = %.2lf", num1, num2, num1 / num2);
else
printf("Fout! Delen door nul.");
break;
default:
printf("Ongeldige operator!");
}
return 0;
}
2.2 Geavanceerde Functies Toevoegen
Voeg wetenschappelijke functies toe met math.h:
#include <math.h>
// Voeg deze cases toe aan je switch statement
case 's':
printf("sin(%.2lf) = %.2lf", num1, sin(num1));
break;
case 'c':
printf("cos(%.2lf) = %.2lf", num1, cos(num1));
break;
case 't':
printf("tan(%.2lf) = %.2lf", num1, tan(num1));
break;
case 'l':
printf("log(%.2lf) = %.2lf", num1, log(num1));
break;
3. Geheugenbeheer en Pointers
3.1 Dynamisch Geheugen Gebruiken
Voor complexe rekenmachines met historische gegevens:
#include <stdlib.h>
typedef struct {
double operand1;
double operand2;
char operation;
double result;
} CalculationHistory;
CalculationHistory* history = NULL;
int historySize = 0;
void addToHistory(double op1, double op2, char op, double res) {
historySize++;
history = realloc(history, historySize * sizeof(CalculationHistory));
history[historySize-1] = (CalculationHistory){op1, op2, op, res};
}
3.2 Best Practices voor Pointers
- Controleer altijd op NULL na malloc/calloc
- Gebruik altijd free() om geheugenleks te voorkomen
- Overweeg smart pointers in C++ als je met complexe projecten werkt
4. Foutafhandeling en Validatie
4.1 Input Validatie
Voorkom crasht door onjuiste input:
if(scanf("%lf", &num1) != 1) {
printf("Ongeldige input!\n");
// Wis de input buffer
while(getchar() != '\n');
continue;
}
4.2 Veilige Berekeningen
| Potentieel Probleem | Oplossing | Voorbeeld Code |
|---|---|---|
| Delen door nul | Controleer noemer voor berekening | if(denominator == 0) { /* fout */ } |
| Overflow/underflow | Gebruik grotere datatypes | long double voor grote getallen |
| Ongeldige operator | Gebruik switch met default case | default: printf("Ongeldig"); |
| Geheugenlek | Gebruik valgrind om te testen | valgrind ./je_programma |
5. Geavanceerde Onderwerpen
5.1 Grafische Interface met GTK
Voor een GUI-rekenmachine:
#include <gtk/gtk.h>
static void on_activate(GtkApplication* app) {
GtkWidget *window = gtk_application_window_new(app);
// Voeg hier je widgets toe
gtk_window_present(GTK_WINDOW(window));
}
int main(int argc, char **argv) {
GtkApplication *app = gtk_application_new("org.example.calculator", G_APPLICATION_DEFAULT_FLAGS);
g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
return g_application_run(G_APPLICATION(app), argc, argv);
}
5.2 Multithreading voor Complexe Berekeningen
Gebruik threads voor zware berekeningen:
#include <pthread.h>
void* calculate(void* arg) {
// Zware berekening hier
pthread_exit(NULL);
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, calculate, NULL);
pthread_join(thread, NULL);
return 0;
}
6. Prestatie Optimalisatie
6.1 Compiler Optimalisaties
Gebruik GCC optimalisatie flags:
gcc -O3 -march=native -mtune=native je_programma.c -o calculator -lm
| Optimalisatie Flag | Beschrijving | Prestatie Impact |
|---|---|---|
| -O0 | Geen optimalisatie | Basislijn |
| -O1 | Basis optimalisaties | 10-20% sneller |
| -O2 | Extra optimalisaties | 20-30% sneller |
| -O3 | Agressieve optimalisatie | 30-50% sneller (kan debuggen bemoeilijken) |
| -march=native | Optimaliseer voor lokale CPU | 5-15% extra |
6.2 Inline Assembly voor Kritische Secties
Voor maximale prestaties in performance-kritische delen:
__asm__("fld %1;\n"
"fld %2;\n"
"faddp;\n"
"fstp %0;"
: "=m"(result)
: "m"(num1), "m"(num2));
7. Testen en Debuggen
7.1 Unit Testing met Check
Gebruik het Check framework voor automatische tests:
#include <check.h>
START_TEST(test_addition) {
ck_assert_double_eq(add(2, 3), 5);
}
END_TEST
Suite *calc_suite(void) {
Suite *s = suite_create("Calculator");
TCase *tc_core = tcase_create("Core");
tcase_add_test(tc_core, test_addition);
suite_add_tcase(s, tc_core);
return s;
}
int main(void) {
int number_failed;
Suite *s = calc_suite();
SRunner *sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
7.2 Debuggen met GDB
Essentiële GDB commando’s:
gcc -g je_programma.c -o calculator -lm gdb ./calculator break main run print variabele_naam step next continue quit
8. Implementatie Voorbeelden
8.1 Complete Basis Rekenmachine
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define MAX_INPUT 100
double performOperation(double a, double b, char op) {
switch(op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/':
if(b == 0) {
printf("Fout: Delen door nul!\n");
exit(1);
}
return a / b;
case '^': return pow(a, b);
case 's': return sin(b * M_PI / 180); // Graden naar radialen
case 'c': return cos(b * M_PI / 180);
case 't': return tan(b * M_PI / 180);
case 'l': return log(b);
case 'q': return sqrt(b);
default:
printf("Ongeldige operator: %c\n", op);
exit(1);
}
}
int main() {
char input[MAX_INPUT];
double num1, num2;
char op;
printf("Wetenschappelijke Rekenmachine in C\n");
printf("Gebruik: getal [operator] [getal]\n");
printf("Voorbeelden:\n");
printf(" 5 + 3 (optellen)\n");
printf(" 10 - 4 (aftrekken)\n");
printf(" 2 ^ 8 (macht)\n");
printf(" s 30 (sinus van 30 graden)\n");
printf(" q 16 (vierkantswortel van 16)\n");
while(1) {
printf("\nVoer berekening in (of 'exit' om te stoppen): ");
if(fgets(input, MAX_INPUT, stdin) == NULL) {
printf("\nProgramma beëindigd.\n");
break;
}
// Verwijder newline karakter
input[strcspn(input, "\n")] = '\0';
if(strcmp(input, "exit") == 0) {
break;
}
// Parse input
if(sscanf(input, "%lf %c %lf", &num1, &op, &num2) >= 3) {
// Twee-operand bewerking
double result = performOperation(num1, num2, op);
printf("Resultaat: %.4lf\n", result);
}
else if(sscanf(input, "%c %lf", &op, &num2) >= 2) {
// Eén-operand bewerking (wetenschappelijk)
double result = performOperation(0, num2, op);
printf("Resultaat: %.4lf\n", result);
}
else {
printf("Ongeldige input. Probeer opnieuw.\n");
}
}
return 0;
}
8.2 Rekenmachine met Geheugenfunctie
#include <stdio.h>
#include <stdlib.h>
double memory = 0.0;
void clearMemory() {
memory = 0.0;
}
void addToMemory(double value) {
memory += value;
}
void recallMemory(double *result) {
*result = memory;
}
int main() {
// ... (vorige code)
// Voeg deze cases toe aan je switch statement
case 'm':
addToMemory(result);
printf("Resultaat opgeslagen in geheugen (totaal: %.2lf)\n", memory);
break;
case 'r':
recallMemory(&result);
printf("Geheugenwaarde: %.2lf\n", result);
break;
case 'c':
clearMemory();
printf("Geheugen gewist\n");
break;
// ...
}
9. Veelgemaakte Fouten en Oplossingen
9.1 Drijvende Komma Nauwkeurigheid
Probleem: 0.1 + 0.2 ≠ 0.3 door binaire representatie
Oplossing: Gebruik een tolerantie bij vergelijkingen:
#define EPSILON 0.00001
if(fabs(a - b) < EPSILON) {
// a en b zijn "gelijk"
}
9.2 Integer Overflow
Probleem: 2,000,000,000 + 2,000,000,000 = -294,967,296 (met int)
Oplossing: Gebruik grotere datatypes:
long long bigNumber1 = 2000000000; long long bigNumber2 = 2000000000; long long result = bigNumber1 + bigNumber2; // Correct: 4000000000
9.3 Verkeerde Operator Prioriteit
Probleem: result = a + b * c; vs result = (a + b) * c;
Oplossing: Gebruik altijd haakjes voor duidelijkheid:
result = (a + b) * c;
10. Bronnen voor Verdere Studie
Voor diepgaandere kennis over C-programmering en rekenmachine-implementaties:
- GNU GCC Compiler Documentatie - Officiële documentatie voor de GCC compiler met geavanceerde optimalisatie opties
- ISO C18 Standaard - De officiële C programmeertaal specificatie
- Harvard CS50 C Cursus - Gratis online cursus over C programmeren van Harvard University
- NIST Software Testing Resources - Richtlijnen voor software testen en validatie
11. Conclusie
Het bouwen van een rekenmachine in C is een uitstekende oefening die je helpt om:
- Basis C-syntaxis onder de knie te krijgen
- Foutafhandeling en input validatie te leren
- Geheugenbeheer en pointers te begrijpen
- Modulaire programmeertechnieken toe te passen
- Prestatie optimalisaties te implementeren
Begin met een eenvoudige versie en breid geleidelijk uit met geavanceerde functies. Gebruik versiebeheer ( zoals Git) om je voortgang bij te houden en experimenteer met verschillende benaderingen om de beste oplossing voor je specifieke behoeften te vinden.
Onthoud dat de sleutel tot een goede rekenmachine ligt in:
- Robuuste input validatie
- Duidelijke foutmeldingen
- Efficiënte berekeningen
- Gebruiksvriendelijke interface
- Goede documentatie en comments
Met deze kennis kun je nu aan de slag om je eigen geavanceerde rekenmachine in C te bouwen!