Coding Challenge: Modeling Complex Objects and Relationships (1)
Practice everything learned so far by building an application to handle a flower business.
Introduction
We aim to practice working with pointers, structures, and dynamic memory allocations by building a mini-project to manage a flower business.
Our business is quite the hit with flower enthusiasts, so we want to be able to expand in different continents and keep track of each continent individually.
Each continent can have multiple flower shops, depending on how many we decide to open in that continent.
Each flower shop sells only two types of flowers since they are exotic flowers and quite expensive. It is a very focused business, quality over quantity. In reality, the restriction of selling only two flowers is here to simplify our code.
Suppose you’re familiar with object-oriented programming (OOP). In that case, you may recognize that we’re defining a hierarchy using structures similar to how we would with classes in an OOP language.
Note: This lesson has two parts, which appear to be pretty long. The complete solution at the end takes the most time to read. We promise the text doesn’t take an hour to read! In fact, without the playground code widget, the lessons take around 24 minutes to read. It may take a few hours to solve all the challenges.
Lesson structure
This lesson is interactive by providing a mix of a tutorial, a follow-along session, and a coding challenge. We’ll build the project bit by bit, incrementally.
- Some functions will be provided as a tutorial.
- You will have to complete other functions and pass the test cases.
Note: Tasks marked with “demo task” are tutorial tasks. You don’t have to write any code. Make sure to understand the code. However, if you want, you can attempt to write the code on your own in any text editor or IDE and then compare it with the provided solution.
Structures
We’ll use the following structures to model the objects in our business.
Flower
We represent a flower using the TFlower
structure. It contains:
flowerName
: Pointer to the name of the flowercolor
: 32-bit string holding the color of the flowerprice
: The price of the flower (float
)quantity
: The quantity we have in stock (int
)totalSold
: Number of units we sold (int
)
Flower shop
A flower shop sells flowers. We represent it using the TFlowerShop
structure, with the following members:
name
: Pointer to the name of the flower shopaddress
: 64-bit string containing the shop’s addressopeningYear
: The year when we opened this shop (int
)flowers
: Array of two flowers. Recall that we only sell two flowers
Continents
We identify flower shops using the continent where they are operating. We represent a continent using the TContinent
structure, with the following members:
continentId
:int
, the id of the continent. The available ids are:EUROPE
:0
ASIA
:1
AFRICA
:2
NORTH_AMERICA
:3
SOUTH_AMERICA
:4
OCEANIA
:5
flowerShops
: A resizable dynamically allocated array of pointers to shops.INITIAL_FLOWER_SHOPS_SIZE
:1
as the initial size for the array.
length
: The current number of flower shops in the array (int
). We use it to regrow when there is no more empty space.size
: The maximum size of the array (int
). We use it to regrow when there is no more empty space.
Flower business
We use a structure called TFlowerBusiness
to keep track of the whole business. It contains:
continents
: An array of size6
of pointers to continents. Instead of the magic number6
, use the definitionMAX_CONTINENTS
.onContinent
: An array of size6
of integers, where each value can be either0
or1
.0
: The flower business is not present on this continent.1
: The flower business is present on this continent.
Note: Ensure you’re familiar with the objects involved and their members.
Task 1: Demo task
We’ll provide the definitions to ensure that we’re all on the same page. However, before reading the solution, write them on your own and compare them with the ones provided below…
Note: This step is ungraded as we can’t check it, but it’s important. Please write the definitions on your own, in any text editor or IDE.
According to the specifications provided above, we can write the structures like so:
typedef struct
{
char* flowerName;
char color[32];
float price;
int quantity;
int totalSold;
} TFlower;
typedef struct
{
char* name;
char address[64];
int openingYear;
TFlower flowers[2];
} TFlowerShop;
typedef struct
{
int continentId;
TFlowerShop** flowerShops;
int length;
int size;
} TContinent;
typedef struct
{
TContinent* continents[MAX_CONTINENTS];
int onContinents[MAX_CONTINENTS];
} TFlowerBusiness;
Check the structure definitions against the specifications to understand why some types are the way they are.
The most complicated type is TFlowerShop** flowerShops;
from TContinent
. Make sure you understand why it is TFlowerShop**
.
- We want an array of pointers to
TFlowerShop
, each element inside the array is of typeTFlowerShop*
. - We want an array where each element is of type
TFlowerShop*
. We know that for a typeT
we can represent an array asT*
. - Then, for the type
TFlowerShop*
, the array is of typeTFlowerShop**
. - Another way to think about this is in terms of array decaying. We can represent an array as a pointer to the first element. A pointer to
TFlowerShop*
isTFlowerShop**
. - Another way to think about this is that
malloc
orcalloc
returns a pointer to the memory block. We want a memory block of typeTFlowerShop*
. A pointer to it isTFlowerShop**
.
Helpful defines
We use the following defines
for convenience, to avoid using magic numbers.
//Continent IDs
#define EUROPE 0
#define ASIA 1
#define AFRICA 2
#define NORTH_AMERICA 3
#define SOUTH_AMERICA 4
#define OCEANIA 5
#define MAX_CONTINENTS 6
//Macro to check if a continent id is valid
#define IsContinentIdValid(x) (EUROPE <= x <= OCEANIA)
//The initial size of the flower shops array for each continent
#define INITIAL_FLOWER_SHOPS_SIZE 1
const char* CONTINENT_NAMES[MAX_CONTINENTS] =
{
"Europe",
"Asia",
"Africa",
"North America",
"South America",
"Oceania"
};
Get hands-on with 1400+ tech skills courses.