Содержание

Команды

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

Помеченные команды

Любой команде может предшествовать метка. Метки, обычно, являются целью для команды 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 без выражения.