Introduction to the D Programming Language

Andrew J. Pounds, Ph.D.

Learning Objectives

After completing this exercise, you will be able to:

1 What is D?

D is a multi-paradigm systems programming language created by Walter Bright at Digital Mars and released in 2001. It was developed by re-engineering C++, offering modern features while maintaining efficiency and the ability to interface directly with hardware and system APIs.

D supports multiple programming paradigms including:

D source files use the .d extension. We will use the GNU D Compiler (GDC), which is a front-end for the GCC back-end.

2 Your First D Program

Create a file called hello.d with the following content:

import std.stdio;

void main() {
    writeln("Hello, World!");
}

Compiling and Running

Open a terminal and navigate to the directory containing your file. Compile and run with:

gdc hello.d -o hello
./hello

You should see Hello, World! printed to the console.

Tip: If you omit the -o hello option, GDC will create an executable called a.out by default.

Understanding the Code

Code Element Explanation
import std.stdio; Imports the standard I/O module from D's standard library (Phobos)
void main() The entry point of the program; void means no return value
writeln(...) Writes output to the console followed by a newline

3 Variable Types

D is a statically typed language, meaning you must declare the type of each variable. D provides a rich set of fundamental types:

Integer Types

Type Size Range
byte 8 bits -128 to 127
short 16 bits -32,768 to 32,767
int 32 bits -2,147,483,648 to 2,147,483,647
long 64 bits -9.2×1018 to 9.2×1018

Each signed type has an unsigned counterpart: ubyte, ushort, uint, ulong.

Floating-Point Types

Type Size Description
float 32 bits Single precision (~7 significant digits)
double 64 bits Double precision (~15 significant digits)
real 80+ bits Highest precision available on the platform

Other Fundamental Types

Type Description Example
bool Boolean (true or false) bool flag = true;
char Single UTF-8 code unit char letter = 'A';
string Immutable array of characters string name = "Alice";

Type Inference with auto

D can infer types automatically using the auto keyword:

import std.stdio;

void main() {
    // Explicit type declarations
    int age = 25;
    double gpa = 3.85;
    string name = "Alice";
    bool enrolled = true;
    
    // Type inference with auto
    auto count = 100;        // inferred as int
    auto pi = 3.14159;       // inferred as double
    auto greeting = "Hi";   // inferred as string
    
    writeln("Name: ", name);
    writeln("Age: ", age);
    writeln("GPA: ", gpa);
    writeln("Enrolled: ", enrolled);
}

4 Console Output

The std.stdio module provides four primary output functions:

Function Description
write() Outputs without adding a newline
writeln() Outputs and adds a newline at the end
writef() Formatted output without newline
writefln() Formatted output with newline
import std.stdio;

void main() {
    int x = 42;
    double price = 19.99;
    string item = "widget";
    
    // Basic output
    writeln("The answer is ", x);
    
    // Multiple values
    writeln("Item: ", item, ", Price: $", price);
    
    // Using write (no newline)
    write("Processing");
    write("...");
    writeln(" Done!");
    
    // Formatted output
    writefln("The %s costs $%.2f", item, price);
    writefln("Hex: %x, Padded: %05d", x, x);
}

Common Format Specifiers

Specifier Description Example Output
%d Integer (decimal) 42
%f Floating-point 3.141593
%.2f Float with 2 decimal places 3.14
%s String (or any type) hello
%x Hexadecimal 2a
%05d Zero-padded to 5 digits 00042

5 Console Input

Reading input in D requires the readln() function and typically the std.string and std.conv modules for processing:

import std.stdio;
import std.string;    // for strip()
import std.conv;      // for to!int(), to!double()

void main() {
    // Reading a string
    write("Enter your name: ");
    string name = readln().strip();
    
    // Reading an integer
    write("Enter your age: ");
    int age = to!int(readln().strip());
    
    // Reading a double
    write("Enter your GPA: ");
    double gpa = to!double(readln().strip());
    
    writefln("Hello, %s! Age: %d, GPA: %.2f", name, age, gpa);
}
Important: The readln() function includes the newline character in the returned string. Always use .strip() to remove leading and trailing whitespace (including the newline) before processing.

Understanding the Input Process

  1. readln() reads a line of text from the console (including the newline)
  2. .strip() removes the trailing newline and any extra whitespace
  3. to!int() or to!double() converts the string to a number

6 Assignment Statements and Operators

Basic Assignment

The assignment operator = stores a value in a variable:

int x = 10;           // declare and initialize
x = 20;                // assign new value
int y = x;            // copy value from x to y

Arithmetic Operators

Operator Description Example Result (if a=10, b=3)
+ Addition a + b 13
- Subtraction a - b 7
* Multiplication a * b 30
/ Division a / b 3 (integer division)
% Modulus (remainder) a % b 1

Compound Assignment Operators

int x = 10;
x += 5;    // x = x + 5  → x is now 15
x -= 3;    // x = x - 3  → x is now 12
x *= 2;    // x = x * 2  → x is now 24
x /= 4;    // x = x / 4  → x is now 6
x %= 4;    // x = x % 4  → x is now 2

Increment and Decrement

int count = 5;
count++;      // count is now 6 (post-increment)
++count;      // count is now 7 (pre-increment)
count--;      // count is now 6 (post-decrement)
--count;      // count is now 5 (pre-decrement)

Integer vs. Floating-Point Division

import std.stdio;

void main() {
    writeln(7 / 2);       // prints 3 (integer division)
    writeln(7.0 / 2);     // prints 3.5 (floating-point)
    writeln(7 / 2.0);     // prints 3.5 (floating-point)
    
    int a = 7, b = 2;
    writeln(a / b);            // prints 3
    writeln(cast(double)a / b); // prints 3.5
}
Tip: To force floating-point division with integer variables, cast one operand to double using cast(double).

7 Control Structures

The if Statement

Execute code conditionally based on a boolean expression:

import std.stdio;
import std.string;
import std.conv;

void main() {
    write("Enter your score (0-100): ");
    int score = to!int(readln().strip());
    
    if (score >= 90) {
        writeln("Grade: A");
    } else if (score >= 80) {
        writeln("Grade: B");
    } else if (score >= 70) {
        writeln("Grade: C");
    } else if (score >= 60) {
        writeln("Grade: D");
    } else {
        writeln("Grade: F");
    }
}

Comparison Operators

Operator Meaning Example
== Equal to x == 5
!= Not equal to x != 5
< Less than x < 5
> Greater than x > 5
<= Less than or equal to x <= 5
>= Greater than or equal to x >= 5

Logical Operators

Operator Meaning Example
&& Logical AND x > 0 && x < 100
|| Logical OR x < 0 || x > 100
! Logical NOT !found
import std.stdio;
import std.string;
import std.conv;

void main() {
    write("Enter your age: ");
    int age = to!int(readln().strip());
    
    write("Do you have a license? (yes/no): ");
    string response = readln().strip();
    bool hasLicense = (response == "yes");
    
    if (age >= 16 && hasLicense) {
        writeln("You can drive.");
    } else if (age >= 16 && !hasLicense) {
        writeln("You're old enough but need a license.");
    } else {
        writeln("You're too young to drive.");
    }
}

The switch Statement

Use switch when comparing a single value against multiple options:

import std.stdio;
import std.string;
import std.conv;

void main() {
    write("Enter a day number (1-7): ");
    int day = to!int(readln().strip());
    
    switch (day) {
        case 1:
            writeln("Monday");
            break;
        case 2:
            writeln("Tuesday");
            break;
        case 3:
            writeln("Wednesday");
            break;
        case 4:
            writeln("Thursday");
            break;
        case 5:
            writeln("Friday");
            break;
        case 6, 7:  // D allows multiple case values!
            writeln("Weekend!");
            break;
        default:
            writeln("Invalid day number");
    }
}
D Feature: Unlike C/C++, D allows you to list multiple values for a single case using commas: case 6, 7:

8 Looping Structures

The while Loop

Repeats a block of code as long as a condition is true. The condition is checked before each iteration:

import std.stdio;

void main() {
    // Count from 1 to 5
    int i = 1;
    while (i <= 5) {
        writeln("Count: ", i);
        i++;
    }
}

The do-while Loop

Similar to while, but the condition is checked after each iteration. The loop body always executes at least once:

import std.stdio;
import std.string;
import std.conv;

void main() {
    int guess;
    int secretNumber = 7;
    
    do {
        write("Guess a number (1-10): ");
        guess = to!int(readln().strip());
        
        if (guess < secretNumber) {
            writeln("Too low!");
        } else if (guess > secretNumber) {
            writeln("Too high!");
        }
    } while (guess != secretNumber);
    
    writeln("Correct!");
}

The for Loop

Best for counting a specific number of iterations:

import std.stdio;

void main() {
    // Basic counting
    for (int i = 1; i <= 5; i++) {
        writeln("Iteration ", i);
    }
    
    // Counting down
    writeln("\nCountdown:");
    for (int i = 10; i >= 0; i--) {
        writeln(i);
    }
    
    // Step by 2
    writeln("\nEven numbers:");
    for (int i = 2; i <= 10; i += 2) {
        writeln(i);
    }
}

Nested Loops

import std.stdio;

void main() {
    writeln("Multiplication Table (1-5):\n");
    
    // Print header row
    write("     ");
    for (int col = 1; col <= 5; col++) {
        writef("%4d", col);
    }
    writeln("\n    --------------------");
    
    // Print table rows
    for (int row = 1; row <= 5; row++) {
        writef("%2d |", row);
        for (int col = 1; col <= 5; col++) {
            writef("%4d", row * col);
        }
        writeln();
    }
}

Loop Control: break and continue

import std.stdio;

void main() {
    // break - exit the loop early
    writeln("Finding first multiple of 7:");
    for (int i = 1; i <= 100; i++) {
        if (i % 7 == 0) {
            writeln("Found: ", i);
            break;  // exit loop immediately
        }
    }
    
    // continue - skip to next iteration
    writeln("\nNumbers 1-10, skipping multiples of 3:");
    for (int i = 1; i <= 10; i++) {
        if (i % 3 == 0) {
            continue;  // skip this iteration
        }
        writeln(i);
    }
}

9 Complete Example: Temperature Converter

This program demonstrates input, output, control structures, and loops working together:

import std.stdio;
import std.string;
import std.conv;

void main() {
    writeln("=== Temperature Converter ===");
    writeln("1. Celsius to Fahrenheit");
    writeln("2. Fahrenheit to Celsius");
    writeln("3. Quit");
    
    bool running = true;
    
    while (running) {
        write("\nEnter choice (1-3): ");
        int choice = to!int(readln().strip());
        
        switch (choice) {
            case 1:
                write("Enter temperature in Celsius: ");
                double celsius = to!double(readln().strip());
                double fahrenheit = (celsius * 9.0 / 5.0) + 32.0;
                writefln("%.2f°C = %.2f°F", celsius, fahrenheit);
                break;
                
            case 2:
                write("Enter temperature in Fahrenheit: ");
                double f = to!double(readln().strip());
                double c = (f - 32.0) * 5.0 / 9.0;
                writefln("%.2f°F = %.2f°C", f, c);
                break;
                
            case 3:
                writeln("Goodbye!");
                running = false;
                break;
                
            default:
                writeln("Invalid choice. Please enter 1, 2, or 3.");
        }
    }
}

10 Quick Reference

Task D Syntax
Print with newline writeln("text", variable);
Print without newline write("text");
Formatted print writefln("Value: %d", x);
Read string string s = readln().strip();
Read integer int n = to!int(readln().strip());
Read double double d = to!double(readln().strip());
Compile gdc filename.d -o outputname
Run ./outputname

📝 Programming Exercise: Prime Number Analyzer

Write a D program that analyzes prime numbers. Your program should:

  1. Prompt the user to enter a positive integer n (must be ≥ 2)
  2. Validate the input: If the user enters a number less than 2, display an error message and ask again (use a loop)
  3. Determine if n is prime: A prime number is only divisible by 1 and itself
  4. If n is prime: Display "n is a prime number!"
  5. If n is not prime: Display "n is not prime. Its smallest factor is f." (where f is the smallest factor other than 1)
  6. Count and display how many prime numbers exist from 2 up to and including n
  7. Ask the user if they want to check another number (y/n). If yes, repeat; if no, exit with a goodbye message

Sample Output

=== Prime Number Analyzer === Enter a positive integer (>= 2): 1 Invalid input. Please enter a number >= 2. Enter a positive integer (>= 2): 29 29 is a prime number! There are 10 prime numbers from 2 to 29. Check another number? (y/n): y Enter a positive integer (>= 2): 100 100 is not prime. Its smallest factor is 2. There are 25 prime numbers from 2 to 100. Check another number? (y/n): n Goodbye!

Hints

Submission

Save your program as prime_analyzer.d and compile with:

gdc prime_analyzer.d -o prime_analyzer
./prime_analyzer