← thecodex.expert · The Codex Family of Knowledge
Tier 0 · Absolute Beginner · C Project

Temperature Converter

A small, pure-logic C project: convert a temperature between Celsius, Fahrenheit, and Kelvin, given on the command line. No loops, no state — just one function that either succeeds or reports it can't.

🧠 Teaches how to think spoonfed, every age Last verified:

1 The Problem

We want a small program that converts a temperature from Celsius to Fahrenheit, or the other way around. The user picks which direction, types a number, and the program does the maths and shows the answer. Simple — but it teaches the core shape of every calculator-style tool: ask, calculate, show.

Where this shows up in real life: every app that takes a number and transforms it — a currency converter, a tip calculator, a unit converter, a tax estimator — is doing exactly this. Take input, apply a formula, return a result. Master the pattern here and you can build any of them.

2 How to Think About It

Six conversions, one function. The interesting design question isn’t the math — it’s how a C function reports failure when it has no exceptions to throw.

The plan — in plain English
1. Take a conversion code (like CtoF) and a number. → 2. Look up which formula that code means. → 3. If it’s a real code, compute the result and report success. → 4. If it isn’t, report failure without touching the output. That’s the whole program.

1

2

Ask: convert which way?

Choice 1 or 2?

Read Celsius

Read Fahrenheit

Multiply by 9/5, add 32

Subtract 32, multiply by 5/9

Show Fahrenheit

Show Celsius

3 The Build — explained part by part

The conversion logic lives in convert, which returns a bool and writes its real answer through an output parameter — C’s standard way to report both a result and whether that result is even valid.

CTemperatureConverter.h / TemperatureConverter.c / main.c
#ifndef TEMPERATURE_CONVERTER_H
#define TEMPERATURE_CONVERTER_H
#include <stdbool.h>

/* Converts `value` using `choice` (one of "CtoF","FtoC","CtoK","KtoC","FtoK","KtoF").
 * On success returns true and writes the result to *out. On an unknown choice
 * returns false and leaves *out untouched. */
bool convert(const char *choice, double value, double *out);

#endif

#include "TemperatureConverter.h"
#include <string.h>

bool convert(const char *choice, double value, double *out) {
    if (strcmp(choice, "CtoF") == 0) { *out = value * 9.0 / 5.0 + 32.0; return true; }
    if (strcmp(choice, "FtoC") == 0) { *out = (value - 32.0) * 5.0 / 9.0; return true; }
    if (strcmp(choice, "CtoK") == 0) { *out = value + 273.15; return true; }
    if (strcmp(choice, "KtoC") == 0) { *out = value - 273.15; return true; }
    if (strcmp(choice, "FtoK") == 0) { *out = (value - 32.0) * 5.0 / 9.0 + 273.15; return true; }
    if (strcmp(choice, "KtoF") == 0) { *out = (value - 273.15) * 9.0 / 5.0 + 32.0; return true; }
    return false;
}

#include "TemperatureConverter.h"
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <CtoF|FtoC|CtoK|KtoC|FtoK|KtoF> <value>\n", argv[0]);
        return 1;
    }
    double value = atof(argv[2]);
    double result;
    if (!convert(argv[1], value, &result)) {
        fprintf(stderr, "Unknown conversion: %s\n", argv[1]);
        return 1;
    }
    printf("%.2f\n", result);
    return 0;
}
⚠ No in-browser playground here
C compiles to a real, native binary, so unlike the Python version of this project there is no editor above you can run in the browser. Copy the code below and run it on your own machine — it takes seconds once GCC or Clang is installed.
What each part does — in plain words
bool convert(const char *choice, double value, double *out) — C has no way to return “a number, or nothing” the way Java’s Optional or Rust’s Option can. The idiomatic C pattern instead is an out-parameter: the return value (bool) says whether it worked, and the real answer is written through a pointer only when it did. Callers who ignore the bool and read *out anyway get whatever garbage was already sitting in that memory — which is exactly why the test below checks that a failed call leaves *out completely untouched.

strcmp(choice, "CtoF") == 0 — C has no string switch the way Java or JavaScript does (a switch in C only works on integers), so a chain of strcmp-and-compare calls is the standard, if verbose, C idiom for branching on string content.

const char *choice — marking the parameter const is a promise, checked by the compiler, that convert will never modify the string it was handed.
Common mistakes — and how to avoid them
✗ Returning the result directly as a double and using some sentinel value like -999.0 to mean “invalid” — a legitimate conversion could genuinely produce that number.
✓ Use a separate bool return value and an out-parameter, as shown, so success/failure can never be confused with a real answer.
✗ Reading *out without first checking the bool that convert returned.
✓ Always check the return value first, exactly as main.c does before printing.
✗ Comparing floating-point results with == in tests — rounding error means two mathematically equal values can differ in their last bit.
✓ Compare with a small tolerance instead, as the CLOSE macro in the tests below does.

4 Test & Prove Each Part

C has no built-in test framework, and this sandbox cannot reach a package registry for one, so these tests use plain assert() calls — a small, genuinely real practice for a project this size, covered in the course’s Testing lesson.

0°C converts to exactly 32°F
212°F converts to exactly 100°C
Converting and converting back (C→F→C) returns to the original value
An unknown conversion code returns false and leaves the output untouched
Ctest_TemperatureConverter.c
#include "TemperatureConverter.h"
#include <assert.h>
#include <stdio.h>
#include <math.h>

#define RUN(name) do { name(); printf("PASS: %s\n", #name); } while (0)
#define CLOSE(a, b) (fabs((a) - (b)) < 0.001)

static void freezing_point_c_to_f(void) {
    double out;
    assert(convert("CtoF", 0.0, &out));
    assert(CLOSE(out, 32.0));
}

static void boiling_point_f_to_c(void) {
    double out;
    assert(convert("FtoC", 212.0, &out));
    assert(CLOSE(out, 100.0));
}

static void absolute_zero_c_to_k(void) {
    double out;
    assert(convert("CtoK", -273.15, &out));
    assert(CLOSE(out, 0.0));
}

static void round_trip_c_to_f_to_c_is_identity(void) {
    double f, c;
    assert(convert("CtoF", 37.0, &f));
    assert(convert("FtoC", f, &c));
    assert(CLOSE(c, 37.0));
}

static void unknown_choice_returns_false_and_leaves_out_untouched(void) {
    double out = 999.0;
    assert(!convert("CtoMoon", 10.0, &out));
    assert(CLOSE(out, 999.0));
}

int main(void) {
    RUN(freezing_point_c_to_f);
    RUN(boiling_point_f_to_c);
    RUN(absolute_zero_c_to_k);
    RUN(round_trip_c_to_f_to_c_is_identity);
    RUN(unknown_choice_returns_false_and_leaves_out_untouched);
    printf("All tests passed.\n");
    return 0;
}

Compile and run with gcc -o test_run TemperatureConverter.c test_TemperatureConverter.c -lm && ./test_run. The -lm links the math library for fabs(), used by the tolerance-based CLOSE comparison macro.

5 The Interface

Even a tiny program has an interface. Here is its contract, documented plainly.

INPUTArgumentsa conversion code and a number, e.g. CtoF 100
What it expects
./convert CtoF 100
OUTPUTResultthe converted value, printed to two decimal places
What it returns
212.00

6 Run It & Automate It

Save the code as TemperatureConverter.h / TemperatureConverter.c / main.c and compile it with gcc — that turns your source directly into a native executable for your machine. No separate runtime needed: the compiled binary runs on its own.

Run it locally
gcc -o convert main.c TemperatureConverter.c -lm && ./convert CtoF 100
Swap CtoF for FtoC, CtoK, KtoC, FtoK, or KtoF, and the number for anything you like.

A CI tool like Jenkins runs the same compile-then-test-then-check-for-leaks steps automatically whenever the code changes — every line below has a plain explanation.

What you should see when it works
Terminala real run
$ ./convert CtoF 100
212.00
$ ./convert FtoC 32
0.00
If it breaks — how to fix it
🚨 Unknown conversion: CtoF (but I typed CtoF!)
Command-line arguments are case-sensitive and must match exactly — check for a typo like ctof or a stray space.
🚨 undefined reference to `sqrt' or similar math errors
This particular project doesn't need sqrt, but if you extend it to use one, remember C's math functions require linking with -lm — it's not automatic.
🚨 The result looks wildly wrong.
Double-check the argument order: it's choice then value, e.g. CtoF 100, not 100 CtoF.
GroovyJenkinsfile
// Jenkinsfile — compiles, tests, and checks for leaks on every change.
pipeline {
    agent any

    stages {
        stage('Get the code') {
            // download the latest code
            steps { checkout scm }
        }
        stage('Compile') {
            steps {
                // confirm a compiler is installed
                sh 'gcc --version'
                // compile with strict warnings on
                sh 'gcc -std=c17 -Wall -Wextra -o app *.c -lm'
            }
        }
        stage('Run the tests') {
            steps {
                // prints PASS/FAIL, exits non-zero on failure
                sh './app'
            }
        }
        stage('Check for memory leaks') {
            steps {
                // fails the build on any leak or invalid access
                sh 'valgrind --error-exitcode=1 --leak-check=full ./app'
            }
        }
    }

    post {
        success { echo 'All tests passed, no leaks found.' }
        failure { echo 'A test or Valgrind check failed — see above.' }
    }
}
Try extending it
Add an interactive mode that loops reading conversions from stdin instead of requiring command-line arguments each time. Or add a table mode that prints a whole Celsius-to-Fahrenheit conversion chart.
What you learned
The out-parameter pattern C uses in place of a nullable or optional return type; why strcmp chains replace a string switch; marking parameters const; and comparing floating-point results with a tolerance instead of ==.