Бьярн Страустрап. Введение в язык Си++, Операторы

9. Операторы

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

9.1 Оператор выражение

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

9.2 Составной оператор, или блок

Составной оператор (называемый также "блок", что эквивалентно) дает возможность использовать несколько операторов в том месте, где предполагается использование одного:

     составной_оператор:
          { список_описаний opt список_операторов opt }
     список_описаний:
          описание
          описание список_описаний
     список_операторов:
          оператор
          оператор список_операторов

Если какой-либо из идентификаторов в списке_описаний был ранее описан, то внешнее описание выталкивается на время выполнения блока, и снова входит в силу по его окончании. Каждая инициализация auto или register переменных производится всякий раз при входе в голову блока. В блок делать передачу; в этом случае инициализации не выполняются. Инициализации переменных, имеющих класс памяти static (
#4.2) осуществляются только один раз в начале выполнения программы.

9.3 Условный оператор

Есть два вида условных операторов

     if ( выражение ) оператор 
     if ( выражение ) оператор else оператор

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

9.4 Оператор while

Оператор while имеет вид

     while ( выражение ) оператор

Выполнение подоператора повторяется, пока значение выражения остается ненулевым. Проверка выполняется перед каждым выполнением оператора.

9.5 Оператор do

Оператор do имеет вид

    do оператор while (выражение);

Выполнение подоператора повторяется до тех пор, пока значение выражения не станет нулем. Проверка выполняется после каждого выполнения оператора.

9.6 Оператор for

Оператор for имеет вид

     for (  выражение_1 opt  ; выражение_2  opt ; выражение_3 opt )
          оператор

Этот оператор эквивалентен следующему:
     выражение_1;
     while    (выражение_2)
                  {
                   оператор
                                выражение_3;
                   }

Первое выражение задает инициализацию цикла; второе выражение задает осуществляемую перед каждой итерацией проверку, по которой производится выход из цикла, если выражение становится нулем; третье выражение часто задает приращение, выполняемое после каждой итерации.
Каждое или все выражения могут быть опущены. Отсутствие выражения_2 делает подразумеваемое while-предложение эквивалентным while(1); остальные опущенные выражения просто пропускаются в описанном выше расширении.

9.7 Оператор switch

Оператор switch вызывает передачу управления на один из нескольких операторов в зависимости от значения выражения. Он имеет вид

     switch ( выражение ) оператор

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

где константное выражение должно иметь тот же тип что и выражение- переключатель; производятся обычные арифметические преобразования. В одном операторе switch никакие две константы, помеченные case, не могут иметь одинаковое значение. Константные выражения точно определяются в
#15.
Может также быть не более чем один префикс оператора вида
     default :

Когда выполнен оператор switch, проведено вычисление его выражения и сравнение его с каждой case константой. Если одна из констант равна значению выражения, то управление передается на выражение, следующее за подошедшим префиксом case. Если никакая case константа не соответствует выражению, и есть префикс default, то управление передается на выражение, которому он предшествует. Если нет соответствующих вариантов case и default отсутствует, то никакой из операторов в операторе switch не выполняется.
Префиксы case и default сами по себе не изменяют поток управления, который после задержки идет дальше, перескакивая через эти префиксы. Для выхода из switch см. break, #9.8.
Обычно зависящий от switch оператор является составным. В голове этого оператора могут стоять описания, но инициализации автоматических и регистровых переменных являются безрезультатными.

9.8 Оператор break

Оператор

     break ;

прекращает выполнение ближайшего охватывающего while, do, for или switch оператора; управление передается на оператор, следующий за законченным.

9.9 Оператор continue

Оператор

  continue ;

вызывает передачу управления на управляющую продолжением цикла часть наименьшего охватывающего оператора while, do или for; то есть на конец петли цикла. Точнее, в каждом из операторов

while (...)
do
 for (...)
{    
{ 
{  
  ... 
   ... 
   ... 
contin:;
contin:;
contin:; 
}    
}    
} 
while (...);

continue эквивалентно goto contin. (За contin: следует пустой оператор,
#9.13.)

9.10 Оператор return

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

      return ;
      return выражение ;

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

9.11 Оператор goto

Можно осуществлять безусловную передачу управления с помощью оператора

      goto идентификатор ;

Идентификатор должен быть меткой (
#9.12), расположенной в текущей функции.

9.12 Помеченные операторы

Перед любым оператором может стоять префикс метка, имеющий вид

 
    идентификатор :

который служит для описания идентификатора как метки. Метка используется только как объект для goto. Областью видимости метки является текущая функция, исключая любой подблок, в котором был переписан такой же идентификатор. См.
#4.1.

9.13 Пустой оператор

Пустой оператор имеет вид

 
    ;

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

9.14 Оператор delete

Оператор delete имеет вид

     delete выражение ;

Результатом выражения должен быть указатель. Объект, на который он указывает, уничтожается. Это значит, что после оператора уничтожения delete нельзя гарантировать, что объект имеет определенное значение; см.
#17. Эффект от применения delete к указателю, не полученному из операции new (#7.1), не определен. Однако, уничтожение указателя с нулевым значением безопасно.

9.15 Оператор asm

Оператор asm имеет вид

     asm ( строка) ;

Смысл оператора asm не определен. Обычно он используется для передачи информации через компилятор ассемблеру.

10. Внешние Определения

Программа на C++ состоит из последовательности внешних определений. Внешнее определение описывает идентификатор как имеющий класс памяти static и определяет его тип. Спецификатор типа (#8.2) может также быть пустым, и в этом случае принимается тип int. Область видимости внешних определений простирается до конца файла, в котором они описаны, так же, как действие описаний сохраняется до конца блока. Синтаксис внешних определений тот же, что и у описаний, за исключением того, что только на этом уровне и внутри описаний классов может быть задан код (текст программы) функции.

10.1 Определения функций

Определения функций имеют вид

 
    определение_функции:
          спецификаторы_описания описатель_функции  opt инициализатор_базового_класса opt
          тело_функции

Единственными cпецификаторами класса памяти (sc-cпецификаторами), допустимыми среди спецификаторов описания, являются extern, static, overload, inline и virtual. Описатель функции похож на описатель "функции, возвращающей ...", за исключением того, что он включает в себя имена формальных параметров определяемой функции.
Описатель функции имеет вид
     описатель_функции:
          описатель ( список_описаний_параметров )

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

Вот простой пример полного определения функции:
  int max (int a,int b,int c)
    {
      int m = (a > b) ? a : b;
      return  (m > c) ? m : c;
    }

Здесь int является спецификатором типа ; max (int a, int b, int c) является описателем функции ; { ... } - блок, задающий текст программы (код) оператора.
Поскольку в контексте выражения имя (точнее, имя как формальный параметр) считается означающим указатель на первый элемент массива, то описания формальных параметров, описанных как "массив из ...", корректируются так, чтобы читалось "указатель на ...".
Инициализатор базового класса имеет вид
     инициализатор_базового_класса:
          : ( список_параметров opt )

Он используется для задания параметров конструктора базового класса в конструкторе производного класса. Например:
  struct base { base (int); ... };
  struct derived : base { derived (int); ... };

  derived.derived (int a) : (a+1) { ... }

  derived d (10);

Конструктор базового класса вызывается для объекта d с параметром 11.

10.2 Определения внешних данных

Определения внешних данных имеют вид

     определение_данных:
          описание

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

11. Правила Области Видимости

См. #4.1.

12. Командные Строки Компилятора

Компилятор языка C++ содержит препроцессор, способный выполнять макроподстановки, условную компиляцию и включение именованных файлов. Строки, начинающиеся с #, относятся к препроцессору. Эти строки имеют независимый от остального языка синтаксис; они могут появляться в любом месте оказывать влияние, которое распространяется (независимо от области видимости) до конца исходного файла программы.
Заметьте, что определения const и inline дают альтернативы для большинства использований #define.

12.1 Замена идентификаторов

Командная строка компилятора имеет вид

     #define идент строка_символов

вызывает замену препроцессором последующих вхождений идентификатора, заданного строкой символов. Точка с запятой внутри (или в конце) строки символов является частью этой строки.
Строка вида
     #define идент( идент , ..., идент ) строка_символов

где отсутствует пробел между первым идентификатором и (, является макроопределением с параметрами. Последующие вхождения первого идентификатора с идущими за ним (, последовательностью символов, разграниченной запятыми, и ), заменяются строкой символов, заданной в определении. Каждое местоположение идентификатора, замеченного в списке параметров определения, заменяется соответствующей строкой из вызова. Фактическими параметрами вызова являются строки символов, разделенные запятыми; однако запятые в строке, заключенной в кавычки, или в круглых скобках не являются разделителями параметров. Число формальных и фактических параметров должно совпадать. Строки и символьные константы в символьной строке сканируются в поисках формальных параметров, но строки и символьные константы в остальной программе не сканируются в поисках определенных (с помощью define) идентификаторов.
В обоих случаях строка замещения еще раз сканируется в поисках других определенных идентификаторов. В обоих случаях длинное определение может быть продолжено на другой строке с помощью записи \ в конце продолжаемой строки.
Командная строка вида
     #undef идент

влечет отмену препроцессорного определения идентификатора.

12.2 Включение файлов

Командная строка компилятора вида

     #include "имя_файла"

вызывает замену этой строки полным содержимым файла имя_файла. Сначала именованный файл ищется в директории первоначального исходного файла, а затем в стандартных или заданных местах.
Альтернативный вариант, командная строка вида
      #include <имя_файла>

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

12.3 Условная компиляция

Командная строка компилятора вида

     #if выражение

проверяет, является ли результатом вычисления выражения не-ноль. Выражение должно быть константным выражением, которые обсуждаются в
#15; применительно к использованию данной директивы есть дополнительные ограничения: константное выражение не может содержать sizeof или перечислимые константы. Кроме обычных операций C может использоваться унарная операция defined. В случае применения к идентификатору она дает значение не-ноль, если этот идентификатор был ранее определен с помощью #define и после этого не было отмены определения с помощью #undef; иначе ее значение 0.
Командная строка вида
     #ifdef идент

проверяет, определен ли идентификатор в препроцессоре в данный момент; то есть, был ли он объектом командной строки #define.
Командная строка вида
     #ifndef идент

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

и далее до командной строки
     #endif

Если проверенное условие истинно, то все строки между #else и #endif игнорируются. Если проверенное условие ложно, то все строки между проверкой и #else или, в случае отсутствия #else, #endif, игнорируются.
Эти конструкции могут быть вложенными.

12.4 Управление строкой

Для помощи другим препроцессорам, генерирующим программы на C, строка вида

     #line константа "имя_файла"

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

13. Неявные Описания

См. #8.1.

14. Обзор Типов

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

14.1 Классы

Классовые объекты могут присваиваться, передаваться функциям как параметры и возвращаться функциями. Другие возможные операции, как, например, проверка равенства, могут быть определены пользователем; см. #8.5.10.

14.2 Функции

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

  typedef int (*PF) ();
  extern g (PF);
  extern f ();
  ...
  g (f);

Тогда определение g может иметь следующий вид:
  g (PF funcp)
    {
      ...
      (*funcp) ();
      ...
    }

Заметьте, что f должна быть описана явно в вызывающей программе, поскольку ее появление в g(f) не сопровождалось (.

14.3 Массивы, указатели и индексирование

Всякий раз, когда в выражении появляется идентификатор типа массива, он преобразуется в указатель на первый член массива. Из-за преобразований массивы не являются адресами. По определению операция индексирования [] интерпретируется таким образом, что E1[E2] идентично *((E1)+(E2)). В силу правил преобразования, применяемых к +, если E1 массив и E2 целое, то E1[E2] относится к E2-ому члену E1. Поэтому, несмотря на такое проявление асимметрии, индексирование является коммутативной операцией.
Это правило сообразным образом применяется в случае многомерного массива. Если E является n-мерным массивом ранга i*j*...*k, то возникающее в выражении E преобразуется в указатель на (n-1)-мерный массив ранга j*...*k. Если к этому указателю, явно или неявно, как результат индексирования, применяется операция *, ее результатом является (n-1)-мерный массив, на который указывалось, который сам тут же преобразуется в указатель.
Рассмотрим, например,

  int x[3][5];

Здесь x - массив целых размером 3*5. Когда x возникает в выражении, он преобразуется в указатель на (первый из трех) массив из 5 целых. В выражении x[i], которое эквивалентно *(x+1), x сначала преобразуется, как описано, в указатель, затем 1 преобразуется к типу x, что включает в себя умножение 1 на длину объекта, на который указывает указатель, а именно объект из 5 целых. Результаты складываются, и используется косвенная адресация для получения массива (из 5 целых), который в свою очередь преобразуется в указатель на первое из целых. Если есть еще один индекс, снова используется тот же параметр; на этот раз результат является целым.
Именно из всего этого проистекает то, что массивы в C хранятся по строкам (быстрее всего изменяется последний индекс), и что в описании первый индекс помогает определить объем памяти, поглощаемый массивом, но не играет никакой другой роли в вычислениях индекса.

14.4 Явные преобразования указателей

Определенные преобразования, включающие массивы, выполняются, но имеют зависящие от реализации аспекты. Все они задаются с помощью явной операции преобразования типов, см. ##7.2 и 8.7.
Указатель может быть преобразован к любому из целых типов, достаточно больших для его хранения. То, какой из int и long требуется, является машинно зависимым. Преобразующая функция также является машинно зависимой, но предполагается, что она не содержит сюрпризов для того, кто знает структуру адресации в машине. Подробности для некоторых конкретных машин были даны в #2.6.
Объект целого типа может быть явно преобразован в указатель. Преобразующая функция всегда превращает целое, полученное из указателя, обратно в тот же указатель, но в остальных случаях является машинно зависимой.
Указатель на один тип может быть преобразован в указатель на другой тип. Использование результирующего указателя может вызывать особые ситуации, если исходный указатель не указывает на объект, соответствующим образом выравненный в памяти. Гарантируется, что указатель на объект данного размера может быть преобразован в указатель на объект меньшего размера и обратно без изменений.
Например, программа, выделяющая память, может получать размер (в байтах) размещаемого объекта и возвращать указатель на char; это можно использовать следующим образом.

  extern void* alloc ();
  double* dp;

  dp = (double*) alloc (sizeof (double));
  *dp= 22.0 / 7.0;

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

15. Константные Выражения

В нескольких местах C++ требует выражения, вычисление которых дает константу: в качестве границы массива (#8.3), в case выражениях (#9.7), в качестве значений параметров функции, присваиваемых по умолчанию, (#8.3), и в инициализаторах (#8.6). В первом случае выражение может включать только целые константы, символьные константы, константы, описанные как имена, и sizeof выражения, возможно, связанные бинарными операциями

   + - * / % & | ^ << >> == != < > <= >= && ||

или унарными операциями
  - ~ !

или тернарными операциями
  ? :

Скобки могут использоваться для группирования, но не для вызова функций.
Большая широта допустима для остальных трех случаев использования; помимо константных выражений, обсуждавшихся выше, допускаются константы с плавающей точкой, и можно также применять унарную операцию & к внешним или статическим объектам, или к внешним или статическим массивам, индексированным константным выражением. Унарная операция & может также быть применена неявно с помощью употребления неиндексированных массивов и функций. Основное правило состоит в том, что инициализаторы должны при вычислении давать константу или адрес ранее описанного внешнего или статического объекта плюс или минус константа.

16. Соображения Мобильности

Определенные части C++ являются машинно зависимыми по своей сути. Следующий ниже список мест возможных затруднений не претендует на полноту, но может указать на основные из них.
Как показала практика, характеристики аппаратуры в чистом виде, такие, как размер слова, свойства плавающей арифметики и целого деления, не создают особых проблем. Другие аппаратные аспекты отражаются на различных программных разработках. Некоторые из них, особенно знаковое расширение (преобразование отрицательного символа в отрицательное целое) и порядок расположения байтов в слове, являются досадными помехами, за которыми надо тщательно следить. Большинство других являются всего лишь мелкими сложностями.
Число регистровых переменных, которые фактически могут быть помещены в регистры, различается от машины к машине, как и множество фактических типов. Тем не менее, все компиляторы на "своей" машине все делают правильно; избыточные или недействующие описания register игнорируются.
Некоторые сложности возникают при использовании двусмысленной манеры программирования. Писать программы, зависящие от какой-либо из этих особенностей, районе неблагоразумно.
В языке не определен порядок вычисления параметров функции. На некоторых машинах он слева направо, а на некоторых справа налево. Порядок появления некоторых побочных эффектов также недетерминирован.
Поскольку символьные константы в действительности являются объектами типа int, то могут быть допустимы многосимвольные константы. Однако конкретная реализация очень сильно зависит от машины, поскольку порядок, в котором символы присваиваются слову, различается от машины к машине. На некоторых машинах поля в слове присваиваются слева направо, на других справа налево.
Эти различия невидны для отдельных программ, не позволяющих себе каламбуров с типами (например, преобразования int указателя в char указатель и просмотр памяти, на которую указывает указатель), но должны приниматься во внимание при согласовании внешне предписанных форматов памяти.

17. Свободная Память

Операция new (#7.2) вызывает функцию

  extern void* _new (long);

для получения памяти. Параметр задает число требуемых байтов. Память будет инициализирована. Если _new не может найти требуемое количество памяти, то она возвращает ноль.
Операция delete вызывает функцию
   extern void _delete (void*);

чтобы освободить память, указанную указателем, для повторного использования. Результат вызова _delete() для указателя, который не был получен из _new(), не определен, это же относится и к повторному вызову _delete() для одного и того же указателя. Однако уничтожение с помощью delete указателя со значением ноль безвредно.
Предоставляются стандартные версии _new() и _delete(), но пользователь может применять другие, более подходящие для конкретных приложений.
Когда с помощью операции new создается классовый объект, то для получения необходимой памяти конструктор будет (неявно) использовать new. Конструктор может осуществить свое собственное резервирование памяти посредством присваивания указателю this до каких-либо использований. С помощью присваивания this значения ноль деструктор может избежать стандартной операции дерезервирования памяти для объекта его класса. Например:
  class cl
    {
      int v[10];
      cl () { this = my_own_allocator (sizeof (cl)); }
      ~cl () { my_own_deallocator (this); this = 0; }
    }

На входе в конструктор this является не-нулем, если резервирование памяти уже имело место (как это имеет место для автоматических объектов), и нулем в остальных случаях.
Если производный класс осуществляет присваивание this, то вызов конструктора (если он есть) базового класса будет иметь место после присваивания, так что конструктор базового класса ссылаться на объект посредством конструктора производного класса. Если конструктор базового класса осуществляет присваивание this, то значение также будет использоваться конструктором (если таковой есть) производного класса.

18. Краткое Изложение Синтаксиса

Мы надеемся, что эта краткая сводка синтаксиса C++ поможет пониманию. Она не является точным изложением языка.

18.1 Выражения

     выражение:
          терм
          выражение           бинарная_операция           выражение
          выражение       ?       выражение       :       выражение
          список_выражений
     терм:
          первичный
          *                                                    терм
          &                                                    терм
          -                                                    терм
          !                                                    терм
          ~                                                    терм
          ++терм
          --терм
          терм++
          терм--
          (                   имя_типа)                   выражение
          имя_простого_типа           (           список_выражений)
          sizeof                                          выражение
          sizeof             (              имя_типа              )
          new                                              имя_типа
          new ( имя_типа )
     первичный:
          id
          ::                                          идентификатор
          константа
          строка
          this
          (                       выражение                       )
          первичный[                  выражение                   ]
          первичный      (       список_выражений       opt       )
          первичный.id
          первичный->id
     id:
          идентификатор
          typedef-имя :: идентификатор
     список_выражений:
          выражение
          список_выражений, выражение
     операция:
          унарная_операция
          бинарная_операция
          специальная_операция
  Бинарные операции имеют приоритет, убывающий в указанном порядке:
     бинарная_операция:
          *                          /                            %
          +                                                       -
          <<                                                     >>
          <                                                       >
          ==                                                     !=
          &
          ^
          |
          &&
          ||
          =   +=  -=  *=  /=  %=  ^=  &=  |=  >>=  <<=
                                                  унарная_операция:
           *   &   -   ~   !   ++   --
     специальная_операция:
          ()       []
     имя_типа:
          спецификаторы_описания абстрактный_описатель
     абстрактный_описатель:
          пустой
          *                                   абстрактный_описатель
          абстрактный_описатель  (   список_описаний_параметров   )
          абстрактный_описатель  [   константное_выражение  opt   ]
          ( абстрактный_описатель )
     простое_имя_типа:
          typedef-имя
          char
          short
          int
          long
          unsigned
          float
          double
     typedef-имя:
          идентификатор

18.2 Описания

     описание:
          спецификаторы_описания  opt   список_описателей   opt   ;
          описание_имени
          asm-описание
     описание_имени:
          агрег                   идентификатор                   ;
          enum  идентификатор ;
     агрег:
          class
          struct
          union
     asm-описание:
          asm ( строка );
     спецификаторы_описания:
          спецификатор_описания спецификатор_описания opt
     спецификатор_описания:
          имя_простого_типа
          спецификатор_класса
          enum_спецификатор
          sc_спецификатор
          фнк_спецификатор
          typedef
          friend
          const
          void
     sc_спецификатор:
          auto
          extern
          register
          static
     фнк-спецификатор:
          inline
          overload
          virtual
     список_описателей:
          иниц-описатель
          иниц-описатель , список_описателей
     иниц-описатель:
          описатель инициализатор opt
     описатель:
          оп_имя
          (                       описатель                       )
          *             const             opt             описатель
          &             const             opt             описатель
          описатель      (       список_описаний_параметров       )
          описатель [ константное_выражение opt ]
     оп_имя:
          простое_оп_имя
          typedef-имя . простое_оп_имя
     простое_оп_имя:
          идентификатор
          typedef-имя
          -                                             typedef-имя
          имя_функции_операции
     имя_функции_операции:
          операция операция
     список_описаний_параметров:
          список_описаний_прм opt ... opt
     список_описаний_прм                                          :
          список_описаний_прм         ,          описание_параметра
          описание_параметра
     описание_параметра:
          спецификаторы_описания                          описатель
          спецификаторы_описания описатель = константное_выражение
     спецификатор_класса:
          заголовок_класса       {список_членов        opt        }
          заголовок_класса    {список_членов     opt    public    :
     список_членов opt }
     заголовок_класса                                             :
          агрег                  идентификатор                  opt
          агрег идентификатор opt : public opt typedef-имя
     список_членов                                                :
          описание_члена список_членов opt
     описание_члена:
          спецификаторы_описания opt описатель_члена ;
     описатель_члена:
          описатель
          идентификатор opt : константное_выражение
     инициализатор:
          =                                               выражение
          =                {                список_инициализаторов}
          =       {        список_инициализаторов,                }
          (список_выражений )
     список_инициализаторов                                       :
          выражение
          список_инициализаторов        ,    список_инициализаторов
          { список_инициализаторов }
     enum-спецификатор:
          enum идентификатор opt { enum-список }
     enum-список:
          перечислитель
          enum-список , перечислитель
     перечислитель:
          идентификатор
          идентификатор = константное_выражение

18.3 Операторы

     составной_оператор:
          { список_описаний opt список_операторов opt }
     список_описаний:
          описание
          описание список_описаний
     список_операторов:
          оператор
          оператор список_операторов
     оператор:
          выражение                                               ;
          if         (         выражение         )         оператор
          if    (    выражение    )    оператор    else    оператор
          while        (         выражение        )        оператор
          do     оператор     while     (     выражение     )     ;
          for (  выражение opt  ; выражение  opt ;  выражение opt )
               оператор
          switch        (        выражение        )        оператор
          case      константное      выражение      :      оператор
          default                    :                     оператор
          break;
          continue;
          return            выражение             opt             ;
          goto                   идентификатор                    ;
          идентификатор                 :                  оператор
          delete                    выражение                     ;
          asm           (            строка           )           ;
          ;

18.4 Внешние определения

     программа:
          внешнее_определение
          внешнее_определение программа
     внешнее_определение:
          определение_функции
          описание
     определение_функции:
          спецификаторы_описания       opt        описатель_функции
     инициализатор_базового_класса opt тело_функции
     описатель_функции:
          описатель ( список_описаний_параметров)
     тело_функции:
          составной_оператор
     инициализатор_базового_класса:
          : ( список_параметров opt )

18.5 Препроцессор

     #define идент строка_символов
     #define идент( идент,...,идент ) строка символов
     #else
     #endif
     #if выражение
     #ifdef идент
     #ifndef идент
     #include "имя_файла"
     #include <имя_файла>
     #line константа "имя_файла"
     #undef идент

19. Отличия от "старого C"

19.1 Расширения

Типы параметров функции могут быть заданы (#8.4) и будут проверяться (#7.1). Могут выполняться преобразования типов.
Для выражений с числами с плавающей точкой может использоваться плавающая арифметика одинарной точности; #6.2.
Имена функций могут быть перегружены; #8.6
Операции могут быть перегружены; #7.16, #8.5.10.
Может осуществляться inline-подстановка функций; #8.1.
Объекты данных могут быть константными (const); #8.3.
Могут быть описаны объекты ссылочного типа; #8.3, #8.6.3
Операции new и delete обеспечивают свободное хранение в памяти; #17.
Класс может обеспечивать скрытые данные (#8.5.8), гарантированную инициализацию (#8.6.2), определяемые пользователем преобразования (#8.5.6), и динамическое задание типов через использование виртуальных функций (#8.5.4).
Имя класса является именем типа; #8.5.
Любой указатель может присваиваться [указателю] void* без приведения типов; #7.14.

[Назад] [Содержание]

Copyright © CIT