fbpx

Каталог статей

Каталог статей для размещения статей информационного характера

Как выучить

Как научиться программированию на языке Си для начинающих

Как научиться программированию на языке Си для начинающих

Что вы хотели делать в Windows? Я думаю, что это зависит от компилятора не меньше, чем от кода.

Я также порекомендую C++ FQAs [1]. Это C++, да, но во многих местах он имеет отношение к C, и поскольку сейчас многие системные программисты используют C++ как лингва франка, это хороший, развлекательный ресурс.

Я бы прочитал K&R, это право на прохождение. Посмотрите на некоторые крупные проекты с открытым исходным кодом, чтобы понять, как организовать и управлять большим проектом на Си.

И практикуйте указатели. Ознакомьтесь с тем, как работает память.

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

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

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

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

Я работаю над достаточно большим (и кроссплатформенным) проектом на C (https://domeengine.com) уже пару лет, и я обнаружил, что C в целом требует определенной дисциплины. Когда вы взаимодействуете с API, вам нужно прочитать документацию, выяснить их специфические предварительные условия и требования по обработке освобождений, определить, что вам принадлежит, а что нет.

Это также поможет убедиться, что если вы что-то выделяете, то очищаете после этого ясным и четко определенным способом.

Вы также должны хорошо познакомиться с отладчиками (LLDB/GDB) и другими инструментами разработчика, чтобы помочь, когда вы допустили ошибку.

В современную эпоху у вас есть несколько вариантов для запуска под Windows. C89 с помощью Visual Studio, инструментарий gcc MSYS2/MinGW2 или инструментарий gcc с помощью WSL. Я использую инструменты MSYS2 для DOME, потому что они требуют наименьших модификаций кода под конкретную платформу, но это зависит от конкретного случая.

Довольно очевидный ответ, но для тех, кто только начинает изучать язык программирования C, книга “Язык программирования C” Брайана Кернигана и Денниса Ричи – хорошее место для начала.

Важный вопрос, который следует задать себе, – зачем вы хотите изучать язык Си? Если вы хотите уметь читать большие кодовые базы на языке Си, это имеет смысл. Но если вы хотите писать встроенное программное обеспечение или системное программное обеспечение, rust или c++11 (ух), вероятно, лучшее место для начала. Я говорю это как человек, который изучал C, но перешел на c++ без сожаления. Хотя у меня есть серьезная зависть к rust 🙂

Хотя это и не окончательное руководство, https://matt.sh/howto-c – хороший пример того, как следует подходить к “современному” Си.

c99 полностью реализован в windo

Харбисон и Стил объясняют гораздо лучше, чем K&R. Книга Фьюра научила меня многому о декларациях языка Си. Декларации – это та часть языка Си, которая вызывает наибольшие неоправданные трудности.

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

В качестве примера мне нравится исходный код QEMU[1]. Он огромен, но стиль и практика, которые можно найти в любом файле, помогут вам освоить хороший C.

Это интересная книга для начала “Эксперт программирования на C Пит ван Линден” .

Определение в стандарте Си возможного расширения макроса NULL довольно свободное; это просто должна быть константа нулевого указателя. Поэтому компилятор языка Си может выбрать для него любое из следующих значений: 0U, 0, ‘’, 0UL, 0L, 0ULL, 0LL или (void * )0. Важно, что тип, стоящий за NULL, не предписан стандартом Си. Часто люди используют его, чтобы подчеркнуть, что они говорят о константе-указателе, которая на многих платформах таковой не является. Использование NULL в контексте, который мы еще не до конца освоили, даже опасно. В частности, это проявляется в контексте функций с переменным числом аргументов.

NULL больше скрывает, чем проясняет. Либо используйте 0, либо, если вы действительно хотите подчеркнуть, что значение является указателем, используйте магическую последовательность токенов (void * )0 напрямую.

Предпочтительным способом записи константы нулевого указателя является NULL. Вы также можете использовать 0 или (void * )0 в качестве константы нулевого указателя, но использование NULL более чистое, так как делает назначение константы более очевидным.

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

NULL – это предпочтительная константа нулевого указателя. Используйте NULL вместо (type * )0 или (type * )NULL во всех случаях, кроме аргументов переменных функций, где компилятор не знает тип.

Читатели: что вы думаете по этому поводу и почему?

Это нормально – проводить черту в том, какие реализации вас волнуют. Честно говоря, в наше время меня не волнуют реализации, которые определяют NULL как 0 (или что-то другое, не являющееся указателем). Это тот случай, когда не стоит тратить время каждого на то, чтобы ориентироваться на наименьший общий знаменатель… вместо этого исправьте эту реализацию или просто скачайте лучшую.

Если бы я мог сегодня внести в C изменения, я бы удалил макрос NULL и заменил его ключевым словом null (или nil, или что угодно) со строчной буквы на определение, которое не налагает бессмысленного бремени на своих пользователей.

Тем не менее, у меня все еще сохраняется привычка приводить NULL к void*, когда я использую его в качестве аргумента.

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

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

И прежде всего, независимо от всего остального: Не начинайте глупых ссор из-за этой ерунды. Ииии.

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

Я сейчас заново изучаю C, я пробовал разные книги в течение многих лет (чтобы не сказать десятилетий), никогда не делал больше, чем игрушечный проект. Книга, которую я взял на этот раз, была extreme c (судя по отзывам на Amazon, новая книга), автор Камран Амини, и пока что (несколько глав) она довольно приятная, поскольку рассматривает C, процесс компиляции и т.д. с “основ”, довольно практично, не слишком теоретично и относительно быстро. Мне кажется, что книга больше о том, как сделать программу на C, а не учит буквально самому языку. Так что это действительно то, что мне было нужно.

Я думаю, что вы можете использовать любую версию Visual studio для написания программ на C. Недавно я сделал видеоурок по Visual Studio: на тему “Оператор модуля в C++” .

Что я бы посоветовал, так это изучить синтаксис и попытаться понять ядро linux (которое является одним из очень хорошо продуманных программ, написанных на C). Если вы не хотите так глубоко погружаться, вы можете ознакомиться с исходным кодом sqlite. Написание кода начинается с его чтения. Сделайте себе одолжение и потратьте больше времени на чтение кода, чем на чтение книг.

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

Криптобиблиотека размером в сотню твитов [ https://twitter.com/tweetnacl ] от единственного человека с искренней претензией на способность писать безопасный C & company.

Клон UNIX v6 на ANSI C, созданный влиятельным сотрудником Bell Laboratories времен Plan 9, а ныне влиятельным сотрудником Google Рассом Коксом, а также влиятельным автором компьютерных вирусов и сыном одного из первоначальных авторов UNIX Роберта Т. Моррисона; весь исходный код умещается на ста страницах хорошо отпечатанных документов [ предупреждение, старая копия, вам следует создать современную: https://pdos.csail.mit.edu/6.828/2011/xv6/xv6-rev6.pdf ].

Не самые последние проекты, но все, что реализовал DJB, абсолютно элегантно, не содержит ошибок и отлично подходит в качестве урока по написанию безопасного Си. Например, qmail, djbdns, daemontools, там есть много идей, которые можно перенять: https://cr.yp.to/.

Также полезно со временем наращивать (и дорабатывать) свой инструментарий, обёртки для работы с памятью, логирование, ввод/вывод, демонизация (https://github.com/jirihnidek/daemon) и т.д., чтобы не приходилось изобретать велосипед.

Любая программа на базе языка C

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

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

Это нелегко. Очевидный ответ – читайте и пишите много кода. Экспериментируйте. Убедитесь, что вы действительно знаете, как работает компьютер. Изучение ассемблера, даже если вы никогда им не пользуетесь, может помочь в этом (бесплатная электронная книга “Программирование с нуля” может быть пройдена за выходные). Читайте существующие базы кода, различные стили, например, linux или GNOME.

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

Я бы сказал, что нет правильного способа. Каждый проект имеет свой способ написания языка C. С использованием макросов, иногда это выглядит как DSL для каждого конкретного проекта.

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

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

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

Это случается со мной с кодом на C++ и lisp. Мне требуется некоторое время, чтобы понять, что автор использует (или злоупотребляет), прежде чем я смогу понять код. С кодом на Си такого не происходит.

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

Получите рецензии на код от кого-то, кто действительно знает C. Наставничество и жесткие рецензии на код – лучший способ научиться.

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

На мой взгляд, одна из главных причин, по которой C выглядит устаревшим и непрактичным для новичков, – это стандартный API libs.

Но вполне возможно использовать C как язык с более современным и легко читаемым API, но вам, вероятно, придется создавать свой собственный.

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

Научитесь использовать valgrind и, возможно, ddd.

Главное, что предоставляет C

Я думаю, что основная тема, которую нужно освоить в Си, – это указатели. Это то место, где большинство терпит неудачу. На “освоение” уйдет несколько лет (если вообще получится). Здесь я бы рекомендовал “Понимание и использование указателей в языке Си”, Ричард Риз. [2]

Если вы интересуетесь сетевыми технологиями, любая из классических книг “TCP/IP Illustrated Vols I/II/III”, W. Richard Stevens, [3] содержит тонну кода на языке Си для реализации компонентов TCP/IP.

Если вы интересуетесь графикой, то “Graphics Gems”, Andrew Glassner [4] – хороший источник.

“An Introduction to GCC”, Brian Gough, [5] для понимания инструментария и его различных “колокольчиков и свистков”.

Мой опыт обучения плаванию путем прыжка в глубокую часть бассейна был реализован при изучении программирования Windows по книге Чарльза Петцольда и навигации по Microsoft Foundation Classes в конце 80-х/начале 90-х годов. В те времена уровень развития инструментария был не так высок, и я потратил месяцы на то, чтобы разобраться с книгой. Это было сделано после того, как я построил фундамент с помощью K&R и приличного количества сетевого программирования Unix.

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

Удачи на вашем пути.

На самом деле, я вчера прочитал пару глав в Modern C :). Вот некоторые вещи, которые я делаю, чтобы улучшить свои навыки в Си, чтобы сравняться с некоторыми из восхищенных/профессиональных разработчиков.

Решить, какую платформу использовать

К сожалению, чтобы стать опытным в нем, нам нужно писать код и ориентироваться на платформу. Я боролся между тем, разрабатывать ли на Windows и Linux. У меня большой опыт работы в среде Windows (использование отладчиков/cl/linkers/Windbg и т.д.), но когда дело доходит до написания качественного кода на языке C (не C++) и обучения написанию хорошего сопровождаемого исходного кода умеренного размера, мои исследования показали, что компиляторы/стандартные API Windows не очень хороши, фактически они мешают вашей производительности. Я потратил бесчисленное количество часов, чтобы понять, как правильно создать простой проект на C с достойной системой сборки. К сожалению, я не смог найти такую систему. Самое близкое, что я смог найти, это CMake, так как MSBuild – это кошмар для работы. Я даже попробовал NMAKE, но потерпел неудачу. Когда дело доходит до документации по поиску базового использования C Api, MSDN . ) делает приличную работу. Но во имя безопасности вы найдете миллион вариаций (_s, _l) API, и по умолчанию компилятор msvc не позволит вам использовать некоторые из API в их простых формах. Вместо этого вы должны определить _CRT_SECURE_NO_WARNINGS и т.д. Я думаю, что для человека, только начинающего разрабатывать/научиться писать приличную кодовую базу на C, эти ограничения очень мешают продуктивности. В итоге, я решил сосредоточить свое обучение на платформе Linux (в настоящее время через WSL – подсистему Windows для Linux) с ее POSIX apis. Знаете что, `man 3 printf` или `man 3 strlen` намного лучше, чем гуглить msdn.

Я думаю, что простой и прямой ответ здесь – это чтение хорошего кода и написание “хорошего” кода, а также чтение хорошего контента на Си (будь то книги или статьи). Я думаю, что это три ингредиента, необходимые для начала работы. Из всех проектов с открытым исходным кодом, которые я исследовал, я обнаружил, что Linux Kernel и связанные с ним проекты имеют очень хороший вкус с точки зрения качества кода. В настоящее время я просто сосредоточен на том, как они используют язык, а не на том, что они на самом деле делают в проекте. Например, как они структурируют проект, как они называют вещи, как они используют типы, как они создают структуры, как они передают структуры в функции, как они используют легковесные объектные конструкции, как они обрабатывают ошибки в функции (например, только прямой выход goto), как они используют знаковые/беззназнаковые переменные и т.д. (это я узнаю ближе к концу), как они используют свои собственные структуры данных. Я думаю, что лучше изначально сосредоточиться/нацелиться на ANSI C API с C99 вместо того, чтобы сильно полагаться на API конкретной ОС на той платформе, которую вы выбрали. Например, такими проектами могут быть написание парсеров двоичных файлов для таких проектов, как формат файлов .ISO и т.п.

Хорошие проекты/статьи по Си

1. Winlib.net – https://github.com/jcpowermac/wimlib – отличный источник информации.

5. CCAN – https://github.com/rustyrussell/ccan/tree/master/ccan – отличный источник лакомых кусочков о Си от не кого иного, как Расти Рассела – я прочитал не все.

продолжение в комментариях.

1. Суффикс _t – понятие, используемое для обозначения typedef для данного типа /* Этот пример взят из do_journal.c в e2fsprogs / struct journal_transaction_s

; typedef struct journal_transaction_s journal_transaction_t;

2. Знать о таких заголовках, как stddef.h и stdint.h, и когда их следует использовать. например: Когда использовать обычные типы данных, такие как int против int16_t и т.д.

3. Из https://en.cppreference.com/w/c/types/integer мы можем понять, что int_t являются типами точной ширины, что может иметь некоторые побочные эффекты, если базовое оборудование не поддерживает эту ширину изначально.

4. Знать свой компилятор Предопределенные стандартные макросы В компиляторе Microsoft _WIN64 – определяется, когда мы компилируем для x64 кода _WIN32 – определяется, когда компилируется как x86, так и x64 код _MSC_VER – определяет, какую версию компилятора мы используем, указывает на различные версии visual studio __cplusplus – определяется, когда блок трансляции компилируется как C++

5. Мы можем получить FILE* из HANDLE, используя следующие API из io.h и fcntl.h Fd = _open_osfhandle((intptr_t)Handle, _O_TEXT); File = _wfdopen(Fd, L “r”); Получив FILE*, мы можем использовать fgets для строковых операций.

6. Узнали о var args и выровненной памяти Выровненная память означает, что адрес, возвращаемый _aligned_malloc, всегда кратен указанному нами выравниванию. Например: char p = _aligned_malloc(10, 4); адрес, возвращаемый в p, всегда будет кратен 4. Мы также должны освободить выделенную память с помощью _aligned_free(p).

7. atoi(str) этот api также обрабатывает входную строку до преобразования. Например, atoi(“123asda”) все равно выдаст 123 в качестве возвращаемого результата. Любой пробел в начале входной строки будет проигнорирован. Поэтому atoi(” 123asd”) все равно вернет 123. Для преобразования строк в типы int/long/float рекомендуется использовать функции strto, поскольку они также могут возвращать указатель на символ, не являющийся целым числом.

8. UCRT поддерживает около 40 API системного уровня POSIX, но большинство из них имеют префикс _. wimlib в wimlib_tchar.h определяет #define topen _open для Win32 и #define topen open для POSIX-систем Отсюда следует, что реализация UCRT хоть и отличается по названию, но параметры абсолютно одинаковы.

9. Мы можем установить только инструменты сборки (компилятор VC), исключая IDE с .

10. Лучшее видео о стандарте C и некоторых его менее известных возможностях – “Новые” возможности в C – Dan Saks Год C Standard Comments 1983 C standard committee is formed 1989 C89 C89 US standard 1990 C90 C89 International Standard 1999 C99 C99 Standard 2011 C11 C11 Standard 2018 C18 C18 Bugfix release< . blk64_t start, end; . >11. Еще одно хорошее видео о менее известных возможностях языка C – Choosing the Right Integer Types in C and C++ – Dan Saks – code::dive 2018

12. size_t обозначает поддерживаемый родной архитектурой естественный размер слова. Так, для 32-битной это 4 байта без знака, а для 64-битной – 8 байт без знака. Следовательно, он определяется следующим образом

13. В C11 было введено понятие статических утверждений. По сути, это условные утверждения, которые могут быть оценены во время компиляции. Поэтому в C11 появилось новое ключевое слово _Static_assert(expr, message) Причина такого уродливого названия – та же идея не ломать существующий код. Поэтому для удобства в заголовке assert.h предусмотрен макрос static_assert, который означает то же самое. Ниже приведен один из примеров использования статических утверждений

14. Еще одно хорошее видео о некоторых деталях низкого уровня – Продолжительность хранения и связывание в C и C++ – Dan Saks

15. #define _CRT_SECURE_NO_WARNINGS может быть использовано для отключения предупреждения CRT для обычных функций.

16. Любая функция ucrt, начинающаяся с _, является нестандартным api, предоставляемым ucrt. Например, в string.h это _strdup, _strlwr, _strrev. Отсюда можно сделать вывод, что легко определить, какие функции являются частью стандарта C, а какие нет. Интересно, что некоторые (не все) из этих нестандартных функций являются частью posix, поэтому в glibc (который реализует posix) в них нет _.

17. Все функции posix в стандарте posix с аннотацией [CX] указывают на расширение стандарта ISO C. Например, нижеприведенная функция из stdlib.h является расширением posix. UCRT определяет аналогичный api под названием _putenv, так как он не является частью стандарта C, версия UCRT имеет _.

18. Узнал о CGold: The Hitchhiker’s Guide to the CMake. Потрясающий учебник по CMake. Теперь очень легко начать проект на C, не беспокоясь об отдельных системах сборки.

Надеюсь, это поможет.

Суффикс _t – это понятие, используемое для обозначения typedef для данного типа /* Этот пример взят из do_journal.c в e2fsprogs / struct journal_transaction_s

; typedef struct journal_transaction_s journal_transaction_t;

>< . blk64_t start, end; . >11. Еще одно хорошее видео о менее известных возможностях языка C – Choosing the Right Integer Types in C and C++ – Dan Saks – code::dive 2018

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

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