Что полезно знать начинающему в Си/Си++, грамматика языка Си/Си++. Часть 1

Всем доброго, с вами Мирра Андрюхан. В прошлой статье мы с вами разобрались с тем, как же собирать исполняемый файл из нескольких файлов с исходным кодом. А теперь, пора разобраться в грамматике языка Си/Си++. Однако ООП (объектно ориентированное программирование) в данной статье мы рассматривать не будем, поэтому при упоминании языка программирования будет указываться просто «Си».

Введение.

Для начала стоит сказать несколько слов о пресловутой сложности, о которой молвят в среде «программистов», дабы не усложнять вам изучение самого языка Си/Си++. Просто все дело в том, что язык требует понимания того, что пишешь на нем и не стоит излишне переутомляться, чтобы потом не отправить ракету в короткий полет из-за того, что знак «=» в языке Си является знаком присвоения и в круглых скобках оператора условия if аналогично. В то время, когда знак «==» является знаком сравнения «равно».

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

И да, работа со строками в Си сложнее, чем в Паскале. Однако, при этом есть свои удобства в обработке текста, т.к. кодировок тоже много и в языке Си меньше костылеобразности с этим, чем в языке Паскаль. В принципе, со строками проще в языке Си++, т.к. в нем есть ООП, чем собственно он и отличается от языка Си, все остальное одинаково. В общем, язык не столь сложен, сколько лени бывает в самих хомячках и программистах. И не спорю, что «времени всегда нет», однако никогда меня не удивлял результат подобного суждения с возвратом по принципу все же иначе делать, т.е. как надо. И время, тогда находится, и все остальное получается. Ведь, помимо жалоб, есть слабые стороны языка Си, но у какого языка программирования их нет?!

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

С другой стороны, кодер может научиться быть программистом, т.к. это тоже самое, как подводящие упражнения при тренировке мышц спины, чтобы она включалась и давала доступность качественных заходов для раскачки спины. А программист, будучи кодером всего лишь кует, кует и еще раз кует на наковальне, оттачивая свой опыт. Я к примеру, учился быть программистом и умею это делать, т.к. учился у практикующего программиста. Учился в группе школьников и мне это нравилось, а кодирование это лишь рутина, без которой никуда. Она ничему не учит. Это как молотком научиться забивать гвоздь в стену, не значит что научиться ремонту по дому и быть мастером в этом, это только научиться работать молотком по гвоздям. Но, никто не говорит, что это плохо. Просто, стоит помнить о разнице в подходе к собственному росту. Т.к. быть программистом в нынешнее время сложно, потому что у многих кодирование, у многих рутина. Но, никто за вас не выбирал, как вы поступите в этой ситуации и чему будете учиться. Просто, важно помнить о приоритетах ежедневной работы, но и про себя не забывать. Самое важное, не давать себя выжигать на работе, вы это обязаны помнить сами и заботиться о себе, ведь нигде в договоре не указано конкретно, что работодатель имеет право губить ваше здоровье, а подразумевать можно все, что угодно, ведь этого нет на бумаге в договоре.

Ладно, расскажу еще про одну ситуацию, с которой вероятно вы могли бы столкнуться или сталкивались, будучи программистом. Когда-то я тоже был молодым человеком (сейчас мне 35 полных лет) и тоже не знал, кто я на пути быть «программистом чего», поэтому искал себя и пробовал разные области. Но, при этом я не работал в этих областях. Однако, мой опыт в работе с CMS системами на работе оказался не результативнее свободного опыта, т.е. не обязательно было идти на работу, чтобы понять, куда хочешь двигаться. Достаточно позаниматься с этим в свободное время и понять для себя начало, а потом выбрать хочешь ли в этом развиваться. А дальше, жизнь проверит это на прочность, по другому называется проверка жизнью.

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

Проработал я там всего два месяца, но смог увидеть себя и понять, почему это не мое. Не спорю, была депрессия из-за того, что меня уволили из-за недостатка опыта, но для меня это был полезный опыт подумать «а стоит ли оно того?!». Не просто убеждать себя, а подумать, оценить и разобраться в этом. И надо признать, мне честно сказали, что я хороший программист, но у меня нету одного года опыта работы с сайтами. Такова жизнь, мы были разными людьми в плане быть «программистом чего». И у меня не получалось сверстать тему внешнего вида для сайта. Просто, как потом это понял, оказалось, что я был программистом прикладного ПО и у меня в голове не укладывалось, зачем нужно так делать. Это сейчас, я могу сделать тему для сайта, изучив нужное в CMS сайта, т.к. знаю, как переключаться между опытом прикладного программиста и веб-программиста, а тогда я этого не умел. А чего вы еще ожидали от молодости идущей одним путем?! Однако, этому научился на своей нынешней работе, будучи инженером АСУ ТП. Просто, понял, что могу накопить сколько угодно опыта и знаний, навыков и умений, и применять их, переключаясь в своем восприятии между ними. И я знаю, что могу и сделаю, а чего не знаю, того могу узнать и научиться делать. А какой жизненный опыт был у вас в жизни, который научил вас тому, что вы цените и поныне? Смотреть, с такой позиции восприятия и взгляда на жизнь полезно, т.к. идеалов в жизни нет, они просто не существуют, остальное лишь убеждения, которыми мы сами себя кормим каждый день, пока не надоест.

И да, за время вышеописанной работы у меня была возможность увидеть два типа участников в программировании, программиста и кодера, оба делали свою работу качественно. Я тоже, ничуть ни хуже был, но мне не хватало практики. Конторка была маленькой, работали все, хватало и рутины. В основном мне доставалось заниматься кодированием веб-страниц, по-русски я занимался версткой страниц. Написал, один калькулятор чего-то там для одного сайта на пробу на JavaScipt, но уже тогда понял, почему я не веб-программист, у меня было плохо с внешним видом. Т.к. в прикладном программировании, это не важно, важен алгоритм, а в вебке все наоборот, алгоритм является дополнением внешнего вида. Это при программировании сценариев в PHP для меня все привычно, однако в верстке все наоборот. В общем, при всей работе, у меня была один раз ситуация, когда кодер обратился ко мне за помощью и мы вместе разобрались в PHP сценарии за 2-3 часа, поправили под свои нужды и сделали работу. Потому, что кодировать — это не значит уметь программировать, это всего лишь грамотно переводить с человеческого на формальный язык логики, понятный машине. Но, как говорится на работе нужны были верстальщики, а не программисты. Такова жизнь.

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

Грамматика языка Си/Си++.

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

  1. Любая программа начинается с функции main, которая является точкой входа;
  2. Любое выражение заканчивается  «;», за исключением последних фигурных скобок «{  }» в исходном коде файла;
  3. Объявленные переменные и функции, а также классы (для Си++), обязаны быть где-то определены, при этом объявленная функция выше функции main, может быть определена ниже функции main;
  4. Фигурные скобки»{ }» обозначают блок операторов со своим сроком жизни переменных (применяется в основном в операторах ветвления и функциях, а также при объявлении структур и классов (для Си++);
  5. Переменные имеют свой срок жизни в зависимости от того, где определены/объявлены, поэтому везде могут применяться только глобальные переменные, которые имеют срок жизни в течении выполнения всей программы;
  6. Переменная перед ее применение всегда обязана быть объявлена и определена;
  7. Язык программирования Си/Си++, сам, не задает значение по умолчанию, при определении переменной, поэтому инициализацию необходимо прописывать в исходном коде файла, это также допустимо делать при объявлении переменной;
  8. Чаще всего объявление и определение переменной прописывается в одной строке, тоже самое справедливо, если к этому добавить инициализацию;
  9. Имена функций и переменных могут начинаться с «_» или латинской буквы, но не с числа, а также имеет значение регистр букв (например, «F» и «f» — это разные переменные).
  10. В языке программирования Си есть зарезервированные слова, которые не могут быть заняты исходным кодом программы, а это следующие слова: alignas, alignof, and, and_eq, asm, auto, bitand, bitor, bool, break, case, catch, char, char8_t (начиная с Си++20), char16_t, char32_t, class, compl, concept (начиная с Си++20), const, consteval (начиная с Си++20), constexpr, constinit (начиная с Си++20), const_cast, continue, co_await (начиная с Си++20), co_return (начиная с Си++20), co_yield (начиная с Си++20), decltype, default, delete, do, double, dynamic_cast, else, enum, explicit, export, extern, false, float, for, friend, goto, if, inline, int, long, mutable, namespace, new, noexcept, not, not_eq, nullptr, operator, or, or_eq, private, protected, public, register, reinterpret_cast, requires (начиная с Си++20), return, short, signed, sizeof, static, static_assert, static_cast, struct, switch, template, this, thread_local, throw, true, try, typedef, typeid, typename, union, unsigned, using, virtual, void, volatile, wchar_t, while, xor, xor_eq.

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

Этого достаточно для начинающего в Си/Си++.

Основные типы данных в Си/Си++.

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

  • char — знаковый по по умолчанию, занимает всегда только 1 байт (8 бит), мин. значение равно -128, а максимальное 127. Данный тип данных является типом данных, применяемым для работы с символами, а массивы данного типа для работы со строками.
  • short — знаковый по по умолчанию, занимает 2 байта, мин. значение равно -32768, а максимальное 32767.
  • int — знаковый по умолчанию, занимает 4 байта, мин. значение равно -2147483648, а максимальное 2147483647.
  • long — тоже самое, что и int.
  • float — всегда знаковое, дествительное число с плавающей запятой с ординарной точностью, занимает 4 байта, мин. значение равно 1.175494351 E – 38, а максимальное 3.402823466 E + 38. Это тип данных дается для информации, т.к. говорить о нем стоит уже на уровне любителя.
  • double — всегда знаковое, дествительное число с плавающей запятой с двойной точностью, занимает 8 байт, мин. значение равно 2.2250738585072014 E – 308, а максимальное 1.7976931348623158 E + 308. Это тип данных дается для информации, т.к. говорить о нем стоит уже на уровне любителя.
  • bool — логический тип данных, занимает только один байт, всегда без знаковый, мин. значение 0 (ложь, false), а максимальное  значение 1 (истина, true).
  • массив — сложный тип данных, который в исходном коде обозначается квадратными скобками «[ ]» после указанного типа данных, при этом структуры, объединения, классы и указатели тоже могут быть типами данных для массива.
  • struct — сложный тип данных (называется объединение), состоящий из вышеприведенных типов данных, хранящихся в одном месте памяти, но определяющиеся, как разные составляющие части. Это как, массив адресов памяти, т.к. данный цикл статей рассчитан на начинающего, то этот тип данных будет затрагиваться по необходимости.
  • union — сложный тип данных (называется объединение), состоящий из вышеприведенных типов данных, включая struct, при этом память выделяется одна на все части, в отличие от от типа данных struct, т.к. данный цикл статей рассчитан на начинающего, то этот тип данных будет затрагиваться по необходимости.
  • class — сложный тип данных (называется класс и его в языке Си), состоящий из всех вышеприведенных типов данных, описать в несколько слов само понятие класс не представляется возможным и выходит за рамки данного цикла статей. Это тип данных дается для информации, т.к. говорить о нем стоит уже на уровне любителя.
  • указатель — это тип данных, который ссылается на адрес в памяти выделенный для других переменных, с этим типом данных стоит быть аккуратней, т.к. данный цикл статей рассчитан на начинающего, то этот тип данных будет затрагиваться по необходимости.
  • константа — это тип данных, который после своего определения не изменяет свое значение.

Также отмечу, что есть преффиксы (слова ставящиеся слева от типа данных) «unsigned» (беззнаковое) и «signed» (знаковое), вся их разница лишь в том, что они влияют на наличие отрицательных чисел. Т.е., к примеру «unsigned char» имеет минимальное значение «0», а максимальное «255». В итоге, составляет 1 байт памяти (8 бит в двоичной системе счисления, будет 256). Тип данных «unsigned char» тоже может применяться для работы с символами (см. char, выше). А также, например, тип данных «unsigned short», имеет минимально «0» и максимальное значение «65535» (для справки, в Паскале это тип данных Word), и занимает также, как «short» всего два байта. При этом, не забывайте, что значение нуля тоже надо учитывать, когда интересуетесь вопросом а почему положительные на одну единицу меньше отрицательных чисел по пограничным значениям типов данных. И в основном, чаще всего применяются знаковые числовые переменные, так что безнаковые вам будут нужны не часто.

Как видите, типов данных в языке программирования Си/Си++ предостаточно, чтобы написать любую программу. А насчет сложности восприятия данного материала могу сказать следующие. Когда головушка ваша немного напрягается, то в этот момент в вашей голове происходит мышление, т.к. быстрый ответ ума идет только при наличии наработанных автоматизмов. А вы еще только учитесь основам и азам программирования в языке Си/Си++. Но, и я в данном случае, ограничусь в этой статье только определением, объявлением и инициализацией переменных. Дабы, у вас все из головы не повылетало, так что читайте с перерывами. А вы, как думали, легко будет ?! Легко бывает только в играх, а не в жизни. А теперь, давайте немного разберемся в объявлении, определении и инициализации переменных и в заключении покажем наглядный пример для данной статьи.

Объявление, определение, инициализация переменных в Си/Си++.

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

И чуть, не забыл упомянуть, что исходный файл читается компилятором сверху вниз и слева направо. Это даю, вам, для информации, чтобы у вас не возникало странных вопросов типа «а почему я так красиво написал код, а он не хочет работать».

А теперь, поговорим по инициализацию переменных. Как вам будет известно язык программирования Си/Си++ не определяет изначально значения по умолчанию для созданной переменной, там всегда будет какой-то мусор. И, если вы не хотите получать загадочные ошибки во время выполнения программы, тогда всегда инициализируйте переменную, присвоив ей значение. Т.е. для символа char можно присвоить пустой символ «», для чисел ноль, а для булева присвоить «ложь». Этого достаточно в качестве значения по умолчанию. Или присваивайте, нужные вам значения по вашему алгоритму программы. Но, никогда не применяйте не инициализированные переменные, иначе заработайте себе много головной боли.

И теперь, перейдем к примерам.

Наглядные примеры по данной статье.

Для начала посмотрите на этот пример, где показаны объявления и определения переменных по вышеприведенным типам данных. А также, обратите внимание на инициализацию переменных.

Как видите из примера выше, мы объявляем о переменной «i», а после функции «main» определяем переменную. В самой функции «main», мы присваиваем переменной значение «0», т.к. это первое обращение к переменной в исходном коде, то такое присвоение является инициализацией. Т.е. компилятор инициализацию не делает обязательной, в отличие от определения и/или объявления переменной.

А теперь, посмотрим как производится, и объявление, и определение, и инициализация переменной сразу.

И напоследок, посмотрим, как это выглядит для разных типов данных с учетом рамок данного цикла статей.

И, конечный пример сборки программы, представляю вашему вниманию, чтобы вам было доступно увидеть, что объявление и определение в Си/Си++ имеет значение. Т.к. следующий пример, рабочий и проверялся мной лично.

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

Как видите, файл собирается и линковка проходит нормально, даже несмотря на то, что нигде в исходном коде программы не прописывал обращение к файлу «drugoifile.cpp». А файл «makefile» является файлом программы «make», а не частью исходного кода программы.

К тому же, можете закомментировать две строчки начиная с задачи «drugoifile.o» и убрать обращение к данному объектному файлу в задаче выше и вы увидите при сборке, нормальную ошибку, которую выдаст компилятор, когда не найдет определение переменной «i».

В заключение.

Поздравляю, вас, с тем, что вы освоили первую половину грамматики языка Си/Си++, немного подумали головой, прочитали наглядные примеры, а заодно кое-что додумали сами, например как задать и инициализировать массив символов из char, отталкиваясь от наглядного примера в качестве отправной точки.

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

 

 

Loading