Что делает код c
Перейти к содержимому

Что делает код c

  • автор:

Объясните что делает этот код

Этот код объявляет пользовательский тип данных T , в котором содержатся указатели на ещё 2 пользовательских типа данных _T_ и _T .
Задание странное, с точки зрения использования имен вложенных структур — как будто специально старались запутать. Но на самом деле ничего страшного нет. Его можно только для наглядности переписать так:

struct _T_ < int* a; >; struct _T < int a; int b; double c; >; struct T < _T_ *p1; float* a; int* b; _T *p2; int c; >; 

Тогда задание значений будет так:

int main() < T value; > 
int main()

Тут по-умолчанию указателям присваиваются nullptr . Если нужны конкретные значения, то нужно сначала создать конкретные объекты, а потом присвоить указателям их адреса:

int main()

Либо через динамическое выделение памяти

int main()

Главное не забудьте всё, что выделяли через new удалить через delete .

Кому и для чего нужен C++

Кому и для чего нужен C++

C++ — как конструктор Lego: вы можете собрать свой замок мечты, а можете кричать от боли, наступая на забытые на полу детали. На нем пишут игры и обучают нейросети, благодаря ему работает поиск Google и роботы торговых бирж. Вместе с Никитой Лестенко, ментором курса «Программирование на C++», рассказываем, где применяют язык и зачем его учить.

Что такое язык программирования C++

Пример кода на языке C++

C++ — это язык программирования, который был разработан в 80-х годах прошлого века как расширение языка C. Этот язык отличается от Си тем, что имеет больший набор возможностей, включая объектно-ориентированное программирование и шаблоны. Когда нужно принимать важные решения, связанные с будущим, профессией и самоопределением, бывает страшно. Но важный шаг к избавлению от страха — озвучить его. Сделайте это, ответив на пять коротких вопросов. Ссылка в конце статьи.

Для чего нужен C++

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

Что можно написать на C++

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

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

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

Что пишут на C++

Многие известные приложения были написаны на C++, включая операционные системы Windows и OS X, многие игры, такие как World of Warcraft и Counter-Strike. На C++ можно написать практически любое программное обеспечение, от простых консольных утилит до сложных приложений. Благодаря C++ работают Unreal Engine 4, Microsoft Office и Adobe Photoshop.

Кратчайшая история создания C++

Первые версии языка C++ (си-плюс-плюс, еще его называют «си-пи-пи», С plus plus, «плюсы») появились в начале 1980-х годов. Их создатель — датский программист из компании Bell Laboratories Бьерн Страуструп. Он моделировал распределения вызовов по АТС (автоматическим телефонным станциям). Тогда у Страуструпа было два типа языков: низкоуровневые и языки на основе Фортрана или Алгола, которые были очень медленными.

«IT-специалист с нуля» наш лучший курс для старта в IT
IT-специалист с нуля

Наш лучший курс для старта в IT. За 2 месяца вы пробуете себя в девяти разных профессиях: мобильной и веб-разработке, тестировании, аналитике и даже Data Science — выберите подходящую и сразу освойте ее.

картинка - 2023-03-14T191232.869

Профессия / 8 месяцев
IT-специалист с нуля
Попробуйте 9 профессий за 2 месяца и выберите подходящую вам
4 490 ₽/мес 7 483 ₽/мес

vsrat_7 1 (2)

С++: наглядно что такое классы

Низкоуровневые языки дают полный контроль над ресурсами компьютера: процессором, памятью, периферийными устройствами. Но в таких языках мало готовых инструментов, а значит, любые сложные концепции и функции придется кодить самостоятельно. В языках высокого уровня уже есть все необходимые абстракции. Страуструп объединил возможности объектно-ориентированного C и языка Simula, основанного на Алголе. Главное привнесенное в C новшество — классы, основной элемент в объектно-ориентированном программировании, содержащий в себе данные и код, который ими управляет. Так появился «‎C с классами»‎ или «‎новый C»‎, который в 1983 году получил свое окончательное название — C плюс-плюс. «++» — это оператор, который означает «добавить единицу». То есть к возможностям языка C добавили еще одну.

А мне нужен C++?

Да, если вы хотите разрабатывать сложные продукты и сервисы. Опытные C++-программисты — это разработчики ИИ, беспилотных автомобилей, нейронных сетей, банковских, поисковых и ГИС-систем, операционных систем, микроконтроллеров, браузеров, серверов и видеоигр.

Основные области использования C++
  • разработка игр и игровых движков (Unreal Engine, Unity);
  • GPU-вычисления (cryptocurrency, deep learning);
  • разработка высоконагруженных и высокопроизводительных приложений.

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

Курс для новичков «IT-специалист
с нуля» – разберемся, какая профессия вам подходит, и поможем вам ее освоить

Производительность — важная характеристика любой компьютерной игры. Counter-Strike, StarCraft: Brood War, Diablo I, World of Warcraft — все они появились давно и были написаны на C++, как и операционные системы консолей Xbox и PlayStation, ядра популярных игровых движков Unreal Engine или Unity, на базе которых сделано огромное количество 3D-игр, симуляторов, шутеров и стратегий.

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

Благодаря распараллеливанию (одновременному выполнению большого количества несложных математических операций) язык подходит для биткоинов и нейросетей. Первые можно майнить на C++, вторые — обучать: большинство библиотек для глубокого обучения (TensorFlow, Caffe, Shogun, Pytorch C++ API и т.д.) используют C++-код CUDA и OpenCL.

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

С помощью C++ Mozilla Corporation разработала opensource-браузер Firefox, была создана Amadeus — система электронного бронирования авиабилетов, гостиниц, круизных туров и проката автомобилей (через нее проходит 5000 транзакций в секунду, более миллиарда транзакций ежедневно), благодаря C++ работают Яндекс.Поиск и поисковый движок Google.

C++ нужен не только программистам, но и:

  • техническим художникам: связующему звену между обычными художниками и 3D-моделистами на игровом проекте и разработчиками игрового движка (у которых очень популярен C++) для решения проблем необходимо разбираться в языке и понимать, как все запрограммировано;
  • математикам: такие типовые задачи вычислительной математики, как решение систем алгебраических уравнений, дифференцирование и интегрирование функций, оптимизация, интерполяция, экстраполяция и аппроксимация, решаются при помощи реализаций численных методов на C++;
  • физикам и химикам: на C++ моделируются среды и физические процессы;
  • музыкантам: да, даже им! Это могут быть исследования акустических эффектов, попытки формализации творческого процесса или даже создание музыки: на «‎алгорейвах»‎ люди зажигают под треки, генерируемые алгоритмами, которые вживую на сцене пишет диджей-программист, — вот это настоящий киберпанк.

А еще C++ — это круто, потому что с его помощью:

  • пишут критическое ПО для истребителей F-16 и F-35;
  • разрабатывают пакеты программ и библиотек ROOT для обработки экспериментальных данных физики высоких энергий;
  • по поверхности Марса самостоятельно ездят роверы NASA и летает телескоп Хаббл;
  • делает практически все Microsoft (Windows OS, пакет Office, Internet Explorer, Visual Studio);
  • творят искусство (генеративное) художники: пишут алгоритмы для генерации предметов живописи и скульптур.

C++: плюсы и минусы

Плюсы

  • Высокая производительность, потому что он не накладывает никакой избыточной нагрузки на программу, не использующую какие-либо возможности.
  • Поддержка множества стилей программирования (процедурное программирование, абстракцию данных, объектно-ориентированное программирование и обобщенное программирование). Поэтому разработчик может сам выбрать, в каком стиле ему писать программу.
  • Большое сообщество.

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

Никита Лестенко
ментор курса «Программирование на C++»

Минус

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

C++ сложно выучить?

В отличие от Python, учить C++ cложно, но реально. C++ — это достаточно серьезный язык, к нему требуется осознанный подход. Чтобы начать изучать язык, желательно иметь общие представления о том, что такое программирование на C, как устроено «железо» и как работает операционная система.

Программируя на C++, нужно держать в голове сразу много нюансов: отслеживать типы данных, контролировать выделение и освобождение памяти. Сейчас появляется все больше средств, которые перекладывают эти обязанности на сам язык. Уже на этапе написания кода современные IDE (среды разработки) помогают следить за соблюдением стандартов, отлавливают большое количество ошибок, не всегда очевидных человеку без глубокого знания языка, автоматизируют процесс написания большого объема кода с помощью типовых конструкций.

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

Никита Лестенко
ментор курса «Программирование на C++»

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

Полезные ссылки

Книги:

  • Герберт Шилдт «C++: полное руководство»
  • Бьерн Страуструп «Язык программирования C++»
  • Бьерн Страуструп «Программирование. Принципы и практика использования C++»

Другое:

  • Бесплатный курс на Coursera
  • YouTube-канал о C++ и С#
  • Справочная информация о языке
  • Русскоязычное сообщество разработчиков

Бонус: C++ как оружие

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

Сравнение языка C++ с другими в виде шутки

Основные отличия между C++ и C#

Особенности C++ C#
Тип языка Процедурный и объектно-ориентированный. Строго объектно-ориентированный.
Тип памяти Управление памятью вручную (неявное и явное). Управление памятью автоматическое (сборка мусора).
Платформозависимость Более низкая платформозависимость, можно скомпилировать код для различных архитектур. Работает в среде .NET Framework или .NET Core, что обеспечивает высокую переносимость между платформами.
Синтаксис Синтаксис C++ более гибкий и позволяет более «оптимизированный» код. C# предлагает более простой и читаемый синтаксис.
Библиотеки Меньше стандартных библиотек, но множество сторонних библиотек и фреймворков. Большая стандартная библиотека классов .NET, включая поддержку работы с различными облачными сервисами и протоколами.
Безопасность Требуется больше внимания к безопасности при работе с памятью и указателями. C# обеспечивает более высокий уровень безопасности за счет управления памятью сборщиком мусора.
Производительность Исполнение кода на C++ может быть быстрее из-за отсутствия накладных расходов сборки мусора и других оптимизаций. C# может быть менее производительным из-за сборки мусора и других абстракций .NET Framework. Однако в большинстве случаев производительность обеспечивает достаточный уровень для множества приложений.
Платформенные возможности Более низкоуровневый доступ к аппаратным ресурсам и операционной системе. C# предоставляет богатые возможности для взаимодействия с платформой .NET, включая готовые API для работы с файлами, сетью, GUI и т. д.
Области применения Часто используется в системном программировании, играх и других задачах, где важна производительность и контроль над аппаратурой. Часто используется для разработки приложений под Windows, веб-приложений, приложений для мобильных устройств и других проектов, где удобство разработки и высокая стабильность более важны.

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

Пиши на C как джентльмен

Я думаю, многим знакома эта шикарная песня Jonathan Coulton’а, и эта жизненная ситуация, когда «Rob say Code Monkey very diligent», но «his output stink» и «his code not ‘functional’ or ‘elegant’».

Язык Си, подаривший нам столько полезного софта, потихоньку был вытеснен из десктопа и энтерпрайза такими высокоуровневыми гигантами как Java и C# и занял нишу системного программирования. И все бы хорошо, но системщики — очень отбитые своеобразные ребята. Задачи, которые порой возникают перед ними даже своей формулировкой способны вогнать в ужас простых смертных. Собственно говоря, так же, как и некоторые решения.

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

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

Практика I: Соблюдайте единый Code Style и фундаментальные принципы «хорошего тона»

Функция принимает в качестве аргумента переменную INPUT, парсит её в массив IncomingValues и возвращает result_to_return? Отставить быдлокод!

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

Вот несколько самых распространенных рекомендаций к оформлению кода на Си:

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

#define MAX_ARRAY_SIZE 32 #define INCORRECT_VALUE -1 #define IPC_FIND_NODE(x) ipc_find_node(config.x) 
int my_int_variable = 0; char *hello_str = "hello_habrahabr"; pid_t current_pid = fork();

Вообще, этот пункт спорный. Мне доводилось видеть проекты, где имена переменных и функций пишутся в camelCase и PascalCase соответственно.

UPD: Спасибо пользователю fogree за то, что он обнаружил косяк с перепутанным PascalCase и camelCase.

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

То же можно сказать и про переменные — никаких a, b, c — в названии должен быть отражен смысл (итераторы не в счет). Самодокументируемый код — очень хорошая практика.

Как правило, можно выбрать между стилем написания названия: PascalCase и under_score, тут уже зависит от вас.

/* пример функции общего пользования */ static void dgtprint(char *str) < int i; for (i = 0; i < strlen(str); i++) < if (isdigit(str[i])) printf("%c", str[i]); else print("_"); >> /* пример функции для работы со специфичным контекстом */ /* PascalCase */ void EnableAllVlans(struct vlan_cfg *vp) < int i; for (i = 0; i < VLAN_COUNT; i++) < EnableVlanByProto(vp.vlan[i]); >> /* under_score */ void enable_all_vlans(struct vlan_cfg *vp) < int i; for (i = 0; i < VLAN_COUNT; i++) < enable_vlan_by_proto(vp.vlan[i]); >>
int array[MAX_ARRAY_SIZE] = arrinit(); register int i, j, k; for (i = 0; i < MAX_ARRAY_SIZE; i++) for (j = 0; j < MAX_ARRAY_SIZE; j++) for (k = MAX_ARRAY_SIZE; k >= 0; k--) dosmthng(i, j, k, array[i]);
if (condition) < dosmthng(); >else < dont_do_something(); >/* Не делайте так */ if (condition) < dosmthng(); >else < dont_do_something(); >/* Гораздо правильнее будет следовать одному правилу переноса скобок, как тут */ if (condition) < dosmthng(); >else < dont_do_something(); >/* Или как тут */ /* Ну, или как тут, но это уже совсем экзотика */ if (condition) < dosmthng(); >else

По возможности инициализируйте переменные при объявлении. Численные с помощью нуля, указатели — NULL:

int counter = 0, start_position = 0, unknown_position = 0; struct dhcp_header * dhcp = NULL, * dhcp_temp = NULL; char input_string[32] = < 0 >;

Ну оставили мы переменные неинициализированными, и что?

А то. Если смотреть их (до инициализации) в отладке (в том же gdb), там будет лежать мусор. Это нередко сбивает с толку (особенно, если мусор «похож на правду»). Про указатели я вообще молчу.

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

Оптимальное решение — писать описания функций, если из аргументов и названия сложно понять весь её функционал. Для переменных — правила те же, в пояснении нуждаются только какие-то нелинейный вещи, где одного названия мало.

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

/* Возвращает 1, если параметры, связанные с модемным * соединением изменились, и 0, если нет. */ static int CheckModemConnection() < int i = 0; /* Проверка сети отдельно - меняется чаще всего */ if (CHECK_CFG_STR(Network.LanIpAddress) || CHECK_CFG_STR(Network.LanNetmask)) return 1; for(i = 0; i < MAX_MODEM_IDX; i++) < if (CHECK_CFG_INT(Modems.Modem[i].Proto) || CHECK_CFG_INT(Modems.Modem[i].MTU) || CHECK_CFG_STR(Modems.Modem[i].Username) || CHECK_CFG_STR(Modems.Modem[i].Password) || CHECK_CFG_STR(Modems.Modem[i].Number) || CHECK_CFG_STR(Modems.Modem[i].AdditionalParams) || CHECK_CFG_STR(Modems.Modem[i].PIN) || CHECK_CFG_STR(Modems.Modem[i].MRU) || CHECK_CFG_STR(Modems.Modem[i].PppoeIdle) || CHECK_CFG_STR(Modems.Modem[i].USBPort) || CHECK_CFG_STR(Reservation.Prefer) || CHECK_CFG_STR(Modems.Modem[i].PppoeConnectType) || CHECK_CFG_INT(Modems.Mode) || CHECK_CFG_INT(Aggregation.usb1) || CHECK_CFG_INT(Aggregation.usb2)) return 1; >return 0; >

Если вы постоянно работаете с трекерами (вроде RedMine), то при внесении правок в код можно указать номер задачи, в рамках которой эти правки были внесены. Если у кого-то при просмотре кода возникнет вопрос а-ля «Зачем тут этот функционал?», ему не придется далеко ходить. В нашей компании еще пишут фамилию программиста, чтобы если что знать, к кому идти с расспросами.

/* Muraviyov: #66770 */

Практика II: Оптимизируйте структуру вашего проекта

Если у вас в проекте несколько файлов — имеет смысл хорошо подумать над структурой проекта.
Каждый проект уникален, но, тем не менее, существует ряд рекомендаций, которые помогут удобно структурировать проект:

    Называйте файлы так, чтобы всем было ясно, какой файл за что отвечает.

Не следует называть файлы file1.c, mySUPER_COOL_header.h и т.д.
main.c — для файла с точкой входа, graph_const.h — для заголовочника с графическими константами будет в самый раз.

  • project/
    • common.c
    • common.h
    • main.c
    • network.h
    • networking.c
    • networking_v6.c
    • packet.c
    • packet.h
    • Makefile

    Если он точно не знает, какой файл ему нужен? Можно сберечь много нервов, если сделать так:

    • project/
      • include/
        • common.h
        • network.h
        • packet.h
        @$(CC) $(OBJS) -o networkd -L$(ROMFS)/lib -linteraction -Wall -lpthread -I ./include

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

        .PHONY clean build build: cd sound/ && make clean && make cd graphics/ && make clean && make cd engine/ && make clean && make sound: cd sound/ && make clean && make graphics: cd graphics/ && make clean && make engine: cd engine/ && make clean && make clean: cd sound/ && make clean cd engine/ && make clean cd greaphics/ && make clean

        Практика III: Используйте враппер-функции для обработки возвращаемых значений

        Враппер-функция (функция-обертка) в языке Си используется как функция со встроенной обработкой возвращаемого значения. Как правило, в случае ошибки в работе функции, возвращаемое значение вам об этом скажет, а глобальная переменная errno примет в себя код ошибки.

        Если вы пишите в системе (а сейчас большинство программ на си — именно системные программы), то нет ничего хуже, чем «немое» падение программы. По-хорошему, она должна красиво завершиться, напоследок сказав, что именно пошло не по плану.

        Но обрабатывать значение от каждой функции в коде — такое себе решение. Тут же упадет читаемость, и объем (+ избыточность) кода увеличится в пару раз.

        Тут и помогают врапперы. Рассмотрим первый пример — безопасный код без врапперов:

        int sock_one = 0, sock_two = 0, sock_three = 0; /* операция сравнения имеет больший приоритет, чем операция присваивания, * поэтому присваивание выполняется в скобках */ if ((socket_one = socket(AF_INET , SOCK_STREAM , 0)) if ((socket_two = socket(AF_INET , SOCK_DGRAM , 0)) if ((socket_three = socket(PF_INET , SOCK_RAW , 0))

        Ну, такое себе, не правда ли? Теперь попробуем с обертками.

        /* Где-то в коде. */ int Socket(int domain, int type, int proto) < int desk = socket(domain, type, proto); if (desk return desk; > /* . n строчек спустя - наш предыдущий пример . */ int socket_one = 0, socket_two = 0, soket_three = 0; socket_one = Socket(AF_INET , SOCK_STREAM , 0); socket_two = Socket(AF_INET , SOCK_DGRAM , 0); socket_three = Socket(PF_INET , SOCK_RAW , 0);

        Как видите, код по-прежнему безопасен (не будет «немого» падения), но теперь его функциональная часть гораздо компактнее.

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

        В использовании оберток есть небольшой минус, который, если захотеть, можно решить костылем. А что это за минус — можете предположить в комментариях 🙂

        Практика IV: Используйте keywords как профи

        Хорошее знание keywords никогда не будет лишним. Да, и без них ваш код будет работать, не спорю. Но когда речь зайдет об экономии места, быстродействии и оптимизации — это именно то, чего вам будет не хватать.

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

          register — дает компилятору указание по возможности хранить переменную в регистрах процессора, а не в оперативной памяти. Использование модификатора register при объявлении переменной-итератора цикла с небольшим телом может повысить скорость работы всего цикла в несколько раз.

        register byte i = 0; for (i; i < 256; i++) check_value(i);
        void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val); 
        int var = 1; if (!var) /* Эти 2 строчки будут отброшены компилятором */ dosmthng(); volatile int var = 1; if (!var) /* А вот эти - нет */ dosmthng(); 

        Практика V: Не доверяйте себе. Доверяйте valgrind.

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

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

        Более подробно о нем можно узнать тут.

        Практика VI: Помогайте тем, кто хочет улучшить ваш софт

        Пример будет взят из исходников busybox 1.21. Для тех кто не знает, что такое busybox, можете посмотреть эту вики-статью.

        UPD: до этого здесь был пример «плохого» кода из busybox. Спасибо пользователю themiron за то, что показал, что этот код был понят мною неправильно — это были лишь тонкости реализации, причем реализации очень хорошей. В качестве извинения за свою «клевету» на busybox, здесь будет пример хорошего кода.

        Причем, все так же из busybox.

        Код busybox очень эллегантен, пусть и совсем не прост. Всем, кто хочет взглянуть на язык си под другим углом — рекомендую ознакомиться с исходниками.

        Теперь обобщения по этому пункту на примерах из busybox. Все примеры взяты из udhcpc — крохотного DHCP клиента:

          Оставляй комментарии, там где они нужны.

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

        (файл networking/udhcp/common.h)

        struct dhcp_packet < uint8_t op; /* BOOTREQUEST or BOOTREPLY */ uint8_t htype; /* hardware address type. 1 = 10mb ethernet */ uint8_t hlen; /* hardware address length */ uint8_t hops; /* used by relay agents only */ uint32_t xid; /* unique id */ uint16_t secs; /* elapsed since client began acquisition/renewal */ uint16_t flags; /* only one flag so far: */ #define BROADCAST_FLAG 0x8000 /* "I need broadcast replies" */ uint32_t ciaddr; /* client IP (if client is in BOUND, RENEW or REBINDING state) */ uint32_t yiaddr; /* 'your' (client) IP address */ /* IP address of next server to use in bootstrap, returned in DHCPOFFER, DHCPACK by server */ uint32_t siaddr_nip; uint32_t gateway_nip; /* relay agent IP address */ uint8_t chaddr[16]; /* link-layer client hardware address (MAC) */ uint8_t sname[64]; /* server host name (ASCIZ) */ uint8_t file[128]; /* boot file name (ASCIZ) */ uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */ uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS]; >PACKED; 

        Листинг выше частично вошел сюда, т.к. подходит для еще одного примера.

        Посмотрите: описание упакованных структур идет перед перечислением, отражающим размеры этих структур.

        (файл networking/udhcp/common.h)

        struct dhcp_packet < uint8_t op; /* BOOTREQUEST or BOOTREPLY */ uint8_t htype; /* hardware address type. 1 = 10mb ethernet */ uint8_t hlen; /* hardware address length */ uint8_t hops; /* used by relay agents only */ uint32_t xid; /* unique id */ uint16_t secs; /* elapsed since client began acquisition/renewal */ uint16_t flags; /* only one flag so far: */ #define BROADCAST_FLAG 0x8000 /* "I need broadcast replies" */ uint32_t ciaddr; /* client IP (if client is in BOUND, RENEW or REBINDING state) */ uint32_t yiaddr; /* 'your' (client) IP address */ /* IP address of next server to use in bootstrap, returned in DHCPOFFER, DHCPACK by server */ uint32_t siaddr_nip; uint32_t gateway_nip; /* relay agent IP address */ uint8_t chaddr[16]; /* link-layer client hardware address (MAC) */ uint8_t sname[64]; /* server host name (ASCIZ) */ uint8_t file[128]; /* boot file name (ASCIZ) */ uint32_t cookie; /* fixed first four option bytes (99,130,83,99 dec) */ uint8_t options[DHCP_OPTIONS_BUFSIZE + CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS]; >PACKED; #define DHCP_PKT_SNAME_LEN 64 #define DHCP_PKT_FILE_LEN 128 #define DHCP_PKT_SNAME_LEN_STR "64" #define DHCP_PKT_FILE_LEN_STR "128" struct ip_udp_dhcp_packet < struct iphdr ip; struct udphdr udp; struct dhcp_packet data; >PACKED; struct udp_dhcp_packet < struct udphdr udp; struct dhcp_packet data; >PACKED; enum < IP_UDP_DHCP_SIZE = sizeof(struct ip_udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS, UDP_DHCP_SIZE = sizeof(struct udp_dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS, DHCP_SIZE = sizeof(struct dhcp_packet) - CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS, >;

        Если в этом же файле мы спустимся чуть пониже, то увидим, что объявления функций работы с опциями так же находятся в одном месте:

        (файл networking/udhcp/common.h)

        unsigned FAST_FUNC udhcp_option_idx(const char *name); uint8_t *udhcp_get_option(struct dhcp_packet *packet, int code) FAST_FUNC; int udhcp_end_option(uint8_t *optionptr) FAST_FUNC; void udhcp_add_binary_option(struct dhcp_packet *packet, uint8_t *addopt) FAST_FUNC; void udhcp_add_simple_option(struct dhcp_packet *packet, uint8_t code, uint32_t data) FAST_FUNC; #if ENABLE_FEATURE_UDHCP_RFC3397 char *dname_dec(const uint8_t *cstr, int clen, const char *pre) FAST_FUNC; uint8_t *dname_enc(const uint8_t *cstr, int clen, const char *src, int *retlen) FAST_FUNC; #endif struct option_set *udhcp_find_option(struct option_set *opt_list, uint8_t code) FAST_FUNC;

        В udhcpc есть огромное количество опций, которые по умолчанию не используются. Каждая опция соответствует макросу, который закрывает ее номер.

        Если бы они были раскомментированы — то это были бы макросы, которые не всплывают нигде в коде. Человек, который спросил бы «а зачем все эти опции?» искал бы ответ очень долго. И не нашел бы.

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

        (файл networking/udhcp/common.h)

        #define DHCP_PADDING 0x00 #define DHCP_SUBNET 0x01 //#define DHCP_TIME_OFFSET 0x02 /* (localtime - UTC_time) in seconds. signed */ //#define DHCP_ROUTER 0x03 //#define DHCP_TIME_SERVER 0x04 /* RFC 868 time server (32-bit, 0 = 1.1.1900) */ //#define DHCP_NAME_SERVER 0x05 /* IEN 116 _really_ ancient kind of NS */ //#define DHCP_DNS_SERVER 0x06 //#define DHCP_LOG_SERVER 0x07 /* port 704 UDP log (not syslog) //#define DHCP_COOKIE_SERVER 0x08 /* "quote of the day" server */ //#define DHCP_LPR_SERVER 0x09 #define DHCP_HOST_NAME 0x0c /* either client informs server or server gives name to client */ //#define DHCP_BOOT_SIZE 0x0d //#define DHCP_DOMAIN_NAME 0x0f /* server gives domain suffix */ //#define DHCP_SWAP_SERVER 0x10 //#define DHCP_ROOT_PATH 0x11 //#define DHCP_IP_TTL 0x17 //#define DHCP_MTU 0x1a //#define DHCP_BROADCAST 0x1c //#define DHCP_ROUTES 0x21 //#define DHCP_NIS_DOMAIN 0x28 //#define DHCP_NIS_SERVER 0x29 //#define DHCP_NTP_SERVER 0x2a //#define DHCP_WINS_SERVER 0x2c #define DHCP_REQUESTED_IP 0x32 /* sent by client if specific IP is wanted */ #define DHCP_LEASE_TIME 0x33 #define DHCP_OPTION_OVERLOAD 0x34 #define DHCP_MESSAGE_TYPE 0x35 #define DHCP_SERVER_ID 0x36 /* by default server's IP */ #define DHCP_PARAM_REQ 0x37 /* list of options client wants */ //#define DHCP_ERR_MESSAGE 0x38 /* error message when sending NAK etc */ #define DHCP_MAX_SIZE 0x39 #define DHCP_VENDOR 0x3c /* client's vendor (a string) */ #define DHCP_CLIENT_ID 0x3d /* by default client's MAC addr, but may be arbitrarily long */ //#define DHCP_TFTP_SERVER_NAME 0x42 /* same as 'sname' field */ //#define DHCP_BOOT_FILE 0x43 /* same as 'file' field */ //#define DHCP_USER_CLASS 0x4d /* RFC 3004. set of LASCII strings. "I am a printer" etc */ #define DHCP_FQDN 0x51 /* client asks to update DNS to map its FQDN to its new IP */ //#define DHCP_DOMAIN_SEARCH 0x77 /* RFC 3397. set of ASCIZ string, DNS-style compressed */ //#define DHCP_SIP_SERVERS 0x78 /* RFC 3361. flag byte, then: 0: domain names, 1: IP addrs */ //#define DHCP_STATIC_ROUTES 0x79 /* RFC 3442. (mask,ip,router) tuples */ #define DHCP_VLAN_ID 0x84 /* 802.1P VLAN ID */ #define DHCP_VLAN_PRIORITY 0x85 /* 802.1Q VLAN priority */ //#define DHCP_MS_STATIC_ROUTES 0xf9 /* Microsoft's pre-RFC 3442 code for 0x79? */ //#define DHCP_WPAD 0xfc /* MSIE's Web Proxy Autodiscovery Protocol */ #define DHCP_END 0xff

        Довольно сложный для восприятия аспект, требующий пояснения. В двух словах: если у вас есть функция, которая внутри проекта вызывается с n комбинациями различных параметров (где n — небольшое число), причем каждая комбинация вызывается по нескольку раз, имеет смысл сделать для каждой комбинации отдельную функцию, вызывающую внутри себя целевую функцию, но уже с нужными параметрами. Например. У нас есть функция, отправляющая пакеты на определенный порт и адрес:

        (файл networking/udhcp/packet.c)

        /* Construct a ip/udp header for a packet, send packet */ int FAST_FUNC udhcp_send_raw_packet(struct dhcp_packet *dhcp_pkt, uint32_t source_nip, int source_port, uint32_t dest_nip, int dest_port, const uint8_t *dest_arp, int ifindex) < struct sockaddr_ll dest_sll; struct ip_udp_dhcp_packet packet; unsigned padding; int fd; int result = -1; const char *msg; fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_IP)); if (fd < 0) < msg = "socket(%s)"; goto ret_msg; >memset(&dest_sll, 0, sizeof(dest_sll)); memset(&packet, 0, offsetof(struct ip_udp_dhcp_packet, data)); packet.data = *dhcp_pkt; /* struct copy */ dest_sll.sll_family = AF_PACKET; dest_sll.sll_protocol = htons(ETH_P_IP); dest_sll.sll_ifindex = ifindex; dest_sll.sll_halen = 6; memcpy(dest_sll.sll_addr, dest_arp, 6); if (bind(fd, (struct sockaddr *)&dest_sll, sizeof(dest_sll)) < 0) < msg = "bind(%s)"; goto ret_close; >/* We were sending full-sized DHCP packets (zero padded), * but some badly configured servers were seen dropping them. * Apparently they drop all DHCP packets >576 *ethernet* octets big, * whereas they may only drop packets >576 *IP* octets big * (which for typical Ethernet II means 590 octets: 6+6+2 + 576). * * In order to work with those buggy servers, * we truncate packets after end option byte. */ padding = DHCP_OPTIONS_BUFSIZE - 1 - udhcp_end_option(packet.data.options); packet.ip.protocol = IPPROTO_UDP; packet.ip.saddr = source_nip; packet.ip.daddr = dest_nip; packet.udp.source = htons(source_port); packet.udp.dest = htons(dest_port); /* size, excluding IP header: */ packet.udp.len = htons(UDP_DHCP_SIZE - padding); /* for UDP checksumming, ip.len is set to UDP packet len */ packet.ip.tot_len = packet.udp.len; packet.udp.check = inet_cksum((uint16_t *)&packet, IP_UDP_DHCP_SIZE - padding); /* but for sending, it is set to IP packet len */ packet.ip.tot_len = htons(IP_UDP_DHCP_SIZE - padding); packet.ip.ihl = sizeof(packet.ip) >> 2; packet.ip.version = IPVERSION; packet.ip.ttl = IPDEFTTL; packet.ip.check = inet_cksum((uint16_t *)&packet.ip, sizeof(packet.ip)); udhcp_dump_packet(dhcp_pkt); result = sendto(fd, &packet, IP_UDP_DHCP_SIZE - padding, /*flags:*/ 0, (struct sockaddr *) &dest_sll, sizeof(dest_sll)); msg = "sendto"; ret_close: close(fd); if (result < 0) < ret_msg: bb_perror_msg(msg, "PACKET"); >return result; > 

        Но dhcp не всегда нуждается в отправке пакета на один IP адрес. В основном используется широковещательная (BROADCAST) рассылка.

        Но широковещательная отправка пакета — всего лишь отправка пакета по адресу, зарезервированному под broadcast. Собственно, для того, чтоб отправить широковещательный запрос, достаточно использовать описанную выше функцию, но в качестве адреса указать тот, что зарезервирован про бродкаст. Отсюда получаем функцию:

        (файл networking/udhcp/dhcpc.c)

        static int raw_bcast_from_client_config_ifindex(struct dhcp_packet *packet) < return udhcp_send_raw_packet(packet, /*src*/ INADDR_ANY, CLIENT_PORT, /*dst*/ INADDR_BROADCAST, SERVER_PORT, MAC_BCAST_ADDR, client_config.ifindex); >

        Заключение

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

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

        Пиши на Си как джентльмен.

        Чем отличается C от C++

        Короткий ответ: C++ — это улучшенный C. У этих языков одинаковый на 99% синтаксис и команды, но C — это больше про структурное и процедурное программирование, а C++ — про объектно ориентированное.

        C — язык, который сделал в 1973 году Деннис Ритчи. Главная цель языка — скорость, быстродействие и универсальность. Язык изначально проектировался как системный, чтобы на нём можно было писать код для процессоров, драйверов и создавать на нём операционные системы. В то время большинство этих вещей делали на ассемблере, и Ритчи хотел это упростить.

        C++ придумал Бьёрн Страуструп в начале восьмидесятых, когда ему не хватало возможностей стандартного C. Он сделал язык более строгим, добавил в него классы, ООП-подход и перегрузку операторов, сохранив скорость оригинального С. В 1983 году Бьёрн переименовал язык из «C с классами» в C++.

        �� О перегрузке операторов как-нибудь в другой раз 🙂

        C C++

        Код читается проще

        Проще портировать код на другие платформы

        Есть структуры и переменные типа «структура»

        Наследование — это структура внутри структуры

        void — обязательное слово

        Можно объявлять глобальную переменную несколько раз

        Исключения нужно конструировать самому

        Нет перегрузки операторов

        Почти всё стандартно и предсказуемо

        Для чего изучать

        C — классический язык разработки системного ПО и любого софта для микропроцессоров. На нём написаны Linux, большая часть Windows и MacOS. Если взять любой современный носимый гаджет или электронное устройство, в большинстве случаев они работают тоже под управлением программы на C. В мире огромное количество кода, который написан на C (и ещё столько же будет написано), поэтому проблем с работой у C-программистов не предвидится.

        C++ — выбор тех, кому одновременно нужна вся мощь C и гибкость объектно ориентированного программирования. Counter-Strike, StarCraft и World of Warcraft написаны на C++, а это значит, что можно сочетать производительность C и современные технологии. Часть движка Unity тоже написана на C++, чтобы получить прямой доступ к памяти и ресурсам системы.

        Нельзя ли подробнее?

        На самом деле нет такой уж большой разницы между C и C++. Это одинаково мощные и быстрые языки, просто у них разная область применения и стиль программирования.

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

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

        Чтобы было понятнее, скоро напишем подробно про оба языка. А вы не болейте и берегите себя.

        Получите ИТ-профессию

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

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

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