Уроки > Структура программ на языке Си
Мы не продаём знания — мы ими делимся!

Если для вас это ценно, то поддержите проект.

Ваша поддержка — топливо для новых уроков!

Структура программ на языке Си

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

  1. IDE Pelles C
  2. IDE Code::Blocks

Важно!
Прежде чем продолжить чтение, установите IDE!

Все программы, написанные на языке Си, имеют общую структуру, которую мы и обсудим в этом уроке. В этом нам поможет программа «Hello, World!», написанная при установке IDE (она же Листинг 1 в этом уроке).

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

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

Рис.1 Начальное состояние карты со структурой программы

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

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

Листинг 1. Программа «Hello, World»

#include <stdio.h>

int main(void)
{
        printf("Hello, World!\n");
  
        return 0;
}

Листинг 2.

// комментарий

int main(void)
{
        int a, b, c;
        
        a = 5;
        b = 10;
        c = a + b;
        
        return 0;
}

Листинг 3.

#include <stdio.h>

int main(void)
{
        FILE *fp;

        fp = fopen("input.txt", "w");
        fprintf(fp, "This is Sparta!");
        fclose(fp);

        return 0;
}

Не торопитесь смотреть продолжение урока и правильный ответ на эту задачу. Для начала подумайте и попробуйте ответить самостоятельно.

Картинка с мемом Think about it.

Итак, ответ: Во всех программах выше присутствует следующая конструкция:

Листинг 4. Главная функция любой программы на языке Си — функция main.

int main(void)
{

        return 0;
}

Что же это за конструкция? Это определение функции с именем main (с английского "главный", "основной"). Такая функция должа быть в каждой программе, которая написана на языке Си. Большая программа или маленькая, компьютерная игра или программа «Hello, World!», написана вами или Брайаном Керниганом — если программа написана на языке Си, то в ней есть функция main. Это главная функция нашей программы. Когда мы запускаем программу, то можно думать, что запускаем функцию main этой программы.

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

Карта "Структура программ на языке Си." На рисунок добавлен блок с функцией main

Рис.2 Карта "Структура программ на языке Си." Функция main

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

Давайте я расскажу немного о функции main да и о функциях вообще.

Каждая функция в программе имеет своё имя функции. В нашем случае это main. Перед именем функции написано int. Это сокращение от слова integer (с английского "целое", "целое число").

Это слово, записанное перед именем функции, означает, что когда функция main завершит свою работу, она должна вернуть вызывающей программе (в нашем случае это операционная система) какое-нибудь целое число. Обычно, для функции main это число нуль, которое оповещает операционную систему: "Мол, всё хорошо. Происшествий не случилось."

Случалось ли вам видеть сообщения об ошибках на экране своего компьютера? Обычно там написано что-то вроде "Программа аварийно завершена ... бла-бла-бла... Код -314." Вот это примерно тоже самое. Разница в том, что когда случаются проблемы операционная система нас об этом оповещает, а когда всё хорошо она нас лишний раз не беспокоит.

После имени функции в круглых скобках записано слово void (от английского "пустота", "пустой"). Вообще, когда в программе записывют определение функции, в круглых скобках после её имени указывают параметры функции — данные, которые нужны функции для работы. В нашем случае, когда в скобках написано void, это означает, что параметров у функции нет. Другими словами, функции main для работы не требуется никаких внешних параметров. Мы ещё будем говорить обо всём этом подробне, а пока просто запомним, что слово void вместо параметров функции обозначает, что для работы данной функции никаких параметров не требуется.

Далее идёт текст, заключённый в фигурные скобки. Фигурные скобки { и } используются в языке Си для группировки инструкций, чтобы несколько инструкций воспринимались компилятором как нечто целое.

Инструкция (statement) — это, скажем так, базовый строительный блок нашей программы. Каждая инструкция определяет какое-то действие, которое должно быть выполнено программой. Инструкции выполняются одна за другой в том порядке, в котором они записаны в программе.

Важно! Обратите внимание, каждая инструкция языка Си заканчивается символом ; (точкой с запятой).

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

Но вернёмся к фигурным скобкам в определении функции main. В нашем случае мы объединяем две инструкции, давая понять компилятору, что они обе относятся к функции main.

Про первую инструкцию мы скажем чуть ниже, а пока посмотрим на вторую инструкцию.

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

Вы можете спросить, а почему именно нуль? А чёрт его знает! Просто так договорились. Можно, в принципе, возвращать какое-нибудь другое целое число, например 100, или -236. Лишь бы оно было целым числом. Вы же ещё помните про int? Поэтому и целое.

Вот мы и разобрались с функцией main. Ещё один момент. То, что записано в фигурных скобках обычно называют телом функции (по сути, пошаговое описание того, что должна делать функция). А первая часть, та, что перед фигурными скобками, называется заголовок функции.

Вернёмся теперь к тексту программы «Hello, World!».

Листинг 5. Программа «Hello, World»

#include <stdio.h> // ???

int main(void) // заголовок функции main
{       // тело функции main (начало)
        printf("Hello, World!\n");  // ???

        return 0;  // инструкция для возврата значения 0 из функции
}  // тело функции main (конец)

Кое-что в этой программе нам теперь уже понятно. Остаётся пояснить только две строки, которые я отметил ???. Пойдём по порядку.

Листинг 6. Директива include

#include <stdio.h>

Данная строчка — это указание компилятору, которое буквально означает «подключи файл stdio.h». Такие указания, начинающиеся с символа #, называются директивами. Во время компиляции вместо этой строчки будет подставлено содержимое файла stdio.h. Пара слов об этом файле. stdio.h (от англ. STanDard Input Output) — это один из стандартных заголовочных файлов. В этом файле объявлены различные стандартные функции, связанные с вводом и выводом данных.

Возникают резонные вопросы: "И зачем писать эту строчку?", "Зачем нам вообще понадобилось вставлять сюда этот файл?". Это нужно для того, чтобы в своей программе мы могли использовать стандартную функцию вывода на экран printf.

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

Так же и компилятор. Когда он встречает какую-нибудь функцию, он ищет её определение (заголовок и тело функции) в начале программы (с самого начала и до момента её использования в программе). Так вот, функция printf объявлена в файле stdio.h. Поэтому мы и подключаем его. И вот когда мы его подключим с помощью директивы #include, компилятор сможет найти функцию printf, иначе он выдаст ошибку.

Важно!
Обратите внимание, директивы не являются инструкциями, а поэтому у них в конце не ставят ;.

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

Рис.3 Карта "Структура программ на языке Си." Блок подключения заголовочных файлов

Продолжим разбираться с нашей программой.

Листинг 7. Функция printf

printf("Hello, World!\n");

В этой строке записана инструкция, которая вызывает стандартную функцию вывода printf. В обыденной речи для краткости такую фразу сокращают до "вызов функции printf".

Функция printf, как мы уже знаем, предназначена для вывода данных. В данном простейшем случае мы вызываем её с одним аргументом — строкой, заключённой в двойные кавычки "Hello, world!\n". Её работа будет заключаться в том, что она просто выведет переданную ей строку на экран терминала (консоли) без изменений.

Последовательность символов в двойных кавычках называют строкой или в официальной книжной терминологии строковым литералом (string literal). В русскоязычных книжках иногда ещё используют название строковая константа.

Важно!
Из-за того, что все эти различные названия для строк выделены красным цветом, может сложиться впечатление будто бы их очень важно запомнить/заучить. Это не так! Запоминать их не нужно. Более того, они настолько причудливы и странны, что единожды их прочитав, скорее всего вы их уже никогда не забудете.

Постойте, а что это за \n? На экране во время запуска программы никакого \n не было. Зачем, спрашивается, тогда мы тут это написали?

На самом деле этот символ там тоже был, просто вы его не заметили. Последовательность \n — используют для записи символа переноса строки. Это как в Wordе нажать клавишу Enter.

Обобщим информацию про вызов функций. Чтобы вызвать какую-нибудь функцию, необходимо написать её имя и указать передаваемые ей аргументы в круглых скобках. Ну и написать ;, конечно.

Количество передаваемых аргументов зависит от количества параметров, указанных в определении функции. Например, выше мы вызвали функцию printf и передали ей один аргумент — строку, которую необходимо вывести на экран. Если функции не нужны аргументы, то в круглых скобках ничего писать не нужно, но сами круглые скобки всё же надо написать.

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

Давайте приведу наглядную аналогию. Представьте себе заголовок функции, как бланк бумажной анкеты (у всех, кто сдаёт ЕГЭ прошу прощения за возможные флешбеки).

Допустим, есть анкета с пустыми графами "Имя" и "Фамилия". Пустые графы — это параметры (то, что от нас ожидают). А когда мы берём такой бланк и туда вписываем свои имя и фамилию (заполняем анкету или в нашей аналогии вызываем функцию) — то наши имя и фамилия будут уже аргументами (то, что от нас фактически получают). Если бы другой человек взял такой же бланк и вписал бы туда свои имя и фамилию, то параметры остались те же самые (имя и фамилия), а вот аргументы (конкретные значения) изменились.

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

Листинг 8. Стандартная заготовка для программ на языке Си.

#include <stdio.h>

int main(void)
{
  
        return 0;
}

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

Например, программа «Hello, World!» могла бы выглядеть вот так:

Листинг 9. Программа «Hello, World!» в стиле В.В. Маяковского

#include <stdio.h> 
int
        main
                (
void
        )
                {
printf
        (
                "Hello, World!\n"
)
        ;
                return
0
        ;
                }

или так:

Листинг 10. Программа «Hello, World!», записанная в две строки

#include<stdio.h> 
int main(void){printf("Hello, World!\n");return 0;}

и даже вот так:

Листинг 11. Программа «Hello, World!», записанная ромбиком

#include<stdio.h> 
       int 
      main(
     void) {
    printf  (
"Hello, World!\n"
    );return
       0;}

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

Ну вот вроде бы и всё. На этом первый урок можно считать законченным. Подведём итог.

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

Программа-открытка на 8 марта

UPD от 27.05.2025
Это новая версия первого урока. Добавлены всякие подробности про функциии. Пожалуйста, если какое-то место было трудно понять, напишите об этом комментариях ниже.

Практика

  • Попробуйте добавить управляющий символ \n после слова Hello. Посмотрите, как изменится ситуация на экране.

  • Попробуйте добавить в программу ещё один-два вызова функции printf.

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

  • Попробуйте убрать из программы директиву #include <stdio.h>. Как на это отреагирует ваш компилятор?

  • Попробуйте заменить 0 в инструкции return 0; на какое-нибудь другое целое число. Как изменится поведение программы?

  • Проверьте, что программы из Листингов 9-11 рабоают.

Исследовательские задачи для хакеров:

  • В уроке было сказано, что пробелы и переносы почти никогда не влияют на программу. Но иногда они всё-таки необходимы. Теперь, добавляя и убирая пробелы и/или переносы строк, попробуйте "сломать" программу "Hello, World!". Найденные варианты добавляйте в комментарии к этому шагу, если варианта с таким способом сломать программу ещё не было.

Шаблон для добавления кода программы:

```c
тут
пишите
ваш код
```
  • Попробуйте заменить return 0; на return;. Вообще говоря, так делать нельзя и должна появляться ошибка при компиляции. Но, говорят, что некоторые компиляторы на такой код не ругаются. Если ваш компилятор как раз из таких, напишите об этом в комментариях (с указанием используемого компилятора и/или IDE).

Дополнительные материалы

  1. Переживаете из-за того, получится ли у вас программировать или нет? Тогда посмотрите следующее видео.




    Зеркало на RuTube, на VK.Видео

  2. Исходный код программы-открытки к 8 марта

  3. Творчество учеников Anonymous 255391790 и Костя Левин

    Консольный флаг США

  4. Посмотрите фильм компании СТВ — }{отт@бь)ч. C 1:10:32 до 1:11:58 Гена рассказывает старику Хоттабычу о роботах, программах-роботах и как они выглядят изнутри.



  5. Существует некоторая путанница в переводе английских слов statement и operator на русский язык. Насколько я могу судить, она берёт своё начало из первого официального русского перевода K&R на русский язык. Поэтому сейчас в некоторых источниках statement переводят на русский как оператор, а operatorоперация.
    Кмк, самое главное — понимать о чём идёт речь в каждом конкретном случае. Но чтобы в рамках курса привести терминологию к единому формату, я постараюсь использовать указанные в уроке варианты перевода, т.е. "инструкция" для statement и "оператор" для operator. Но при этом буду упоминать оригинальное английское слово и делать соответствующие оговорки про другие возможные названия.

  6. Начиная со стандарта С99 в функции main можно вовсе не добавлять инструкцию return 0;. Предполагается, что если мы дошли до } в функции main, то подразумевается, что всё было хорошо и ошибок не произошло. Но я всё-таки рекомендую вам добавлять эту строку в свои программы, а не полагаться на компилятор.