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

Лексемы

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

Из допустимых символов формируются лексемы — предопределённые константы, идентификаторы и знаки операций. В свою очередь, лексемы являются частью выражений; а из выражений составляются инструкции и операторы.

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

Символ # не может быть частью никакой лексемы и используется в препроцессоре.

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

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

Встроенный идентификатор только один — __func__, который определяется как константная строка, неявно объявляемая в каждой функции и содержащая её название.

Литеральные константы 
Специально оформленные литералы в Си принято называть константами. Литеральные константы могут быть целочисленными, вещественными, символьными и строковыми.

Целые числа по умолчанию задаются в десятичной системе счисления. Если указан префикс 0x, то — в шестнадцатеричной системе. Префикс в виде цифры 0 указывает, что число задаётся в восьмеричной системе. Суффикс определяет минимальный размер типа константы, а также определяет, является ли число знаковым или беззнаковым. В качестве итогового типа берётся такой минимально возможный, в котором данную константу можно представить.

Константы вещественных чисел по умолчанию имеют тип double. При указании суффикса f константе назначается тип float, а при указании l или L — long double. Константа будет считаться вещественной, если в ней присутствует знак точки, либо буквы p или P в случае шестнадцатеричной записи с префиксом 0x. Десятичная запись может включать экспоненту, указываемую после букв e или E. В случае шестнадцатеричной записи экспонента указывается после букв p или P в обязательном порядке, что отличает вещественные шестнадцатеричные константы от целых. В шестнадцатеричном виде экспонента является степенью числа 2.

Символьные константы заключаются в одинарные кавычки ('), а префикс задаёт как тип данных символьной константы, так и кодировку, в которой символ будет представлен. В Си символьная константа без префикса имеет тип int, в отличие от C++, в котором символьной константе соответствует char.

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

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

Именованные константы 

В языке Си для задания констант принято использовать макроопределения, объявляемые с помощью директивы препроцессора #define:

 #define имя константы [значение]

Введённая таким образом константа будет действовать в области своей видимости, начиная с момента задания константы и до конца программного кода или до тех пор, пока действие заданной константы не будет отменено директивой #undef:
 #undef имя константы

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

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

Также можно создавать константы в виде переменных с квалификатором const, но в отличие от двух других способов, такие константы потребляют память, на них можно получить указатель, и их нельзя использовать на этапе компиляции:

 для указания размера битовых полей,
 для задания размера массива (за исключением массивов переменной длины),
 для задания значения элемента перечисления,
 в качестве значения оператора case.

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

Зарезервированные идентификаторы 
Помимо ключевых слов стандарт языка определяет зарезервированные идентификаторы, использование которых может привести к несовместимости с будущими версиями стандарта. Зарезервированными являются все, за исключением ключевых, слова, начинающиеся со знака подчёркивания (_), после которого идёт либо заглавная буква (A—Z), либо другой знак подчёркивания. В стандартах С99 и С11 часть таких идентификаторов была использована под новые ключевые слова языка.

В области видимости файла зарезервировано использование любых имён, начинающихся со знака подчёркивания (_), то есть со знака подчёркивания допускается именовать типы, константы и переменные, объявленные в рамках какого-либо блока инструкций, например, внутри функций.

Также зарезервированными идентификаторами являются все макросы стандартной библиотеки и связываемые на этапе линковки названия из неё.

Использование зарезервированных идентификаторов в программах стандарт определяет как неопределённое поведение. Попытка отмены любого стандартного макроса через #undef также повлечёт за собой неопределённое поведение.

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

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

Следующий стандарт, C99, ввёл ещё один способ оформления комментариев: комментарием считается текст, начинающийся с последовательности символов // и заканчивающийся концом строки.

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

Операторы 

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

Унарные операторы 
Унарные операторы выполняют операцию над единственным аргументом и имеют следующий формат операции:

 [оператор] [операнд]

Операции постфиксного инкремента и декремента имеют обратный формат:

 [операнд] [оператор]

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

Бинарные операторы 
Бинарные операторы располагаются между двумя аргументами и осуществляют операцию над ними:

 [операнд] [оператор] [операнд]

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

Тернарные операторы 
В Си имеется единственный тернарный оператор — сокращённый условный оператор, который имеет следующий вид:
 [условие] ? [выражение1] : [выражение2]

Сокращённый условный оператор имеет три операнда:
 [условие] — логическое условие, которое проверяется на истинность,
 [выражение1] — выражение, значение которого возвращается в качестве результата выполнения операции, если условие истинно;
 [выражение2] — выражение, значение которого возвращается в качестве результата выполнения операции, если условие ложно.

Оператором в данном случае является сочетание знаков ? и :.

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

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

Приоритет выполнения операций 
Приоритет операций определяется стандартом и задаёт порядок, в котором операции будут производиться. Операции в Си выполняются в соответствии приведённой ниже таблице приоритетов.

Приоритеты операций в Си не всегда себя оправдывают и иногда приводят к интуитивно трудно предсказуемым результатам. Например, поскольку унарные операторы имеют ассоциативность справа налево, то вычисление выражения *p++ приведёт к увеличению указателя с последующим разыменовыванием (*(p++)), а не к увеличению значения по указателю ((*p)++). Поэтому в случае сложных для понимания ситуаций рекомендуется явно группировать выражения с помощью скобок.

Другой важной особенностью языка Си является то, что вычисление значений аргументов, передаваемых в вызов функции не является последовательным, то есть запятая, разделяющая аргументы, не соответствует последовательному вычислению из таблицы приоритетов. В следующем примере вызовы функций, указываемые в качестве аргументов другой функции, могут идти в произвольном порядке:int x;
x = compute(get_arg1(), get_arg2()); // первым может быть вызов get_arg2()Также нельзя полагаться на приоритет операций в случае наличия побочных эффектов, появляющихся в ходе вычисления выражения, поскольку это будет приводить к неопределённому поведению.

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

Полными выражениями считаются:

 инициализатор, не являющийся частью составного литерала;
 обособленное выражение;
 выражение, указанное в качестве условия условного оператора (if) или оператора выбора (switch);
 выражение, указанное в качестве условия цикла while с предусловием или с постусловием;
 каждый из параметров цикла for, если таковой указан;
 выражение оператора return, если таковое указано.

В следующем примере переменная изменяется трижды между точками следования, что приводит к неопределённому результату:int i = 1;    // Описатель - первая точка следования, полное выражение - вторая
i += ++i + 1; // Полное выражение - третья точка следования
printf("%d\n", i); // Может быть выведено как 4, так и 5Другие простые примеры неопределённого поведения, которого необходимо избегать:i = i++ + 1; // неопределённое поведение
i = ++i + 1; // тоже неопределённое поведение

printf("%d, %d\n", --i, ++i); // неопределённое поведение
printf("%d, %d\n", ++i, ++i); // тоже неопределённое поведение

printf("%d, %d\n", i = 0, i = 1); // неопределённое поведение
printf("%d, %d\n", i = 0, i = 0); // тоже неопределённое поведение

a[i] = i++; // неопределённое поведение
a[i++] = i; // тоже неопределённое поведение

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

Пустой оператор 
Самая простая языковая конструкция — это пустое выражение, называемое пустым оператором:
 ;

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

Инструкции 
Инструкция — это некое элементарное действие:
 (выражение);
Действие этого оператора заключается в выполнении указанного в теле оператора выражения.

Несколько идущих подряд инструкций образуют последовательность инструкций.

Блок инструкций 
Инструкции могут быть сгруппированы в специальные блоки следующего вида:

{
 (последовательность инструкций)
},

Блок инструкций, также иногда называемый составным оператором, ограничивается левой фигурной скобкой ({) в начале и правой фигурной скобкой (}) — в конце.

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

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

Самая простая форма оператора if
 if((условие)) (оператор)
 (следующий оператор)
Оператор if работает следующим образом:
 если выполнено условие, указанное в скобках, то выполняется первый оператор, и затем выполняется оператор, указанный после оператора if.
 если условие, указанное в скобках, не выполнено, то сразу выполняется оператор, указанный после оператора if.

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

 if((условие)) ;

Более сложная форма оператора if содержит ключевое слово else:
 if((условие)) (оператор)
 else (альтернативный оператор)
 (следующий оператор)

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

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

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

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

В языке Си предусмотрено два варианта циклов с предусловием: while и for.

 while(условие) [тело цикла]
 for( блок инициализации;условие;оператор) [тело цикла],

Цикл for ещё называется параметрическим, он эквивалентен следующему блоку операторов:

 [блок инициализации]
 while(условие)
 {
 [тело цикла]
 [оператор]
 }

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

У цикла do-while условие указывается после тела цикла:

 do [тело цикла] while( условие)

Условие цикла — это логическое выражение. Однако неявное приведение типов позволяет использовать в качестве условия цикла арифметическое выражение. Это позволяет организовать так называемый «бесконечный цикл»:

 while(1);

То же самое можно сделать и с применением оператора for:
 for(;;);

На практике такие бесконечные циклы обычно используются совместно с операторами break, goto или return, которые осуществляют прерывание работы цикла разными способами.

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

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

 goto [метка],

Метка — это некоторый идентификатор, передаёт управление тому оператору, который помечен в программе указанной меткой:

 [метка] : [оператор]

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

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

Другие операторы перехода связаны с циклами и позволяют прервать выполнения тела цикла:
 оператор break немедленно прерывает выполнение тела цикла, и происходит передача управления на оператор, следующий непосредственно сразу за циклом;
 оператор continue прерывает выполнение текущей итерации цикла и инициирует попытку перехода к следующей.

Оператор break также может прерывать работу оператора switch, поэтому внутри оператора switch, запущенного в цикле, оператор break не сможет прервать работу цикла. Указанный в теле цикла, он прерывает работу ближайшего вложенного цикла.

Оператор continue может быть использован только внутри операторов do, while и for. У циклов while и do-while оператор continue вызывает проверку условия цикла, а в случае цикла for — исполнение оператора, заданного в 3-м параметре цикла, перед проверкой условия продолжения цикла.

Оператор возврата из функции 
Оператор return прерывает выполнение той функции, в которой использован. Если функция не должна возвращать значение, то используется вызов без возвращаемого значения:
 return;

Если функция должна возвращать какое-либо значение, то после оператора указывается возвращаемое значения:
 return[значение];

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

Переменные 
При объявлении переменной указывается её тип и название, а также может указываться начальное значение:

 [описатель] [имя] ;

или

 [описатель] [имя] = [инициализатор] ;,

где

 [описатель] — тип переменной и предшествующие типу необязательные модификаторы;
 [имя] — имя переменной;
 [инициализатор] — начальное значение переменной, присваиваемое при её создании.

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

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

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

Для того, чтобы задать функцию в Си, необходимо её объявить:
 сообщить имя (идентификатор) функции,
 перечислить входные параметры (аргументы)
 и указать тип возвращаемого значения.
Также необходимо привести определение функции, которое содержит блок операторов, реализующих поведение функции.

Отсутствие объявления определённой функции является ошибкой, если функция используется вне области видимости определения, что, в зависимости от реализации, приводит к выдаче сообщений или предупреждений.

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

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

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

Объявление функции 
Объявление функции имеет следующий формат:
 [описатель] [имя] ( [список] );,

где

 [описатель] — описатель типа возвращаемого функцией значения;
 [имя] — имя функции (уникальный идентификатор функции);
 [список] — список (формальных) параметров функции или void при их отсутствии.
Признаком объявления функции является символ «;», таким образом, объявление функции — это инструкция.

В самом простом случае [описатель] содержит указание на конкретный тип возвращаемого значения. Функция, которая не должна возвращать никакого значения, объявляется как имеющая тип void.

При необходимости в описателе могут присутствовать модификаторы, задаваемые с помощью ключевых слов:
 extern указывает на то, что определение функции находится в другом модуле;
 static задаёт статическую функцию, которая может быть использована только в текущем модуле.

Список параметров функции задаёт сигнатуру функции.

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

Определение функции 
Определение функции имеет следующий формат:
 [описатель] [имя] ( [список] ) [тело]
Где [описатель], [имя] и [список] — те же, что и в объявлении, а [тело] — это составной оператор, который представляет собою конкретную реализацию функции. Компилятор различает определения одноимённых функций по их сигнатуре, и таким образом (по сигнатуре) устанавливается связь между определением и соответствующим ему объявлением.

Тело функции имеет следующий вид:
 {
 [последовательность операторов]
 return ([возвращаемое значение]) ;
 }

Возврат из функции осуществляется с помощью оператора return, у которого либо указывается возвращаемое значение, либо не указывается, в зависимости от возвращаемого функцией типа данных. В редких случаях функция может быть помечена как не делающая возврат с помощью макроса noreturn из заголовочного файла stdnoreturn.h, в таких случаях оператор return не требуется. Например, подобным образом можно помечать функции, безусловно вызывающие внутри себя abort().

Вызов функции 
Вызов функции заключается в выполнении следующих действий:
 сохранение точки вызова в стеке;
 автоматическое выделение памяти под переменные, соответствующие формальным параметрам функции;
 инициализация переменных значениями переменных (фактических параметров функции), переданных в функцию при её вызове, а также инициализация тех переменных, для которых в объявлении функции указаны значения по умолчанию, но для которых при вызове не были указаны соответствующие им фактические параметры;
 передача управления в тело функции.
В зависимости от реализации, компилятор либо строго следит за тем, чтобы тип фактического параметра совпадал с типом формального параметра, либо, если существует такая возможность, осуществляет неявное преобразование типа, что, очевидно, приводит к побочным эффектам.

Если в функцию передаётся переменная, то при вызове функции создаётся её копия (в стеке выделяется память и копируется значение). Например, передача структуры в функцию вызовет копирование всей структуры целиком. Если же передаётся указатель на структуру, то копируется только значение указателя. Передача в функцию массива также вызывает лишь копирование указателя на его первый элемент. При этом для явного обозначения того, что на вход функции принимается адрес начала массива, а не указатель на единичную переменную, вместо объявления указателя после названия переменной можно поставить квадратные скобки, например:void example_func(int array[]); // array — указатель на первый элемент массива типа int

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

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

Примечания

Комментарии

Источники

Литература 
 
 
 

Язык программирования Си