Перечисления в C
Agar sizning kompliyatoringiz eski bo’lsa, unda yozishingiz lozim bo’ladi.
1-dars. C++ algoritmik tilining asosiy tushunchalari
C++ tilida so’z deb bir nechta belgilar ketma-ketligi tushuniladi. Xizmatchi so’z deb C++ tilidagi standart nom tushuniladi. Bu maxsus ma’noni anglatadi va uni ma’lumotlarga berib bo’lmaydi.
Masalan: int, float, for, while va hokazo.
C++ tilida ma’lumotlarning elementlari bo’lib o’zgaruvchilar, o’zgarmaslar, izohlar xizmat qiladi.
O’zgaruvchi:
Xotiraning nomlangan qismi bo’lib, o’zida ma’lum bir toifadagi qiymatlarni saqlaydi. O’zgaruvchining nomi va qiymatlari bo’ladi. O’zgaruvchining nomi orqali qiymat saqlanayotgan xotira qismiga murojaat qilinadi. Programma ishlashi jarayonida o’zgaruvchining qiymatini o’zgartirish mumkin. Har qanday o’zgaruvchini ishlatishdan oldin, uni e’lon qilish lozim.
Quyidagi butun sozlardan foydalanish uchun b, haqiqiy sonlardan foydalanish uchun h o’zgaruvchisi e’lon qilingan:
int b;
float h;
O’zgarmaslar (const)
Hisoblash jarayonida qiymatini o’zgartirmaydigan kataliklarga aytiladi.
float const pi = 3.14;
Programmaning ma’lum qismini tavsiflash uchun ishlatiladi va bu qatorda hech qanday aml bajarilmaydi, ya’ni programmaning biror qismini yaxshiroq tushuntirish uchun xizmat qiladi. Izoh «/*» va «*/» simvollari orasida beriladi.
/* Bu yerga izoh yoziladi. */
Bundan tashqari birsatrli izohlardan ham foydalanish mumkin. Buning uchun izoh boshiga «//» belgisi qo’yiladi.
Operator
Tilning yakunlangan jumlasi hisoblanadi va ma’lumotlar taxlilining tugallangan bosqichini ifodalaydi. Operator nuqtali vergul «;» bilan ajratiladi.
Identifikator
Programmist tomonidan programma elementlari (funksiya, o’zgaruvchilar, o’zgarmaslar …) uchun ixtiyoriy nom.
Identifikator tanlanganda quyidagilarga ahamiyat berish kerak:
- Iden tifikator lotin harflaridan boshlanishi shart;
- Ikkinchi simvoldan boshlab raqamlardan foydalanish mumkin;
- C++ da katta kichik harflar farq qiladi. Ya’ni quyidagilarning har biri alohida identifikator hisoblanadi: KATTA, katta, KaTTa, kAttA, KattA, …
- Probel C++ da so’zlarni ajratish uchun ishlatiladi. Shuning uchun identifikatorda probeldan foydalanib bo’lmaydi.
- Xizmatchi (int, float, for, while kabi) so’zlardan identifikator sifatida foydalanib bo’lmaydi;
Eslatma
Identifikator tanlashda birinchi belgi sifatida «_» belgisidan foydalanmaslik tavsiya etiladi.
C++ da programma
C++ da programma funksiya yoki funksiyalardan tashkil topadi. Agar programma bir nechta funksiyadan iborat bo’lsa, bir funksiyaning nomi main bo’lishi shart. Programma aynan main funksiyasining birinchi operatoridan birinchi operatoridan boshlab bajariladi.
Funksiyaning aniqlashishi quyidagicha bo’ladi:
qaytariluvchi_qiymat_toifasi funksiya_nomi ( [parametrlar] )
funksiya tanasini tashkil qiluvchi operatorlar
Qoida bo’yicha funksiya qandaydir bir qiymatni hisoblash uchun ishlatiladi. Shuning uchun funksiya nomi oldindan, funksiya qaytaradigan qiymat toifasi yoziladi. Agar funksiya hech qanday qiymat qaytarmaydigan bo’lsa, void toifasi yoziladi. Agar funksiya qaytaradigan qiymat toifasi yozilmagan bo’lsa, int (butun) toifali qiymat qaytariladi deb qabul qilinadi.
Funksiya bilan keyingi mavzularda batafsil tanishamiz.
C++ da oddiy matnni ekranga chiqaruvchi programmani ko’rib chiqamiz
- // Muallif: Haydarov Abdulaminxon
- //Sana: 18 oktabr 2016 yil
- // Maqsad: Matnni ekranga chiqaruvchi programma
- # include // ekranga ma’lumot chiqarish uchun
- int main ()
- std :: cout
- return 0;
- >
Har bir satrni o’rganib chiqamiz:
1, 2, 3- satrlar izoh hisoblanadi. Malakali programmistlar har qanday programma muallif, programmaning tuzilish sanasi va maqsadini ifodalovchi izoh bilan boshlanishini maslahat berishadi.
4, 6, 10- satrlar bo’sh satrlar hisoblanadi. Bosh satrlar programma qismlarini bir- biridan ajratib qo’yish uchun ishlatiladi. Programma qismlarining bir- biridan ajralib turishi, programma o’qilishini osonlashtiradi.
5- satrda, klaviaturadan ma’lumotlarni kiritish va ekranga chiqarish uchun sarlavha fayli programmaga qo’shilyapti. Bu satr klaviatura orqali ma’lumot kirituvchi va ekranga nimadir chiqaruvchi har qanday programmada bo’lishi shart. Aks xolda xato sodir bo’ladi.
Agar sizning kompliyatoringiz eski bo’lsa, unda yozishingiz lozim bo’ladi.
7- satrda butun toifadagi qiymat qaytaruvchi main funksiyasi berilgan int xizmatchi so’zi butun toifadagi ma’lumotlarni e’lon qilishi uchun ishlatiladi.
12- satrdagi yopuvchi figirali > funksiya tanasining tugaganini bildiradi.
11- satrdagi return xizmatchi so’zi orqali funksiya 0 qiymat qaytaradi va programma muvoffaqqiyatli yakunlanadi.
O’zgaruvchilarni e’lon qilish
Programmada ishlatilgan barcha o’zgaruvchilarni qaysi toifaga tegishli ekanligini e’lon qilish kerak. Ma’lumotlarni e’lon qilishining umumiy ko’rinishi quyidagicha:
toifa_nomi o’zgaruvchi;
Agar bir nechta o’zgaruvchi bir toifaga mansub bo’lsa, ularni vergul bilan ajratib berish mumkin.
Butun sonlarni ifodalash uchun int va haqiqiy sonlarni ifodalash uchun float xizmatchi so’zlaridan foydalaniladi. Bu darsda shu 2 tasini bilish kifoya qiladi. Keyingi mavzuda butun va haqiqiy sonlar haqida batafsil gaplashamiz
int x, y; // butun toifadagi o’zgaruvchilarni e’lon qilish
float a, b, c; // haqiqiy toifadagi o’zgaruvchilarni e’lon qilish
Kiritish va chiqarish operatorlari
Programmada klaviatura orqali ma’lumot kiritish va ekranga chiqarish uchun preprotsessor direktivasini, ya’ni #include ni programmaga qo’shish shart.
Ma’lumot kiritish std::cin >>, ma’lumotlarni chiqarish std::cout operatori orqali amalga oshiriladi.
Bu operator bajarilgan ekranda kursor paydo bo’ladi. Kerakli ma’lumot klaviatura orqali kiritilgandan so’ng Enter tugmasi bosiladi.
cout orqali ekranga ixtiyoriy ma’lumotni chiqarish mumkin. Satrli ma’lumotlarni ekranga chiqarish uchun, ularni qo’shtirnoq orasida yozish kerak.
Quyidagi a va b sonlarning yig’indisini chiqaruvchi programma berilgan:
// Muallif: Abdulaminxon Haydarov
// Maqsad: a va b sonlari yig’indisini chiqaruvchi programma
// standart nomlar fazosidan foydalanishni e’lon qilish
using namespace std;
int main ()
int a, b, c;
return 0;
Arifmetik amallar:
- + qo’shish
- – ayirish
- * ko’paytirish
- / bo’lish
Ba’zi matematik funksiyalar:
Matematik funksiyalardan programmada foydalanish uchun math.h faylini programmaga qo’shish kerak
Funksiyaning C++ da ifodalanishi Funksiyaning matematik ifodalanishi
abs(x) – butun sonlar uchun |x|
fabs(x) – haqiqiy sonlar uchun |x|
pow(x, y) x ni y darajasi
Haqida Abdulaminxon
Saytda siz o’zingizga kerakli narsa oldingizmi demak bizni sayt sizga qandaydir yordam bergan. Sayt ya’nada ko’proq hizmat qilishi uchun qo’llab quvvatlang: Yandex Money: 410015117683936 WebMoney WMZ: Z501426318717 Sizning har qanday yordamingiz biz uchun muhum!
Перечисления в C++
Перечисления в C++ (enum) — это один из способов определения типов. Их отличительная особенность заключается в том, что перечисления содержат набор числовых констант. Рассмотрим простейшее перечисление:
Чтобы определить перечисление, используют ключевое слово enum, после которого пишут название перечисления. Далее в фигурных скобках числовые константы перечисляют через запятую. При этом каждой константе по умолчанию присваивается числовое значение, которое начинается с 0. Таким образом, в нашем примере spring=0, a winter, соответственно, трем.
Итак, у нас есть возможность определить переменную типа seasons с последующим присвоением этой переменной значения одной из констант, которые объявлены в перечислении. По факту это будет являться числовым значением. Посмотрим на консольный вывод этой программы:
В то же самое время перечисление — это отдельный тип, что означает, что мы не сможем присвоить нашей переменной числовое значение напрямую:
Но что делать, если дефолтные значения для констант нас не устраивают? Есть возможность задать значения явным образом, к примеру, установив начальное значение:
В таком случае значения 2-й и последующих констант станет повышаться на единицу.
Есть возможность задать значение и для каждой константы:
Когда вообще нужны перечисления? Например, когда мы имеем ряд логически связанных констант, которые лучше определять в одном общем типе данных. Посмотрите на код ниже:
В нашем примере все арифметические операции сохраняются в перечислении operations. А уже с учетом выбранной операции в switch-конструкции выполняются определенные действия.
114 стандартных алгоритмов C++. Введение
Добро пожаловать в новую серию статей о стандартных алгоритмах C++. Стандартные алгоритмы предлагают безопасные и оптимизированные строительные блоки, которые могут заменить удивительное количество пользовательского кода.
Сегодня мы рассмотрим основы алгоритмов, объясним концепцию итераторов, немного поговорим об истории и о том, что, скорее всего, появится в C++23, и, наконец, рассмотрим алгоритмы for_each и swap .
Стандартные алгоритмы
Вы можете критиковать стандартную библиотеку C++ за отсутствие функциональности. Однако, когда дело доходит до обработки данных и чисел, стандартная библиотека C++ предоставляет универсальный набор алгоритмов. Поэтому, если вы разработчик C++, вы обязаны знать, какие средства доступны в ней.
Итераторы: уровень взаимодействия
В основе, которая находится между структурами данных C++ и алгоритмами, лежат итераторы. Итераторы абстрагируются от деталей обхода конкретной структуры данных, фиксируя поведенческие ограничения, которые налагает структура данных.
Например, массив (например, std::vector ) допускает произвольный доступ, что означает, что мы можем переходить от одного элемента к другому за постоянное время. С другой стороны, связанный список (например, std::list ) позволяет нам переходить за постоянное время только к следующему и предыдущему элементам, а перемещение на расстояние n требует n операций (линейная сложность).
C++ распознает следующие категории итераторов:
- итератор ввода (input iterator): движение вперед, чтение, один проход;
- однонаправленный итератор (forward iterator): движение вперед, чтение;
- двунаправленный итератор (bidirectional iterator): однонаправленный итератор + движение назад;
- итератор произвольного доступа (random access iterator): двунаправленный итератор + движение вперед и назад на любое целое число, вычисление расстояния между двумя итераторами;
- непрерывный итератор (contiguous iterator): произвольный доступ + хранение элементов непрерывно;
- итератор вывода (output iterator): движение вперед, запись, один проход.
< std::vectorvec; auto it = vec.begin(); it += 5; // *it == 6 > < std::listlst; auto it = lst.begin(); // it += 5; не компилируется std::advance(it, 5); // линейное движение вперед, *it == 6 >
Эта категоризация позволяет алгоритмам указывать требуемый тип итератора либо явно (с использованием концептов C++20), либо неявно, используя операции, поддерживаемые определенным типом итераторов.
Например, для std::sort требуются итераторы произвольного доступа, поскольку необходимо эффективно вычислять расстояние между двумя итераторами. Поэтому следующий код не будет компилироваться (поскольку std::list предоставляет двунаправленные итераторы):
std::list data = < 9, 1, 8, 2, 7, 3>; std::sort(data.begin(), data.end()); // не скомпилируется
Диапазоны (ranges)
В то время как C++20 формализовал понятие диапазона, это понятие присутствовало в C++ с самого начала. Ожидается, что каждый контейнер будет предоставлять доступ к двум итераторам, begin и end . Семантика здесь [begin,end) , то есть begin – это итератор для первого элемента, end – это итератор, следующий за последним элементом.
std::vector v; for (auto it = v.begin(); it != v.end(); it++)
Диапазоны (ranges) можно классифицировать с помощью тех же категорий итераторов. В данной серии статей мы будем использовать номенклатуру диапазонов вместо итераторов (например, диапазон ввода, однонаправленный диапазон, двунаправленный диапазон и т. д.).
Немного истории
Из предыдущих разделов вы могли уже предположить, что C++20 представляет собой важную веху в истории алгоритмов. И это происходит с введением диапазонов и ленивых представлений. Кроме того, несколько других стандартов C++ внесли существенные изменения, затронувшие стандартные алгоритмы.
- С++11 представил лямбда-выражения;
- C++17 представил параллельные алгоритмы;
- C++20 представил диапазоны и ленивые представления;
- ожидается, что C++23 представит поддержку реализованных пользователем представлений и, возможно, графовых алгоритмов.
for_each , for_each_n
Хватит теории. Давайте поговорим о конкретных алгоритмах, и начнем мы с самого простого, for_each и for_each_n .
for_each | |
constexpr | начиная с C++20 |
параллельность | начиная с C++17 |
использование диапазонов | начиная с C++20 |
ленивые вычисления | недоступно |
Ограничения | |
область применения | input_range |
область применения при параллельных вычислениях | forward_range |
функтор | indirectly_unary_invocable |
Поскольку в C++11 появился цикл на основе диапазона, for_each стал менее актуальным алгоритмом. Тем не менее, еще есть пара ситуаций, когда for_each предлагает множество возможностей.
Параллельная версия, вероятно, самый простой параллельный функционал в C++. Если всё, что вам нужно, это выполнить дорогостоящую операцию для каждого элемента изолированно, параллельный for_each – идеальное решение:
std::vector data = get_data(); std::for_each(std::execution::par_unseq, data.begin(), data.end(), [](int e) < /* какие-то дорогостоящие вычисления */ >);
Обратите внимание, что если операции не полностью изолированы, внутри лямбды вам потребуется дополнительная синхронизация.
Версия для диапазона может предложить более краткий код, если всё, что вам нужно, это спроецировать элемент, а затем отправить результат в другую функцию. Здесь у нас показан одинаковый код, выраженный двумя способами: с помощью for_each и с помощью цикла на основе диапазона:
struct Elem < double value() < return 10.2; >>; void some_function(double); int main() < std::vectordata(10, Elem<>); std::ranges::for_each(data, some_function, &Elem::value); for(auto& e: data) < some_function(e.value()); >>
В версии с ranges (строка 10) первый параметр – это диапазон, второй параметр – это функция, которую мы хотим вызвать для каждого элемента, а третий – проекция. В этом случае мы используем указатель на член. Если вы хотите углубиться в детали, у меня есть отдельная статья о диапазонах в C++20.
for_each_n (начиная с C++17) | |
constexpr | начиная с C++20 |
параллельность | начиная с C++17 |
использование диапазонов | начиная с C++20 |
ленивые вычисления | недоступно |
Ограничения | |
область применения | (input_iterator, iter_difference) |
область применения при параллельных вычислениях | (forward_iterator, iter_difference) |
функтор | indirectly_unary_invocable |
В то время как for_each работает со всем диапазоном, т.е. с интервалом [begin, end), for_each_n работает с диапазоном [first, first+n). Важно отметить, что поскольку этот алгоритм даже не имеет доступа к конечному итератору исходного диапазона, он не выполняет проверки выхода за границы, и вызывающий несет ответственность за обеспечение того, чтобы диапазон [first,first+n) являлся допустимым.
Для демонстрации давайте посмотрим на фрагмент кода, который оценивает квалификационный раунд в турнире. Мы хотим пригласить на основной турнир лучших игроков, а затем опубликовать окончательный счет онлайн, разбитый по 100 записей:
struct Player < std::string display_name; std::string contact_email; uint32_t score; >; std::vector get_ranking(); void send_invitation_to_main_tournament(const Player& player); void store_final_score(uint32_t page, const std::string& name, uint32_t score); inline constexpr const ssize_t MAIN_TOURNAMENT_SEATS = 10; inline constexpr const ssize_t PAGE_SCORE_SIZE = 100; int main() < std::vectorfinal_ranking = get_ranking(); std::ranges::sort(final_ranking, std::greater<>(), &Player::score); std::for_each_n(std::execution::par_unseq, final_ranking.begin(), std::min(MAIN_TOURNAMENT_SEATS, final_ranking.size()), send_invitation_to_main_tournament); auto it = final_ranking.begin(); uint32_t page = 0; while (it != final_ranking.end()) < ssize_t cnt = std::min(PAGE_SCORE_SIZE, final_ranking.end()-it); std::for_each_n(it, cnt, [page](const Player& p) < store_final_score(page, p.display_name, p.score); >); page++; it += cnt; > >
Отправку приглашений можно выполнять параллельно (строка 18), но мы должны избегать выхода за границы ( std::min в строке 19). Для разбиения на страницы мы переходим блоками размером PAGE_SCORE_SIZE и для каждого блока вызываем for_each_n (строка 26).
swap , swap_ranges , iter_swap
Вторая группа алгоритмов, которую мы сегодня обсудим, – это группа операций обмена.
swap | |
noexcept | начиная с C++11 |
constexpr | начиная с C++20 |
использование диапазонов | начиная с C++20 |
Ограничения | |
область применения | (T&, T&) (T(&)[N], T(&)[N]) |
область применения при использовании диапазонов | (T&&, U&&) |
Однако сначала нам нужно обсудить небольшую сложность, связанную с поиском, зависящим от аргумента. Нет ничего необычного в том, что структуры данных дешевы для операций обмена, поэтому для них мы захотим настроить операцию обмена.
Мы можем специализировать std::swap внутри пространства имен std , но это будет означать, что эта специализация не будет сопоставляться с использованием поиска, зависящего от аргумента (он будет жить в пространстве имен, отличном от его параметров). Это означает, что неквалифицированный вызов swap не найдет правильной реализации.
Правильный способ специализации swap – предоставить (дружественную) функцию в том же пространстве имен, что и структура данных:
namespace SomeLib < struct SomeStruct < std::vectordata; friend void swap(SomeStruct& left, SomeStruct& right) < left.data.swap(right.data); >>; >
И правильный способ вызвать swap – это подтянуть std::swap перед неквалифицированным вызовом:
void some_algorithm(auto& a, auto& b)
К счастью, версия swap для диапазонов в C++20 устраняет эту сложность. Она служит окончательным решением, и она:
- вызовет пользовательскую или стандартную версию swap , соответствующую типам;
- если такой версии не существует и параметры являются диапазонами, будет выполнена swap_range ;
- если параметры не являются диапазонами, по умолчанию используется версия swap с перемещением
V v(std::move(t)); t = std::move(u); u = std::move(v);
namespace Library < struct Storage < int value; >; void swap(Storage& left, Storage& right) < std::ranges::swap(left.value, right.value); >> int main() < int a = 1, b = 2; std::ranges::swap(a, b); Library::Storage j, k; std::ranges::swap(j, k); // вызов пользовательской Library::swap() >
Наконец, давайте поговорим о двух других вариантах, iter_swap и swap_ranges .
iter_swap | |
constexpr | начиная с C++20 |
использование диапазонов | начиная с C++20 |
Ограничения | |
область применения | (forward_iterator, forward_iterator) |
область применения при использовании диапазонов | ослаблена до непрямых заменяемых/перемещаемых типов |
iter_swap также можно назвать косвенным обменом, когда базовые значения заменяются итераторами или другими косвенными типами. Это в основном полезно для реализации пользовательских алгоритмов, поскольку они работают с итераторами.
Вот пример реализации алгоритма разбиения с использованием iter_swap (строка 12):
template requires std::forward_iterator && std::indirectly_swappable && std::predicate auto partition(It first, It last, Cond cond) < while (first != last && cond(first)) ++first; if (first == last) return last; for (auto it = std::next(first); it != last; it++) < if (!cond(it)) continue; std::iter_swap(it, first); ++first; >return first; >
swap_ranges | |
constexpr | начиная с C++20 |
параллельность | начиная с C++17 |
использование диапазонов | начиная с C++20 |
Ограничения | |
область применения | (forward_range, forward_iterator) |
область применения при использование диапазонов | (input_range, input_iterator) |
Операция обмена значений диапазонов – это перестановка частями двух непересекающихся диапазонов (возможно, из одного контейнера). Итератор начала задает второй диапазон, и вызывающий несет ответственность за обеспечение достаточной емкости целевого диапазона.
std::vector data< 1, 2, 3, 4, 5, 6, 7, 8, 9>; std::swap_ranges(data.begin(), data.begin()+3, data.rbegin()); // 9, 8, 7, 4, 5, 6, 3, 2, 1
Здесь мы меняем местами первые три элемента массива с последними тремя элементами массива. Порядок элементов обратный, потому что мы используем rbegin (начальный итератор для обратной итерации).
Спасибо за чтение
Следующая статья будет об алгоритмах сортировки и разбиения.
Comments are closed, but trackbacks and pingbacks are open.