(Updated December 17, 2025)
Table of contents
Overview
As C is a primitive system-level language, it makes sense that you will have practically nothing but primitive tools. From there, you can create so much more. The details in this chapter delve into preparing for handling more extensive data structures.
These tools provide the means to build more than simple primitive types. From this point forward, we can begin crafting more complex data types. The approach in this chapter is to start building a card game. We will introduce methods of representing playing cards, including rank and suit. Further, we will see how to define the deck of cards and a mechanism to describe a player’s hand.
Finally, we will begin by looking at how to rank poker hands and an approach to scoring a blackjack hand.
Enumerations
Up to this point if we wanted to name something that has a value, we’d use a variable. Seems simple right? Well, it is until we was to be able to treat the representation as a type. In other words, rather than have a bunch of int variables that represent names of things, we’d like a formal collection of named items that have a relationship.
Enumerations allow us to use names within a predefined list. For example:
enum primary { Red, Blue, Yellow};
enum secondary { Orange, Green, Purple };
We’ve create two new types. These are enum primary and enum secondary. From here we can create variables and assign values without having to think about the complexity of how thing will be represented by the compiler. Let’s combine our colors into one enumeration.
enum colors { Red, Orange, Yellow, Green, Blue, Indigo, Violet };
int main(void) {
enum colors pants, shirt, shoes;
pants = Blue;
shirt = Yellow;
shoes = Indigo;
}
Example 1a: Enumeration of colors.
Here’s what to note about Example 1:
-
The type
enum colors has 7 values Red through Violet.The ordinal values are Red is 0, Orange is 1 and so on with Violet being 6.
The variables pants, shirt, and shoes are simply
Here’s another version with some printf statements.
#include <stdio.h>
enum colors { Red, Orange, Yellow, Green, Blue, Indigo, Violet };
int main(void) {
enum colors pants, shirt, shoes;
pants = Blue;
shirt = Yellow;
shoes = Indigo;
printf("pants is %d\n", pants);
printf("shirt is %d\n", shirt);
printf("shoes is %d\n", shoes);
pants = 25;
printf("pants is %d\n", pants);
}
Example 1b: Enumeration of colors.
We mentioned that
There are a few things to keep in mind with enumerations.
- A given name in the enumeration cannot appear in another enumeration.
- Each name has a ordinal value. The default is the first has an ordinal value of 0 and counts up by one there after.
- You can assign ordinal values, but they cannot repeat. For example
- Variables of any enumeration is essentially an
unsigned int.
typedef
The use of typedef allows us to create some shorthand representations of types or aliases. Essentially, we’re trying to make the code more readable and even more portable. It also offered a level of code maintainability. How?
Consider a project where an important data value is an unsigned char. Now, we could simply use unsigned char throughout the entire project, across many source code files. Then one day a change is required where we need to move to unsigned int.
This could be a maintenance nightmare. We’d have to find every occurrence of that usage and change it – without affecting other areas of the code that ought to remain unsigned char. Was this a poor design on the part of the data type selected? Possibly, but hindsight being what it is, we’re still faced with a lot of code maintenance.
A simple way to avoid this is to do something like this in a header file:
typedef unsigned char ImportantType;
We would include the header file and then use ImportantType in all the places we would have used unsigned char. Now if we need to make a major change to the type, we can do it in one place:
typedef unsigned char ImportantType;
becomes
typedef unsigned int ImportantType;
And we simply recompile the code. There may be other things that need to change, like the conversions of printf‘s and such, but the overall maintenance could be greatly reduced.
unsigned char was used and printf was printing the values as numbers instead of actual chars, It was likely using %u and would not need to be changed.Structures
Structures are a tool that allow us to package a bunch of values together. For example the code below creates a kind of string once used in UCSD Pascal. This is not how it was done, but a possible implementation.
struct PString {
unsigned char length; // length as the first value
char chars[255]; // 255 chars
};
A new type is created called struct PString. It contains two members: length and chars.