Advanced C Programming

Spring 2021 ECE 264 :: Purdue University

⚠ This is a PAST SEMESTER (Spring 2021).
Due 4/2

Tree sort


The goals of this assignment are as follows:
  1. Practice writing code using binary search trees (dynamic structures).
  2. Practice writing code using recursion.
  3. Learn to use function addresses (function pointers).
  4. Learn to use the qsort(…) function.
  5. Gain some perspective on different sorting algorithms.


This homework is about sorting. You will implement the tree sort algorithm to help you learn about binary search trees (BSTs) and sorting algorithms. For the more practical approach, you will also write a wrapper function for the standard qsort(…) function, an implementation of the quicksort algorithm.

Note: quick_sort_array(…) is completely separate from tree_sort_array(…). They are alternative ways of sorting an array. They should have the same result. They do not call each other. We are including quick_sort_array(…) mainly so you can get some experience with function addresses, and so you don't go away thinking that you need to actually implement sorting algorithms in the real world.

There are no starter files.

Outline of a solution

👌 Okay to copy/adapt anything you see in these two screenshot images. (You may not copy code from lecture or anywhere else.)


Reminder: Do not copy this code or anything else into your code (unless marked as "Okay to copy/adapt" by one of the instructors). However, you may run this in your account by running 264get HW13.
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>
#include "sorts.h"

void _print_array(int* array, size_t size, const char* title) {
    printf("%s\n", title);
    for(int i = 0; i < size; i++) {
        printf("%d ", array[i]);

int main(int argc, char* argv[]) {

    // Demonstrate create_bst(…)
    BST bst = create_bst((int[7]) { 4, 2, 6, 1, 3, 5, 7 }, 7);
    assert(bst.root        != NULL && bst.root->value        == 4);
    assert(bst.root->left  != NULL && bst.root->left->value  == 2);
    assert(bst.root->right != NULL && bst.root->right->value == 6);

    // Demonstrate tree_sort_array(…)
    int array1[] = { 5, 4, 2, 1, 7, 6, 3 };
    size_t size1 = sizeof(array1) / sizeof(*array1);  // # of elements
    _print_array(array1, size1, "Before tree_sort_array(array1)");
    tree_sort_array(array1, size1);
    _print_array(array1, size1, "After  tree_sort_array(array1)");

    // Demonstrate quick_sort_array(…)
    int array2[] = { 5, 4, 2, 1, 7, 6, 3 };
    size_t size2 = sizeof(array2) / sizeof(*array2);  // # of elements
    _print_array(array2, size2, "Before quick_sort_array(array2)");
    quick_sort_array(array2, size2);
    _print_array(array2, size2, "After  quick_sort_array(array2)");

    return EXIT_SUCCESS;
/* vim: set tabstop=4 shiftwidth=4 fileencoding=utf-8 noexpandtab: */


Before tree_sort_array(array1)
5 4 2 1 7 6 3

After  tree_sort_array(array1)
1 2 3 4 5 6 7

Before quick_sort_array(array2)
5 4 2 1 7 6 3

After  quick_sort_array(array2)
1 2 3 4 5 6 7


  1. Your submission must contain each of the following files, as specified:
  2. file contents
    sorts.h type definitions
    struct type with 2 fields: root (BSTNode*) and size (int).
    • Whenever size==0, root must be NULL.
    struct type with 3 fields: value (int), left, and right (BSTNode*)
    function declarations one for each required function in your sorts.c
    • Do not include helpers (if any) here.
    sorts.c function definitions
    tree sort array(int✶ array, size t size)
    return type: void
    Sort array by creating a BST and then traversing it.
    • Steps: ① call create_bst(…), ② use in-order traversal to store sorted values in array (same memory), ③ call empty_bst(…).
    • Store the result in the same array that was passed in.
    • This may not result in any heap allocation (i.e., calls to malloc(…)), except for the BST nodes allocated as a result of calling create_bst(…); those must be freed before this function returns.
    quick sort array(int✶ array, size t size)
    return type: void
    Sort array using the qsort(…) standard library function.
    • This should simply call the qsort(…) library function.
    • The qsort(…) function requires the use of function addresses (function pointers).
    • This may not result in any heap allocation (i.e., calls to malloc(…)) by your code.
    • Yes, this is easy, but make sure you understand how it works!
    create bst(const int✶ array, int size)
    return type: BST
    Create a new BST.
    • size is the number of elements in array.
    • When size==0 array must be NULL and vice versa.
    • If size==0 then return a BST with the root set to NULL.
    • malloc(…) may be called from a total of one place in this function and any it depends on.
    empty bst(BST✶ a bst)
    return type: void
    Free all the nodes in the BST.
    • Set root to NULL, and size to 0.
    • free(…) may be called from a total of one place in this function and any it depends on.
    • Update: This parameter should be called a_bst, not bst, since it is the address of an object.
    test_sorts.c function definitions
    main(int argc, char✶ argv[])
    return type: int
    Test your functions in sorts.c.
    • This must cause every line of code in your sorts.c to be executed.
    • Every public function in sorts.c must be called directly from main(…) and/or from a helper within test_sorts.c.
    expected.txt functions Expected output from running your test_sorts.c
  3. Use the typedef syntax to declare all struct types.
  4. Only the following externally defined functions and constants are allowed in your .c files. (You may put the corresponding #include <…> statements in the .c file or in your sorts.h, at your option.)
    header functions/symbols allowed in…
    stdbool.h bool, true, false sorts.c, sorts.h, test_sorts.c
    assert.h assert sorts.c, test_sorts.c
    stdio.h printf, fprintf, stdout, FILE test_sorts.c
    stdlib.h malloc, free, NULL, EXIT_SUCCESS, EXIT_FAILURE sorts.c, test_sorts.c, sorts.h
    Feel free to suggest additional header files or functions that you would like to use.
  5. Submissions must meet the code quality standards and the policies on homework and academic integrity.


To submit HW13 from within your hw13 directory, type 264submit HW13 sorts.h sorts.c test_sorts.c expected.txt miniunit.h clog.h Makefile


The pre-tester for HW13 has been released and is ready to use.


  1. Where's the starter code? How am I supposed to start?
    Learning to program in C entails learning to set up your files and code your project to meet a specification—without being given step-by-step instructions. That said, here is a general process you can use for doing that.
    How to do any coding homework in ECE 264
    1. First, identify the files you are responsible for producing. In this case, there are four files: sorts.c, sorts.h, test_sorts.c, and expected.txt. Create an (almost) empty file for each.
    2. In the .c and .h files, add a Vim modeline. (Tip: In Vim on ecegrid, type newc and press Tab to get a skeleton file, including the modeline. Remove any #include statements or anything else that isn't needed or allowed.)
    3. In the .h file (sorts.h), add an include guard.
    4. Create your makefile. This is optional, but recommended. You do not need to use miniunit.h or clog.c in order to use make (but you may if you wish).
    5. Submit. Your code is nearly empty, but this gives you a backup.
      make submit (or 264submit sorts.h sorts.c test_sorts.c expected.txt miniunit.h clog.h Makefile for Luddites).
    6. Decide on a general order in which to implement the various parts of the assignment. For HW13, you could do the three parts in any order. If you have no opinion, we lightly suggest doing in this order: ①tree_sort_array(…), ②quick_sort_array(…). But again, it's up to you.
    7. In your test code file (test_sorts.c), create your first test.
    8. Write one very simple test (e.g., sort empty array). At this point, your sorts.c should have no useful code in it.
    9. Write just enough code in sorts.c to pass your easy test.
    10. Run your test (e.g., make test (or ./test_sorts for Luddites). Hopefully, your simple test passes. This step is to test the mechanics of your testing, and verify that you have the correct function signatures and such.
    11. Add a slightly harder test (e.g., sort array of size one).
    12. Add code to your sorts.c so that both of your tests should pass.
    13. Run your tests. Make any fixes so that both of your tests do pass.
    14. Gradually add tests, extend implementation, run tests, and fix, until one section of your project is complete (e.g., until create_bst(…) is working).
    15. Follow the above steps to complete the rest of HW13.
    16. Re-read the specification—especially the Requirements table—and make sure you have done everything, and your types/fields/functions all match the specification.
    17. Check for gaps in your tests. Make sure you get 100% line coverage from make coverage. This is no guarantee of perfection, but if you don't have 100% coverage, you need to fix that. Make sure your tests cause every part of your implementation code to be executed.
    18. Think through your tests carefully. Make sure you have covered all:
      • “edge cases” – extreme values
      • “corner cases” – values that cause your code to behave differently
      • “special cases” – exceptions to normal functionality described in the specification
      • "easy cases" – easy for you to understand
      (Note: These are not standard terms.)
  2. Can I add helper functions to sorts.c?
    Yes. Make sure the names begin with "_".
  3. Is there a warm-up?
  4. Is it a violation of the spec if qsort(…) calls malloc(…)?
    No. The only requirement is that your code not call malloc(…) as a result of calling quick_sort_array(…).
  5. Should I use recursion for the BST operations (create_bst(…), tree_sort_array(…), and empty_bst(…))?
    Short answer: Yes.
    Long answer: All of these things can be accomplished without recursion, using only while loops. However, those methods are messier.
    The methods demonstrated in class (see snippets) use recursion, and that is what we recommend.
  6. How do I create a BST? … delete a BST?
    To create a BST, start with an empty BST (root = NULL) and then insert each element you wish to add.
    To delete a BST, delete the left and right subtree (recursively). Then, free the root.
  7. What is “in-order traversal”?
    With in-order traversal means you “visit” (i.e., do something with) the left subtree, then the root, and finally the right subtree. In that example, “visit” meant printing the value of a node. For a BST, this results in printing the values in order.
    You will learn a bit more about tree traversals later, but for HW13, this is all you need to know.
    In the print_bst(Node* root) function in the snippet:
    1. Print the left subtree by calling print_bst(root -> left)recursively.
    2. Print the root by calling printf("%d\n", root -> value).
    3. Print the right subtree by calling print_bst(root -> right)recursively.
    If the tree has zero nodes (i.e., empty), we do nothing.
    If the tree has one node (i.e., root has no children):
    1. print_bst(root -> left) has no effect.
    2. printf("%d ", root -> value) prints 4 .
    3. print_bst(root -> right) has no effect.
    If the tree has three nodes (i.e., root has children but no grandchildren):
    1. print_bst(root -> left) prints 2 .
    2. printf("%d ", root -> value) prints 4 .
    3. print_bst(root -> right) prints 6 .
    Altogether, it prints 2 4 6 .
    If the tree has seven nodes (i.e., root has children but no grandchildren):
    1. print_bst(root -> left) prints 1 2 3 .
    2. printf("%d ", root -> value) prints 4 .
    3. print_bst(root -> right) prints 5 6 7 .
    Altogether, it prints 1 2 3 4 5 6 7 .
    For tree_sort_array(…), you are following the same process, except instead of printing a value with printf(…), you store it in the array.