Что пишут на c в 2021
Перейти к содержимому

Что пишут на c в 2021

  • автор:

С++ на практике

Все мы знаем, что С++ — мощный язык, у которого много сторонников. Но чем могут быть недовольны даже сторонники? Где сталкиваешься с неудобствами и чем они вызваны? Почему в примитивном приложении могут вылезти неожиданные сложности и чего не хватает в стандартной библиотеке? А главное, что можно сделать для улучшения ситуации?

Антон Полухин (antoshkka), состоящий в комитете по стандартизации C++ и работающий в «Яндекс.Такси», рассказал обо всём этом в докладе «C++ на практике». Сам доклад появился ещё в 2019-м, и с выходом C++20 что-то изменилось, но главные тезисы и вывод остались актуальны. Поэтому теперь, готовя новую конференцию C++ Russia 2021, мы решили сделать для Хабра пост на основе этого доклада. Под катом — и текст, и видеозапись. Далее повествование идёт от лица Антона.

Вступление

Меня зовут Антон Полухин, я сотрудник компании «Яндекс.Такси». В свободное и рабочее время я занимаюсь развитием C++: разрабатываю Boost-библиотеки, провожу мастер-классы и читаю лекции о своём любимом языке программирования.

Сегодня мы поговорим о том, насколько приятно пользоваться C++ в повседневной жизни: рассмотрим два совершенно разных приложения и увидим всю боль, ужасы и приятности «плюсов».

В «Википедии» умные люди пишут, что C++ — это «компилируемый, статически типизированный язык программирования общего назначения». А это значит, что на нём можно писать практически всё, что угодно: и консольные вспомогательные утилиты, и игры, и Android-приложения, и поисковые движки! По крайней мере, так говорят. Давайте проверим.

Пример «Ёлочка»

Под Новый год знакомый скинул мне программу на Bash, которая выводила на экран ёлочку в ASCII-графике с красиво мигающими лампочками:

Насколько сложно написать то же самое на «плюсах»? У меня это заняло день. Но написание на Perl, Python или Bash отняло бы столько же времени и было бы чуть неприятнее.

Что собой представляет это мини-приложение? Ёлочка мигает, а при нажатии на клавишу меняется режим «гирлянды»: все лампочки становятся одного цвета.

Суперсерьёзная программа! Она занимает примерно 100 строчек кода, но даже тут есть некрасивости. Например, что происходит в этом блоке?

std::thread t([&lamp]() < char c; while (std::cin >> c) < lamp.change_mode(); >>);

Блок отвечает за управление лампочками, но это приходится делать в отдельном потоке, так как в C++ нельзя одновременно мигать лампочками и смотреть, нажал ли пользователь клавишу. Чтение из потока — блокирующее. Как только мы дошли до std::cin >> c , лампочки перестают мигать, поток останавливается, а мы ждём, когда пользователь нажмёт клавишу.

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

Нам всего лишь хотелось нарисовать ёлочку в терминале, а тут внезапно вылезла многопоточность! Не очень приятно.

А вот и вторая неприятность:

std::ifstream ifs< filename.c_str() >; std::string tree; std::getline(ifs, tree, '\0');

Есть файл с ASCII-графикой, и мы его никак не меняем — из него нужны только байты. Но в С++ нельзя работать с файлом как с массивом байт: придётся открыть поток, считать информацию из потока в контейнер (string или vector) и только тогда работать с этим контейнером.

Почему всё так криво?

Казалось бы, программа простая, почему же возникают такие сложности?

Круг задач, которые можно решать с помощью C++, очень широк. И за то, чтобы эти задачи можно было решать, отвечают вот эти люди:

Знакомьтесь — комитет по стандартизации языка C++. Они эксперты в своих областях: одни в машинном обучении, другие в компиляторах, третьи великолепно знают алгоритмы. Но есть маленькая проблема: все члены комитета находятся в круге задач, отвечающих за высокую производительность.

Если обозначить экспертов зелёными точками, область задач с высокой производительностью — чёрным кругом, а область всех задач, которые можно решать на C++ — жёлтым, ситуация выглядит так:

Так сложилось исторически.

Например, есть стартап, где мало денег и где код пишут как придётся. Со временем стартап ширится, развивается и через 20 лет вырастает в огромный монолит и конгломерат. И тогда компания задумывается о том, что хорошо бы влиять на тот язык программирования, на котором у них всё написано. Поэтому нужен человек, который хорошо разбирается в языке и преследует интересы компании — его-то и отправляют в комитет по стандартизации C++.

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

Около двадцати лет назад в комитете было 20 человек, а теперь уже ближе к двумстам. И по-прежнему это люди, которые решают задачи, связанные с высокой производительностью.

А наша задача с ёлочкой помечена на картинке звёздочкой, и очень далека от целей комитета.

На примере этой программы мы видим, что несмотря на шероховатости, всё работает. А какие будут проблемы в приложении, требующем высокой производительности?

«Ява не тормозит»

В основе «Яндекс.Такси» лежит микросервисная архитектура. Если взять какой-то один сильно нагруженный сервис в отдельном дата-центре, то он будет обрабатывать где-то 20 000 сообщений в секунду. Что-то приходит в виде запроса, что-то он считывает, ищет на графе, обращается к базе и выдает ответ. Каждое такое событие нужно логировать, то есть записать информацию в файл. Один микросервис в отдельном дата-центре порождает 30 гигабайт логов в час, а в сумме все микросервисы генерируют в час больше терабайта логов и миллиарда событий.

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

Раньше для отправки таких сообщений мы использовали Logstash — популярное, бесплатное и открытое приложение, написанное на Java, которым пользуется огромное количество компаний.

Казалось бы, что может пойти не так?

Демон — это то, что принимает запрос, обрабатывает его, обращается к удалённому хранилищу, получает ответ, что-то считает на графе, производит криптографические операции, пишет лог и отдаёт ответ. А Java — это Logstash, который просто отправляет этот лог в удалённое хранилище. При этом потребляет Logstash в два раза больше оперативной памяти и в девять раз больше ресурсов процессора.

Что тут происходит под капотом? Logstash делает следующее:

  • Считывает данные с диска.
  • Разбивает запись на ключ—значение.
  • Применяет простые правила трансформации ключей и значений (например, меняет формат времени).
  • Формирует запись в конечном формате.
  • Отправляет запись в удалённое хранилище.

Вооружимся perf и натравим его на Java. Что мы видим? Logstash постоянно что-то сует в какие-то конкурентные ассоциативные контейнеры, в которых тратится огромное количество времени.

Для чего здесь вообще эти контейнеры? У нас ведь обычный пайплайн и простые правила по преобразованию логов, где по ключу и значению можно сразу понять, что там произошло и как это трансформировать. Нет никаких сложных правил, например, по разной обработке в зависимости от того, встретился ли до этого один ключ или другой.

Давайте сделаем свой Logstash без излишеств — шустрый, модный и современный. Для этого воспользуемся всеми доступными фишками C++17.

«Пилорама»

Представляю вашему вниманию «Пилораму». Почему такое название? Говоря по-английски, пилорама — это «factory in which logs are sawed». То есть что-то, что «пилит логи»! Вот и наша «Пилорама» пилит их и отправляет куда-то дальше.

Учтём ошибки Logstash и вместо трёх стадий с обменом контейнерами между ними (разделение на ключ—значение, применение правил, формирование записи) сделаем одну. Есть кусок данных, мы знаем, как преобразовать его в конечный формат, и никаких сложных промежуточных шагов не нужно.

Как мы это делаем? Данные приходят в виде std::string_view , то есть в виде указателя на кусок данных, над которыми нужно работать, и size_t — количества этих данных. Со string_view мы делаем следующее:

do < const std::string key = GetKey(); if (state_ == State::kIncompleteRecord) < // Stop parsing to let the producer write more data and finish the record. return 0; >const utils::string_view value = GetValue(); if (state_ == State::kIncompleteRecord) < return 0; >WriteWithFilters(writer, key, value); > while (state_ == State::kParsing);

Берём ключ, проверяем, нормальный ли он. Затем достаём значение и проверяем, пришло ли оно целиком. Записываем всё в выходной формат, применяя какие-то правила. Повторяем до тех пор, пока есть что разбирать.

Но для чего тут нужны две проверки?

«Пилорама» и основной демон работают в разных процессах. Основной демон может захотеть записать четыре с лишним килобайта логов, но успеет записать всего два, когда вклинится «Пилорама» и начнёт эти логи читать. В какой-то момент она заметит, что запись не готова целиком. Для таких случаев и нужны проверки.

С этой функцией конвертации получается более 360 тысяч записей в секунду, а это более 75 мегабайт данных в секунду. Итого больше 270 гигабайт логов в час. Таким образом можно держать 10 самых загруженных микросервисов на одном ядре «Пилорамы» или вообще все логи «Яндекс.Такси» на четырёх ядрах.

Честно говоря, когда я только написал этот конвертер и впервые запустил его, то был недоволен результатом: всего в 30 раз быстрее Java. Но потом я выспался и понял, что собирал всё в режиме отладки. Поэтому сейчас мы примерно в 100 раз быстрее Java, что более-менее приемлемо, но все равно недостаточно.

Почему так медленно? Вот вам немного нашей боли. В приложении есть конвертация данных и преобразование времён, а в C++17 для этого ничего нет. Поэтому приходится пользоваться сторонними библиотеками, например cctz. А эта «редиска» в цикле динамически аллоцирует память.

 // Formats a std::tm using strftime(3). void FormatTM(std::string* out, const std::string& fmt, const std::tm& tm) < for (int = 2; i !=32; i*=2) < size_t buf_size = fmt.size() * i; std::vectorbuf(buf_size); if (size_t len = strftime(&buf[0], buf_size, fmt.c_str(), &tm)) < out->append(&buf[0], len); return; > > >

Если это безобразие убрать и заменить на работу с датой/временем из C++20, то скорость конвертации увеличится практически в два раза.

С++ на практике

Теперь попробуем считать данные с диска. Стандартный подход в C++ — использовать стримы. Есть файл, с которым нужно что-то сделать. Мы открываем ifstream и даём ему команду читать из конкретного файла в определённый контейнер. Тот, в свою очередь, говорит операционной системе отдать какой-то файл. ОС не умеет работать напрямую с диском и записывать байты прямо с него в пользовательские буферы. Сначала ОС подтягивает большой кусок файла в оперативную память и уже оттуда копирует байты в нужный контейнер.

Такой способ работы с файлами — стандартный, но большинство современных ОС могут работать лучше. Операционная система сразу даст доступ к этому фрагменту памяти и работать с ним можно будет без всякого копирования.

Именно это делает boost::interprocess::mapped_region . Вы называете файл, с которым будете работать, оффсет и примерный требуемый размер. И boost::interprocess::mapped_region возвращает указатель на начало этих данных и размер, что отлично сочетается со string_view . Мы получаем указатель и размер, создаем от них string_view и отправляем на конвертацию. В итоге без всяких динамических аллокаций и промежуточных контейнеров получаем пайплайн, начиная с чтения файла до получения итогового формата. Осталось самое сложное — отправить данные в удалённое хранилище. Выглядит это так:

std::string result; do < AppendNewData(result); if (result.size() < treshold) < engine::SleepFor(1s); continue; >SendToRemote(result); result.clear(); > while (true);

Здесь есть какой-то result и функция, которая читает данные с диска, конвертирует и добавляет их в result. Затем мы проверяем, что данных для отправки достаточно, и если их недостаточно, то засыпаем на секунду, а если достаточно, то отправляем их в удаленное хранилище.

Вы можете спросить: «Sleep в коде? Это точно высокопроизводительный сервис?» Если есть 100 файлов, значит, нужно 100 потоков, и все эти потоки иногда спят. Изредка им нужно резко пробудиться, что-то сделать и снова уснуть. ОС будет переключаться между этими потоками и пытаться угадать, какие и когда нужно разбудить. Если файлов 10 тысяч, то и потоков будет 10 тысяч, и тогда все станет ещё хуже: оперативная память будет съедаться, а операционная система — зашиваться. Выглядит не очень производительно.

Но устроено здесь всё хитро и функции engine::SleepFor() и SendToRemote() на самом деле асинхронные методы из нашего асинхронного фреймворка userver. Перепишем их с использованием Coroutines TS, и тогда к методам добавится co_await . В результате получим co_await engine::SleepFor() и co_await SendToRemote(result) .

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

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

Аналогичным способом данные отправляются в удаленное хранилище, через асинхронный метод co_await SendToRemote() .

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

Чего не хватает в C++

Некоторые вещи, которые хотелось бы использовать для этой задачи, появятся в C++20. Например, появятся таймзоны, тогда можно будет выкинуть cctz. Появится в С++23 flat_map — это как std::map , но намного эффективнее для небольших наборов данных. Там, где всего 10-20 элементов и количество данных расти не будет, отлично впишется flat_map . А еще в С++20 появится замечательная вещь — тип char8_t .

Казалось бы, две одинаковые функции, но вся разница в том, что в одном случае у нас unsigned char* , а в другом char8_t* :

void do_something(unsigned char* data, int& result) < result += data[0] - u8’0’; result += data[1] - u8’0’; >void do_something(char8_t* data, int& result)

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

Функция, принимающая char8_t* на треть короче. Почему?

Когда вы работаете с unsigned char* , char* или byte* , компилятор думает, что char* может указывать на все, что угодно: на integer , string или пользовательскую структуру. И когда компилятор видит, что на вход передается char* и что-то извне посылки, то думает, что любая модификация этой переменной может поменять те данные, на которые указывает char* . Это работает и в обратную сторону. Поэтому компилятор генерирует такой код, который лишний раз выгружает это из регистра в оперативную память (ну, если быть полностью корректным, в кеш процессора) и из неё подтягивает в регистр. В результате мы имеем лишние инструкции. В char8_t* можно такое убрать.

Но и в C++20 будет не всё. В С++ по-прежнему не хватает memory map: он нужен и для простых приложений, вроде нашей «Ёлочки», и для высоконагруженных. Предложение по mmap есть в комитете по стандартизации, и оно может быть принято в C++23.

Не хватает стандартной библиотеки побольше. У нас удалённое хранилище, но оно не неоптимальное: написано на Java и работает с JSON. А в С++, к сожалению, из коробки нет возможности работать с JSON. Также хочется Protobuf.

Ну и не хватает правильной работы с вводом/выводом. Есть networking TS, где под капотом используется библиотека Asio, и возможно, её добавят в C++23. Есть Boost.Beast, который работает с HTTP асинхронно — им сложно пользоваться, но код в итоге получается красивым. И есть библиотека AFIO, которая позволяет асинхронно работать с файловой системой и может сказать ОС: «Когда в этой директории появится новый файлик, вызови эту функцию». А там просыпается какая-то корутина и начинает делать с файликом что-то полезное. К несчастью, AFIO — это прототип, работающий, но не быстро.

Что можете сделать лично вы?

Мы рассмотрели два приложения, в каждом из которых есть шероховатости. В случае второго приложения большинство этих недочетов исправят в C++20, но «осадочек остался». Ведь, наверное, с подобными проблемами встречаюсь в разработке не только я. И скорее всего, у разных людей «шероховатости» разные. Что в связи с этим делать?

В комитете по стандартизации хотелось бы видеть больше людей. Причём из разных областей, не обязательно из области высокой производительности. Нужны люди из стартапов, которые будут говорить, где им становится неудобно в начале работы над приложением. Нужны люди из академического мира. В комитете есть преподающие профессора, которые могут рассказать, где студенты стреляют себе в ноги. И с задних рядов слышен голос: «Да-да, я эксперт, и я тоже себе в этом месте ногу отстрелил».

Также требуются люди из тех областей, где C++ не популярен или вообще не используется. Хорошо, если в комитете будет сидеть матерый разработчик embedded-железок, слушать о том, как механизм исключений уместили в 200 байт, и говорить: «А у нас всего 128 байт (нет, не килобайт), думайте дальше».

Если вам что-то не нравится в C++ и вы хотите это улучшить или донести свою боль до комитета по стандартизации, начните с сайта stdcpp.ru. Там люди обмениваются мыслями, желаниями и проблемами, связанными с развитием языка C++. Идеи обсуждаются, обрабатываются и некоторые из них становятся официальными предложениями для международного комитета. При этом рук не хватает, поэтому особенно ценная помощь — когда человек готов не только генерировать идеи, но и браться за написание предложений к ним (хотя бы черновиков).

Драматичным шёпотом: и таким человеком может стать каждый из вас!

Если оптимизация программ на С++ для вас не пустой звук, обратите внимание на C++ Russia 2021, где будет много всего интересного. Конференция пройдёт с 15 по 18 ноября 2021, информация и билеты на сайте.

Языки C: все о типах данных

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

К наиболее популярным языкам программирования относят Си-семейство. Программер, который освоит его, может составлять приложения, игры и утилиты для самых разных нужд. Такие специалисты всегда в цене.

Для того, чтобы грамотно использовать свои знания на практике, требуется изучить особенности и нюансы выбранного языкового направления. А еще – его объекты и способы их применения. Немаловажными моментами являются переменные и типы данных. Об этих особенностях зайдет речь далее.

Что входит в Си-семейство

Перед использованием того или иного языка на практике требуется выяснить их особенности. В Си-семейство включают:

  1. C – первый язык «блока». Создан в 60-е годы 20-го века. Использовался для реализации в системе Unix. Отлично адаптирован под другие «операционки».
  2. C++ — современный и более совершенный язык программирования. Сохранил синтаксис «прародителя». Поддерживает ООП и шаблоны. Используется повсеместно современными разработчиками.
  3. Objective C – возник в 80-х годах, совместим с «Си». Имеет уникальный синтаксис. Применяется при разработке софта для продукции Apple.
  4. C# (Си Шарп) – современный и удобный вариант. Похож на Java. Относится к объектно-ориентированному типу. Имеет нетипичные конструкции и оригинальным синтаксисом. Применяется при машинном обучении, а также веб-разработке.

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

Понятие переменной в Си

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

Проявляются соответствующие свойства в следующих моментах:

  • объект объявляется в коде перед использованием;
  • перед тем, как «обозначить» рассматриваемый элемент, требуется определить тип данных;
  • типы информации остаются неизменными в процессе выполнения кода;
  • переменная будет бронировать ячейку памяти, соответствующую указанному типу сведений (расширение/увеличение оной невозможно).

Яркий пример переменной – то или иное число. Оно может быть как целым, так и дробным. Без подобных материалов невозможно описать ни одно приложение. Поэтому к типам информации необходимо относиться внимательно. Неправильное «бронирование памяти» под int main и не только приводит к замедлению работы софта. Иногда – к критическим ошибкам.

Тип информации – определение и особенности

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

Это – способ хранения информации (чисел и не только), которые требуются для выполнения созданного машинного кода. Используется для объявления переменных. Но делается это перед непосредственным применением обозначенного элемента в приложении.

Важно: размер переменных, массивов и констант определяются их типами и никак иначе.

Разновидности

Условно в Си-семействе выделяют следующие «разновидности» информации:

  • базовые;
  • перечисление;
  • производные;
  • void.

Первая категория задействована в каждом программном коде. Именно им стоит уделить пристальное внимание, особенно новичкам.

Базовые типы

Традиционно знакомство с рассматриваемой темой и числами начинается с «базы». Каждый вид переменной в том или ином случае обладает собственными нюансами, свойствами, а также областями применения. Полученные знания помогут составлять элементарные приложения.

Char

Char – выделяет для хранения информации 1 байт. Этого хватает для того, чтобы разместить число в диапазоне от -128 до 127 или от 0 и до 255. В основном задействуется в целях создания и удержания ASCII-последовательностей. Иногда интерпретируется в качестве числа.

Стоит запомнить следующие данные о нем:

  • хранит всего один символ;
  • определяется ключевым словом «char» (без кавычек);
  • при задействовании строк можно хранить в предложенном элементе больше одного «числа».

Но большей популярностью пользуется иной вариант объявления переменных. Он знаком даже новичкам.

Integer

Целочисленные типы – основа каждого приложения. Int C – способ объявления переменной, рассчитанной для выделения объема памяти в размере 4 байта. Относится к целому семейству типов «чисел».

Иногда int позволяет выделять 8 байт, а когда-то – всего 2 байта. Позволяет включать в себя число 2 147 483 647.

Запомнить необходимо следующие особенности:

  • тип int объявляется при помощи ключевого слова «int»;
  • отвечает за хранение целых чисел;
  • размер выделяемой памяти напрямую зависит от разрядности используемой машины;
  • при 16 bit выделяется 2 байта, при 32 – 4, при 64 – 8;
  • 2 bytes позволяет указывать числа от -32 768 до 32 767;
  • 4 bytes – от -2 147 483 648 до 2 147 483 647;
  • возможно выделение дополнительной памяти при помощи long int и long long int.

Также существует тип short int, занимающий всего 1 байт. Числа с десятичной частью здесь не размещаются ни при каких обстоятельствах. При попытках воплотить задумку в жизнь десятичная часть «обрезается».

Float

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

Float предусматривает хранение числа с 6-ю элементами после запятой. Пример – 15.457236. Предельные значения здесь колеблются от 1,2E-38 до 3,4E+38.

Со знаками и без

Когда речь заходит о числах, стоит помнить, что они могут быть двух типов. А именно – unsigned long и signed. Во втором случае целесообразно говорить о том, что подобные переменные хранят числа со знаками, в первом – без них.

Это – отличный способ указать диапазон значений. Если не должны рассматриваться отрицательные числа, программисту необходимо использовать type unsigned.

Важно: если пользователь не указал types data, по умолчанию они будут относиться к типу signed.

Double

Тип Double относится к основным «видам переменных» в Си-семействе. Предусматривает хранение чисел с плавающей точкой (дробной частью). Содержит дробные и целые значения. Может включать в себя до 15 символов. Пример – 372 036 854,55.

  • похож на float;
  • после запятой хранит до 10 символов;
  • предельные значения колеблются от 1E-37 до 1E+37.

Программисты относят тип переменных double к тем, что используются «по умолчанию».

SizeOf()

Переменные, которые представлены в программе, могут требовать выделения дополнительной памяти. Эта операция осуществляется при помощи функции sizeOf(). Помогает справляться с поставленной задачей на конкретной машине.

Приведение наглядного примера – лучший способ осознания того, о чем идет речь. Вот фрагмент кода, в котором представлены разные переменные и выделение дополнительной памяти:

В итоге на экране появится надпись типа:

Языки C: все о типах данных

Можно заметить, что здесь выделяется память для всех типов: double, int и так далее. Подобное использование функции на практике применяется редко.

Целочисленные типы – характеристики

Приведение примеров кодификаций может быть бесконечным, так как в программировании нет пределу совершенству. Но каждый пользователь должен понимать, что целочисленные типы (числа) имеют собственные свойства.

Так, long имеет диапазон от -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807, а s byte – от -128 до 127, byte – 0-255. Таблица ниже поможет разобраться в свойствах на примере C#.

Языки C: все о типах данных

Последние 2 элемента – это числа собственного размера.

Перечисляемые

Enum – это перечисляемый тип данных. Ему в программировании уделяется меньше внимания, чем «базе». Предусматривает следующие особенности:

  • содержит целочисленные именованные константы, представляемые списком;
  • первая const по умолчанию получает значение 0;
  • увеличение происходит на 1 «число».

Задается по принципу: enum тег переменные.

Тип Void

Следующий вариант – это void. Относится к «пустой информации». Это значит, что объявленная переменная не имеет никакого значения. Активно применяется в функциях, а также структурах. Не выделяет большого количества byte под свои нужды.

Задействуется в основном при:

  • указании на то, что функция не возвращает значение;
  • у функции нет никаких параметров;
  • указании на любой другой тип данных.

Активно встречается в Си-семействе.

Работа с переменными и их разновидностями – основа, с которой должен уметь справляться каждый программист. Освоив типы данных вроде double и float, можно составлять приложения, которые будут использовать в процессе не только целые числа. Это удобно, особенно когда речь заходит о точных математических расчетах.

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

Языки C: все о типах данных Языки C: все о типах данных

Также вам может быть интересен курс «Программист C» в Otus.

Почему Си все еще популярен?

Несмотря на свой возраст, язык программирования Си до сих пор является одним из наиболее популярных языков. А возраст у него действительно почтенный. В этой статье мы поговорим, почему Си до сих пор на плаву, в чем секрет его популярности и по какой причине его продолжают изучать на уроках и курсах программирования. Также вы узнаете, что до сих пор пишут на C.

Немного истории

Сегодня многие считают язык Си устаревшим. В каком-то смысле это так, ведь он появился в далеком 1972 году. Разрабатывался он с учетом того времени, то есть в соответствии с характеристиками компьютеров, которые существовали полвека назад. А какими были эти компьютеры? Если сказать упрощенно, то по своему функционалу они напоминали современный калькулятор.

Несмотря на все вышесказанное, спустя полвека Си совсем не умер, о нем не забыли. Секрет долголетия прост — язык постоянно развивается и поддерживается, несмотря на все «но». А еще он обеспечивает быстрое выполнение и хороший отклик, то есть быстродействие и производительность.

Многие компании успешно применяют Си десятки лет, ведь он до сих пор часто работает быстрее, чем конкуренты. Почему он так быстр? Потому что выполняется, по сути, на уровне процессора.

Достаточно посмотреть на рейтинг хотя бы TIOBE, чтобы увидеть, что C уже много лет в топе и уходить оттуда не собирается. Мало того, он нередко занимает первые места в этом рейтинге, вот, к примеру, результат за май 2021 года.

Почему Си все еще популярен?

Интересный факт: на основе C создано много других языков программирования, речь идет о так называемых «Си-подобных» языках — он построены на базе C.

  • C++,
  • C#,
  • Java,
  • Objective C,
  • Perl,
  • Scala и прочие.

Не менее интересно и следующее наблюдение: именно для Си выпущено наибольшее количество различных самоучителей и учебников в стиле «для чайников», «основы», «с нуля» и так далее. Но, учитывая возраст C, этот факт как раз таки удивления не вызывает.

Каковы еще причины популярности?

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

на основе C создано множество проектов, а эти проекты требуют постоянной поддержки, следовательно, без подготовленных кадров не обойтись;

— так как Си — это родитель многих других programming languages, то нередко советуют начать изучение программирования именно с него. И дело тут совершенно не в ностальгии — его изучение даст вам базовое понимание фундаментальных принципов работы компьютера/программ/языков программирования. То есть такие уроки не будут бесполезными. Вдобавок к этому, никто же не помешает потом перейти на другой язык: С++, C#, Java, Python и т. п. А с наработанной базой последующее изучение будет явно проще.

Программируем на Си — что на нем пишут?

Язык C до сих пор востребован на практике, вот, что можно на нем писать:

операционные системы. Когда-то давно Unix была написана на ассемблере. Потом появился Си, и Unix переписали. В 1985 году C пригодился при написании Windows. Сегодня компьютеры Apple работают с помощью ОС macOS, ядро которой создано тоже с помощью C. Девять из десяти наиболее мощных суперкомпьютеров — это тоже герой сегодняшнего разговора. Еще добавим ядра для iOS , Android и Windows Phone. Результат очевиден: Си нередко находится в фундаменте работы популярного ПО из разных сфер: от мобильных устройств до суперкомпьютеров;

Open Source-программы. Проекты, имеющие открытый исходный код, тоже нередко создаются на C. По статистике таковых чуть менее половины:

Почему Си все еще популярен?

драйверы устройств. Они необходимы для подключения к вашему компьютеру различных устройств: клавиатуры, мыши, принтера, сканера и пр. Именно драйвер взаимодействует с операционной системой. Разве это не означает, что его лучше создавать на C?

языки программирования. Мы уже говорили о заслугах Си в этой области. Однако до сих пор при создании нового языка нередко применяют именно универсальный C;

базы данных. О, да… Самые популярные БД, такие как Oracle Database, MS SQL Server MySQL, SQLite и PostgreSQL, написаны на C. Почему? Потому что базы должны обладать максимальной надежностью и производительностью. Просто спросите любого C-программиста, почему для баз данных лучше выбирать этот язык. Вам ответят что-то в стиле:

Почему Си все еще популярен?

графические библиотеки. Опять же, работая с графикой, мы снова ожидаем максимального быстродействия, которое может обеспечить C. В крайнем случае, если речь идет о наиболее ответственных местах, некоторые участки кода частично пишутся на языке ассемблера. Примеры библиотек: Cairo, OpenGL, SDL;

встроенные устройства. Торговые автоматы, кассовые аппараты, парковочные роботы, программно-техническая начинка вашего автомобиля — все это Си — как без него?

космические и авиационные системы. Раз опять нужна максимальная надежность, то по традиции вопрос выбора становится риторическим.

Вот и все, надеемся, материал был вам полезным.

Зачем писать на C++ в 2022 году?

C++ — это язык программирования, основы которого были заложены более 40 лет назад, но который по-прежнему повсеместен. В этой статье мы с вами разберемся, где и почему он используется, и порассуждаем, есть ли у него будущее.

Где сегодня используется C++?

С++ повсюду. Код, написанный на C++, можно найти в вашем телефоне, в вашей стиральной машине, в вашем автомобиле, в самолетах, в банках и вообще везде, где только можно представить.

Но давайте будем более конкретными. Многие приложения для работы с изображениями, такие как Adobe Photoshop или Illustrator, написаны на C++. 3D-игры также часто пишут на C++. Программное обеспечение для 3D-анимации, моделирования и рендеринга также в основном написано на C++. Манипуляции с изображениями — довольно сложная и ресурсоемкая область, требующая скорости и близости к аппаратной части C++.

Но работа с изображениями — далеко не единственная область, в которой доминирует C++. С большой долей вероятности браузер, который вы используете для чтения этой статьи, также был написан на C++, как, например, Chrome и Firefox.

Если мы спустимся еще ниже и посмотрим на компиляторы и операционные системы, то многие из них написаны на C++. Если нет, то, скорее всего, это C.

Но это все пока только примеры в пределах десктопного мира.

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

В мире встраиваемых систем, где и память, и ЦП более ограничены, чем у десктопных компьютеров, C++ процветает. Независимо от того, смотрите ли вы на свои умные часы, телефон, включаете ли вы стиральную машину, садитесь ли в автомобиль и включаете зажигание, вам следовало бы испытывать немного благодарности в отношении неизвестного C++ разработчика, который сумел не испортить все из-за ошибки сегментации сразу после запуска.

Почему С++ используется до сих пор?

Итак, мы увидели, что C++ по-прежнему используется почти везде. Но почему? Можно встретить очень много скептиков, которые считают, что это чистой воды легаси и его следует удалять из кодовой базы большинства современных компаний.

Так все-таки это легаси?

Некоторые люди утверждают, что C++ все еще используется только потому, что это технология, унаследованная от старых приложений. Под “старым” я часто подразумеваю программное обеспечение десятилетней давности.

Это правда только отчасти.

Давайте порассуждаем о Cobol-ковбоях. Мало кто хорошо владеет Cobol, поэтому, если на него есть спрос, то они могут заработать кучу денег.

Cobol по-прежнему широко используется в финансовой индустрии. Эти системы были написаны много десятилетий назад и до сих пор работают довольно хорошо. Может быть, они не соответствуют всем современным требованиям, но они устойчивы, надежны и настолько сложны, что никто не осмеливается их переписывать.

C++ не так уж плох, он не так стар, как Cobol, и все больше людей изучают его и знают, более или менее, как его использовать.

Но иногда он используется только потому, что компания уже очень много в него инвестировала. У нее вокруг C++ уже развились целые экосистемы. Мигрировать их было бы слишком дорого. Даже руководители, которые по каким-либо причинам не в восторге от C++, сочтут такую ​​миграцию экономически бессмысленной.

Но является ли C++ таким уж прям легаси?

C++ эволюционирует

Вовсе нет! C++ развивается совершенно предсказуемым образом. Как я подробно объяснял в одной из своих предыдущих статей, с 2011 года C++ следует модели подобной отправлению поездов. Каждые три года выпускается новая версия с новыми языковыми фичами и библиотеками, а также с исправлениями ошибок и доработками более ранних фич.

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

В то же время одной из суперсил C++ является обратная совместимость. Код, скомпилированный вчера, скорее всего, скомпилируется и завтра. Даже больше — код, который можно было скомпилировать в 1985 году, скорее всего, можно будет скомпилировать и в 2025 году.

Эволюция C++ была направлена ​​на то, чтобы минимизировать головную боль разработчиков и сделать написание более безопасного кода проще.

Одной из важнейших особенностей C++ является предсказуемое управление памятью. Тут нет сборки мусора, которая в конечном итоге происходит (или нет). Когда и как память будет освобождена и возвращена операционной системе — абсолютно детерминировано. Хотя все всегда было абсолютно детерминировано, было также довольно легко выстрелить себе в ногу и испортить все, не высвобождая память или наоборот пытаясь высвободить ее дважды или даже больше раз.

Современный C++ предоставляет интеллектуальные указатели, которые сделали динамическое управление памятью менее подверженным ошибкам за счет добавления указателей, которые могут “убирать за собой”.

Еще одним поводом для головной боли у многих разработчиков выступают шаблоны. SFINAE, невероятно длинные и трудные для чтения сообщения об ошибках, перестают быть такой большой проблемой с введением концептов в C++20, которые помогают нам ограничивать типы, принимаемые шаблонами, и предоставлять релевантные и относительно легко читаемые сообщения об ошибках, если что-то все-таки идет не так.

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

Экономическое преимущество

C++ близок к аппаратному обеспечению, может легко манипулировать ресурсами, поддерживает процедурное программирование для функций, интенсивно использующих ЦП, и является очень быстрым. Он также отлично справляется со сложностями 3D-игр и позволяет создавать многослойные сетевые конфигурации. Все эти преимущества делают его главным выбором для разработки игровых систем, а также инструментария для разработки игр.

Если вы используете так называемый “современный” язык, такой как Python или Javascript, зачастую вам придется прибегать к написанию некоторых важных функций или библиотек на C или C++, просто чтобы сделать их скорость приемлемой.

Существует очень мало языков, которые могут конкурировать с C++ по скорости, и один из них это — C.

Но скорость — это еще не все.

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

Вас можно понять.

Как мы уже говорили ранее, C++ становится все проще в разработке. Конечно, легкость написания современного C++ не идет ни в какое сравнение с Python, но все не так однозначно.

Некоторые современные языки ориентированы на простоту написания кода, другие — на большие функциональные возможности.

Когда вы выбираете автомобиль, вы думаете не только о комфорте или скорости, хотя они могут быть очень важны. Скорее всего вам также придется учитывать расход топлива. Делаем ли мы то же самое, когда разрабатываем приложения? Думаем ли мы о том, сколько энергии они будут потреблять? В этом смысле трио C/C++/Rust работает намного лучше, чем все остальные языки. По сути, они находятся совершенно на другом уровне.

Приведенные выше цифры впечатляют.

Теперь давайте посмотрим на слайд, который был представлен на CPPP Дэмиеном Булом (Damien Buhl).

Используя C++, мы можем значительно сократить выбросы CO2. Удивительно, не правда ли?

Получается, что по большей части, даже если ваши требования к производительности не являются определяющим фактором, энергопотребление и защита окружающей среды все-таки подталкивают вас к использованию C++.

Каковы его недостатки?

Если C++ так развивается и становится проще в работе, и, кроме того, если он даже позитивно сказывается на счетах за электроэнергию и, следовательно, на нашей родной планете, то в чем проблема? Почему многие люди так не хотят с ним связываться?

Давайте обсудим пару моментов.

Плохая реклама

Нужно признать, что C++ имеет плохую репутацию.

Если вы читали Coders At Work, то могли отметить, что многие писали о том, что C и C++ слишком сложны в использовании, и вообще существует всего несколько причин чтобы их использовать в принципе. С C очень легко выстрелить себе в ногу, с C++ это немного сложнее, но когда это происходит, вы можете отстрелить себе ногу целиком.

Не очень обнадеживающие заявления, правда?

Эти комментарии определенно были небезосновательны, но их актуальность постепенно угасает.

Язык развивается, но старые книги и интервью никуда не денутся. Очень сложно изменить общественное мнение, особенно среди тех, кто больше не пишет код. Как, например, большинство нынешних руководителей.

По мере того, как язык развивается, его становится все труднее изучать

Как я уже несколько раз говорил ранее, C++ развивается. Он получает все больше и больше фич, и на нем становится все проще писать выразительный код.

То, что раньше было стандартным циклом, сегодня можно записать таким вот функциональным образом:

const std::vector numbers = ; // вместо auto count = 0; for (const auto& n : numbers) < if ( n % 2 == 0) < ++count; >> // теперь мы можем написать auto isEven = [](auto number) < return number % 2 == 0; >; auto count = std::ranges::count_if(numbers, isEven);

Хотя это все очень модно и прекрасно, это также означает, что те, кто хочет писать лучший код на C++, должны больше учиться. Многие считают, что самая большая суперсила C++ заключается в том, что он почти полностью обратно совместим. Такая важная фича, что Мэтт Годболт (Matt Godbolt) посвятил ей почти весь свой главный доклад на CPPP 2021!

Также правда, что некоторые старые лучшие практики со временем стали антипаттернами. Но они по-прежнему компилируются, их синтаксис по-прежнему валиден, и обычно это базовый синтаксис, поэтому мы изучаем их. Возможно, вам больше не нужно заниматься арифметикой указателей, по крайней мере, не в такой степени, как раньше, но вам все равно нужно в этом разбираться. То же самое касается ручного управления памятью, C-массивов и так далее.

Я лично думаю, что такие темы больше не должны преподаваться настолько глубоко, но, как я могу наблюдать, в большинстве университетов преподают устаревший C++, и людям приходится заново изучать современный C++, когда они начинают работать в реальных компаниях. Если конечно компания использует более современную версию.

Интеллектуальная небрежность

Как поделился со мной в Твиттере Марек Краевски (Marek Krajewski), некоторые люди просто не стали бы использовать C++ из-за интеллектуальной инертности. Да, его сложнее изучать, чем Python или Javascript. Да, вы можете создавать отличные вещи с более простыми в освоении альтернативами. И на самом деле вам не всегда нужны возможности C++. Это все правда.

Вы должны использовать самый подходящий инструмент для конкретной работы.

Проблема в том, что многим просто лень изучать эти инструменты или даже признать, что иногда это правильные инструменты. Это происходит часто из-за фанатизма, из-за ограниченности и, в основном, из-за интеллектуальной небрежности.

Наша работа — показать и объяснить, когда C++ (или Rust. ) — излишен, а когда — правильное решение. Что еще более важно, мы должны показать, что это уже не тот язык, которым он когда-то был.

Экосистема

В своем докладе на C++ MythBusters Виктор Чиура (Victor Ciura) развеял миф о том, что у C++ не все так гладко с вспомогательными инструментами. Они есть, и их в нашем распоряжении достаточно много. Но Виктор считает, что у нас никогда не будет “стандартизированных” инструментов, нам всегда нужно искать подходящий инструмент, разбираться, как он работает, и только потом использовать его.

Хоть я и разделяю его точку зрения, мы должны признать, что в других языках есть более простые решения простых проблем. Если вы работаете с Python, вы точно знаете, как и откуда вы должны получать свои пакеты. Похожая ситуация и с Java, не говоря уже о Javascript. Эти языки не стандартизированы, но в них есть стандартные способы простой доставки и использования библиотек, совместного использования и создания кода, которые не требует много времени и сил на то, чтобы разобраться с ними.

C++ этим похвастаться не может.

Написание мейкфайлов — не самая простая задача. Многие де-факто принимают CMake за стандарт для написания скриптов сборки, но это явно не так. Многим он не нравится из-за его синтаксиса, и существует множество других способов создания скриптов сборки. У многих компаний даже есть собственные системы, в том числе у Amadeus.

А как насчет управления пакетами?

C++ в этом плане еще есть куда расти.

Так что на счет будущего C++?

Я опросил некоторых выдающихся представителей сообщества C++, и вот что они сказали:

C++ сегодня как никогда верен своей первоначальной миссии по предоставлению абстракций с нулевой стоимостью над низкоуровневым системным кодом, где это возможно, и недорогих абстракций, за которые вы платите только тогда, когда используете, когда первое невозможно. И мы получаем это вместе с совместимостью с C и более ранними версиями C++, несмотря на то, что язык постоянно развивается и внедряет современные языковые фичи. — Фил Нэш (Phil Nash), автор Catch2, главный организатор C++ On Sea

C++ — это и наше наследие, и наше будущее. Несмотря на все его недостатки и исторические проблемы, он имеет множество современных фич, многие из которых специально разработаны для смягчения/замены старых идиом/конструкций. В настоящее время C++ программисты могут легко писать программы, полностью избегая таких опасных старых вещей. [. ] STL C++ значительно выросла благодаря стандартам ISO 11,14,17,20, а C++23 принесет еще ряд очень ценных дополнений. От новых алгоритмов и диапазонов (ranges) до различных утилит и вспомогательных библиотек для IO, сетей, корутин, параллелизма, гетерогенного параллелизма и многого другого. Да, есть более специализированные вещи, которые могут понадобиться программисту, но здесь приходит на помощь экосистема C++, которая заполняет пробелы множеством стабильных библиотек коммерческого качества практически под каждую потребность. Каждая ключевая часть программного обеспечения, которое мы используем сегодня, содержит в себе C++: может быть, это целиком C++, может быть, там там только некоторые важные компоненты написаны на C++, может быть, его библиотека изначально скомпилирована на C++, может быть, его компилятор/среда выполнения написаны на C++, .

C++ по-прежнему остается королем языков программирования. Да здравствует король! — Виктор Сиура, старший инженер-программист команды Visual C++ в Microsoft

C++, широко используется в разработке игр, в частности, игр для консолей и ПК. Он обеспечивает прямой доступ к аппаратному обеспечению через абстракции с нулевой стоимостью. Мощь и гибкость, которые он предоставляет, делают его трудным для изучения, потому что в вашем распоряжении оказывается огромное количество возможных решений широчайшего спектра задач. Глядя на международный стандарт с обязательством обратной совместимости, вы уверены, что не будет ситуации Python2/Python3. Будущее выглядит радужным, параллелизм и работа в сети выглядят очень многообещающими в C++26, не говоря уже о множестве фич, предназначенных для рационализации и упрощения языка. — Джей. Гай Дэвидсон (J. Guy Davidson), руководитель инженерной практики Creative Assembly, соавтор книги Beautiful C++, член с правом голоса комитета ISO C++

Заключение

C++ мог бы считаться устаревшим в глазах тех, кто был знаком только со старыми шаблонами, со старыми стандартами, но язык постоянно развивается. С 2011 года, начиная с C+11, каждые 3 года мы получаем новую версию с исправлениями ошибок и новыми фичами. Экосистема растет, хотя она далеко не так проста, как у некоторых других новых языков, где, например, управление пакетами везде выполняется очень похожим образом.

Тем не менее, язык и экосистема растут, сообщество очень большое, а C++ неизбежно повсеместен. Так или иначе, его хотя бы частично можно найти почти в каждом написанном на сегодня программном обеспечении. Я не говорю, что C++ — это молоток, который должен превратить все вокруг вас в гвозди, но его все же стоит изучить и освоить. Даже в 2022 году и далее.

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

  • c++
  • программирование на с++
  • генерация фракталов

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *