Из чего состоит простейшая программа на языке программирования си
Язык программирования С (си) является одним из самых популярных и распространенных языков. Он представляет компилируемый язык программирования общего назначения со статической типизацией, разработанный в 1969—1973 годах в компании Bell Labs программистом Деннисом Ритчи (Dennis Ritchie).
Язык С нередко называют языком программирования "среднего уровня" или даже "низкого уровня", так как он сочетает элементы языков высокого уровня с функциональностью и производительностью ассемблера и работает близко к аппаратной части компьютера. В итоге мы можем манипулировать данными на низком уровне и при этом использовать высокоуровневые конструкции для управления работы программы.
Первоначально язык С предназначался для написания операционной системы Unix. Впоследствии Си стал одним из популярных языков, а его основной сферой применения стало системное программирование, в частности, создание операционных систем, драйверов, различных утилит, антивирусов и т.д. К слову сказать, Linux большей частью написан на Си. Однако только системным программированием применение данного языка не ограничивается. Данный язык можно использовать в программах любого уровня, где важны скорость работы и производительность. Так, мы можем писать с помощью Си и прикладные приложения, и даже веб-сайты (используя технологию CGI - Common Gateway Interface). Но, конечно, для создания графического интерфейса и веб-приложений, как правило, выбираются более подходящие инструменты и технологии, но тем не менее круг использования Си довольно широк. Это в немалой степени определило популярность языка. Например, в известном рейтинге языков программирования TIOBE язык С долгое время уверенно удерживает второе место.
Несмотря на большие возможности язык Си одновременно довольно прост. Он не содержит много конструкций, библиотек, его легко осваивать и изучать. Поэтому нередко его выбирают в качестве языка для изучения в целом программированию.
Си является компилируемым языком, а это значит, что компилятор транслирует исходный код на Си в исполняемый файл, который содержит набор машинных инструкций. Но разные платформы имеют свои особенности, поэтому скомпилированные программы нельзя просто перенести с одной платформы на другую и там уже запустить. Однако на уровне исходного кода программы на Си обладают переносимостью, а наличие компиляторов, библиотек и инструментов разработки почти под все распространенные платформы позволяет компилировать один и тот же исходный код на Си в приложения под эти платформы.
Основные особенности Си
Универсальность - один и тот же код может быть скомпилирован на почти каждой платформе (при наличии для нее компилятора)
Высокая скорость выполнения
Компактность, небольшой размер выходных скомпилированных файлов
Основные этапы развития
В 1978 году Брайан Керниган и Деннис Ритчи опубликовали первое издание своего знаменитого труда "Язык программирования Си". Долгое время эта книга служила неформальной спецификацией языка Си. Однако быстрое распространение Си привело к необходимости выработки общих стандартов. И в 1983 году организация ANSI (Американский национальный институт стандартов) создала комитет для разработки спецификации Си. А в 1989 году спецификация была утверждена. Эту версию языка принято называть ANSI C или C89. В 1990 году спецификация ANSI C была немного дополнена Международной организацией по стандартизации (ISO). Новый стандарт стал называться ISO/IEC 9899:1990 или сокращенно С90.
В конце 1990-х годов стандарт подвергся пересмотру, что привело к выходу нового стандарта в 1999 году, который принято называть C99 (официальное название ISO 9899:1999).
И в декабре 2011 был опубликован новый и последний на данный момент стандарт для языка Си - С11 (официальное название ISO/IEC 9899:2011).
Программа на языке Си состоит из одной или более подпрограмм, называемых функциями .
Язык Си является блочно-структурированным. Каждый блок заключается в фигурные скобки <> .
Основным блоком в программе консольного приложения на языке Си является главная функция, имеющая имя main() .
Каждое действие в языке Си заканчивается символом «точка с запятой» — ; . В качестве действия может выступать вызов функции или осуществление некоторых операций.
Имя функции — это коллективное имя группы описаний и операторов,
заключенных в блок (фигурные скобки). За именем функции в круглых скобках указываются параметры функции.
Комментарии в языке Си
В языке Си для комментариев используются символы
/* — начало комментария;
*/ — конец комментария.
Вся последовательность, заключенная между этими символами, является комментарием.
Это удобно для написания многострочных комментариев:
Многострочные комментарии также удобно использовать при отладке для сокрытия от выполнения части кода.
В дополнение к этому, для написания коротких комментариев могут использоваться символы // . При этом комментарием является все, что расположено после символов // и до конца строки:
Главная функция
При выполнении консольного приложения, написанного на языке Си, операционная система компьютера передаёт управление функции с именем main() . Функцию main() нельзя вызывать из других функций программы, она является управляющей.
Следующие за именем функции круглые скобки предназначены для указания параметров (аргументов), которые передаются в функцию при обращении к ней. В данном случае операционная система не передаёт в функцию main() никаких аргументов, поэтому список аргументов в круглых скобках пустой.
Главную функцию можно записать по-разному:
Перед именем функции указывается тип возвращаемого значения. При обращении к главной функции значение возвращается операционной системе. Последняя запись не будет возвращать значения. Однако void main() — не совсем корректная запись, так как сообщает компилятору, что функция main() не возвращает никакого значения.
При этом запись int main() сообщает компилятору о возвращении целочисленного значения, которое необходимо операционной системе и сообщает ей о том, что программа завершилась корректно. Если же это значение не возвращено, то операционная система понимает, что программа завершилась в аварийном режиме.
Для возврата целочисленного значения перед завершением функции добавляется строка
В фигурные скобки заключены описания и операторы.
В общем случае программа может содержать несколько функций. Каждая функция имеет список передаваемых в нее параметров, указанный в круглых скобках, и набор операций, заключенных в блок, ограниченный фигурными скобками.
Результат работы программы:
Теперь попробуем написать текст на русском языке.
Результат работы программы:
Проблема русского языка в консольных приложениях заключается в том, что консоль и редактор кода Microsoft Visual Studio поддерживают разные кодовые страницы. Для того, чтобы увидеть русские символы в консоли необходимо поменять кодовую страницу в консоли, чтобы она соответствовала кодовой странице редактора (1251). С этой целью вызывается функция system( "chcp 1251" ) с соответствующей командной строкой. Прототип функции system() содержится в библиотеке .
При этом текст программы будет выглядеть следующим образом:
Результат работы программы:
В соответствии с вариантом реализовать программу обработки массива на ал-горитмическом языке С. Длина массива не более 20 элементов. Элементы массива вводятся вручную или генерируются случайным образом. Размер массива задается пользователем с клавиатуры. 7. Дана последовательность натуральных чисел
Язык программирования Си — универсальный язык программирования, который завоевал особую популярность у программистов, благодаря сочетанию возможностей языков программирования высокого и низкого уровней. Большинство программистов предпочитают использовать язык Си для серьезных разработок потому, что их привлекают такие особенности языка, как свобода выражения мыслей, мобильность и чрезвычайная доступность.
Язык Си даёт возможность программисту осуществлять непосредственный доступ к ячейкам памяти и регистрам компьютера, требуя при этом знания особенностей функционирования ЭВМ. В этом Си схож с языком низкого уровня — ассемблером, хотя на самом деле он представляет собой гораздо более мощное средство решения трудных задач и создания сложных программных систем.
Язык Си был разработан американцем Деннисом Ритчи в исследовательском центре Computer Science Research Center of Bell Laboratories корпорации AT&T в 1972 г. Первоначальная реализация Си была выполнена на ЭВМ PDP-11 фирмы DEC для создания операционной системы UNIX. Позже он был перенесен в среду многих операционных систем и существует независимо от любой из них. Программы, написанные на языке Си, как правило, можно перенести в любую другую операционную систему или на другой компьютер либо с минимальными изменениями, либо вовсе без них.
Язык Си также используется при составлении программ для микроконтроллеров.
Добрый день Елена, если у вас есть к этому интерес, могли бы вы разобрать структуру Zip файла на си, точнее: как из него можно вытащить информацию о самих файлах. Спасибо.
С ходу не подскажу, а времени нет разбираться с этим вопросом. Если кто-нибудь ответит, буду признательна.
char * reverse( const char * text)
< char * NewTxt = "kjkjhk" ;
char sumbol;
int poz=strlen(text)-1;
while (poz>=0)
< //
sumbol=text[poz];
NewTxt+=sumbol;
printf( " Char - %s,%d\n" ,sumbol,poz);
poz--;
>
return NewTxt;
>
int main()
printf( " dfskhlkjhdfg \n" );
char * reversed;
reversed = reverse( "Hello world!" );
printf( "%s\n" , reversed);
// "!DLROW OLLEH"
>
char * reverse( const char * text)
char * NewTxt = ( char *)malloc(strlen(text) + 1);
char sumbol;
int poz = strlen(text) - 1;
int j = 0;
while (poz >= 0)
< //
sumbol = text[poz];
NewTxt[j++]= sumbol;
printf( " Char - %c,%d\n" , sumbol, poz);
poz--;
>
NewTxt[j++] = '\0';
return NewTxt;
>
Я думаю, многим знакома эта шикарная песня Jonathan Coulton'а, и эта жизненная ситуация, когда «Rob say Code Monkey very diligent», но «his output stink» и «his code not 'functional' or 'elegant'».
Сегодня мы поговорим о некоторых полезных практиках, которые я вынес из глубин системного программирования на Си. Поехали.
Пункты будут располагаться от самых фундаментальных и очевидных (ориентированных на новичков в языке Си) до самых специфичных, но полезных. Если чувствуете, что вы это знаете — листайте дальше.
Практика I: Соблюдайте единый Code Style и фундаментальные принципы «хорошего тона»
Функция принимает в качестве аргумента переменную INPUT, парсит её в массив IncomingValues и возвращает result_to_return? Отставить быдлокод!
То, что в первую очередь выдает новичка — несоблюдение единого стиля написания кода в рамках конкретного приложения. Следом идет игнорирование правил «хорошего тона».
Вот несколько самых распространенных рекомендаций к оформлению кода на Си:
-
Названия макросов и макрофункций пишутся капсом, слова в названиях отделяются друг от друга нижним подчеркиванием.
Вообще, этот пункт спорный. Мне доводилось видеть проекты, где имена переменных и функций пишутся в camelCase и PascalCase соответственно.
UPD: Спасибо пользователю fogree за то, что он обнаружил косяк с перепутанным PascalCase и camelCase.
Кстати, рекомендую взять за привычку писать код так, чтобы одна функция делала только одну вещь. Именно это должно отразиться в названии.
То же можно сказать и про переменные — никаких a, b, c — в названии должен быть отражен смысл (итераторы не в счет). Самодокументируемый код — очень хорошая практика.
Как правило, можно выбрать между стилем написания названия: PascalCase и under_score, тут уже зависит от вас.
По возможности инициализируйте переменные при объявлении. Численные с помощью нуля, указатели — NULL:
Ну оставили мы переменные неинициализированными, и что?
А то. Если смотреть их (до инициализации) в отладке (в том же gdb), там будет лежать мусор. Это нередко сбивает с толку (особенно, если мусор «похож на правду»). Про указатели я вообще молчу.
Не надо комментировать каждую строчку кода — если вы пишите самодокументируемый код, большая часть его будет простой для понимания.
Оптимальное решение — писать описания функций, если из аргументов и названия сложно понять весь её функционал. Для переменных — правила те же, в пояснении нуждаются только какие-то нелинейный вещи, где одного названия мало.
На самом деле, в вопросе документации у вас есть полная свобода действий — надо лишь следить, чтобы комментариев было не много, но достаточно, чтобы человек, видящий ваш код в первый раз, не задавал вопросов.
Если вы постоянно работаете с трекерами (вроде RedMine), то при внесении правок в код можно указать номер задачи, в рамках которой эти правки были внесены. Если у кого-то при просмотре кода возникнет вопрос а-ля «Зачем тут этот функционал?», ему не придется далеко ходить. В нашей компании еще пишут фамилию программиста, чтобы если что знать, к кому идти с расспросами.
Практика 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
Если у вас игра, в которой есть файлы, отвечающие за движок/звук/графику — будет удобно раскидать их по папкам. Звук, графику и движок — отдельно друг от друга.
Практика III: Используйте враппер-функции для обработки возвращаемых значений
Враппер-функция (функция-обертка) в языке Си используется как функция со встроенной обработкой возвращаемого значения. Как правило, в случае ошибки в работе функции, возвращаемое значение вам об этом скажет, а глобальная переменная errno примет в себя код ошибки.
Если вы пишите в системе (а сейчас большинство программ на си — именно системные программы), то нет ничего хуже, чем «немое» падение программы. По-хорошему, она должна красиво завершиться, напоследок сказав, что именно пошло не по плану.
Но обрабатывать значение от каждой функции в коде — такое себе решение. Тут же упадет читаемость, и объем (+ избыточность) кода увеличится в пару раз.
Тут и помогают врапперы. Рассмотрим первый пример — безопасный код без врапперов:
Ну, такое себе, не правда ли? Теперь попробуем с обертками.
Как видите, код по-прежнему безопасен (не будет «немого» падения), но теперь его функциональная часть гораздо компактнее.Я называю обертки именем самих функций, но с большой буквы. Каждый сам волен выбрать, как их оформлять.
В использовании оберток есть небольшой минус, который, если захотеть, можно решить костылем. А что это за минус — можете предположить в комментариях :)
Практика IV: Используйте keywords как профи
Хорошее знание keywords никогда не будет лишним. Да, и без них ваш код будет работать, не спорю. Но когда речь зайдет об экономии места, быстродействии и оптимизации — это именно то, чего вам будет не хватать.
К тому же, мало кто может похвастаться хорошим знанием ключевых слов, поэтому их повседневное использование может быть шансом блеснуть знаниями перед коллегами. Однако, не надо бездумно пихать кейворды всюду, куда только можно. Вот вам несколько фич:
-
register — дает компилятору указание по возможности хранить переменную в регистрах процессора, а не в оперативной памяти. Использование модификатора register при объявлении переменной-итератора цикла с небольшим телом может повысить скорость работы всего цикла в несколько раз.
Практика 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)
Листинг выше частично вошел сюда, т.к. подходит для еще одного примера.
Посмотрите: описание упакованных структур идет перед перечислением, отражающим размеры этих структур.
(файл networking/udhcp/common.h)
Если в этом же файле мы спустимся чуть пониже, то увидим, что объявления функций работы с опциями так же находятся в одном месте:(файл networking/udhcp/common.h)
В udhcpc есть огромное количество опций, которые по умолчанию не используются. Каждая опция соответствует макросу, который закрывает ее номер.
Если бы они были раскомментированы — то это были бы макросы, которые не всплывают нигде в коде. Человек, который спросил бы «а зачем все эти опции?» искал бы ответ очень долго. И не нашел бы.
Как итог — у нас получилось подобие интерфейса, где комментарием закрыты те опции, методы для которых еще не реализованы.
(файл networking/udhcp/common.h)
Довольно сложный для восприятия аспект, требующий пояснения. В двух словах: если у вас есть функция, которая внутри проекта вызывается с n комбинациями различных параметров (где n — небольшое число), причем каждая комбинация вызывается по нескольку раз, имеет смысл сделать для каждой комбинации отдельную функцию, вызывающую внутри себя целевую функцию, но уже с нужными параметрами. Например. У нас есть функция, отправляющая пакеты на определенный порт и адрес:
(файл networking/udhcp/packet.c)
Но dhcp не всегда нуждается в отправке пакета на один IP адрес. В основном используется широковещательная (BROADCAST) рассылка.(файл networking/udhcp/dhcpc.c)
Заключение
Пиши код так, чтобы те, кто будет его сопровождать любили тебя, а не ненавидели. Сложная гибкая реализация гораздо лучше простого костыля.Описывай интерфейсы доступа, комментируй проблемные моменты. Не делай констант, от изменения которых придется переписывать весь код. Не допускай утечек памяти. Следи за безопасностью и отказоустойчивостью кода.
Первая программа, которую мы рассмотрим, — это «Hello World» — программа, которая выведет на экран строку текста «Hello, World!» («Здравствуй, мир!») и закончит своё выполнение.
Аббревиатуру stdio можно перевести как стандартный ввод-вывод (англ. standard input/output ). Буква «h» после точки означает заголовок (англ. header ). В заголовках (которые как правило представлены отдельными заголовочными файлами) обычно объявляются предоставляемые соответствующими им библиотеками функции, типы данных, константы и определения препроцессора. [3]
Далее идёт определение функции main . Оно начинается с объявления:
что значит: «функция с именем main , которая возвращает целое число (число типа int от англ. integer ) и у которой нет аргументов (void) »
В качестве варианта, стандарт допускает определение функции main как функции двух аргументов ( int argc и char *argv[] — имена, разумеется, могут быть произвольными), что используется для получения доступа к аргументам командной строки из программы. В данном случае, эта возможность не требуется, поэтому функция определена как безаргументная (что также явно разрешено стандартом.) [4]
Английское слово void можно перевести как «пустота». Далее открываются фигурные скобки и идёт тело функции, в конце фигурные скобки закрываются. Функция main — главная функция программы, именно с нее начинается выполнение программы.
Тело функции, в свою очередь, определяет последовательность действий, выполняемых данной функцией — логику функции. Наша функция выполняет одно единственное действие:
Это действие, в свою очередь, есть вызов функции puts стандартной библиотеки. [5] В результате выполнения этого вызова, на стандартный вывод (которым, скорее всего, окажется экран или окно на экране) печатается строка Hello, world! .
Затем идёт команда return 0; , которая завершает выполнение функции с возвратом значения 0, определяемого стандартом (для функции main ) как код успешного завершения. [6] [7]
Этот вариант отличается использованием функций printf (вместо puts ) и getchar .
В отличие от функции puts , выводящей переданную в качестве аргумента символьную строку, первый и обязательный аргумент функции printf определяет формат вывода. [8]
В общем случае, формат состоит из произвольного текста (не включающего символ % ) «перемешанного» с указателями преобразований (предваряемыми символом % ). В данном случае, однако, эта возможность не используется и никаких преобразований не выполняется.
Обратите внимание на появившуюся в строковой константе комбинацию \n — она включает в выводимую строку управляющий код (или управляющий символ) перевода (также разрыва или завершения) строки. В отличие от функции puts , всегда добавляющей этот код к выводимой строке, printf требует явного его указания.
Действующая редакция стандарта определяет семь таких комбинаций, причем все они записываются с помощью символа обратной косой черты \ (см. ASCII коды символов). [9]
Обратим внимание и на следующее новшество:
Окружение, в котором запускается программа, как правило можно настроить так, что вывод программы будет оставаться на экране после ее выполнения неограниченно долго. Проще всего это обеспечить вызывая программу из командного интерпретатора (который, в свою очередь, может быть запущен в окне эмулятора терминала) или (в зависимости от системы) окна Cmd.exe.
Однако, при запуске непосредственно из графического окружения, отведенное программе окно может закрыться сразу же после завершения программы. Функция getchar [10] ожидает ввод пользователя, тем самым «откладывая» завершение программы ( return ). Какие именно действия могут прервать это ожидание — зависит от системы, однако можно надеяться, что нажатие клавиши ⏎ Enter завершит эту функцию в любой системе.
В некоторых руководствах для этой же цели предлагается функция getch . Однако, эта функция (в отличие от getchar ) не является стандартной и, к тому же, зависима от платформы. Так, в некоторых системах использование getch требует включения файла curses.h и предшествующего вызова функции initscr . В других системах, однако, getch может быть объявлена в conio.h , и выполнение initscr — не требуется.
Наконец, рассмотрим следующий, выходящий за рамки стандарта, вариант этой программы.
Использование заголовка windows.h может произвести впечатление приемлемости этого варианта кода в рамках только лишь одной конкретной системы. Однако, для использованного здесь интерфейса существует и независимая свободная реализация — Wine, — позволяющая собрать и выполнить данный вариант на таких системах, как, например, GNU/Linux, FreeBSD, Solaris и Mac OS X.
Читайте также:
- include/