Решение начнем с анализа данных. Для представления номенклатурной единицы супермаркета создадим структуру, хранящую данные о штрихкоде товара, его наименовании и цене. Цену зададим в центах. Определение структуры:
/// Информация о товаре магазина
struct Item {
/// Штрихкод товара
int m_barCode;
/// Наименование товара, не более 85 символов
char m_name[86];
/// Цена 1 единицы товара в центах
int m_price;
};
|
База данных (БД) товаров в простейшем случае – массив, который хранит структуры товара и количество номенклатурных позиций супермаркета. Опишем БД товаров в виде следующей структуры:
/// База данных товаров
struct ItemDatabase {
/// количество товаров в БД
int m_count;
/// массив товаров
struct Item *m_items;
};
|
Для поиска товара по его штрихкоду в БД создадим функцию FindItem(),способную принимать два аргумента: указатель на БД товаров и штрихкод товара, информацию по которому необходимо извлечь из БД:
/// Поиск товара в БД по штрихкоду
struct Item *
FindItem (const struct ItemDatabase *database, int barCode){
int i;
assert (database!= NULL);
assert (database->m_count > 0);
for (i = 0; i < database->m_count; ++i)
if (database->m_items[i].m_barCode == barCode)
return database->m_items + i;
return database->m_items;
}
|
Функция линейным поиском проходит по БД товаров и, если ей удается найти товар с соответствующим штрихкодом, возвращает указатель на структуру с информацией о нем. Если товара с таким штрихкодом нет, функция возвращает указатель на первый товар в БД.
Для представления товара в чеке введем дополнительную структуру, которая будет хранить пару «товар – количество товара в чеке»:
/// Строка счета
struct BillItem {
/// Товар
const struct Item *m_item;
/// Количество в счете
int m_quantity;
};
// Чек – это массив структур BillItem.
/// Счет
struct Bill {
/// Количество товаров в счете
int m_count;
/// Массив товаров в счете
struct BillItem *m_items;
};
|
Создадим две вспомогательные функции, которые будут осуществлять управление памятью для структуры чека. Функция AllocBill() выделяет память под структуру чека и резервирует память под массив товаров, функция FreeBill() освобождает всю память, связанную с чеком:
///Выделяет память под счет
struct Bill * AllocBill (int itemsCount) {
struct Bill *result;
result = malloc (sizeof (struct Bill));
if (!result)
return NULL; // выделение памяти было неуспешным
result->m_count = 0;
result->m_items = malloc (itemsCount * sizeof (struct BillItem));
if (!result->m_items && itemsCount > 0) {
free (result);
return NULL;
}
return result;
}
/// Освобождает память, связанную со структурой счете
void FreeBill (struct Bill *bill) {
assert (bill!= NULL);
if (bill->m_items)
free (bill->m_items);
free (bill);
}
|
Для печати чека на экране создадим функцию PrintBill(). На входе она принимает указатель на печатаемый чек и выводит его на экран с соответствующим форматированием:
/// Печатает счет на экран
void PrintBill (const struct Bill *bill) {
int i;
int totalBill = 0;
assert (bill!= NULL);
// Печатаем заголовок
printf ("C supermarket\n\n");
// Печатаем список товаров
for (i = 0; i < bill->m_count; ++i) {
int totalLine = bill->m_items[i].m_item->m_price*bill->m_items[i].m_quantity;
totalBill += totalLine;
printf ("%c (x%d) $%d.%d\n",
bill->m_items[i].m_item->m_name,
bill->m_items[i].m_quantity,
totalLine / 100,
totalLine % 100
);
}
// Печатаем строку итого
printf ("\nTOTAL $%d.%d\n", totalBill / 100, totalBill % 100);
}
|
Центральной является функция ProduceBill(), которая по массиву штрихкодов создает структуру счета. Она сначала выделяет память под результат при помощи AllocBill(), затем проходит по списку переданных штрихкодов, ищет информацию о соответствующем товаре в БД и включает ее в счет. Если в счете уже имеется такой товар, то функция просто увеличивает его количество. Если товара нет, функция включает в счет новую строку:
/// Создает счет по списку товаров
struct Bill *
ProduceBill (const struct ItemDatabase *database, const int *barCodes, int count)
{
int i;
struct Bill *result;
assert (database!= NULL);
assert (barCodes!= NULL);
result = AllocBill (count);
if (!result)
return NULL;
// Формируем счет
for (i = 0; i < count; ++i) {
// Выполняем поиск элемента в счете, может он был добавлен ранее
int index = IndexOfBillItem (result, barCodes[i]);
if (index == -1) {
// Не нашли, добаляем новый элемент
result->m_items[result->m_count].m_item = FindItem (database, barCodes[i]);
result->m_items[result->m_count].m_quantity = 1;
++result->m_count;
}
else {
// Нашли, тогда увеличиваем количество
++result->m_items[index].m_quantity;
}
}
return result;
}
|
Для проверки того, есть ли товар с некоторым штрихкодом в счете, функция ProduceBill() использует специальную вспомогательную функцию IndexOfBillItem(), которая возвращает либо индекс товара в счете, либо число –1, если товара в счете нет. Программный код вспомогательной функции IndexOfBillItem() выглядет так:
/// Поиск товара в счете
int IndexOfBillItem (const struct Bill *bill, int barCode){
int i;
assert (bill!= NULL);
for (i = 0; i < bill->m_count; ++i)
if (bill->m_items[i].m_item->m_barCode == barCode)
return i;
return -1;
}
|
Базу данных товаров определим в виде константы с помощью спецификатора static:
/// База данных товаров в супермаркете
static struct Item g_databaseItems[] = {
{ 0, "Unknown item", 0 },
{ 4719, "Fish Fingers", 1211 },
{ 5643, "Nappies", 1010 },
{ 3814, "Orange Jelly", 561 },
{ 1111, "Hula Hoops", 211 },
{ 1112, "Giant Hula Hoops", 1331 },
{ 1234, "Dry Sherry, 1lt", 5401 }
};
const struct ItemDatabase g_database = {
sizeof (g_databaseItems) / sizeof (g_databaseItems[0]),
g_databaseItems
};
|
Функция main() создает и печатает счет:
int main (int argc, char** argv[])
{ int codes[] = { 1234, 4719, 3814, 1112, 1111, 1111, 1234 };
struct Bill *bill = ProduceBill (&g_database, codes, sizeof (codes) / sizeof (codes[0]));
if (!bill) {
printf ("Error: out of memory if creating a bill");
return -1;
}
PrintBill (bill);
FreeBill (bill);
return 0;
}
|
Задание
1. Произведите сборку программы и осуществите ее компиляцию.
2. Форматирование счета. Измените функцию PrintBill(), чтобы она печатала счет в соответствии с форматированием, приведенным в условии. Ширина строки счета – 40 символов.
3. Отбрасывание неизвестных товаров. Измените функцию ProduceBill() таким образом, чтобы товары, не найденные в БД, не включались в счет.
4. Вычисление скидки. За каждые две купленные бутылки шерри (код 1234) супермаркет дает скидку $5.00 с суммы счета. Добавьте вычисление скидки по счету. Результирующий счет должен выглядеть следующим образом: