Sorting in C

We will look at three sorting algorithms – selection sort, bubble sort, and insertion sort.

In selection sort, the list is divided into two lists, one of which is sorted and the other is unsorted. We then find the smallest element from the unsorted list and swap it with the element at the beginning of the unsorted data; this is known as a sort pass. This sorting algorithm requires as many sort passes as there are elements, minus one. Each time a sort pass is accomplished, the sorted list grows by one element and the unsorted list shrinks by one element.

#include <stdio.h>

void switchSmallest(int array[], int current, int last){
    
    int i;
    int smallest;
    int tempData;
    smallest = current;
    
    
    //find smallest value remaining in array
    for(i = current + 1; i < last; i++){
        if(array[i] < array[smallest]){
            smallest = i;
        }
    }
    
    //swap out the values
    if(smallest!=current){
        printf("\nSwitching %d and %d\n", array[current], array[smallest]);
        tempData = array[current];
        //store smallest value we found 
        //in the current element.
        array[current] = array[smallest];
        //store value formerly in current element
        //where smallest value was
        array[smallest] = tempData;
    }    
    
} //end switchSmallest

void printList(int array[], int demarcation, int last){
    int i;
    for(i=0; i < last; i++){
        if(i==demarcation){
            printf("[sorted]\t");
        }
        printf("%d\t", array[i]);
    }
} //end printList

int main(void){
    
    const int Array_Length = 9;
    
    int array[] = {47, 80, 42, 1138, 8088, 256, 1946, 404, 1024};
    
    
    int i,j;
    
    printf("Array before sorting: \n");
    printList(array, 0, Array_Length);
    putchar('\n');
    
    for(i = 0; i < Array_Length; i++){
        switchSmallest(array, i, Array_Length);
        printf("\nSort Pass %d\n", i+1);
        printList(array, i+1, Array_Length);
        printf("\n");
    }
    
}

At the start of the process, the sorted array has zero elements, and the unsorted array is, in essence, the array itself. We then take the first element in the array, at index 0, and compare it with every single element in the remainder of the array. If we find an element smaller than the element stored at index 0, we switch those two elements out and the sorted array gains one element, since we know for sure that at least that element is indeed the smallest of the bunch.

Note that every time we wish to move data, we must use a temporary storage area.

Next,, let’s take a gander at bubble sort. In bubble sort, the list is divided into two sublists, sorted and unsorted. Unlike selection sort, bubble sort will often exchange more than one set of elements per sort pass.

#include <stdio.h>

void printArray(int array[], int last){
    int i;
    for(i=0; i < last; i++){
        printf("%d ", array[i]);
    }
    putchar('\n');
}

void bubbleUp(int array[], int first, int last){
    int i;
    int temp;
    for(i=first; i < last; i++){
        //with adjoining elements
        //if element is greater than the next
        //switch them
        if(array[i] > array[i+1]){
            printf("%d > %d\t", array[i], array[i+1]);
            temp = array[i];
            array[i] = array[i+1];
            array[i+1] = temp;
        }
    }    
}

void bubbleSort(int array[], int last){
    int i;
    for(i=0; i < last; i++){
        printf("\nPass %d:\n", i+1);
        printArray(array, last);
        bubbleUp(array, i, last);
    }
}

int main(void){
    const int Num_Elements = 10;
    
    int array[Num_Elements] = {208, 57, 416, 117, 361, 57, 1, 248, 141, 118};
    bubbleSort(array, Num_Elements);
    return 0;
}

The advantage to bubble sort is that while in the worst case scenario it takes as many iterations as selection sort, in can, in most circumstances, actually finish with fewer loops. Our first bubbleSort() function would still go through the array element by element even though the data being sorted was already in sequence. With a few modifications, we can end the sort early if all elements are already in sequence.

#include <stdio.h>

void printArray(int array[], int last){
    int i;
    putchar('\n');
    for(i=0; i < last; i++){
        printf("%d ", array[i]);
    }
    putchar('\n');
}

void switchElements(int array[], int first, int second){
    printf("Switching %d and %d\t", array[first], array[second]);
    int temp = array[second];
    array[second] = array[first];
    array[first] = temp;
}

//this version is made up of nested for loops
//first loop using int i for a counter
//second loop using int j for a counter
void bubbleSort(int array[], int last){
    int i, j, marker;
    for(i = 0; i < last; i++){
        printf("\nSort Pass %d\n", i+1);
        //marker set to zero
        //if no elements are switched
        //marker will still be zero after end
        //of inner loop
        marker = 0;
        
        //last-2 as we are counting from zero
        for(j=last-2; j >= 0; j--){
            if(array[j] > array[j+1]){
                switchElements(array, j, j+1);
                marker++;
            }
        } //end inner for loop
        
        //if marker still 0
        //then array already sorted
        //sorting is finished!
        if(marker==0){
            printf("\nArray sorted!\n");
            break;
        } else {
            printArray(array, last);
        }
    }//end outer for loop
}


int main(void){
    const int Num_Elements = 10;
    int array[Num_Elements] = {9651, 2529, 131, 55, 9, 364, 11640, 758, 51408, 2059};
    
    printArray(array, Num_Elements);
    
    bubbleSort(array, Num_Elements);
    
    printArray(array, Num_Elements);
    return 0;
}

Our final sort function is insertion sort. Unlike bubble and selection sort, we can sometimes see insertion sort actually implemented in the real world. Insertion sort is a subfunction of the popular Quicksort.

#include <stdio.h>

void insertionSort(int array[], int last);
void insertElement(int array[], int selected);

void printArrayWithSelected(int array[], int selected, int last);
void printArray(int array[], int last);

int main(void){
    const int Array_Size = 11;
    
    int array[Array_Size] = {34, 89, 30, 98, 79, 99, 3, 73, 49, 37, 32};
    insertionSort(array, Array_Size);
    
    printArray(array, Array_Size);
    
} //end main

void insertionSort(int array[], int last){
    int selected;
    //note index begins at one
    for(selected = 1; selected < last; selected++){
        printArrayWithSelected(array, selected, last);
        insertElement(array, selected);
    }
} //end insertionSort

void insertElement(int array[], int selected){
    int i, found;
    int temp;
    
    //give found Boolean value
    //of false AKA 0
    found = 0;
    //assign current element in array to temp value
    temp = array[selected];
    for(i = selected - 1; i >= 0 && !found; i){
        //move every element greater than 
        //the selected value down one
        if(temp < array[i]){
            printf("\t[%d] < %d\n", temp, array[i]);
            //move element down the list one
            array[i+1] = array[i];
            i--;
        } else {
            found = 1;
        }
    } //end for loop
    
    //place the selected value
    //back into the array
    array[i+1] = temp;
} //end insertElement



void printArrayWithSelected(int array[], int selected, int last){
    int i;
    putchar('\n');
    for(i = 0; i < last; i++){
        if(i!=selected){
            printf(" %d ", array[i]);
        } else {
            printf(" [%d] ", array[i]);
        }
    }
    putchar('\n');
} //end printArrayWithSelected

void printArray(int array[], int last){
    int i;
    putchar('\n');
    for(i = 0; i < last; i++){
        printf(" %d ", array[i]);
    }
    putchar('\n');
    
}//end printArray

If you’re interested in learning more about C, buy a copy of my book for the Amazon Kindle and Amazon Kindle app. The book includes over 100 example programs and for those viewing it from your Kindle Cloud browser or on a tablet the code is in color.

http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

Initializing, Indexing, and Passing Arrays

First, let’s review how to declare and define arrays. An array must be declared and defined before it can be used. The size of the array is a constant.

#include <stdio.h>

int main(void){

    //each element in the array
    //must be of the same type
    int testScores[10];
    double gpa[30];

    char name[50];

    //use an index of zero
    //to access the first element
    testScores[0] = 1138;

    printf("%d\n", testScores[0]);

    return 0;
}

The declaration and definition of an array tells the compiler the name of the array, the type of each element, and the number of elements in the array. Since an array depends on having its elements occupy consecutive memory locations, once the array size has been set in cannot be resized, as memory immediately following the location of the last element may have already been allocated.

It is possible to use a variable or an expression as the index of an array.

#include <stdio.h>

int main(void){

int i = 0;
double nums[10];

for(i = 0; i < 10; i++){
nums[i] = i + 1;
nums[i] += i / 7.0;
}

i = 0;

while(i < 10){
printf("%f\t", nums[i++]);
}

return 0;

}//end main()

An array’s name is functionally a symbolic reference to the memory location of the first element in the array. The index represents an offset from the first element in the array to the element being referred to; thus, the first element is at index 0, not 1.

Initialization of all elements in an array can be done at the time of declaration, just as with variables. Our initialization values must be placed within curly braces and, if there is more than one, separated by commas.  If the number of values provided is less than the number of elements in the array, the unassigned elements are filled with zeros.

#include <stdio.h>

int main(void){

    int i = 0;
    int intExmp[10] = {5, 9, 14, 23, 37};

    for(i = 0; i < 10; i++){
        printf("%d\n", intExmp[i]);
    }

    return 0;

}

Note that while we can assign individual elements using the assignment operator, we cannot assign entire arrays in one step. Instead, we must copy arrays by looping through each element. It is also quite common to exchanged array elements; this is a major part of sorting algorithms.

We can pass individual elements to function like any other variable. Of course, the element will be passed by value, not by reference, unless we use pointers.

#include <stdio.h>


void switchValues(int *a, int *b);

int main(void){

    int array01[10];
    int array02[10];

    int i = 0;
    int j = 0;
    int k = 0;

    for(i = 0; i < 10; i++){
        array01[i] = i * 2;
    }

    for(i = 0; i < 10; i++){
        array02[i] = array01[i] + 1;
    }

    for(i = 0; i < 10; i++){
        printf("%3d %3d\n", array01[i], array02[i]);
    }

    //move value stored at beginning of the array
    //down the array to the last element
    for(i = 0; i < 9; i++){
        switchValues(&array02[i], &array02[i+1]);
    }

    for(i = 0; i < 10; i++){
        printf("%5d", array02[i]);
        putchar('\n');
    }

    //reverses the values in the array
    for(i = 0, j = 9; i < 9; i++, j--){
        //each loop we shift the value stored
        //in the first element, 0,
        //to the last element in the array
        //minus one for each time we have looped
        for(k = 0; k < j; k++){
            switchValues(&array01[k], &array01[k+1]);
        }
        int x = 0;
        //print out changes to array so far
        for(x = 0; x < 10; x++){
            printf("%5d", array01[x]);
        }
        putchar('\n');
    } //end outer for loop



    putchar('\n');

    return 0;

}//end main()

void switchValues(int *a, int *b){
    int temp; //temp value to store

    temp = *a;
    *a = *b;
    *b = temp;

}//end switchValues()

Bear in mind that the C language does not check the boundary of an array. Is is our job to ensure that all references to indexed elements are valid and within the boundaries of the array.

#include <stdio.h>

#define ARRAY_MAX 50

int main(void){
    
    int i = 0;
    int numToRead = 0;
    int numbers[ARRAY_MAX];
    
    
    printf("You can enter up to %d whole numbers:\n", ARRAY_MAX);
    printf("How many would you like to enter?\t");
    scanf(" %d", &numToRead);
    
    if(numToRead > 50 || numToRead < 0){
        numToRead = 50;
    }
    
    while(i < numToRead){
        printf("Enter value for element %d:\t", i);
        scanf(" %d", &numbers[i++]);
    }
    
    printf("Printing these numbers backwards:\n");
    
    for(i = numToRead-1; i >= 0; i--){
        printf("%5d ", numbers[i]);
    }
    
    
    
}//end main()

Unlike when passing an array element, when we pass the whole array we pass the array automatically by reference, due to the fact that the array name devolves to a pointer to the initial array element. All we need to do is use the array name as the function’s argument.

 

 

 

Basic I/O in C

The C programming language contains no input or output commands per se. Input and output is instead handled by functions contained in the stdio library. The scanf() and printf() functions are often used in beginner C texts, but there are other input/output functions as well. It’s worth remembering as well that scanf() isn’t exactly the safest function to use.

In C, all input an output is viewed as streams of characters. Whether the program gets input from the keyboard, a file, an Internet connection, or a mouse, C only sees a stream of characters.

The two most basic input/output functions are getc() and putc(). The getc() function retrieves a single character from the standard input device, and the putc() function outputs a single character to the standard output device.

#include <stdio.h>

int main(void){

    int intVal;
    
    char charVal01, charVal02;
    
    printf("Enter a letter: ");
    
    intVal = getc(stdin);
    
    charVal01 = intVal;
    
    printf("Enter another letter: ");
    
    intVal = getc(stdin);
    
    charVal02 = intVal;
    
}

When we compile and run the program above we’ll notice a glaring problem right away: the carriage return counts as a character as well, which means that we never get a chance to enter in the second value. When we answer a getc() prompt by typing a character and hitting Enter, the program sees that input as a stream of two characters. Let’s try the program again, this time defining a macro for clearing the input buffer.

#include <stdio.h>
#define CLEAR_BUFFER while((getc(stdin))!='\n'){}

int main(void){

int returnVal;

char charVal01, charVal02;

printf("What is the first letter?\t");

returnVal = getc(stdin);

CLEAR_BUFFER

charVal01 = returnVal;

printf("What is the second letter?\t");

returnVal = getc(stdin);

CLEAR_BUFFER

charVal02 = returnVal;

//output char values
//to the screen
putc(charVal01, stdout);
putc(charVal02, stdout);

}

Note that the program must call two getc()  functions, one for each value desired.

The print() and scanf() functions are called formatted I/O functions because they give us formatting control via conversion and escape characters. Functions like putc() and getc() are character I/0 functions, since they only handle one character at a time. We can still, however,  use the escape characters to add some basic formatting to our output.

#include <stdio.h>
#define CLEAR_BUFFER while((getc(stdin))!='\n'){}
#define NEWLINE putc('\n', stdout);



int main(void){

    int intVal;
    
    char charVal01, charVal02;
    
    printf("Input a number:\t");
    
    intVal = getc(stdin);
    
    CLEAR_BUFFER;
    
    charVal02 = intVal;
    
    putc(charVal02, stdout);
    
    NEWLINE
    
    printf("Input another number:\t");
    
    intVal = getc(stdin);
    
    charVal01 = intVal;
    
    putc(charVal01, stdout);
    
    NEWLINE
    
    
    //converting from ASCII to decimal values
    //can get tricky
    printf("%c + %c = %d\n", charVal01, charVal02, charVal01 + charVal02);
    
    printf("errr...wait\n");
    
    //change ASCII value of number
    //to its decimal value
    charVal01 -= '0';
    charVal02 -= '0';
    
    printf("%d + %d = %d\n", charVal01, charVal02, charVal01 + charVal02);
    
}

The getchar() and putchar() functions do not specify a device because they assume stdin and stdout. They, like getc() and putc(), are called mirrored functions, because they perform complementary actions.

#include <stdio.h>

#define BUFFER_SIZE 50

//use getchar() and putchar() in macros
#define CLEAR_BUFFER while((getchar())!='\n'){}
#define NEWLINE putchar('\n');

//function prototype
int getString(char *string, int buffer);

int main(void){

    char string[BUFFER_SIZE];
    
    printf("Enter a string, %d characters max\n", BUFFER_SIZE);
    getString(string, BUFFER_SIZE);
    
    printf("%s\n", string);
    
    printf("Enter another string, %d characters max", 10);
    
    if((getString(string, 10))==1){
        printf("Please note that this string has been truncated...\n");
    }
    
    printf("%s\n", string);
    
    return 0;
} // end main()

int getString(char *string, int buffer){
    int i = 0;
    
    //*(string+i) equivalent to string[i++]
    do{
        *(string+i)=getchar();
    }while((*(string+i++)!='\n')&&(i<buffer));
    
    //make the char array into a string
    //note i was incremented once at end of
    //dow while loop, thus we must subtract one
    //to eliminate the newline character
    *(string+(i-1))='';
    
    //buffer length exceeded?
    if(i==buffer){
        //clear buffer of excess characters
        CLEAR_BUFFER
        //return error warning value
        return 1;
    }
    return 0;
}

It’s worth mentioning that the getchar() function can be inserted directly into the while() test expression; this actually is standard practice and does deliver a small performance gain. I myself am not a huge fan of micro-optimization in higher languages such as C# or Java; but, optimization is the whole point of C.  Note that in this case we must include an extra set of parentheses around the function since we will be evaluating a side effect of the getchar() function.

Please buy my book it makes me happy: http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/

 

 

 

Introduction to Recursion in C

A function that calls itself is said to be recursive. Recursion allows something to be defined in terms of smaller instances of itself.

While most of us are comfortable with breaking down problems into smaller pieces using functions, data structures, or methods and classes, using one single function to solve a big problem is somewhat more daunting.

#include <stdio.h>

void simpleRecursion(int num){
    if(num > 0){
        simpleRecursion(num-2);
        printf("%d\t", num);
        simpleRecursion(num-1);
    }
}

int main(void){

    int i;
    for(i=0; i < 6; i++){
        printf("\nPassed the function %d:\n", i);
        simpleRecursion(i);
        putchar('\n');
    }

    return 0;

}

The simpleRecursion() function only prints output if the parameter is greater than zero, meaning that any value printed must be one or greater.

Our second example is simpler still, it in effect goes backward one by one. We will use it to print the ASCII table backwards.

#include <stdio.h>

void simpleRecursion2(int num){
    if(num < 127){
        printf("%2c", num);
    }
    if(num > 33){
        simpleRecursion2(num - 1);
    }
}

int main(void){

    //print ASCII characters backward
    simpleRecursion2(127);

    return 0;

}

Factorials are a common mathematical example of recursion. The factorial of a number n, written n!, is the product of all numbers from n to 1. While we could follow an iterative approach, we can also define n! as itself being the product of smaller factorials. In this case, n! = (n-1)!, with n(0)=1. In this case, n(0) = 1 is what is known as the terminating condition. This is the point at which the function returns a value rather than calling itself again.

#include <stdio.h>

int factorial(int n){

    if(n>1){
        //return a call to the same function
        return n*factorial(n-1);
    }

    //terminating conditions
    if((n==0)||(n==1)){
        return 1;
    }

    //error
    return 0;
}

int main(void){

    printf("%d! = %d\n", 5, factorial(5));
    printf("%d! = %d\n", 3, factorial(3));

    return 0;

}

Remember, every recursive function must have at least one terminating condition.

To understand recursion, it helps to have a rough idea of the way functions are executed in C. When a function is called, a block of storage is allocated on the stack to keep track of data associated with the call. This block of storage is called a stack frame. The stack frame remains on the stack until the function call terminates.

This next example will sum an integer with every positive integer below it.

#include <stdio.h>

int recursiveAdd(int n){

    printf("%d", n);
    
    if(n>1){
        printf(" + ");
        return n + recursiveAdd(n-1);
    }
    
    if(n==1){
        return n;
    }
    //error
    return 0;
}

int main(void){
    
    int i = 0;
    
    while(i++ <= 10){
        printf(" = %d\n", recursiveAdd(i));
    }
            
    return 0;
    
}

The initial call to the recursiveAdd() function placed one stack frame on the stack. If we say, for instance, that the function’s argument was 3, then the initial stack frame does not meet any of the terminating conditions of the function, so the recursiveAdd() function is called again, with the argument being set to the current parameter, being four, minus one. This continues, with each stack frame persisting on the stack, until we reach a terminating condition and that function returns a value, in this case 1. Once this function call returns a value to the function that called it, that function can then return a value to the function call that in turn called it. Finally, we reach the stack frame created by the initial function call, and it returns the final value to the main() function.

You can get a copy of my book on standard C at http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M. I also have a book on Linux commands http://www.amazon.com/Big-Als-Linux-Fedora-CLI-ebook/dp/B00GI25V30/

Two-Dimensional Arrays in C

Arrays can be initialized on their declaration. This initialization takes place when the program is loaded.  The list of initializers should be enclosed in curly braces, and each element must be separated by commas.

#include <stdio.h>


int main(void){
    
    int i;
    
    short smallNums[] = {1, 5, 11, 13, 17, 23};
    int largerNums[7] = {42, 47, 73, 404, 808, 2600, 40000};    
    //shorthand for string array
    char aString[] = "I can't do that, Dave.";
    
    printf("%s\n", aString);
    
    
    //use sizeof() to get array size
    for(i = 0; i < sizeof(smallNums)/sizeof(smallNums[0]); i++){
            printf("%d\t", smallNums[i]);
    }
    
    return 0;
}

Note that if no size of the array is given when initializing the array, the bound is taken to be the number of initializers.

Two-dimensional arrays are often called rectangular arrays, as their structure resembles that of a two-dimensional grid such as seen on a spreadsheet.

#include <stdio.h>


int main(void){
    
    //number of horizontal rows
    const int Row_Size = 10;
    //number of vertical columns
    const int Column_Size = 5;
    
    int twoDimArray[Row_Size][Column_Size];
    
    //sizeof() returns unsigned long
    printf("Size of twoDimArray = %lu\n", sizeof(twoDimArray));
    
    int size = Row_Size * Column_Size * sizeof(int);
    
    printf("Size of twoDimArray = %d\n", size);
    
    printf("Size of one row in twoDimArray = %lu\n", sizeof(twoDimArray[0]));
    
    size = Column_Size * sizeof(int);
    
    printf("Size of one row in twoDimArray = %d\n", size);
    
    return 0;
}

Note that the elements in a two-dimensional array are stored in row-major order, meaning that each row forms an array of elements in memory. Technically speaking, each row is itself a one-dimensional array, and can be used as such.

#include <stdio.h>


int main(void){
    
    int intArray[3][5];
    
    int i, j;
    
    //use nested for loop
    //to assign values to each
    //array element
    for(i = 0; i < 3; i++){
        for(j = 0; j < 5; j++){
            intArray[i][j] = 1 + i + j;    
        }
    }
    
    //print out last row of array
    for(i = 0; i < 5; i++){
        printf("%d\t", intArray[2][i]);
    }
    
    return 0;
}

We can use arrays to recreate the famous Towers of Hanoi puzzle. In this puzzle there are three vertical pegs onto which are placed disks of varying sizes. At the start of the puzzle, all of the disks are placed in hierarchical order from largest to smallest on the leftmost peg. We can move a disk onto another peg only if every disk on that peg is larger than it. The goal is to move all of the disks to the rightmost peg.

This puzzle is typically used in computer science courses as a visual aid to thinking of how the stack data structure works. Each stack of disks is like a programming stack, since items can only be removed or added to the top of the stack.

#include <stdio.h>

const int num_disks = 3;

int pop(int i, int pegs[3][num_disks], int numDisks[]);
void push(int i, int disk, int pegs[3][num_disks], int numDisks[]);
void dumpPegs(int pegs[3][num_disks], int numDisks[]);
void moveDisk(int source, int destination, int pegs[3][num_disks], int numDisks[]);

int main(void){
    
    int pegs[3][num_disks] = {0};

    //number of disks on each peg
    int numDisks[3] = {0};
    
    push(0, 3, pegs, numDisks);
    push(0, 2, pegs, numDisks);
    push(0, 1, pegs, numDisks);
    
    dumpPegs(pegs, numDisks);
    
    moveDisk(0, 2, pegs, numDisks);
    
    dumpPegs(pegs, numDisks);
    
    moveDisk(0, 1, pegs, numDisks);
    
    dumpPegs(pegs, numDisks);
    
    moveDisk(2, 1, pegs, numDisks);
    
    dumpPegs(pegs, numDisks);
    
    moveDisk(0, 2, pegs, numDisks);
    
    dumpPegs(pegs, numDisks);
    
    moveDisk(1, 0, pegs, numDisks);
    
    dumpPegs(pegs, numDisks);
    
    moveDisk(1, 2, pegs, numDisks);
    
    dumpPegs(pegs, numDisks);
    
    moveDisk(0, 2, pegs, numDisks);
    
    dumpPegs(pegs, numDisks);
    
    return 0;
}


void moveDisk(int source, int destination, int pegs[3][num_disks], int numDisks[]){
    int disk = pop(source, pegs, numDisks);
    push(destination, disk, pegs, numDisks);
}

void push(int i, int disk, int pegs[3][num_disks], int numDisks[]){
    if(i >= 0 && i <= 2){
        pegs[i][numDisks[i]++] = disk;
    }
}

int pop(int i, int pegs[3][num_disks], int numDisks[]){
    if(i >= 0 && i <= 2){
        if(numDisks[i] >= 1){
            return (pegs[i][--numDisks[i]]);
        }
    }
    return -1;
}

void dumpPegs(int pegs[3][num_disks], int numDisks[]){
    int i,j;
    
    for(i = 0; i < 3; i++){
        for(j = 0; j < num_disks; j++){
            if(j < numDisks[i]){
                printf("%d", pegs[i][j]);
            } else {
                putchar(' ');
            }
        }
        putchar('\t');
    }
    putchar('\n');
}

This example above provides a brute-force solution to the puzzle if there are three disks. But what if there are four disks? Or five? In that case, we should use a recursive function. But that is for another time.

 

C Preprocessor

We can use the #define statement to define a constant. The #define statement, like all preprocessor commands, begins with a hashtag.

#include <stdio.h>

#define INDEX_SIZE 10

int main(void){
    
    int i = 0;
    
    int dataArray[INDEX_SIZE];
    
    while(i++ < INDEX_SIZE){
        dataArray[i] = 1 + i; 
    }
    
    for(i = 0; i < INDEX_SIZE; i++){
        printf("%d\t", dataArray[i]);
    }
    
    putchar('\n');
    
    
    
    return 0;
}

Note that unlike a regular C statement, a preprocessor directive terminates at the end-of-line. This is different from normal C statements that end in a semicolon.

The #define statement works by creating a replacement macro. The replacement macro is a string of text with an associated value; wherever this string of text appears in the program it is replaced by its associated value. It is considered best practice to use all uppercase letters for macro names.

#include <stdio.h>

#define NUMX 10
#define NUMY NUMX + 10
#define NUMZ (NUMX + 10)

int main(void){
    
    printf("NUMX = %d\n", NUMX);
    printf("NUMY = %d\n", NUMY);
    //does not give the answer we 
    //were expecting
    printf("NUMY^2 = %d\n", NUMY * NUMY);
    printf("NUMZ = %d\n", NUMZ);
    //this does give the answer
    //we were expecting
    printf("NUMZ^2 = %d\n", NUMZ * NUMZ);
    
    return 0;
    
}

Be careful, as the macros are replaced exactly as they are written.

One of the best uses of the preprocessor is in conditional compilation, which depends on the #ifdef and #endif statements.

#include <stdio.h>

#define LIFE 42

int main(void){
    
    //will be compiled
#ifdef LIFE
    printf("The meaning of life is %d\n", LIFE);
#endif
    
    //won't be compiled
#ifdef BESTNUMBER
    printf("The best number is %d\n", BESTNUMBER);
#endif
    
    return 0;
    
}

There are two more preprocessor directives for us to look at, the #ifndef and #else directives. Unlike #ifdef, #ifndef will cause the code to be compiled only if the macro is not defined.

#include <stdio.h>

#define BOOMER 1
#define MONKEY 2


int main(void){
    
#ifdef BOOMER
    printf("Boomer lives!\n");
#else 
    printf("What about Boomer?\n");
#endif
    
#ifndef LAFONDA
    printf("Marc Maron's other cat is named LaFonda\n");
#endif
    
    return 0;
    
}

If you’re interested in learning more about C, have a look at my book filled with 100s of unique example programs coupled with succinct explanations: http://www.amazon.com/Big-Als-C-Standard-ebook/dp/B00A4JGE0M/