Команды описывают то, какие действия должны выполняться. Команды могут располагаться только внутри функций. Команды исполняются последовательно друг за другом, за исключением случаев, описанных ниже.
Любой команде может предшествовать метка. Метки, обычно, являются
целью для команды goto
, и поэтому встречаются редко.
Метка — идентификатор, за которым следует двоеточие. Метки не влияют на ход исполнения программы. Метка, встреченная во время исполнения программы, игнорируется.
Следующий пример демонстрирует команду с меткой:
xyz: i = 0;
Метки могут предшествовать только командам. Отсюда следует, что они могут присутствовать только внутри функций.
Внутри данной функции метка может быть определена только раз.
Идентификатор, использованный для метки может быть таким же, как идентификатор объекта, функции или тега, и таким же, как метка в другой функции. Пространство имён меток — отдельное от идентификаторов не-меток, при этом каждая функция имеет своё пространство.
Составная команда является набором команд, сгруппированных внутри фигурных скобок. Она может иметь свои декларации объектов с инициализаторами или без и может иметь, а может и не иметь любые команды. Составная команда также называется блоком.
Общей формой составной команды является
{ список-деклараций список-команд }
где список-деклараций — список из нуля или более деклараций объектов для использования в блоке. Список-команд — список нуля или более команд, которые будут исполняться после входа в блок.
Любые декларации объектов с автоматической продолжительностью хранения и инициализаторами вычисляются в том порядке, в каком они встречаются.
Объект, декларированный внутри блока с ключевым словом extern
,
не может быть инициализирован, так как память для такого объекта
резервируется в другом месте.
Объект, декларированный в блоке без ключевого слова extern
, не
может быть декларирован ещё раз внутри того же блока, но может быть
декларирован в блоке, который содержится внутри данного блока.
Команда, которая является выражением, такая как присвоение значения при
помощи оператора присваивания, вычисляется как выражение без побочных
эффектов. Результат выражения отбрасывается. Это отбрасывание можно сделать,
явно приведя выражение к типу void
.
Например, команда
count = 3;
состоит из выражения count = 3
, которое имеет в качестве
побочного эффекта присвоение значения 3
объекту
count
. Результатом выражения является 3
, того же
типа, что и тип count
. Этот результат далее не используется.
Другой пример. Команда
(void) memcpy( dest, src, len );
говорит, что независимо от того, какой результат вернёт memcpy
,
он игнорируется. Однако также верно, но более просто, будет написать
memcpy( dest, src, len );
Как часть стиля программирования приведение выражения к типу
void
должно делаться, когда результат выражения будет
использоваться далее. В этом случае приведение к void
показывает, что результат специально отброшен, и тут нет ошибки в результате
оплошности.
Пустая команда, состоящая только из точки с запятой, ничего не делает. Это полезно, когда нужно поставить метку прямо перед закрывающей фигурной скобкой или для обозначения пустого блока, как в циклической команде. Рассмотрим следующие примеры пустых команд:
{ gets( buffer ); while( *buffer++ != '\0' ) ; /* ... */ endblk: ; }
Циклическая команда while
проходит символы, указываемые
buffer
пока не встретится нулевой. Тело цикла — пустое,
так как всю работу делает управляющее выражение. endblk:
декларирует метку прямо перед закрывающей }
, которую может
использовать goto
для выхода из блока.
Команда выбора вычисляет выражение, называемое управляющим выражением, а затем, основываясь на результате, делает выбор из набора команд. После чего они исполняются.
if
if ( выражение ) команда
или
if ( выражение ) команда else команда
В обоих случаях тип управляющего выражения (в скобках) имеет скалярный тип (не структура, объединение или массив). Если вычисление выражения приводит к ненулевому значению, то выполняется первая команда.
Во второй форме, else
исполняется, если результат вычисления
управляющего выражения равен нулю.
Каждая команда может быть составной. Например,
if( delay > 5 ) { printf( "Ждали слишком долго.\n" ); ok = FALSE; } else { ok = TRUE; }
В классическом случае висячего else
, else
связывается с ближайшим if
, у которого ещё нет
else
. Например,
if( x > 0 ) if( y > 0 ) printf( "x > 0 && y > 0\n" ); else printf( "x <= 0\n" );
будет печатать x <= 0
, когда x > 0
— истинно,
а y > 0
— ложно, потому что else
связан со
вторым if
, а не с первым. Чтобы исправить этот пример, нужно
сделать следующее:
if( x > 0 ) { if( y > 0 ) printf( "x > 0 && y > 0\n" ); } else printf( "x <= 0\n" );
Следующий пример демонстрирует, почему использовать фигурные скобки для точного указания субъекта управляющей структуры лучше, чем полагаться на то, одиночная команда также является составной. Лучшим способом написать предыдущий пример является
if( x > 0 ) { if( y > 0 ) { printf( "x > 0 && y > 0\n" ); } } else { printf( "x <= 0\n" ); }
где все субъекты управляющей структуры содержаться внутри скобок, не оставляя
сомнений о своём назначении. Висячий else
не получится, если
всегда использовать фигурные скобки.
Если к командам между if
и else
перейдёт управление
с помощью метки, то команды после else
не будут исполняться.
Однако переход внутрь блока является плохой программистской практикой, так
как это делает программу трудной для понимания.
switch
switch( выражение ) команда
Обычно команда является составной командой (блоком). Внутри команды
находятся метки case
и, возможно, метка default
в
следующем виде:
case константное-выражение : команда default : команда
Управляющее выражение и константное выражение каждой метки case
должны быть целого типа. Два константных выражения у case
не
могут иметь одинаковые значения. Метка default
в блоке команды
switch
может использоваться только один раз.
Сначала вычисляется управляющее выражение, затем результат подвергается
целочисленному расширению. Если расширенное значение выражения совпадёт с
какой-либо меткой case
, приведённой к этому типу, то управление
передаётся команде, следующей за этой меткой. В противном случае, управление
передаётся команде после метки default
(если такая есть). Если
метки default
нет, то в блоке switch
не выполняется
ни одна команда.
Когда исполняются команды внутри блока switch
и встретится ещё
один case
или default
, то они игнорируются, и
исполнение продолжается командой следующей за меткой. Чтобы прервать
выполнение команд блока switch
, можно использовать команду
break
.
Следующий пример
int i; for( i = 1; i <= 8; i++ ) { printf( "%d ", i ); switch( i ) { case 2: case 4: printf( "больше, чем 5 " ); case 6: case 8: printf( "чётное\n" ); break; default: printf( "нечётное\n" ); } }
выведет следующее:
1 нечётное 2 меньше, чем 5 чётное 3 нечётное 4 меньше, чем 5 чётное 5 нечётное 6 чётное 7 нечётное 8 чётное
Циклические команды управляют циклами. Существуют три формы циклических
команд: while
, do
/while
и
for
.
Управляющее выражение должно быть скалярного типа. Тело цикла (часто составная команда или блок) повторно исполняется, пока управляющее выражение не станет равным нулю.
while
while ( выражение ) команда
Вычисление управляющего выражения происходит перед каждым исполнением тела цикла (команды). Если вычисление выражения приводит к нулю в первый же раз, то тело цикла не выполнится ни разу.
Команда может быть составной.
Например,
char * ptr; /* ... */ while( *ptr != '\0' ) { if( *ptr == '.' ) break; ++ptr; }Цикл просматривает символы, на которые указывает
ptr
, пока не
встретится нулевой символ или точка. Если начальное значение ptr
указывает на нулевой символ, то никакая часть цикла не будет исполнена, и
ptr
будет по-прежнему указывать на нулевой символ.
do
do команда while ( выражение );
Вычисление управляющего выражения происходит после каждого выполнения тела цикла (команды). Если в первый раз результат вычисления выражения — нуль, то тело цикла исполняется точно один раз.
Команда может быть составной.
Например,
char * ptr; char * endptr; /* ... */ endptr = ptr + strlen( ptr ); do { --endptr; } while( endptr >= ptr && *endptr == ' ' );
В этом примере цикл прекратится, когда, начиная справа, endptr
встретит непробельный символ, или, когда endptr
вернётся к
началу строки. Если встретится непробельный символ, то endptr
будет указывать на символ слева от этого символа.
for
Команда
for ( выр1; выр2; выр3 ) команда
эквивалентна
выр1;
while (
выр2 ) {
команда
выр3;
}
Разница заключается в том, что команда continue
передаст
управление команде выр3, а не в конец тела цикла.
Выр1 — инициализирующее выражение и может быть опущено.
Выр2 — управляющее выражение, и указывает вычисления, которые производятся перед каждым повторением тела цикла. Если вычисление приводит к нулю, то тело цикла не исполняется, и управление передаётся команде следующей за телом цикла. Если выр2 опущено, то на его место подставляется ненулевое значение. В этом случае команды внутри цикла сами должны организовывать выход из него.
Выр3 операцию, которая производится после каждой итерации. Часто такой операцией будет увеличение счётчика. Выр3 можно опустить.
Команда может быть составной.
Например,
char charvec[256]; int count; for( count = 0; count <= 255; count++ ) { charvec[count] = count; }
Этот пример инициализирует символьный массив charvec
значениями
от 0
до 255
.
Далее — ещё несколько примеров команд for
.
for( ;; ) команда;
Все команды в теле цикла будут исполняться, пока не выполнится команда
break
или goto
, которая передаст управление во вне
цикла, либо не выполнится команда return
, которая приведёт к
выходу из функции. Это, иногда, называется бесконечным циклом.
for( i = 0; i <= 100; ++i ) команда;
Объекту i
присваивается начальное значение нуль, а после каждой
итерации значение объекта увеличивается на единицу. Цикл выполняется 101 раз,
i
будет последовательно иметь значения 0, 1, 2 ... 99, 100, а
после прекращения цикла i
будет равно 101.
for( ; *bufptr != '\0'; ++bufptr ) команда;
Объект bufptr
уже инициализирован, и цикл будет продолжаться,
пока bufptr
не указывает на нулевой символ. После каждого
исполнения цикла, bufptr
увеличивается, чтобы указывать на
следующий символ.
Команды перехода заставляют передать управление в определённое место
программы без исполнения промежуточных команд. Существуют четыре команды
перехода: goto
, continue
, break
и
return
.
goto
goto идентификатор;
Идентификатор — метка где-то в данной функции (включая любой блок внутри функции). Команда, следующая за меткой, будет следующей, которая будет исполняться.
Помните, что частое использование команды goto
может сбить с
толку. Очень просто сделать из кода «спагетти», которое с трудом будет
понимать даже тот, кто его написал. Рекомендуется использовать команду самое
большее для выхода из блока и никогда для входа в него.
continue
continue;
Команда continue
может использоваться только внутри тела цикла.
Она вызывает переход к команде продолжения самого внутреннего цикла (в конец
тела цикла).
В команде while
переход произойдёт обратно к while
.
В команде do
— вниз до while
.
В команде for
переход произойдёт к закрывающей фигурной скобке
составной команды цикла. Затем, до передачи управления в начало цикла, будет
вычислено третье выражение в круглых скобках, в котором часто происходит
увеличение или уменьшение.
break
break;
Команда break
может использоваться только в теле цикла или в
команде switch
.
В цикле break
передаст управление следующей за телом цикла
команде.
В команде switch
break
передаст управление
следующей за switch
команде. Если цикл или switch
,
которые содержат break
, находятся внутри другого цикла или
switch
, то прекратится только самый внутренний цикл или
switch
. Команду goto
можно использовать для выхода
из более, чем одного цикла или switch
.
return
return;
или
return
выражение;
У второй формы есть популярный вариант
return( выражение );
Команда return
вызывает прекращение выполнения текущей функции и
передачу управления в место, откуда она вызывалась. Функция может иметь любое
количество команд return
.
Если функция декларирована как возвращающая void
(не
возвращающая никакого значения), то ни одна команда return
в
функции не может возвращать какое-либо значение.
Если функция декларирована как возвращающая значение отличное от типа
void
, то любая команда return
с выражением будет
его вычислять и конвертировать в возвращаемый тип. Это значение будет
возвращено функцией. Если команда return
выполняется без
выражения, а вызывающая команда использует возвращённое функцией значение, то
поведение не определено, так как никакого значения не возвращено. Возможно
будет использовано произвольное значение.
Достижение закрывающей фигурной скобки }
, которая заканчивает
функцию эквивалентно выполнению команды return
без выражения.