Как создать меню на C или C++

Превью к статье о том, как создать меню для консольного приложения на C/C++

Порой, чтобы продемонстрировать работу консольной программы бывает удобно воспользоваться меню — элементом пользовательского интерфейса, позволяющим выбрать одну из нескольких перечисленных опций программы. Иногда же наличие меню является обязательным условием задания по программированию.

Рассмотрим создание меню для консольной программы на примере следующей задачи: имеется список товаров, состоящих из наименования, цены и количества данного товара. Написать программу, позволяющую находить наименьшую и наибольшую цену. Взаимодействие с пользователем организовать с помощью меню.

Разбиваем программу на пункты

Прежде чем добавлять меню в программу, следует разбить программу на процедуры, которые должны будут выполняться при выборе соответствующих пунктов меню. Нашу задачу можно разбить на следующие пункты:

  • Добавление товара в список
  • Вывод списка товаров (как минимум для контроля вводимых данных)
  • Вывод товара с наибольшей ценой
  • Вывод товара с наименьшей ценой
  • Выход (иначе программа никогда не сможет завершиться)

Выводим пункты меню на экран

Чтобы вывести меню на экран, очистим его и затем просто напечатаем с помощью printf или cout номер пункта и то, что он должен делать:

Вариант для C:

void print_menu() {
	system("cls");  // очищаем экран
	printf("What do you want to do?\n");
	printf("1. Add good to list\n");
	printf("2. Print all goods in list\n");
	printf("3. Print the highest price\n");
	printf("4. Print the lowest price\n");
	printf("5. Exit\n");
	printf(">");
}

Вариант для C++

void print_menu() {
	system("cls"); // очищаем экран
	cout << "What do you want to do?" << endl;
	cout << "1. Add good to list" << endl;
	cout << "2. Print all goods in list" << endl;
	cout << "3. Print the highest price" << endl;
	cout << "4. Print the lowest price" << endl;
	cout << "5. Exit" << endl;
	cout << ">";
}

Считываем введённый пункт меню

В самом простом случае достаточно всего лишь считать число, однако правильнее будет защитить программу от некорректного ввода с помощью считывания строки и проверки ввода на корректность.

Вариант для C:

int get_variant(int count) {
	int variant;
	char s[100]; // строка для считывания введённых данных
	scanf("%s", s); // считываем строку

	// пока ввод некорректен, сообщаем об этом и просим повторить его
	while (sscanf(s, "%d", &variant) != 1 || variant < 1 || variant > count) {
		printf("Incorrect input. Try again: "); // выводим сообщение об ошибке
		scanf("%s", s); // считываем строку повторно
	}

	return variant;
}

Вариант для C++:

int get_variant(int count) {
	int variant;
	string s; // строка для считывания введённых данных
	getline(cin, s); // считываем строку

	// пока ввод некорректен, сообщаем об этом и просим повторить его
	while (sscanf(s.c_str(), "%d", &variant) != 1 || variant < 1 || variant > count) {
		cout << "Incorrect input. Try again: "; // выводим сообщение об ошибке
		getline(cin, s); // считываем строку повторно
	}

	return variant;
}

Обработка выбранного пункта меню

Создадим для каждого пункта свой обработчик в виде процедуры, выполняющей указанное действие:

void add_good(good **goods, int *size, int *capacity) { 
	// реализация процедуры
}

void print_goods(good *goods, int size) { 
	// реализация процедуры 
}

void print_highest(good *goods, int size) { 
	// реализация процедуры 
}

void print_lowest(good *goods, int size) { 
	// реализация процедуры 
}

Чтобы выполнить действие в зависимости от введённого пункта воспользуемся оператором switch-case:

switch (variant) {
	case 1:
		add_good(&goods, &size, &capacity);
		break;

	case 2:
		print_goods(goods, size);
		break;

	case 3:
		print_highest(goods, size);
		break;

	case 4:
		print_lowest(goods, size);
		break;
}

Казалось бы, всё готово, однако пока что программа обработает лишь один пункт меню и затем завершится, хотя должна завершаться лишь при выборе пятого пункта - "выход". Чтобы организовать данный функционал, воспользуемся циклом do-while

int variant;

do {
	print_menu();

	variant = get_variant(5); // получаем номер выбранного пункта меню

	switch (variant) {
	case 1:
		add_good(&goods, &size, &capacity);
		break;

	case 2:
		print_goods(goods, size);
		break;

	case 3:
		print_highest(goods, size);
		break;

	case 4:
		print_lowest(goods, size);
		break;
	}

	if (variant != 5)
		system("pause"); // задерживаем выполнение, чтобы пользователь мог увидеть результат выполнения выбранного пункта
} while (variant != 5);

Теперь наше меню будет работать до тех пор, пока пользователь не введёт число 5 чтобы выйти. При этом для всех остальных пунктов меню вызывается функция задержки system("pause"), чтобы после завершения работы выбранного пункта меню нашей консольной программы пункты меню выводились не сразу, а лишь тогда, когда пользователь самостоятельно решит вернуться в него.

Полный листнинг кода

#include <stdio.h>
#include <stdlib.h>

typedef struct good {
	char name[20]; // название-описание товара
	double price; // цена товара
	int count; // количество товара
} good;

void add_good(good **goods, int *size, int *capacity) {
	printf("Enter good description: ");
	scanf("%s", &(*goods)[*size].name);
	printf("Enter good price: ");
	scanf("%lf", &(*goods)[*size].price);
	printf("Enter good count: ");
	scanf("%d", &(*goods)[*size].count);

	(*size)++;

	if (*size >= *capacity) {
		*capacity *= 2;

		*goods = (good *)realloc(*goods, *capacity * sizeof(good));
	}
}

void print_goods(good *goods, int size) {
	printf("+---------------------+-------------+-------+\n");
	printf("|         good        |    Price    | Count |\n");
	printf("+---------------------+-------------+-------+\n");

	if (size == 0)
		printf("|            No goods was added...          |\n");

	for (int i = 0; i < size; i++)
		printf("| %19s | %11.2lf | %5d |\n", goods[i].name, goods[i].price, goods[i].count);

	printf("+---------------------+-------------+-------+\n");
}

void print_highest(good *goods, int size) {
	double max = goods[0].price;
	int imax = 0;

	for (int i = 1; i < size; i++) {
		if (goods[i].count > max) {
			max = goods[i].price;
			imax = i;
		}
	}

	printf("The highest price of goods is %.2lf (good is %s)\n", max, goods[imax].name);
}

void print_lowest(good *goods, int size) {
	double min = goods[0].price;
	int imin = 0;

	for (int i = 1; i < size; i++) {
		if (goods[i].count < min) {
			min = goods[i].price;
			imin = i;
		}
	}

	printf("The lowest price of goods is %.2lf (good is %s)\n", min, goods[imin].name);
}

void print_menu() {
	system("cls");  // очищаем экран
	printf("What do you want to do?\n");
	printf("1. Add good to list\n");
	printf("2. Print all goods in list\n");
	printf("3. Print the highest price\n");
	printf("4. Print the lowest price\n");
	printf("5. Exit\n");
	printf(">");
}

int get_variant(int count) {
	int variant;
	char s[100]; // строка для считывания введённых данных
	scanf("%s", s); // считываем строку

	// пока ввод некорректен, сообщаем об этом и просим повторить его
	while (sscanf(s, "%d", &variant) != 1 || variant < 1 || variant > count) {
		printf("Incorrect input. Try again: "); // выводим сообщение об ошибке
		scanf("%s", s); // считываем строку повторно
	}

	return variant;
}

int main() {
	int variant; // выбранный пункт меню
	int size = 0; // количество элементов массива товаров
	int capacity = 1; // ёмкость массива товаров

	good *goods = (good *)malloc(capacity * sizeof(good)); // выделяем память под массив товаров

	do {
		print_menu(); // выводим меню на экран

		variant = get_variant(5); // получаем номер выбранного пункта меню

		switch (variant) {
			case 1:
				add_good(&goods, &size, &capacity);
				break;

			case 2:
				print_goods(goods, size);
				break;

			case 3:
				print_highest(goods, size);
				break;

			case 4:
				print_lowest(goods, size);
				break;
		}

		if (variant != 5)
			system("pause"); // задерживаем выполнение, чтобы пользователь мог увидеть результат выполнения выбранного пункта
	} while (variant != 5);

	return 0;
}

Демонстрация работы программы

Демонстрация работы программы с меню
Демонстрация работы программы с меню