Свойства языка си которые могут быть источниками возможных ошибок программирования

Автор: David Chisnall.
Оригинальное название: «Writing Insecure C».
Оригинал статьи: Part 1, Part 2, Part 3 (в данной редакции все три части объединены).
Перевод на русский: n0xi0uzz.
Оригинал перевода: http://netsago.org/ru/docs/1/14/
Коррекция и форматирование: Клуб программистов «Весельчак У».
Публикуется с разрешения автора перевода.

  • Введение.
  • Проверка ошибок.
  • Начальные значения.
  • Проблемы целых.
  • Проблемы с памятью.
  • Буферы и строки.
  • Когда все идет не так.
  • Отказ от привилегий.
  • Разгоняя ядро.
  • Заключение.

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

В этой статье рассмотрены стандартные примеры ошибок в коде Си и то, как их и избежать.

Множество современных языков программирования включают в себя некоторый механизм по обработке исключений. Вообще, исключения — это плохая идея. Они усложняют управление ходом программы, и у них есть много недостатков, от которых страдали программы с использованием GOTO перед рассветом структурного программирования. Программист должен проектировать код программы так, чтобы не пришлось обрабатывать исключения. Тем не менее, у исключений есть одно важное преимущество: вы не можете их игнорировать. В частности, код на Java часто засорен блоками try:catch, которые ничего не делают, кроме отбрасывания ошибок, но даже в этом случае механизм исключений преследует цель: это заставляет программиста помнить о том, что он небезопасно обрабатывает условия ошибок.

В Си большинство функций возвращают неправильное значение, когда произошла какая-либо ошибка. Обычно это делается двумя способами. Множество функций возвращают код ошибки ноль в случае успеха. Функции, возвращающие указатели, возвращают правильное значение в случае успеха или ноль в противном случае. Эта ситуация может немного смущать, так как ноль означает и успешное выполнение одних функций, и ошибку в других. Можно возвращать нулевой указатель, его вы не сможете легко проигнорировать, так как получите ошибку сегментации, как только попытаетесь разыменовать его. Этот подход действительно опасен только в тех функциях, которые почти никогда не завершаются с ошибкой, в других ошибка может быть обнаружена во время тестирования и исправлена. Стандартный пример функции, которая почти никогда не завершается с ошибкой — malloc(), наряду со связанными с ней функциями, вроде calloc(). В спецификации Си указано, что malloc() должна вернуть NULL в случае, если памяти недостаточно, чтобы удовлетворить запрос. Linux не полностью следует этому правилу, он возвращает NULL, если в системе недостаточно виртуального адресного пространства, чтобы выполнить выделение памяти, но в случае недостатка памяти Linux по-прежнему выделяет пространство адресов, а затем возвращает ошибку, если вы пытаетесь использовать эту память. Как бы то ни было, исходя из того, что у вас есть подходящая реализация Си, необходимо проверять значения, которые возвращает malloc.

В большинстве случаев вы не сможете сделать ничего разумного, если malloc завершится с ошибкой. Даже код, восстанавливающий ошибку, обычно нуждается в выделении памяти. Вы можете попробовать выделять эту память, когда программа запускается (не забудьте проверить, что вы можете получать к ней доступ). В качестве альтернативы, вы можете использовать что-нибудь вроде этого макроса:

#define MALLOC(x,y) do { y = malloc(x); if (!y) abort(1); } while(0)

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

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

При объявлении локальной переменной компилятор выполняет это как простой инкремент верхнего регистра стека. Начальное значение переменной будет таким, каким последняя функция его установила, использовав эту часть памяти стека. Здесь существуют две потенциальные проблемы. Первая в том, что невозможно предсказать поведение не инициализированной переменной. Вторая в том, что этого может и не быть. В общем, использование значения перед инициализацией приведет к неопределенному состоянию вашей программы. В худшем случае, это может привести к утечке информации. Например, если вы пишете некоторый код, который использует неинициализированную переменную после вызова функции шифрования, ваше «случайное» значение неопределенной переменной может содержать некоторую часть ключа шифрования. Если ваш код запускает скрипты, которые могут читать информацию из стека, это может быть серьезной уязвимостью.

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

int a = 42;

А затем вы решаете, что вам необходимо условие для инициализации, поэтому вы копируете и вставляете его в выражение if:

if ({некоторое условие})
{
   int a = 42;
}

Ой, вы же забыли объявить переменную за пределами блока if, поэтому вы делаете это позже и даете ей значение по умолчанию:

int a = 0;

if ({некоторое условие})
{
   int a = 42;
}

Теперь у вас есть код, который компилируется и запускается. Большинство времени (когда не встречается условие), он будет работать. Но код внутри фигурных скобок будет «холостым». Он определяет новую переменную под названием «a» и присваивает ей значение 42. Как только блок закончится, эта переменная выйдет из области видимости и останется старая «a», до сих пор со значением 0.

Более незаметный вариант может возникнуть при опечатке в инициализации, когда вы выполняете что-то, вроде этого:

int value = value + 12;

когда на самом деле ожидаете выполнение этого:

int value = othervalue + 12;

Как только компилятор распарсит int value, переменная станет валидной и будет находиться в области видимости. Вы можете считать её значение, добавить 12 к нему и присвоить результат ей же. К несчастью для вас, value, которую вы считываете, не определена. Теперь вам придется присвоить value неопределенное значение. Если вы считываете неосторожно, вы можете подумать, что проинициализировали её, хотя вы этого и не сделали. Компилятор с оптимизацией удалит +12, поскольку неопределенность плюс 12 есть неопределенность, и это будет эквивалентно следующему:

int value;

Если в вашем компиляторе есть выдача предупреждений о том, что не было проинициализировано, он должен выявить эти проблемы. К сожалению, выключение этих предупреждений происходит довольно часто, потому как в Си нет способа указать выходные параметры. Вот почему следующие вещи встречаются относительно часто:

int a;

call_some_function(&a);

Это может и не быть ошибкой, если «a» используется для возвращения некоторого дополнительного значения из функции. В других случаях, функция будет считывать значение «a», которое неопределенно. В компиляторе Си нет способа узнать, правильно ли используется «a», поэтому она может использоваться, будучи неопределенной.

Если вы используете высокоуровневые языки программирования, поддержка числовых переменных в Си может показаться мучительно примитивной. То же самое может быть, если раньше вы писали на ассемблере, так как Си также не открывает программисту условных кодов современных ЦПУ. В высокоуровневых языках числа обычно условно-точные, причем точность определена так, как вам надо. В Си есть набор целых типов. Наиболее часто используемый — это int, который должен соответствовать машинному слову. На компьютерах, на которых я учил Си, оно было длиной в 16 бит. Теперь он обычно размером в 32 бита даже на архитектурах, где машинное слово имеет длину 64 бита, так как большое количество написанного кода подразумевает, что он всегда имеет длину 32 бита. Одна из наиболее частых причин странного поведения — попытка хранить значение указателя в int. На обычных 32-битных платформах этот метод работает хорошо. На некоторых 64-битных он работает, а на некоторых — нет. Стандарт C99 определяет новый целый тип, intptr_t, который гарантированно имеет достаточный размер, чтобы хранить указатель. Если вы планируете хранить указатель в int, вы всегда должны использовать intptr_t.

Указатель — один из других, слегка сбивающих с толку, моментов. Си определяет два типа указателей, один указывает на код, другой указывает на данные. Указатель на функцию не имеет гарантированно такой же размер, что и указатель, указывающий на данные. На множестве встроенных платформ часто используются 16-битные указатели для кода и 32-битные для данных. Преобразование типа в void* указателя на функцию приведет к тому, что некоторые данные будут отброшены.

Другим основным видом целого в Си является char, short и long. В C99 также определен long long. Ни один из них не имеет фиксированного размера, но все они имеют минимальные размеры (технически у них есть минимальный набор значений, который они могут хранить, не предоставляя гарантий по внутреннему формату). Short должен быть, по крайней мере, 16 бит, long как минимум 32, а lon long как минимум 64. Если вам необходимо минимальное количество точности, выберите один из них, и вы сможете получить дополнительное пространство к тому, что запросили, в зависимости от архитектуры.

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

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

a = b + c;

Так как вы храните результат в «a», вы можете предположить, что сложение будет выполнено, каким бы типом ни была «a». По факту, оно будет выполнено с типом «b» и «c». Это имеет смысл тогда, когда вы думаете, что лучше иметь значение (b + c) в зависимости от чего-то, чем «b» и «c» сами по себе. Вы можете думать, что тип этого выражения будет типом «b». Стандарт C99 определяет набор правил для определения типа, но в основном он будет представлять собой тот тип, который больше, «b» или «c». Например, если «a» — char, а «b» — int, тип выражения будет int. Поразительно часто встречается ошибка, когда делают что-то вроде такого:

long long a;
long b; // = чему-то
long c; // = чему-то
a = b * c;

Размерность «a» как минимум 64 бита, «b» и «c» как минимум 32 бита. Так как у вас есть только гарантия того, что «b» и «c» по 32 бита, вы не должны помещать нечто большее в них, даже если ваш компилятор или платформа имеет long в 64 бита. Когда вы умножаете их, вы получаете результат, подходящий для 64-битного int и вы храните его в чем-то, что подходит для 64-битного int. Звучит удобно? Так и есть, кроме того факта, что результат искажается до 32 бит прямо перед присваиванием. Вот правильный способ сделать это:

a = (long long)b * c;

Это расширяет «b» до 64 бит (или больше, в зависимости от реализации). Расширение типа гарантирует, что «c» имеет тип такого же размера, что и «b», так что она также расширена. Тогда умножение происходит как умножение двух long long с 32 или более первыми нулями, а результат (long long) хранится в переменной типа long long. В основном, вы можете избежать сюрпризов путем прямого преобразования типов к чему-то с достаточной точностью перед выполнением операции. Убедитесь, что отсутствие знака выполняется для всех переменных. Это очень частая причина ошибок, так как вы неожиданно теряете первый бит при преобразовании большого беззнакового значения к его знаковому эквиваленту. Более неуловимая ошибка возникает от переполнения int. Она особенно часто возникает при использовании malloc, так как стандартный шаблон написания —

malloc(i * sizeof(x))

. Если у взломщика есть влияние на «i», он может попытаться выполнить это переполнение. Для очень больших значений «i» это даст результат гораздо меньший, чем «i», что приведет к проблеме. Вызов malloc будет успешным, но когда вы попытаетесь использовать массив, полученный в результате, только первые несколько значений будут валидными. Взломщик может вынудить вас переписать другие данные. Простым путем избегания этого вида уязвимости может быть использование calloc() вместо malloc() (конечно, в надежде, что реализация calloc в вашей системе производит проверку границ сама, а не просто делает malloc() и memset() для количество*размер байт).

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

void * xrealloc(void *ptr, size_t nmemb, size_t size)
{
    void *new_ptr;
    size_t new_size = nmemb * size;

    if (SIZE_T_MAX / nmemb < size)
        return NULL;

    return realloc(ptr, new_size);
}

Этот тест довольно прост. SIZE_T_MAX — это максимальное значение, которое может иметь size_t. Когда вы делите на указанное количество элементов, вы получаете максимальный размер, который может быть без переполнения. Если этот размер меньше, чем требуемое пространство, возникает переполнение, поэтому вы возвращаете NULL. realloc возвращает NULL в случае ошибки, так что вам всегда следует проверять возвращаемое значение realloc на валидность. К сожалению, это является наиболее частой причиной утечек памяти (которые, в свою очередь, являются причиной атак DDoS). Если realloc() возвращает NULL, исходный указатель по-прежнему является валидным. Часто разработчики забывают этот принцип и просто делают что-то вроде этого:

ptr = realloc(ptr, newsize);

Когда realloc() возвращает NULL, вы теряете вашу ссылку на предыдущее выделение памяти. FreeBSD предоставляет удобную функцию, reallocf(), которая эквивалентна следующей:

void *reallocf(void* ptr, size_t size)
{
    void *newptr = realloc(ptr, size);
    if (NULL == newptr)
    {
        free(ptr);
    }

    return newptr;
}

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

Модель организации памяти в Си делит память на два участка — куча и стек. Память в куче выделяется и освобождается вручную. Память в стеке является контекстной — она выделяется автоматически при входе в блок и освобождается, когда блок завершается. Эта методика приводит к проблемам при передаче данных назад к вызываемой функции. Для структур очевидным решением является просто вернуть структуру. Когда она скомпилирована, вызывающая функция может выделить структуру и передать указатель к вызываемой функции, которая копирует данные в свое пространство. Тем не менее, это приводит к большому количеству копий. Что ещё хуже, это не работает для всех динамически выделяемых данных. Рассмотрим что-нибудь, вроде sprintf(). Этот аналог printf() пишет в буфер вместо стандартного вывода. Проблема sprintf в том, что вызывающей функции необходимо знать размер буфера, что не всегда просто — на самом деле, это требует реализации большинства кода sprintf в вызывающей функции. По факту, почти невозможно использовать sprintf безопасно. Вам необходимо указать длину каждого отдельного элемента строки формата (вот почему sprintf была создана). Это может привести к завершению процесса (что может также вызвать проблемы безопасности, и мы вернемся к этому позднее), поэтому некоторые реализации libc включают в себя asprintf().

Функция asprintf() — это то же самое, что и sprintf, кроме того, что она выделяет свой собственный буфер с помощью malloc. Это позволяет избежать досрочного завершения процесса или переполнения. К несчастью, она приносит новую проблему, когда вызывающая функция должна освобождать указатели, которые возвращает вызываемая функция.

Большое количество кода на Си содержит в себе эту проблему. Решение обычно заключается в том, чтобы поместить в документацию о функции строку «вызывающая функция должна освободить возвращаемый указатель». К сожалению, этот подход делает сложной возможность взглянуть на часть кода и увидеть, валидна ли она. Одно из решений заключается в том, чтобы заключать в оболочку каждый указатель, например, так:

typedef struct _RefCountedBuffer
{
    void *buffer;
    int refcount;
    void (free*)(struct _RefCountedBuffer*);
} *RefCountedBuffer;

Когда вы возвращаете указатель, вы создаете одну из этих структур и устанавливаете refcount в 1. Когда вы получаете его, вы всегда вызываете функцию, которая увеличивает refcount на единицу и вызывает соответствующую функцию free(), если она достигнет нуля. Эта методика близка для программистов Objective-C, потому как OpenStep реализует схожий механизм работы. GNUstep продвинулся дальше, предоставляя макросы ASSIGN() и DESTROY(). Эти макросы делают ошибки при работе с памятью более редкими, и мы можем сделать нечто похожее на обычном Си.

Прежде всего, нам надо определить функции retain() и release():

RefCountedBuffer retain(RefCountedBuffer *buf)
{
    buffer->refcount++;

    return buffer;
}

void release(RefCountedBuffer *buf)
{
    buf->refcount—;

    if (buf->refcount == 0)
        buf->free(buf);
}

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

++

и

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

Определив эти функции, вы можете определить макросы SET и FREE следующим образом:

#define SET(var, val) do {
    RefCountedBuffer __tmp = retain(val);
    if (NULL != var) release(var); var = __tmp; } while(0)

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

#define FREE(var) do { release(var); var = NULL; } while(0)

Этот макрос гарантирует, что каждый указатель всегда установлен в NULL после его освобождения. Даже если вы не используете подсчет ссылок, этот метод дает результат. Если вы используете оба этих макроса, у вас будет очень мало знаков равенства в коде. Что делает проще просмотр кода и поиск места, где могут быть ошибки, связанные с памятью. Подсчет ссылок — хорошее решение для данных, предназначенных только для чтения. Вы можете вернуть некоторый внутренний компонент большой структуры данных в качестве ссылки, не удаляя его из оригинальной структуры, пока его не перестанут использовать — так долго, как он находится в структуре RefCountedBuffer в обоих случаях. Тем не менее, эта модель не решает нашу первоначальную проблему функций, похожих на asprintf. Она требует вернуть строку, что часто используется только в вызывающей функции. Для этого выделение памяти в куче и заключения её в структуру подсчета ссылок будет лишним. Вам нужен способ выделить пространство в стековом фрейме вызывающей функции.

У разработчиков сервера Dovecot IMAP есть превосходное решение. В дополнение к обычному контролю стека они делают отдельный стек данных и аналог asprintf, которая использует его. Вызывающая функция сначала вызывает функцию, которая выделяет фрейм в новом стеке данных. Затем она вызывает аналог asprintf(), которая выделяет пространство в стеке результатов. Этот подход работает правильно, пока вызывающая функция достает верхний фрейм стека данных. Так как контрольный стек и стек данных работают на разных скоростях, вы можете просто вернуть данные в стек данных. Работа организована таким образом, что сначала создается фрейм в стеке данных, а затем вызывается asprintf(), которая выделяет пространство в текущем фрейме стека данных и помещает результат в него. Вы используете результат, а затем выталкиваете из стека текущий фрейм.

Вам ничего не мешает иметь несколько независимых областей памяти. Вы можете использовать mmap() на /dev/zero, чтобы выделить где-либо непрерывный блок памяти и использовать его по своему желанию. Одной из возможных идей может быть выделение стека данных, который работает на такой же скорости, как и контрольный стек. Используйте этот метод для всех массивов, которые вы будете по-другому выделять в стеке. В отличие от контрольного стека. Вы можете сделать его переместимым, постоянно адресуя его через глобальный указатель к началу. Например, с помощью подобного макроса:

#define NEW_ARRAY(type, name, elements)
    __attribute__((cleanup(popDataStack)))
    type *name = dataStackCalloc(sizeof(type), elements)

__attribute__(cleanup)

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

Как бы то ни было, вы до сих пор можете выйти за пределы массива. Вы не разрушите возвращаемый адрес, но можете переписать некоторые данные, или могут возникнуть другие проблемы. Вы можете избежать выхода за пределы стека данных, вызвав функцию mprotect() на последней странице стека, чтобы удалить все права доступа. У большинства реализаций malloc() есть режим отладки, который вставляет этот вид защитной страницы после каждого выделения памяти. Вы можете сделать это, создав свой стек данных, в котором будут чередоваться доступные и недоступные страницы, с каждым выделенным массивом, так что он завершится недоступной страницей, но эта система достаточно ресурсоемка. Никакая нормальная операционная система не выделит реальную память для защитных страниц, но вы потеряете много пространства в промежутках между началами страниц и началами массивов и будете использовать большое количество адресного пространства.

Строки в Си — вечная причина проблем. Когда Си был создан, было две концепции о том, как лучше всего реализовывать строки — известные сейчас как строки Си и строки Pascal, в соответствии с языками, которые сделали эти идеи популярными. Такие языки, как Lisp использовали третью реализацию: строки являлись связанным списком символов (Erlang до сих пор использует эту модель). Строки в стиле Lisp имеют очевидный недостаток. Каждому символу требуется один байт для хранения символа и 4 или 8 байт для хранения адреса следующего, до девяти байт уходит на хранение одного байта данных. Эта структура далека от идеальной, но делает разделение и конкатенацию строк очень простой.

Более совершенные модели представляют строки как связанный список массивов символов, позволяя легко их объединять. Все эти модели могут быть (и это было сделано) реализованы на Си, но стандартные строковые функции до сих пор работают с массивами байтов.

Большинство «классических» строковых функций практически невозможно использовать безопасно (по этой причине линковщик OpenBSD легко выдает предупреждение, когда вы используете одну из них). Каноническим примером «плохой» функции является strcat(), которая принимает два указателя на строки Си. Функция проходит по первой строке, пока не найдет null, она записывает в нее байты из второй строки пока не дойдет до null во второй строке. Вызывающая функция должна быть уверена, что существует достаточно места в первой строке, чтобы сохранить вторую.

Более новая функция, strncat(), была создана, чтобы сделать это легче. Эта функция принимает в качестве третьего аргумента объем пространства в первой строке. Она гарантирует, что функция никогда не выйдет за пределы первой строки, но создает новую проблему, функция возвращает новую строку в качестве результата, поэтому вы не можете легко протестировать, был ли искажен результат. Это становится большой проблемой, когда соединяемые части являются, например, паролем.

В OpenBSD представлена strlcat, которая похожа на strncat, но возвращает сумму входных строк. Если результат работы функции больше третьего аргумента, имело место искажение. Эта функция находится в каждой ОС семейства BSD (включая Darwin/OS X), но её нет в glibc, так как, согласно главному разработчику glibc, является «бесполезным хламом BSD». К счастью, лицензия BSD позволяет вам копировать эту функцию из libc BSD в ваш код без каких-либо проблем.

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

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

int *a = alloca(sizeof(int) * n);

int a[n];

Разница в поведении, если существует недостаточно пространства для роста стека до n целых значений. Первая строка будет при этом NULL, но вы сможете проверить это, что упрощает отладку — достаточно просто обратиться к началу массива. Во второй строке, если не будет хватать размера стека, он будет указывать: куда-то. А куда именно, будет зависеть от реализации. Вот почему, если вы используете массивы C99 с переменной длиной, невозможно протестировать программу на переполнение стека. В большинстве случаев это не является проблемой. Небольшие выделения памяти более безопасны для работы, но если взломщик сможет увеличить размер n, у вас может получиться массив, указывающий никуда.

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

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

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

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

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

Разбивая компоненты программы на отдельные процессы, вы ограничиваете размеры разрушений, которые может принести один баг. Самым распространенным примером является разделение привилегий кода, находящееся во множестве серверных программ. Многие сервера должны быть запущены под root или с соответствующими правами. Им нужна эта возможность для привязки к соответствующим портам и осуществления других действий в качестве root, таких, как получение доступа к данным разных пользователей или запуск программы под другим пользователем. Хорошим методом для такого вида процессов является заключение кода, осуществляющего привилегированные операции, в отдельный процесс. Например, если вам надо написать на разные почтовые ящики пользователей, у вас может быть процесс, который запускается под root, чтобы открыть почтовый ящик, написать в него письмо и больше ничего. Он проверит аргументы и осуществит запись, но не более того. Это легко реализовать в коде и довольно просто найти возможные баги. Также можно изолировать код, содержащий важную информацию. Большей части программы не надо знать, например, ключей шифрования или паролей. Вы можете хранить такую информацию в отдельном процессе и ограничивать доступ главной программы к нему, усложнив тем самым для взломщика получение доступа к этим данным.

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

Веб-сервер нуждается в запуске в качестве root, так как ему нужно быть привязанным к 80 порту и иметь доступ к файлам в директории public_html каждого пользователя. Тем не менее, как только он привязан к 80 порту, ему не нужно более работать как root, и он может отказаться от прав root. Хотя ему по-прежнему нужен способ получать доступ к директории public_html каждого пользователя. Одно решение — заставить каждого пользователя сделать его файлы доступными для группы веб-сервера. Другим может быть выполнение fork() процесса-потомка для каждого пользователя, который работает в качестве этого пользователя и имеет доступ к файлам в директории пользователя.

Немного повысить безопасность можно, используя системный вызов chroot(), который меняет корневую директорию (которая видна процессу) на специальную директорию. Любые файлы за пределами этой директории находятся вне зоны видимости, хотя к тем, что были открыты, доступ по-прежнему остается. Этот факт важен, так как вы можете держать библиотеки и даже исполняемую программу, а также файлы настройки вне «тюрьмы» chroot.

Пользователь с правами root может легко избежать chroot, создав новое устройство для жесткого диска и смонтировав его внутрь chroot (или даже получая доступ к нему напрямую). Вот почему важно сбросить привилегии root сразу же после входа в chroot.

Ещё легче использовать chroot(), если он встроен в приложение. Команда chroot, которая запускает процесс в окружении chroot, также доступна, но этот подход имеет две проблемы. Первая — он вызывает chroot до запуска процесса, поэтому программа и все необходимые библиотеки должны быть внутри директории chroot. Вторая заключается в том, что он должен выполняться в качестве root, поэтому ему нужно что-то внутри chroot для возможности сброса привилегий. Обычным решением является поместить команду su внутрь chroot. Хотя, когда вы поместите так много кода внутрь chroot, он станет выглядеть, как и внешнее окружение.

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

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

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

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

Другой поток изменяет данные, на которые указывает аргумент-указатель.

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

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

Писать безопасный код на Си сложно, но это возможно. Безопасность таких систем, как OpenBSD доказывает, что это можно сделать. Язык не делает написание безопасного кода простым, но в некоторых случаях этот факт является полезным. Чтобы избежать проблем, программист должен основываться на хорошем коде, а не на возможностях языка.

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

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

Главная / Программирование /
Язык программирования C++ / Тест 16

Упражнение 1:


Номер 1

Какие ключевые слова используются для создания и обработки исключительных ситуаций?

Ответ:

(1) try 

(2) delete 

(3) catch 

(4) return 

(5) throw 


Номер 2

Функция вычисляет произведение двух чисел. Исходные данные вводятся с клавиатуры. Какие проверки целесообразно ввести в программе?

Ответ:

(1) проверка исходных данных на равенство нулю 

(2) проверка, что исходные данные являются числами и эти числа больше нуля 

(3) проверка, что исходные данные являются числами 

(4) проверки не нужны, все возможные ошибки отловит компилятор 


Номер 3

Сколько блоков catch может быть после блока try?

Ответ:

(1) количество блоков catch зависит от количества блоков try 

(2) ни одного 

(3) минимум один 


Упражнение 2:


Номер 1

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

Ответ:

(1) можно, но обрабатывать их следует внутри деструктора 

(2) да, никаких проблем возникнуть не может 

(3) нет, компилятор выдаст ошибку 

(4) да, но результат будет непредсказуем 


Номер 2

На каком этапе обнаруживаются ошибки в алгоритме программы?

Ответ:

(1) на этапе компиляции 

(2) на этапе выполнения 

(3) они могут не проявиться никогда, все зависит от входных данных 


Упражнение 3:


Номер 1

Если заданы классы
 class A {... } A1;
class B : public A { ... } B1;
class C : public A { ... } C1;
то что будет выведено при выполнении оператора
throw (C1);
а обработка исключительной ситуации записана
catch (B& b) { cout << 1; }
catch (C& c) { cout << 2; }
catch (A& a) { cout << 3; }
catch (...) { cout << 4; }

Ответ:

(1) 1 

(2) 2 

(3) 3 

(4) 4 

(5) 3 4 

(6) 2 3 4 


Номер 2

Если заданы классы
class A {... } A1;
class B : public A { ... } B1;
class C : public A { ... } C1;
то что будет выведено при выполнении оператора
throw (A1);
а обработка исключительной ситуации записана
catch (B& b) { cout << 1; }
catch (C& c) { cout << 2; }
catch (A& a) { cout << 3; }
catch (...) { cout << 4; }

Ответ:

(1) 1 

(2) 2 

(3) 3 

(4) 4 

(5) 3 4 

(6) 2 3 4 


Номер 3

Если заданы классы
class A {... } A1;
class B : public A { ... } B1;
class C : public B { ... } C1;
то что будет выведено при выполнении оператора
throw (C1);
а обработка исключительной ситуации записана
catch (B& b) { cout << 1; }
catch (C& c) { cout << 2; }
catch (A& a) { cout << 3; }
catch (...) { cout << 4; }

Ответ:

(1) 1 

(2) 2 

(3) 3 

(4) 4 

(5) 1 2 3 4 

(6) 2 3 4 


Упражнение 4:


Номер 1

Если в конструкторе класса
 class  A {
 public:
    A() { ptr = new char[size];
         Init(); }
    ~A() { if (ptr) delete[] ptr; }
    char* ptr; };
 произойдет исключительная ситуация, будет ли потеряна память при откате по стеку?

Ответ:

(1) да, будет, во всех случаях 

(2) будет, только если объект класса создавался с помощью new 

(3) будет, если создавалась автоматическая переменная класса a 

(4) нет, не будет 

(5) зависит от конкретного компилятора 


Номер 2

Оператор throw без аргументов

Ответ:

(1) повторно вызывает обрабатываемую исключительную ситуацию 

(2) вызывает последнюю необработанную исключительную ситуацию 

(3) вызывает исключительную ситуацию типа Exception 


Номер 3

Блок try catch

Ответ:

(1) должен стоять в функции main 

(2) заключает участок кода, в котором может сложиться исключительная ситуация 

(3) должен заканчиваться catch (...) 

(4) может быть повторен несколько раз в одной функции 

(5) не может быть вложенным 


Номер 4

Об ошибке в конструкторе класса может сигнализировать:

Ответ:

(1) возвращаемое значение 

(2) исключительная ситуация 

(3) вызов деструктора сразу в конструкторе 

(4) установленный атрибут-флаг объекта 


Упражнение 5:


Номер 1

Отметьте, какие возможности языка Си++ помогают предупреждать ошибки:

Ответ:

(1) наличие встроенных типов данных 

(2) контроль типов при компиляции 

(3) возможность использовать указатели вместо массивов 

(4) обязательность объявления функций до их использования 


Номер 3

Отметьте те средства языка Си++, которые относятся к диагностике ошибок

Ответ:

(1) возвращаемое значение функции 

(2) исключительные ситуации 

(3) создание объектов 

(4) глобальные переменные, хранящие состояние 

(5) флаг состояния объекта 

(6) преобразование типов переменной 


Упражнение 6:


Номер 1

Что может быть аргументом оператора throw?

Ответ:

(1) целое число 

(2) объект класса 

(3) строка 

(4) ноль 

(5) условный оператор 

(6) вызов деструктора объекта 

(7) вызов оператора return 

Номер 2

Какие требования предъявляются к классу исключительных ситуаций?

Ответ:

(1) он должен быть наследован от специального класса exception 

(2) он не может использовать множественное наследование 

(3) он должен содержать атрибуты только встроенных типов 

(4) он может быть произвольным классом 


Номер 3

Что происходит при попытке выполнить оператор return внутри блока catch?

Ответ:

(1) аварийная остановка программы 

(2) повторное создание обрабатываемой исключительной ситуации 

(3) выход из функции 

(4) ошибка компиляции 

(5) ошибка выполнения 


Упражнение 7:


Номер 1

Что будет на экране после выполнения программы

#include <iostream.h>
short x = 4, i = 0;
void fun1()
{  if (i == 0) throw 2; }
int fun2()
{ --x; fun1();  x++; return x; }
int main()
{ try
  { fun2(); }
     catch (int)
  { cout << "Exception "; }
  cout << x << " " << i;  
}
    

Ответ:

(1) Exception 

(2) Exception 4 0 

(3) Exception 3 0 

(4) ошибка компиляции 


Номер 2

Что будет на экране после выполнения программы

#include <iostream.h>
short x = 4, i = 0;
void fun1()
{  if (i == 5) throw 2; }
void fun2()
{ --x; fun1();  x++; }
int main()
{ try
  { fun2(); }
   catch (int)
  { cout << "Exception "; }
  cout << x << " " << i;
}

Ответ:

(1) Exception 

(2) Exception 4 0 

(3) Exception 3 0 

(4) 4 0 


Номер 3

Что будет на экране после выполнения программы

#include <iostream.h>
short x = 4, i = 0;
void fun1()
{ double p=2;
   if (!i) throw p; }
void fun2()
{ --x; fun1();  x++; }
int main()
{ try
  { fun2(); }
    catch (double)
   { cout << "Exception "; }
  cout << x << " " << i;  
}

Ответ:

(1) Exception 

(2) Ошибка компиляции 

(3) Exception 3 0 

(4) 4 0 


Упражнение 8:


Номер 1

Какой результат будет у следующего выражения?
    
    int main()
    { try
      { 
         try
         { 
            try {  throw 1; }
            catch (int) { cout << "Exception 1"; }
         }
         catch (int) { cout << "Exception 2"; }
      }
      catch (int){ cout << "Exception 3"; }  
      return 0;
    }
    

Ответ:

(1) Exception 1Exception 2Exception 3 

(2) Exception 1Exception 2 

(3) Exception 1 

(4) Exception 2 

(5) Exception 3 


Номер 2

Какой результат будет у следующего выражения?
    
    int main()
    { 
      try
      { 
         try
         { 
            try{  throw 1; }
           catch (float) { cout << "Exception 1"; }
         }
         catch (int){ cout << "Exception 2"; }
      }
      catch (int){ cout << "Exception 3"; } 
     return 0; 
    }
    

Ответ:

(1) Exception 1Exception 2Exception 3 

(2) Exception 1Exception 2 

(3) Exception 1 

(4) Exception 2 

(5) Exception 3 


Номер 3

Что будет выведено на экран после выполнения программы?
    
    int main()
    { 
       try{ 
            try
            {  throw 3.14; }
           catch (int) { cout << "Exception 1"; }
         }
       catch (...)
         { cout << "Exception 2"; }
       return 0;
      
    }
    

Ответ:

(1) Exception 1Exception 2 

(2) Exception 1 

(3) Exception 2 


Improve Article

Save Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Although C does not provide direct support to error handling (or exception handling), there are ways through which error handling can be done in C. A programmer has to prevent errors at the first place and test return values from the functions.
    A lot of C function calls return a -1 or NULL in case of an error, so quick test on these return values are easily done with for instance an ‘if statement’. For example, In Socket Programming, the returned value of the functions like socket(), listen() etc. are checked to see if there is an error or not.

    Example: Error handling in Socket Programming

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
    {
       perror("socket failed");
       exit(EXIT_FAILURE);
    }
    

    Different methods of Error handling in C

    1. Global Variable errno: When a function is called in C, a variable named as errno is automatically assigned a code (value) which can be used to identify the type of error that has been encountered. Its a global variable indicating the error occurred during any function call and defined in the header file errno.h.
      Different codes (values) for errno mean different types of errors. Below is a list of few different errno values and its corresponding meaning:
      errno value       Error
      1             /* Operation not permitted */
      2             /* No such file or directory */
      3             /* No such process */
      4             /* Interrupted system call */
      5             /* I/O error */
      6             /* No such device or address */
      7             /* Argument list too long */
      8             /* Exec format error */
      9             /* Bad file number */
      10            /* No child processes */
      11            /* Try again */
      12            /* Out of memory */
      13            /* Permission denied */
      
      

      #include <stdio.h>

      #include <errno.h>

      int main()

      {

          FILE * fp;

          fp = fopen("GeeksForGeeks.txt", "r");

          printf(" Value of errno: %dn ", errno);

          return 0;

      }

      Output:

      Value of errno: 2
      

      Note: Here the errno is set to 2 which means – No such file or directory. On online IDE it may give errorno 13, which says permission denied.

    2. perror() and strerror(): The errno value got above indicate the types of error encountered.
      If it is required to show the error description, then there are two functions that can be used to display a text message that is associated with errorno. The functions are:
      • perror: It displays the string you pass to it, followed by a colon, a space, and then the textual representation of the current errno value.
        Syntax:
        void perror (const char *str)
        str: is a string containing a custom message
        to be printed before the error message itself.
      • strerror(): returns a pointer to the textual representation of the current errno value.
        Syntax:
        char *strerror (int errnum)
        errnum: is the error number (errno).

      #include <stdio.h>

      #include <errno.h>

      #include <string.h>

      int main ()

      {

          FILE *fp;

          fp = fopen(" GeeksForGeeks.txt ", "r");

          printf("Value of errno: %dn ", errno);

          printf("The error message is : %sn"

                               strerror(errno));

          perror("Message from perror");

          return 0;

      }

      Output:
      On Personal desktop:

      Value of errno: 2
      The error message is : No such file or directory
      Message from perror: No such file or directory
      

      On online IDE:

       Value of errno: 13
      The error message is : Permission denied
      

      Note: The function perror() displays a string passed to it, followed by a colon and the textual message of the current errno value.

    3. Exit Status: The C standard specifies two constants: EXIT_SUCCESS and EXIT_FAILURE, that may be passed to exit() to indicate successful or unsuccessful termination, respectively. These are macros defined in stdlib.h.

      #include <stdio.h>

      #include <errno.h>

      #include <string.h>

      #include <stdlib.h>

      int main ()

      {

          FILE * fp;

          fp = fopen ("filedoesnotexist.txt", "rb");

          if (fp == NULL)

          {

              printf("Value of errno: %dn", errno);

              printf("Error opening the file: %sn",

                                   strerror(errno));

              perror("Error printed by perror");

              exit(EXIT_FAILURE);

              printf("I will not be printedn");

          }

          else

          {

              fclose (fp);

              exit(EXIT_SUCCESS);

              printf("I will not be printedn");

          }

          return 0;

      }

      Output:

      Value of errno: 2
      Error opening the file: No such file or directory
      Error printed by perror: No such file or directory
      
    4. Divide by Zero Errors: A common pitfall made by C programmers is not checking if a divisor is zero before a division command. Division by zero leads to undefined behavior, there is no C language construct that can do anything about it. Your best bet is to not divide by zero in the first place, by checking the denominator.

      #include<stdio.h>

      #include <stdlib.h>

      void function(int);

      int main()

      {

          int x = 0;

          function(x);

          return 0;

      }

      void function(int x)

      {

          float fx;

          if (x==0)

          {

              printf("Division by Zero is not allowed");

              fprintf(stderr, "Division by zero! Exiting...n");

              exit(EXIT_FAILURE);

          }

          else

          {

              fx = 10 / x;

              printf("f(x) is: %.5f", fx);

          }

      }

      Output:

      Division by Zero is not allowed

    This article is contributed by MAZHAR IMAM KHAN. If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

    Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.

    Improve Article

    Save Article

  • Read
  • Discuss
  • Improve Article

    Save Article

    Although C does not provide direct support to error handling (or exception handling), there are ways through which error handling can be done in C. A programmer has to prevent errors at the first place and test return values from the functions.
    A lot of C function calls return a -1 or NULL in case of an error, so quick test on these return values are easily done with for instance an ‘if statement’. For example, In Socket Programming, the returned value of the functions like socket(), listen() etc. are checked to see if there is an error or not.

    Example: Error handling in Socket Programming

    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
    {
       perror("socket failed");
       exit(EXIT_FAILURE);
    }
    

    Different methods of Error handling in C

    1. Global Variable errno: When a function is called in C, a variable named as errno is automatically assigned a code (value) which can be used to identify the type of error that has been encountered. Its a global variable indicating the error occurred during any function call and defined in the header file errno.h.
      Different codes (values) for errno mean different types of errors. Below is a list of few different errno values and its corresponding meaning:
      errno value       Error
      1             /* Operation not permitted */
      2             /* No such file or directory */
      3             /* No such process */
      4             /* Interrupted system call */
      5             /* I/O error */
      6             /* No such device or address */
      7             /* Argument list too long */
      8             /* Exec format error */
      9             /* Bad file number */
      10            /* No child processes */
      11            /* Try again */
      12            /* Out of memory */
      13            /* Permission denied */
      
      

      #include <stdio.h>

      #include <errno.h>

      int main()

      {

          FILE * fp;

          fp = fopen("GeeksForGeeks.txt", "r");

          printf(" Value of errno: %dn ", errno);

          return 0;

      }

      Output:

      Value of errno: 2
      

      Note: Here the errno is set to 2 which means – No such file or directory. On online IDE it may give errorno 13, which says permission denied.

    2. perror() and strerror(): The errno value got above indicate the types of error encountered.
      If it is required to show the error description, then there are two functions that can be used to display a text message that is associated with errorno. The functions are:
      • perror: It displays the string you pass to it, followed by a colon, a space, and then the textual representation of the current errno value.
        Syntax:
        void perror (const char *str)
        str: is a string containing a custom message
        to be printed before the error message itself.
      • strerror(): returns a pointer to the textual representation of the current errno value.
        Syntax:
        char *strerror (int errnum)
        errnum: is the error number (errno).

      #include <stdio.h>

      #include <errno.h>

      #include <string.h>

      int main ()

      {

          FILE *fp;

          fp = fopen(" GeeksForGeeks.txt ", "r");

          printf("Value of errno: %dn ", errno);

          printf("The error message is : %sn"

                               strerror(errno));

          perror("Message from perror");

          return 0;

      }

      Output:
      On Personal desktop:

      Value of errno: 2
      The error message is : No such file or directory
      Message from perror: No such file or directory
      

      On online IDE:

       Value of errno: 13
      The error message is : Permission denied
      

      Note: The function perror() displays a string passed to it, followed by a colon and the textual message of the current errno value.

    3. Exit Status: The C standard specifies two constants: EXIT_SUCCESS and EXIT_FAILURE, that may be passed to exit() to indicate successful or unsuccessful termination, respectively. These are macros defined in stdlib.h.

      #include <stdio.h>

      #include <errno.h>

      #include <string.h>

      #include <stdlib.h>

      int main ()

      {

          FILE * fp;

          fp = fopen ("filedoesnotexist.txt", "rb");

          if (fp == NULL)

          {

              printf("Value of errno: %dn", errno);

              printf("Error opening the file: %sn",

                                   strerror(errno));

              perror("Error printed by perror");

              exit(EXIT_FAILURE);

              printf("I will not be printedn");

          }

          else

          {

              fclose (fp);

              exit(EXIT_SUCCESS);

              printf("I will not be printedn");

          }

          return 0;

      }

      Output:

      Value of errno: 2
      Error opening the file: No such file or directory
      Error printed by perror: No such file or directory
      
    4. Divide by Zero Errors: A common pitfall made by C programmers is not checking if a divisor is zero before a division command. Division by zero leads to undefined behavior, there is no C language construct that can do anything about it. Your best bet is to not divide by zero in the first place, by checking the denominator.

      #include<stdio.h>

      #include <stdlib.h>

      void function(int);

      int main()

      {

          int x = 0;

          function(x);

          return 0;

      }

      void function(int x)

      {

          float fx;

          if (x==0)

          {

              printf("Division by Zero is not allowed");

              fprintf(stderr, "Division by zero! Exiting...n");

              exit(EXIT_FAILURE);

          }

          else

          {

              fx = 10 / x;

              printf("f(x) is: %.5f", fx);

          }

      }

      Output:

      Division by Zero is not allowed

    This article is contributed by MAZHAR IMAM KHAN. If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

    Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.

    Введение

    Ошибки, увы, неизбежны, поэтому их обработка занимает очень важное место в программировании. И если алгоритмические ошибки можно выявить и исправить во время написания и тестирования программы, то ошибок времени выполнения избежать нельзя в принципе. Сегодня мы рассмотрим функции стандартной библиотеки (C Standard Library) и POSIX, используемые в обработке ошибок.

    Переменная errno и коды ошибок

    <errno.h>

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

    Все коды ошибок имеют положительные значения, и могут использоваться в директивах препроцессора #if. В целях удобства и переносимости заголовочный файл <errno.h> определяет макросы, соответствующие кодам ошибок.

    Стандарт ISO C определяет следующие коды:

    • EDOM – (Error domain) ошибка области определения.
    • EILSEQ – (Error invalid sequence) ошибочная последовательность байтов.
    • ERANGE – (Error range) результат слишком велик.

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

    Нехитрый скрипт печатает в консоль коды ошибок, их символические имена и описания:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use Errno;
    
    foreach my $err (sort keys (%!)) {
        $! = eval "Errno::$err";
        printf "%20s %4d   %sn", $err, $! + 0, $!
    }

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

    Пример:

    /* convert from UTF16 to UTF8 */
    errno = 0;	
    n_ret = iconv(icd, (char **) &p_src, &n_src, &p_dst, &n_dst);   
    	
    if (n_ret == (size_t) -1) {
        VJ_PERROR();
        if (errno == E2BIG)  
            fprintf(stderr, " Error : input conversion stopped due to lack of space in the output buffern");
        else if (errno == EILSEQ)  
            fprintf(stderr, " Error : input conversion stopped due to an input byte that does not belong to the input codesetn");
        else if (errno == EINVAL)  
            fprintf(stderr, " Error : input conversion stopped due to an incomplete character or shift sequence at the end of the input buffern");
    /* clean the memory */   
        free(p_out_buf);
        errno = 0;
        n_ret = iconv_close(icd);      
        if (n_ret == (size_t) -1)  
            VJ_PERROR();
        return (size_t) -1; 
    }

    Как видите, описания ошибок в спецификации функции iconv() более информативны, чем в <errno.h>.

    Функции работы с errno

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

    <stdio.h>

    void perror(const char *s);

    Печатает в stderr содержимое строки s, за которой следует двоеточие, пробел и сообщение об ошибке. После чего печатает символ новой строки 'n'.

    Пример:

    /*
    //  main.c
    //  perror example
    //
    //  Created by Ariel Feinerman on 23/03/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    int main(int argc, const char * argv[]) 
    {
        // Generate unique filename.
        char *file_name = tmpnam((char[L_tmpnam]){0});
       
        errno = 0;
        FILE *file = fopen(file_name, "rb");
    
        if (file) {
            // Do something useful. 
            fclose(file);
        }
        else {
            perror("fopen() ");
        }
    	
        return EXIT_SUCCESS;
    }

    <string.h>

    char* strerror(int errnum);
    Возвращает строку, содержащую описание ошибки errnum. Язык сообщения зависит от локали (немецкий, иврит и даже японский), но обычно поддерживается лишь английский.

    /*
    //  main.c
    //  strerror example
    //
    //  Created by Ariel Feinerman on 23/03/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <errno.h>
    
    int main(int argc, const char * argv[]) 
    {
        // Generate unique filename.
        char *file_name = tmpnam((char[L_tmpnam]){0});
    
        errno = 0;
        FILE *file = fopen(file_name, "rb");
        // Save error number. 
        errno_t error_num = errno;
    	
        if (file) {
            // Do something useful. 
            fclose(file);
        }
        else {
            char *errorbuf = strerror(error_num);
            fprintf(stderr, "Error message : %sn", errorbuf);
        }
        
        return EXIT_SUCCESS;
    }

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

    Поэтому в новом стандарте ISO C11 были предложены две очень полезные функции.

    size_t strerrorlen_s(errno_t errnum);

    Возвращает длину строки с описанием ошибки errnum.

    errno_t strerror_s(char *buf, rsize_t buflen, errno_t errnum);

    Копирует строку с описание ошибки errnum в буфер buf длиной buflen.

    Пример:

    /*
    //  main.c
    //  strerror_s example 
    //
    //  Created by Ariel Feinerman on 23/02/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #define __STDC_WANT_LIB_EXT1__ 1
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <errno.h>
    
    int main(int argc, const char * argv[]) 
    {
        // Generate unique filename.
        char *file_name = tmpnam((char[L_tmpnam]){0});
    	
        errno = 0;
        FILE *file = fopen(file_name, "rb");
        // Save error number. 
        errno_t error_num = errno;
    
        if (file) {
            // Do something useful. 
            fclose(file);
        }
        else {
    #ifdef __STDC_LIB_EXT1__
        size_t error_len = strerrorlen_s(errno) + 1;
        char error_buf[error_len];
        strerror_s(error_buf, error_len, errno);
        fprintf(stderr, "Error message : %sn", error_buf);
    #endif
        }
    	
        return EXIT_SUCCESS;
    }

    Функции входят в Annex K (Bounds-checking interfaces), вызвавший много споров. Он не обязателен к выполнению и целиком не реализован ни в одной из свободных библиотек. Open Watcom C/C++ (Windows), Slibc (GNU libc) и Safe C Library (POSIX), в последней, к сожалению, именно эти две функции не реализованы. Тем не менее, их можно найти в коммерческих средах разработки и системах реального времени, Embarcadero RAD Studio, INtime RTOS, QNX.

    Стандарт POSIX.1-2008 определяет следующие функции:

    char *strerror_l(int errnum, locale_t locale);

    Возвращает строку, содержащую локализованное описание ошибки errnum, используя locale. Безопасна в многопоточной среде. Не реализована в Mac OS X, FreeBSD, NetBSD, OpenBSD, Solaris и прочих коммерческих UNIX. Реализована в Linux, MINIX 3 и Illumos (OpenSolaris).

    Пример:

    /*
     //  main.c
     //  strerror_l example – works on Linux, MINIX 3, Illumos
     //
     //  Created by Ariel Feinerman on 23/03/17.
     //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <errno.h>
    
    #include <locale.h>
    
    int main(int argc, const char * argv[]) 
    {
        locale_t locale = newlocale(LC_ALL_MASK, "fr_FR.UTF-8", (locale_t) 0);
        
        if (!locale) {
            fprintf(stderr, "Error: cannot create locale.");
            exit(EXIT_FAILURE);
        }
    
        // Generate unique filename.
        char *file_name = tmpnam((char[L_tmpnam]){0});
    	
        errno = 0;
        FILE *file = fopen(tmpnam(file_name, "rb");
        // Save error number. 
        errno_t error_num = errno;
    
        if (file) {
            // Do something useful. 
            fclose(file);
        }
        else {
            char *error_buf = strerror_l(errno, locale);
            fprintf(stderr, "Error message : %sn", error_buf);
        }
    	
        freelocale(locale);
    	
        return EXIT_SUCCESS;
    }

    Вывод:

    Error message : Aucun fichier ou dossier de ce type

    int strerror_r(int errnum, char *buf, size_t buflen);

    Копирует строку с описание ошибки errnum в буфер buf длиной buflen. Если buflen меньше длины строки, лишнее обрезается. Безопасна в многоготочной среде. Реализована во всех UNIX.

    Пример:

    /*
    //  main.c
    //  strerror_r POSIX example
    //
    //  Created by Ariel Feinerman on 25/02/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <errno.h>
    
    #define MSG_LEN 1024 
    
    int main(int argc, const char * argv[]) 
    {
        // Generate unique filename.
        char *file_name = tmpnam((char[L_tmpnam]){0});
        
        errno = 0;
        FILE *file = fopen(file_name, "rb");
        // Save error number. 
        errno_t error_num = errno;	
    	
        if (file) {
            // Do something useful.
            fclose(file);
        }
        else {
            char error_buf[MSG_LEN];
            errno_t error = strerror_r (error_num, error_buf, MSG_LEN);
    		
            switch (error) {
                case EINVAL:
                        fprintf (stderr, "strerror_r() failed: invalid error code, %dn", error);
                        break;
                case ERANGE:
                        fprintf (stderr, "strerror_r() failed: buffer too small: %dn", MSG_LEN);
                case 0:
                        fprintf(stderr, "Error message : %sn", error_buf);
                        break;
                default: 
                        fprintf (stderr, "strerror_r() failed: unknown error, %dn", error);
                        break;
            }
        }
        
        return EXIT_SUCCESS;
    }
    

    Увы, никакого аналога strerrorlen_s() в POSIX не определили, поэтому длину строки можно выяснить лишь экспериментальным путём. Обычно 300 символов хватает за глаза. GNU C Library в реализации strerror() использует буфер длиной в 1024 символа. Но мало ли, а вдруг?

    Пример:

    /*
     //  main.c
     //  strerror_r safe POSIX example
     //
     //  Created by Ariel Feinerman on 23/03/17.
     //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
     */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #include <errno.h>
    
    #define MSG_LEN 1024 
    #define MUL_FACTOR 2
    
    int main(int argc, const char * argv[]) 
    {
        // Generate unique filename.
        char *file_name = tmpnam((char[L_tmpnam]){0});
    	
        errno = 0;
        FILE *file = fopen(file_name, "rb");
        // Save error number. 
        errno_t error_num = errno;
    	
        if (file) {
            // Do something useful.
            fclose(file);
        }
        else {
            errno_t error = 0;
            size_t error_len = MSG_LEN; 
    		
            do {
                char error_buf[error_len];
                error = strerror_r (error_num, error_buf, error_len);
                switch (error) {
                        case 0:
                                fprintf(stderr, "File : %snLine : %dnCurrent function : %s()nFailed function : %s()nError message : %sn", __FILE__, __LINE__, __func__, "fopen", error_buf);
    	                    break;
                        case ERANGE: 
                                error_len *= MUL_FACTOR;
                                break;
                        case EINVAL: 
                                fprintf (stderr, "strerror_r() failed: invalid error code, %dn", error_num);
                                break;
                        default:
                                fprintf (stderr, "strerror_r() failed: unknown error, %dn", error);
                                break;
                }
    			
            } while (error == ERANGE);
        }
        
        return EXIT_SUCCESS;
    }

    Вывод:

    File : /Users/ariel/main.c
    Line : 47
    Current function : main()
    Failed function : fopen()
    Error message : No such file or directory

    Макрос assert()

    <assert.h>

    void assert(expression)

    Макрос, проверяющий условие expression (его результат должен быть числом) во время выполнения. Если условие не выполняется (expression равно нулю), он печатает в stderr значения __FILE__, __LINE__, __func__ и expression в виде строки, после чего вызывает функцию abort().

    /*
    //  main.c
    //  assert example
    //
    //  Created by Ariel Feinerman on 23/03/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    #include <math.h>
    
    int main(int argc, const char * argv[]) {
        double x = -1.0;
        assert(x >= 0.0);
        printf("sqrt(x) = %fn", sqrt(x));   
        
        return EXIT_SUCCESS;
    }

    Вывод:

    Assertion failed: (x >= 0.0), function main, file /Users/ariel/main.c, line 17.

    Если макрос NDEBUG определён перед включением <assert.h>, то assert() разворачивается в ((void) 0) и не делает ничего. Используется в отладочных целях.

    Пример:

    /*
    //  main.c
    //  assert_example
    //
    //  Created by Ariel Feinerman on 23/03/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #NDEBUG
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    
    #include <math.h>
    
    int main(int argc, const char * argv[]) {
        double x = -1.0;
        assert(x >= 0.0);
        printf("sqrt(x) = %fn", sqrt(x));   
        
        return EXIT_SUCCESS;
    }

    Вывод:

    sqrt(x) = nan

    Функции atexit(), exit() и abort()

    <stdlib.h>

    int atexit(void (*func)(void));

    Регистрирует функции, вызываемые при нормальном завершении работы программы в порядке, обратном их регистрации. Можно зарегистрировать до 32 функций.

    _Noreturn void exit(int exit_code);

    Вызывает нормальное завершение программы, возвращает в среду число exit_code. ISO C стандарт определяет всего три возможных значения: 0, EXIT_SUCCESS и EXIT_FAILURE. При этом вызываются функции, зарегистрированные через atexit(), сбрасываются и закрываются потоки ввода — вывода, уничтожаются временные файлы, после чего управление передаётся в среду. Функция exit() вызывается в main() при выполнении return или достижении конца программы.

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

    Пример:

    /*
    //  main.c
    //  exit example
    //
    //  Created by Ariel Feinerman on 17/03/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    void third_2(void) 
    {
        printf("third #2n");          // Does not print.
    }
    
    void third_1(void) 
    {
        printf("third #1n");          // Does not print.
    }
    
    void second(double num) 
    {
        printf("second : before exit()n");	// Prints.
        
        if ((num < 1.0f) && (num > -1.0f)) {
            printf("asin(%.1f) = %.3fn", num, asin(num));
            exit(EXIT_SUCCESS);
        }
        else {
            fprintf(stderr, "Error: %.1f is beyond the range [-1.0; 1.0]n", num);
            exit(EXIT_FAILURE);
        }
        
        printf("second : after exit()n");	// Does not print.
    }
    
    void first(double num) 
    {
        printf("first : before second()n")
        second(num);
        printf("first : after second()n");          // Does not print.
    }
    
    int main(int argc, const char * argv[]) 
    {
        atexit(third_1); // Register first handler. 
        atexit(third_2); // Register second handler.
        
        first(-3.0f);
        
        return EXIT_SUCCESS;
    }

    Вывод:

    first : before second()
    second : before exit()
    Error: -3.0 is beyond the range [-1.0; 1.0]
    third #2
    third #1

    _Noreturn void abort(void);

    Вызывает аварийное завершение программы, если сигнал не был перехвачен обработчиком сигналов. Временные файлы не уничтожаются, закрытие потоков определяется реализацией. Самое главное отличие вызовов abort() и exit(EXIT_FAILURE) в том, что первый посылает программе сигнал SIGABRT, его можно перехватить и произвести нужные действия перед завершением программы. Записывается дамп памяти программы (core dump file), если они разрешены. При запуске в отладчике он перехватывает сигнал SIGABRT и останавливает выполнение программы, что очень удобно в отладке.

    Пример:

    /*
    //  main.c
    //  abort example
    //
    //  Created by Ariel Feinerman on 17/02/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    void third_2(void) 
    {
        printf("third #2n");          // Does not print.
    }
    
    void third_1(void) 
    {
        printf("third #1n");          // Does not print.
    }
    
    void second(double num) 
    {
        printf("second : before exit()n");	// Prints.
        
        if ((num < 1.0f) && (num > -1.0f)) {
            printf("asin(%.1f) = %.3fn", num, asin(num));
            exit(EXIT_SUCCESS);
        }
        else {
            fprintf(stderr, "Error: %.1f is beyond the range [-1.0; 1.0]n", num);
            abort();
        }
        
        printf("second : after exit()n");	// Does not print.
    }
    
    void first(double num) 
    {
        printf("first : before second()n");
        second(num);
        printf("first : after second()n");          // Does not print.
    }
    
    int main(int argc, const char * argv[]) 
    {
        atexit(third_1); // register first handler 
        atexit(third_2); // register second handler
        
        first(-3.0f);
        
        return EXIT_SUCCESS;
    }

    Вывод:

    first : before second()
    second : before exit()
    Error: -3.0 is beyond the range [-1.0; 1.0]
    Abort trap: 6

    Вывод в отладчике:

    $ lldb abort_example 
    (lldb) target create "abort_example"
    Current executable set to 'abort_example' (x86_64).
    (lldb) run
    Process 22570 launched: '/Users/ariel/abort_example' (x86_64)
    first : before second()
    second : before exit()
    Error: -3.0 is beyond the range [-1.0; 1.0]
    Process 22570 stopped
    * thread #1: tid = 0x113a8, 0x00007fff89c01286 libsystem_kernel.dylib`__pthread_kill + 10, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
        frame #0: 0x00007fff89c01286 libsystem_kernel.dylib`__pthread_kill + 10
    libsystem_kernel.dylib`__pthread_kill:
    ->  0x7fff89c01286 <+10>: jae    0x7fff89c01290            ; <+20>
        0x7fff89c01288 <+12>: movq   %rax, %rdi
        0x7fff89c0128b <+15>: jmp    0x7fff89bfcc53            ; cerror_nocancel
        0x7fff89c01290 <+20>: retq   
    (lldb) 

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

    В случае же некритической ошибки, например, вы не смогли открыть файл, можно безопасно выйти через exit().

    Функции setjmp() и longjmp()

    Вот мы и подошли к самому интересному – функциям нелокальных переходов. setjmp() и longjmp() работают по принципу goto, но в отличие от него позволяют перепрыгивать из одного места в другое в пределах всей программы, а не одной функции.

    <setjmp.h>

    int setjmp(jmp_buf env);

    Сохраняет информацию о контексте выполнения программы (регистры микропроцессора и прочее) в env. Возвращает 0, если была вызвана напрямую или value, если из longjmp().

    void longjmp(jmp_buf env, int value);

    Восстанавливает контекст выполнения программы из env, возвращает управление setjmp() и передаёт ей value.

    Пример:

    /*
    //  main.c
    //  setjmp simple
    //
    //  Created by Ariel Feinerman on 18/02/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <setjmp.h>
    
    static jmp_buf buf;
    
    void second(void) 
    {
        printf("second : before longjmp()n");	// prints
        longjmp(buf, 1);						// jumps back to where setjmp was called – making setjmp now return 1
        printf("second : after longjmp()n");	// does not prints
    	
        // <- Here is the point that is never reached. All impossible cases like your own house in Miami, your million dollars, your nice girl, etc.
    }
    
    void first(void) 
    {
        printf("first : before second()n");
        second();
        printf("first : after second()n");          // does not print
    }
    
    int main(int argc, const char * argv[]) 
    {
        if (!setjmp(buf))
            first();                // when executed, setjmp returned 0
        else                        // when longjmp jumps back, setjmp returns 1
            printf("mainn");       // prints
        
        return EXIT_SUCCESS;
    }

    Вывод:

    first : before second()
    second : before longjmp()
    main

    Используя setjmp() и longjmp() можно реализовать механизм исключений. Во многих языках высокого уровня (например, в Perl) исключения реализованы через них.

    Пример:

    /*
    //  main.c
    //  exception simple
    //
    //  Created by Ariel Feinerman on 18/02/17.
    //  Copyright  2017 Feinerman Research, Inc. All rights reserved.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    
    #include <setjmp.h>
    
    #define str(s) #s
    
    static jmp_buf buf;
    
    typedef enum {
        NO_EXCEPTION    = 0,
        RANGE_EXCEPTION = 1,
        NUM_EXCEPTIONS
    } exception_t;
    
    static char *exception_name[NUM_EXCEPTIONS] = {
    	
        str(NO_EXCEPTION),
        str(RANGE_EXCEPTION)
    };
    
    float asin_e(float num) 
    {
        if ((num < 1.0f) && (num > -1.0f)) {
            return asinf(num);
        }	
        else {
            longjmp(buf, RANGE_EXCEPTION);        // | @throw  
        }
    }
    
    void do_work(float num) 
    {
        float res = asin_e(num);
        printf("asin(%f) = %fn", num, res);         
    }
    
    int main(int argc, const char * argv[]) 
    {
        exception_t exc = NO_EXCEPTION;
        if (!(exc = setjmp(buf))) {        // |	
            do_work(-3.0f);                // | @try
        }                                  // |
        else {                                                                               // | 
            fprintf(stderr, "%s was hadled in %s()n", exception_name[exc], __func__);       // | @catch
        }                                                                                    // | 
    	
        return EXIT_SUCCESS;
    }

    Вывод:

    RANGE_EXCEPTION was hadled in main()

    Внимание! Функции setjmp() и longjmp() в первую очередь применяются в системном программировании, и их использование в клиентском коде не рекомендуется. Их применение ухудшает читаемость программы и может привести к непредсказуемым ошибкам. Например, что произойдёт, если вы прыгните не вверх по стеку – в вызывающую функцию, а в параллельную, уже завершившую выполнение?

    Информация

    • стандарт ISO/IEC C (89/99/11)
    • Single UNIX Specifcation, Version 4, 2016 Edition
    • The Open Group Base Specifcations Issue 7, 2016 Edition (POSIX.1-2008)
    • SEI CERT C Coding Standard
    • cправочная информация среды программирования
    • справочная информация операционной системы (man pages)
    • заголовочные файлы (/usr/include)
    • исходные тексты библиотеки (C Standard Library)

    Правильные ответы выделены зелёным цветом.
    Все ответы: В систематизированном виде излагаются основные понятия и описываются возможности языка C++. При этом основное внимание уделяется объяснению того, как теми или иными возможностями пользоваться.

    Какие основные области применения языка Си++?

    (1) системное программирование

    (2) прикладное программирование

    (3) программирование дизайна сайтов

    Какие виды наследования бывают (выберите наиболее полный ответ)?

    (1) внешнее,внутреннее,защищающее

    (2) общее,внешнее,внутреннее,защищающее

    (3) внешнее,внутреннее,защищенное

    Какими по умолчанию объявляются методы класса?

    (1) private

    (2) public

    (3) protected

    (4) по умолчанию не объявляются

    Какие бывают конструкторы?

    (1) по умолчанию

    (2) с параметрами

    (3) инициализирующий

    (4) копирующий

    Выберите наиболее правильный вариант объявления оператора присваивания в классе A:

    (1) A& operator=(const A& a);

    (2) const A& operator=(const A& a);

    (3) A& operator=(A a);

    (4) A& operator=(const A a);

    В чем заключается суть компоновки программы?

    (1) в переводе текстового файла в объектный модуль

    (2) в подготовке программы к выполнению

    (3) в объединении нескольких фрагментов программы в один

    У какой переменой в данном коде самое короткое «время жизни»?

      
    char foo(char my_ch)
    {
    char ch= my_ch;
    static int flag = 1;
    if (flag){
    char p;
    p=ch;
    ch=ch+1;
    }
    …..
    return ch;

    }

    Какие ключевые слова используются для создания и обработки исключительных ситуаций?

    (1) try

    (2) delete

    (3) catch

    (4) return

    (5) throw

    Что понимается под потоком в языке C++

    (1) обмен данными между программами

    (2) обмен данными между компилятором и функцией main

    (3) механизм ввода-вывода

    Отметьте все утверждения, которые считаете верными:

    (1) нельзя с помощью шаблона создать функцию с таким же именем, как у явно определенной функции

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

    (3) в качестве описания шаблона функции используется прототип шаблона: template <список _параметров _шаблона >

    Укажите неправильный идентификатор:

    (1) AB_D1

    (2) 10xd

    (3) z1d8_14f3

    Отметьте фрагменты кода, которые можно назвать выражениями:

    (1) f + r*12 – 14

    (2) int z;

    (3) x = y = 13

    Укажите правильное объявление?

    (1) int 5;

    (2) float fl,int i

    (3) float F3v7G8t9F; int iCr3;

    Прототип функции задает

    (1) тип функции, включая количество и тип аргументов и тип результата

    (2) возможность выполнения этой функции из программ на других языках программирования

    (3) имя функции и минимальное количество параметров

    Какие из перечисленных типов являются встроенными типами языка С++?

    (1) float

    (2) real

    (3) integer

    (4) bool

    Для чего нужны классы?

    (1) для определения новых типов в программе

    (2) для упрощения работы со сложными структурами данных

    (3) для упрощения работы с константами

    (4) для соединения данных и операций над ними

    Если в массиве A 132 элемента, каким будет правильное обращение к последнему элементу массива?

    (1) A[132]

    (2) A[131]

    (3) A[133]

    (4) A[-1]

    Что из себя представляет динамическое выделение памяти?

    (1) память под объект (переменную) выделяется каждый раз при обращении к переменной

    (2) память под объект (переменную) может выделяться не сразу, а в процессе работы программы, освобождение памяти производится автоматически после завершения программы

    (3) память под объект (переменную) может выделяться не сразу, а в процессе работы программы, освобождение памяти производится вручную

    Программа на языке Си++ начинает выполняться с:

    (1) первой функции в программе

    (2) функции main

    (3) той функции, которая указана как стартовая при компиляции программы

    Если записано

    class A { public: void f() { cout << 1; } };
    class B : public A
    { public: void f() { cout << 2; } };

    то что будет напечатано в результате выполнения кода?

    B b; A& a=b; a.f();

    (1) 2

    (2) 2 1

    (3) 1 2

    (4) 1

    (5) ошибка

    Что целесообразно определять в public разделе класса?

    (1) все,чтобы уберечь себя от ошибок

    (2) все,что относится к интерфейсу класса

    (3) все невиртуальные методы

    Каким может быть аргумент деструктора?

    (1) адрес объекта

    (2) указатель this

    (3) аргумента не может быть

    (4) уничтожаемый объект

    Класс B наследован от класса A. Отметьте верное для класса B.

    (1) объект класса B может использоваться как объект базового класса

    (2) класс B должен быть определен с ключевым словом derived

    (3) класс B может непосредственно обращаться к внутренним атрибутам базового класса

    Если в функции main() выполняется


    int a=9;  // в глобальном пространстве
    void f() { int a; a = 4; }

    cout << a;

    то что будет выведено?

    (1) 9

    (2) 0

    (3) 4

    (4) другой

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

    (1) можно, но обрабатывать их следует внутри деструктора

    (2) да, никаких проблем возникнуть не может

    (3) нет, компилятор выдаст ошибку

    (4) да, но результат будет непредсказуем

    Имеется функция шаблон

    template <class T>
    T func1(T a,T b)
    {
    if(a)
    a=a%b;
    return a;
    }

    Верен ли код

    float a=5,b=6,c;
    c=func1(a,b);

    (1) да, все верно

    (2) нет, ошибка связана с типом операндов в операторе if

    (3) нет, ошибка связана с типом операндов в операторе %

    Какой длины может быть идентификатор

    (1) не более 64 символов

    (2) не более 128 символов

    (3) в самом языке по длине нет ограничений

    Чему равен результат вычисления выражения

    x + 3 * b + x

    при x = 12 и b = 8 ?

    Если int n=3, какой будет результат ?

    switch(n) {
    case 2: cout << «ааа»; break;
    case 3: cout << «ббб»; break;
    default: cout << «ввв»; break; }

    (1) ошибка компилятора

    (2) ааа

    (3) ббб

    (4) ввв

    (5) неопределенное поведение

    Отметьте, какому определению функции может соответствовать вызов func(5.98):

    (1) int func(double x = 0, double y);

    (2) void func(double x);

    (3) double func(double x, int y = 12, int z = 5);

    (4) void func(float arg1, float arg2 = 0);

    (5) float func(float g, float f);

    Какая из записей соответствует обращению к атрибуту m_arg класса AC в определении метода этого же класса?

    (1) this.m_arg.AC

    (2) m_arg

    (3) AC.this.m_arg

    Что описывает данный программный код?

    struct {
    char fio[30];
    int date, code;
    double salary;
    }staff[100], *ps;

    (1) определение массива структур и указателя на структуру

    (2) копирование строки salary в строку staff

    (3) создание указателя на строку date

    (4) создание итератора с указателем на строку date

    Компилятор языка Си++:

    (1) переводит текст программы в машинные инструкции

    (2) выполняет программу

    (3) форматирует текст программы так, чтобы его было удобно читать

    Если имеется абстрактный класс А и производный от этого класса класс А1
    то какая из записей заведомо неверна?

    (1) A a;

    (2) A1 a1;A& a=a1;

    (3) A* a= new A;

    Можно ли перегружать оператор разрешения области видимости -«::»

    Что произойдет, если определение класса будет находиться в файле в двух местах?

    (1) будет использоваться второе определение

    (2) второе определение будет проигнорировано

    (3) зависит от других факторов

    (4) ошибка компиляции

    (5) ошибка произойдет при сборке программы из нескольких файлов

    Отметьте все верные утверждения о статических атрибутах класса:

    (1) по умолчанию помещаются в public часть класса

    (2) существуют в единственном экземпляре, независимо от количества объектов

    (3) инициализируются при создании первого объекта

    (4) не могут изменяться

    (5) инициализируются в начале выполнения программы

    Если заданы классы

    class A {… } A1;
    class B : public A { … } B1;
    class C : public A { … } C1;

    то что будет выведено при выполнении оператора

    throw (C1);

    а обработка исключительной ситуации записана

    catch (B& b) { cout << 1; }
    catch (C& c) { cout << 2; }
    catch (A& a) { cout << 3; }
    catch (…) { cout << 4; }

    (1) 1

    (2) 2

    (3) 3

    (4) 4

    (5) 3 4

    (6) 2 3 4

    Что будет выведено в результате

    double x = 12.4;
    cout << setw(5) << x << setw(3)
    << setfill(‘*’)<< «» << endl;

    (1) "12.40***"

    (2) " 12.4***"

    (3) "12.4 * *"

    (4) "12.40"

    (5) ".124e2**"

    Отметьте все утверждения, которые считаете верными:

    (1) нельзя с помощью шаблона создать функцию с таким же именем, как у явно определенной функции

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

    (3) в качестве описания шаблона функции используется прототип шаблона: template <список _параметров _шаблона >

    Какое слово из списка не относится к зарезервированным словам Си++?

    (1) try

    (2) union

    (3) cast

    (4) volatile

    Если функция вычисления суммы целых чисел от 1 до n имеет прототип int sum(int n), запишите определение функции, используя рекурсию:

    (1)

    {if (n == 1) return 1;
    else return n + sum (n – 1);}

    (2)

    {if (n == 1) return 1;
    else return sum(n);}

    (3)

    {if (n == 1) return 1;
    else return sum(n) + (n – 1);}

    (4)

    {if (n == 1) return 1;
    else return sum(n) + sum (n-1);}

    Что произойдет после объявления в программе данного набора перечисляемых значений: enum{N=0, E=1, S=2, W=3};?

    (1) программа будет работать с числовыми значениями N, Е, S и W

    (2) программа будет работать с идентификаторами N, Е, S и W

    (3) программа сформирует итераторы с указателями на N, Е, S и W

    (4) программа будет игнорировать все указатели на N, Е, S и W

    Какой из приведенных ниже прототипов операции сложения для класса

    class A {int x; double y; … }

    является наиболее правильным?

    (1) void operator+ (const A& a) const

    (2) const A& operator+(A a)

    (3) A operator+(const A a) const

    (4) const A& operator(const A& a)

    Какой результат следующего выражения ?

    int* a; int b; a = &b; b = 7; *a++; cout << b;

    (1) 7

    (2) 8

    (3) не определено

    (4) ошибка компиляции

    Что будет напечатано в результате выполнения следующего кода?

    int x=39, *p = &x;
    cout << p << «__» << *p ;

    (1) ошибка компиляции

    (2) не определено

    (3) адрес в памяти__39

    (4) 39_адрес в памяти

    Процесс компиляции программы

    (1) переводит исходный текст в исполняемый файл

    (2) приводит программы к единообразному внешнему виду

    (3) для языка Си++ необязателен

    Какая из записей является правильной записью абстрактного класса?

    (1) abstract class A { virtual int f() = 0; };

    (2) class A { virtual int f() = 0; };

    (3) class A { virtual int f(); };

    Какие из следующих объявлений метода func синтаксически правильны?

    (1) void func(const Foo& a);

    (2) void func(Foo& a) const;

    (3) void func(const Foo& a) const;

    Если в классе операция new переопределена как

    void* operator new(size_t size, int a);

    то какой вызов этой операции правильный?

    (1) Foo* ptr = new (20) Foo;

    (2) Foo* ptr = new Foo(20);

    (3) Foo* ptr = new [20] Foo;

    Нужно ли учитывать при перегрузке бинарных операций порядок следования операндов?

    (1) необходимо учитывать

    (2) необходимо определять

    (3) необязательно учитывать

    (4) необязательно определять

    Объявление extern int f; означает:

    (1) переменная определена в другом исходном файле

    (2) переменная определена в стандартной библиотеке

    (3) переменная может быть использована только в этом файле

    Если в конструкторе класса

    class A {
    public:
       A() { ptr = new char[size];
            Init(); }
       ~A() { if (ptr) delete[] ptr; }
       char* ptr; };

    произойдет исключительная ситуация, будет ли потеряна память при откате по стеку?

    (1) да, будет, во всех случаях

    (2) будет, только если объект класса создавался с помощью new

    (3) будет, если создавалась автоматическая переменная класса a

    (4) нет, не будет

    (5) зависит от конкретного компилятора

    Результат работы программы:

    #include <iostream.h>
    int main()
    {
    int ic;
    cout << «Введите любую десятичную цифру:»;
    cin >> ic;
    switch (ic)
    {
    case 0: case 1: cout << «один «;
    case 2: case 3: cout << «три «;
    case 4: case 5: cout << «пять «;
    case 6: case 7: cout << «семь «;
    case 8: case 9: cout << «девять «; break;
    default: cout << «ERROR!!!»;
    }
    return 1;
    }

    (1) компилятор найдет ошибку

    (2) если введена нечетная цифра, выводится ее название

    (3) в любом случае выводится «ERROR!!!»

    (4) выводятся названия всех нечетных цифр больше или равных введенной

    Шаблон A и его специализации объявлены следующим образом:

    template <class T> class A
    {
    public:
    A(){ printf(«1 «);}
    };
    template <> class A<int>
    {
    public:
    A(){ printf(«2 «);}
    };
    template <> class A<char*>
    {
    public:
    A(){ printf(«3 «);}
    };

    Какой будет результат после выполнения кода

    A<int> a;
    A<char> a1;
    A<long> a2;

    (1) 2 3 1

    (2) 2 1 1

    (3) 1 1 1

    (4) ошибка компиляции в строке "template <> class A<int>"

    Выберите правильное объявление константы pi:

    (1) const float pi = 3.14;

    (2) float pi = (const) 3.14;

    (3) const float pi 3.14;

    Операция ++

    (1) увеличивает значение переменной на единицу

    (2) увеличивает значение переменной на два

    (3) уменьшает значение переменной на единицу

    (4) уменьшает значение переменной на два

    (5) в языке Си++ не существует

    Что означает запись for (;;);?

    (1) бесконечный цикл

    (2) цикл, который не выполняется ни разу

    (3) ошибка компиляции

    (4) аварийный выход из программы

    Совокупность типов формальных параметров, их порядка и имени функции определяет:

    (1) тип возвращаемого функцией значения

    (2) сигнатуру (подпись) функции

    (3) идентификатор функции

    (4) последовательность описаний и определений функции

    Укажите в какой строке кода произойдет ошибка компиляции?

    1: class Channel
    2: { public:
    3: void SetNumber (int n) { number = n;};
    4: int GetNumber() const { return number;};
    5: int number;};
    6: int main()
    7: { Channel ch;
    8: ch.number = 9;
    9: ch.SetNumber(10);
    10: Channel ch2(2);
    11: return 1;
    12: }

    (1) в пятой

    (2) в восьмой

    (3) в десятой

    Для получения адреса переменной используется операция

    (1) *

    (2) &

    (3) ->

    (4) .

    (5) нет правильного ответа

    Что выполняет операция «delete [] v;» в данном ниже коде:

    class MyClass
    {
    int sz; // число элементов
    int * v; // указатель на целые
    public:
    MyClass ( int );
    ~MyClass ();
    int&operator [] ( int index ); // операция индексации
    };

    MyClass::~ MyClass()
    {
    delete [] v;
    }

    (1) удаляет первый элемент из массива «v»

    (2) удаляет последний элемент из массива «v»

    (3) удаляет весь массив «v», освобождая память

    (4) удаляет указатель на массив «v» из памяти

    В программе на языке Си++ обязательно имеется функция

    (1) head

    (2) start

    (3) prime

    (4) main

    (5) finish

    Какой будет результат выполнения следующего кода?

    class A {
    public:
    int inc(int x) { return ++x; };
    int inc(short x) { return x + 2; };
    };
    A obj; int y = 5;
    cout << obj.inc(y);

    Отметьте правильные объявления переменных

    (1) const int s = 10; int a[s];

    (2) int s = 10; const int a[s];

    (3) int s = 10; int a[] = new int[s+s];

    Конструктор класса — это метод, который вызывается при создании объекта для …(перечислить )

    (1) выделения памяти под динамические атрибуты класса

    (2) выделения памяти под статические атрибуты класса

    (3) инициализации атрибутов объекта

    (4) загрузки методов класса в память

    Если определена операция умножения для двух объектов класса A и операция преобразования к int, что будет вызвано при

    A a;
    int x;
    int y = a * x;

    (1) операция умножения, а затем преобразование к целому

    (2) преобразование к целому

    (3) только операция умножения

    Может ли статический метод класса быть объявлен как friend?

    Отметьте, какие возможности языка Си++ помогают предупреждать ошибки:

    (1) наличие встроенных типов данных

    (2) контроль типов при компиляции

    (3) возможность использовать указатели вместо массивов

    (4) обязательность объявления функций до их использования

    Какой класс используется для вывода данных во внутреннюю область памяти?

    (1) iostream

    (2) strstream

    (3) cout

    (4) strout

    (5) fstream

    Отметьте правильное определение константы:

    (1) const int DOZEN = 12;

    (2) const double;

    (3) double COFF = (const)1.2e10;

    Чему равно значение выражения !((1 || 0) && 0) ?

    (1) 0

    (2) 1

    (3) ошибка компиляции

    В каких выражениях произойдет зацикливание программы?

    (1) for (int iCount = 0; iCount <= 4;);

    (2) while (true);

    (3) while (false);

    Что вычисляет эта функция:

    double func(double x, int n) {
         if (n == 0) return 1;
         if (x == 0) return 0;
         if (n > 0) return x * func(x, n-1);
         if (n < 0) return func(x, n+1) / x;
    }

    (1) дробную степень вещественного ненулевого числа

    (2) целую степень любого числа

    (3) факториал

    (4) любую степень любого числа

    Укажите в каких выражениях правильно определены целочисленные переменные?

    (1) short x = 23;

    (2) unsigned int a = 2, b = 3;

    (3) const unsigned char c;

    (4) unsigned short g = 0x2;

    Верен ли код

    Team::Player p;
    p.GoGoGo();

    для класса Team, описанного следующим образом:

    class Team
    {
    public:
    int score;
    class Player
    {
    public:
    void GoGoGo(){ printf(«Player is going to a goal.n»); }
    };
    void SetScore(int x){ score = x;}
    void ShowScore(){printf(«%dn»,score);}
    };

    (1) да, верен

    (2) нет, неверен

    (3) верен, если сформировать контекстные указатели

    (4) верен, если увеличить его приоритет

    Укажите какой результат будет у следующего примера?

    int array[10]; array[1] = 2; array[10] = 3;
    cout << array[10];

    (1) ошибка компиляции

    (2) непредсказуемый результат из-за выхода за границы массива

    (3) 3

    (4) 0

    Какой будет результат у данного выражения?

    int *i = new int;

    i = new int;

    delete i;

    (1) ошибка компиляции

    (2) утечка памяти

    (3) выделение памяти под переменную не произойдёт

    Ключевое слово void обозначает что функция

    (1) возвращает число с плавающей запятой

    (2) возвращает целое число

    (3) ничего не возвращает

    (4) является главной

    Укажите какое из выражений будет правильным?

    class A
    { public : int a, b;
    protected : int func (float d) {};
    private : short i; } A1;
    class B : public A
    { private : int k; } B1;
    class C : public B
    { protected : int l, m; } C1;

    (1) B1.k = 3;

    (2) C1.a = C1.b;

    (3) C1.a = C1.i;

    (4) C1->a = C1->b;

    Какая функция класса, не являясь его компонентом, имеет доступ к его защищенным и внутренним компонентам?

    (1) дружественная

    (2) шаблонная

    (3) макрофункция

    (4) статическая

    В каких строках ошибка?

    new long[];
    new long[][2][4];
    new long[3][][4];

    (1) во всех строках ошибка – неизвестен размер

    (2) в строках нет ошибки

    (3) в первой строке ошибка – неизвестен размер

    (4) в последней строке ошибка – неправильный синтаксис

    (5) во всех строках ошибка – неправильный синтаксис

    Какой тип преобразования типов используется в следующем выражении?

    int a = 0; float f = 3.4; a += f;

    (1) неявное преобразование типов

    (2) явное преобразование типов

    (3) пользовательское преобразование типов

    С помошью какой директивы происходит подключение других модулей программы?

    (1) #include

    (2) #define

    (3) #ifndef

    Что является минимальной областью видимости имен?

    (1) модуль

    (2) блок

    (3) функция

    (4) класс

    Что может быть аргументом оператора throw?

    (1) целое число

    (2) объект класса

    (3) строка

    (4) ноль

    (5) условный оператор

    (6) вызов деструктора объекта

    (7) вызов оператора return

    Что произойдет при выводе в файл, открытый с помощью

    ofstream("filename", ios::out|ios::app|ios::trunc)

    (1) вывод будет производиться в двоичном виде

    (2) вывод будет производиться в конец файла

    (3) можно читать из файла

    (4) если файл существовал, его содержимое сотрется

    (5) вывод будет производиться записями фиксированной длины

    Отметьте правильный заголовок шаблона функции:

    (1) template <class T> void Sum(T x1, T x2);

    (2) class template <class T> Sum

    (3) template <T> class

    (4) template class <class T> Sum(T x1, T x2);

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

    (1) X + Y

    (2) c % d + 2

    (3) xx * Y

    (4) xx++

    Укажите каким будет результат вычисления k?

    int func()
    {
    int k = 10;
    for (int i = 0; i <= k; i++)
    {
    return 5;
    k = i;
    }
    }

    (1) 0

    (2) 10

    (3) 1

    (4) бесконечный цикл

    (5) 100

    Какое из следующих утверждений об операторе return является верным?

    (1) оператор return должен стоять последним в теле функции

    (2) оператор return завершает выполнение функции

    (3) в теле функции должен присутствовать только один оператор return

    Какой результат у следующего выражения?

    struct str
    {
    int a = 2;
    float b = 10;
    } m1, m2;
    cout << m1.a * m2.b;

    (1) 20

    (2) 2

    (3) 10

    (4) ошибка компиляции

    Что описывает данная строка программы: float mas=new int[3][2]?

    (1) создание двумерного динамического массива размерности 3*2

    (2) создание одномерного динамического массива из 3 элементов

    (3) создание одномерного динамического массива из 2 элементов

    (4) данная строка представляет собой ошибочную запись и работать не будет

    Какие компоненты могут входить в интегрированную среду программирования

    (1) текстовый редактор

    (2) отладчик

    (3) компилятор

    Сколько производных классов можно получить из базового класса?

    (1) неограниченное количество

    (2) количество производных классов определяется количеством базовых классов

    (3) определяется наличием абстрактного класса

    С помощью механизма friend можно разрешить обращение к внутренним элементам класса:

    (1) отдельной функции

    (2) отдельному методу другого класса

    (3) всем методам другого класса

    При использовании копирующего конструктора:

    (1) вновь созданный объект имеет те же атрибуты что и исходный

    (2) вновь созданный объект зависит от копируемого

    (3) вновь созданный объект не зависит от копируемого

    Какой результат будет у следующего выражения?

    int m = 1, n=2;
    double A = (double)m/n;
    cout << A;

    (1) 0.5

    (2) 0

    (3) 1

    (4) ошибка компиляции

    Если в программе объявлен макрос #define CIRC(x) (3.14159 * (x) * (x)), то как будет подставлен этот макрос в тексте программы при следующем вызове:

    S = CIRC(a + b);

    (1) S = (3.14159 * a + b * a + b);

    (2) S = (3.14159 * (a + b) * (a + b));

    (3) S = (3.14159 * (a + b));

    Что будет выведено в стандартный поток в результате выполнения программы

    int main()
    { int x1 = 5;
    x1 = x2 - 2;
    int x2 = x1;
    cout << x2;
    return 0;
    }

    (1) -2

    (2) 5

    (3) 0

    (4) ошибка компиляции

    Что будет на экране после выполнения программы

    #include <iostream.h>
    short x = 4, i = 0;
    void fun1()
    { if (i == 0) throw 2; }
    int fun2()
    { --x; fun1(); x++; return x; }
    int main()
    { try
    { fun2(); }
    catch (int)
    { cout << "Exception "; }
    cout << x << " " << i;
    }

    (1) Exception

    (2) Exception 4 0

    (3) Exception 3 0

    (4) ошибка компиляции

    Для чего предназначены манипуляторы потоков ввода-вывода?

    (1) для управления состоянием потока

    (2) для перенаправления потоков ввода-вывода

    (3) для создания объектов классов istream и ostream

    Какая строка данного кода производит специализацию шаблона?

    1:template<class Т> class А{
    2:int х;
    3:};
    4:template<class Т> class А<Т*> {
    5:long х;
    6:};
    7:template<template<class U> class V> class C{
    8:V<int> y;
    9:V<int*> z;
    10:};
    11:C<A> c;

    (1) последняя

    (2) предпоследняя

    (3) седьмая

    (4) четвертая

    Укажите правильные присваивания значений переменным и константам

    (1) float Y = 12, int Y = 3;

    (2) int iCode = 12123; int Viv_12 = iCode;

    (3) const int Const = 2; int iConst = Const + 3;

    Укажите в каком выражении используется операция с наивысшим приоритетом?

    (1) x << 3

    (2) c + D

    (3) a2 >> 5

    (4) k++

    Какое определение функции является правильным?

    (1)

    int f(int b,int c)
    {return;}

    (2)

    void f(int b,int c)
    {return b+c;}

    (3)

    int f(int b,int c)
    {return 0;}

    Если функция вычисления суммы целых чисел от 1 до n имеет прототип int sum(int n), то как будет выглядеть запись определения функции с использованием рекурсии?

    1.{if (n == 1) return 1;
    else return n + sum (n - 1);}
    2. {if (n == 1) return 1;
    else return sum(n);}

    (1) первый вариант

    (2) второй вариант

    (3) оба верны

    (4) оба ошибочны

    Представление и диапазоны значений вещественных чисел соответствуют стандарту:

    (1) ASCII

    (2) ANSI

    (3) IEEE

    (4) CP-1251

    Какой будет результат следующей программы?

    class t
    { public: int sum;
    float f(int a)
    {
    sum++;
    return sum * a;
    }
    } cl;
    int main()
    {
    cl.sum = 10;
    cout << cl.sum << " " << cl.f(4);
    }

    (1) 11 44

    (2) 10 44

    (3) 10 240

    Для создания исполняемого файла в системе Unix необходимо

    (1) только набрать текст в текстовый файл

    (2) только запустить любой из имеющихся компиляторов: GNU C++, g++, c++, cc

    (3) набрать текст в текстовый файл и запустить его на компиляцию любым из имеющихся компиляторов C++

    Какой результат будет у следующего выражения?

    class A
    { public : int a, b;
    protected : int z;
    private : short i; } A1;
    class B : private A
    { private : int k;} B1;
    int main()
    { B1.a = 10;
    cout << B1.a;
    }

    (1) 10

    (2) ошибка

    (3) 0

    Какой результат будет у следующего выражения?

    class A
    { friend int Freund();
    friend class B;
    public : int x, y;
    private: short i;
    } A1;
    class B
    { public : void func_B(); }B1;

    int Freund()
    { A1.x = 1; A1.y = 2; A1.i += 3;
    return A1.x + A1.y + A1.i;
    }
    void B::func_B()
    { A1.i = 2; }
    int main()
    { B1.func_B(); cout << Freund(); }

    (1) 6

    (2) 8

    (3) 12

    Если в производном классе переопределена операция new то:

    (1) все объекты класса и все производные от этого класса будут использовать эту операцию

    (2) производные от этого класса могут использовать глобальную операцию применив операцию ::new

    (3) базовый класс также будет использовать переопределенную операцию

    Если в арифметическом выражении участвуют целый и вещественный операнды, то:

    (1) целый тип приводится к вещественному

    (2) вещественный тип приводится к целому

    (3) ошибка компиляции

    Отметьте истинные высказывания в отношении использования макросов и функций:

    (1) в большинстве случаев функции позволяют сокращать объем выполняемого файла

    (2) в большинстве случаев макросы позволяют сокращать время выполнения

    (3) недостатком макросов является отсутствие встроенного контроля согласования типов аргументов и формальных параметров

    namespace a {
         int a;
         void A() { static int a = 4; };
    };

    Какой будет результат выполнения cout << a::A::a; в функции main?

    (1) 4

    (2) 0

    (3) ошибка компиляции

    (4) ошибка выполнения

    Какой результат будет у следующего выражения?

    int main()
    { try
    {
    try
    {
    try { throw 1; }
    catch (int) { cout << "Exception 1"; }
    }
    catch (int) { cout << "Exception 2"; }
    }
    catch (int){ cout << "Exception 3"; }
    return 0;
    }

    (1) Exception 1Exception 2Exception 3

    (2) Exception 1Exception 2

    (3) Exception 1

    (4) Exception 2

    (5) Exception 3

    Что будет на экране после выполнения программы?

    #include <iostream.h>
    #include <fstream.h>
    #include <string.h>
    int main()
    {
    int i = 1, j = 25; double a = 25e6; char s[40];
    strcpy(s, "Test");
    ofstream outfile("c:test.dat");
    if (!outfile)
    { cout << "Ошибка создания файла";
    return 1; }
    outfile << i << ' ' << j << ' ' << a << ' ' << s << endl;
    outfile.close();
    }

    (1) будет создан текстовый файл "C:test.dat" с содержимым "1 25 2.5e+07 Test"

    (2) будет выведено сообщение "Ошибка создания файла"

    (3) будет создан текстовый файл "C:Test" с содержимым "1 25 2.5e+07 Test"

    (4) будет создан двоичный файл

    (5) в ходе выполнения возникнет исключение

    Какой будет результат следующего выражения?

    template <class T> T sum(T *a, T *b)
    { T f = 5;
    return (*a + *b) - f;
    }
    int main()
    { int i = 10, j = 20; double x = 5.1, y = 2.2;
    cout << sum(&i, &j) << " " << sum(&x, &y);
    }

    (1) 25 2.3

    (2) 20 2.3

    (3) 25 22

    (4) ошибка компиляции

    Укажите в каких выражениях используются ключевые слова?

    (1) sdf = 2; int r = 24;

    (2) TStringList *S = new TStringList;

    (3) x = 3; x = x + 4;

    (4) void function()

    Чему будет равен результат вычисления:

    int i, k = 2, m = 10;
    i = (m)/(m/k - 5);

    (1) 10

    (2) 0

    (3) 5

    (4) ошибка во время исполнения

    Что будет выведено на экран в результате выполнения кода?

    int a=3;
    if (a>1)
    cout << "1";
    else
    if(a>2)
    cout << "2";
    else
    if(a>3)
    cout << "3";

    (1) 123

    (2) 12

    (3) 1

    Имеется функция

    int sum(int a,int a1=2,int a2,int a3,int a4)
    {return a+a1+a2+a3+a4;}

    что будет выведено на экран после выполнения кода cout<<sum(9,8,7,6);

    (1) ничего,будет ошибка компиляции

    (2) 30

    (3) 32

    Если имеется объявление char ch1='a',ch2='b',ch3='c';
    допустима ли запись ch1=ch2+ch3;

    (1) нет

    (2) да

    Какие операторы не могут быть переопределены пользователем:

    (1) %

    (2) ::

    (3) .

    Что будет выведено на экран в результате выполнения данного кода?

    int main()
    {
    ......
    float a1 = 7, a2 = 8;
    {
    float p=4;
    float p1=5;
    p1+=5;
    }
    if (a2) a1+=a2+p1++;
    cout << a1;
    .....
    }

    (1) 25

    (2) 24

    (3) ничего из-за ошибки компиляции

    Существует ли в С++ готовый набор шаблонов:

    (1) нет

    (2) да,существует специальная библиотека STL

    (3) зависит от версии компилятора

    Какой статус международного стандарта языка Си++?

    (1) принят только в США и ждет одобрения международной организации

    (2) принят ISO и тем самым автоматически принят во всех странах

    (3) принят проект стандарта, дорабатывается

    Что будет в результате выполнения следующей программы?

    class Base{

    public: void f();
    private: int *baseID;
    };
    class Derived : public Base{

    public: void foo();
    private: int derivedID;
    };
    Derived my1;
    int x;
    my1.baseID=&x;

    (1) присвоение будет выполнено

    (2) результат непредсказуем

    (3) ошибка компиляции

    (4) сообщение об ошибке внутренних адресов

    Какими по умолчанию объявляются элементы структуры?

    (1) private

    (2) public

    (3) protected

    (4) по умолчанию не объявляются

    Имеется три объекта класса А:
    A a1,a2,a3 в каком выражении возможен неявный вызов конструктора копирования

    (1) a3=a1+a2;

    (2) a2.sum(a3);

    (3) a2.sum(&a3);

    Что является результатом компоновки программы?

    (1) заголовочный файл

    (2) исполняемый файл или библиотека

    (3) набор заголовочных файлов с определением в них всех используемых функций

    У какой переменой в данном коде самое длинное "время жизни"?

      
    char foo(char my_ch)
    {
    char ch= my_ch;
    static int flag = 1;
    if (flag){
    char p;
    p=ch;
    ch=ch+1;
    }
    .....
    return ch;

    }

    (1) p

    (2) flag

    (3) ch

    Функция вычисляет произведение двух чисел. Исходные данные вводятся с клавиатуры. Какие проверки целесообразно ввести в программе?

    (1) проверка исходных данных на равенство нулю

    (2) проверка, что исходные данные являются числами и эти числа больше нуля

    (3) проверка, что исходные данные являются числами

    (4) проверки не нужны, все возможные ошибки отловит компилятор

    Какое из представленных выражений выводит на экран

    0000
    0 0
    0000

    Замечание:при написании собственных программ с использованием форматированного ввода/вывода подключите файл iomanip.h

    (1)

    cout<<setw(4)<<setfill('0')<<""<<endl;
    cout<<setw(1)<<setfill('0')<<"";
    cout<<" "<<setw(1)<<setfill('0')<<""<<endl;
    cout<<setw(4)<<setfill('0')<<""<<endl;

    (2)

    cout<<setw(4)<<setfill('0')<<""<<endl;
    cout<<setw(1)<<setfill('0')<<""<<" "<<setw(1)<<setfill('0')<<""<<endl;
    cout<<setw(4)<<setfill('0')<<""<<endl;

    (3)

    cout<<setw(4)<<setfill('0')<<""<<endl<<setw(1)<<setfill('0')<<""
    <<" "<<setw(1)<<setfill('0')<<""<<endl<<setw(4)<<setfill('0')<<""<<endl;

    Какой правильный заголовок шаблона

    (1) template <class t1, class t2>

    (2) template <class t1,t2>

    (3) template <class t, class t>

    (4) template <class t,t>

    Укажите правильный идентификатор для имени переменной:

    (1) FA_Ф12

    (2) _ri18

    (3) int

    (4) 2a

    Операции в выражениях могут быть

    (1) только унарными или бинарными

    (2) только бинарными

    (3) унарными,бинарными или тернарными

    При выполнении фрагмента кода

    int x = 3, y = 2, z = 1;
    if(x >= y)
         if(y <= z)
           std::cout << "Вариант 1";
         else
         if(x >= z)
          std::cout << "Вариант 2";
         else
        std::cout<<"Вариант 3";

    будет напечатано:

    (1) Вариант 1

    (2) Вариант 2

    (3) Вариант 3

    (4) ничего не будет напечатано

    (5) программа не откомпилируется

    Сколько функций может быть в программе С++?

    (1) ни одной

    (2) минимум одна

    (3) не больше 100

    Какие из следующих выражений являются константами типа double?

    (1) 3.0 + 2

    (2) 5.00E+06

    (3) 0.8

    (4) 12lU

    В каком случае описание класса верно?

    1. class A
    {

    public:
    int x;

    int summ(int a){return x+a;}
    };

    2. class my_cl
    {
    public:
    int f;

    int summ(int a){return x+a;}
    };

    3. class B
    {
    public:
    int F;

    void Ch_F(int x)
    {
    F=x;
    return F;
    }
    };

    (1) в первом

    (2) во втором

    (3) в третьем

    Если имеется объявление int a[15], какой фрагмент кода выводит все элементы массива?

    (1) for (int k = 0; k <= 15; k++) cout << a[k];

    (2) for (int k = 1; k < 16; k++) cout << a[k];

    (3) for (int k = 0; k < 15; k++) cout << a[k];

    Какое из приведенных выражений верно?

    (1) int a; a = new int[20];

    (2) int a; a = new int(20);

    (3) int *a; a = new int[20];

    (4) int *a; a = new 20;

    (5) int *a; a = new sizeof(int*20);

    (6) int a; a = new sizeof(int*20);

    Что такое cout?

    (1) объект типа iostream (std::ostream)

    (2) класс, который выводит данные на терминал

    (3) переменная, которую программист должен создать для вывода данных

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

    monstr Super(200, 300), Vasia(50), Z;
    monstr X = monstr(1000);
    monstr Y = 500;

    (1) нет, не будет

    (2) да, будет

    (3) данная конструкция содержит синтаксическую ошибку

    (4) данный код вызовет ошибку компиляции

    Для переопределенного оператора верно:

    (1) переопределенный оператор выполняется с тем же приоритетом, что и исходный оператор

    (2) у переопределенного оператора не может быть аргументов по умолчанию

    (3) переопределенный оператор может быть унарным оператором

    (4) если переопределенный оператор не является методом класса, то у него должен быть, по крайней мере, один аргумент типа класса, определенного пользователем

    Сколько вызовов системных функций всегда будет в программе, независимо от ее реализации?

    (1) всегда 2

    (2) по крайней мере один

    (3) не менее двух

    Если имеется программа

    int a = 7;  //в глобальном пространстве
    void f() { static int a; a += 5; }

    и в функции main() выполняется

    f(); cout << a;

    то какой будет результат?

    (1) 5

    (2) 7

    (3) 12

    (4) другой

    На каком этапе обнаруживаются ошибки в алгоритме программы?

    (1) на этапе компиляции

    (2) на этапе выполнения

    (3) они могут не проявиться никогда, все зависит от входных данных

    Если имеется код int x; cin >> x; и вводится "1.2", то что будет в переменной x?

    (1) 1

    (2) 2

    (3) 1.2

    (4) другое

    (5) произойдет ошибка

    Если есть два объявления int qwerty; int QWERTY; какое из
    утверждений верно

    (1) такие имена переменных недопустимы

    (2) объявления правильные

    (3) такие объявления недопустимы, так как мы пытаемся создать две одинаковые переменные

    Чему равен результат вычисления выражения

    x + 3 * b / 2

    при x = 12 и b = 8 ?

    (1) 24

    (2) 60

    (3) 120

    Если в программе уже имеется функция с прототипом int func(int k, double f), то какое из следующих объявлений не вызовет ошибки компиляции?

    (1) double func(int m, double g)

    (2) int func(double x, int y)

    (3) void func(int m, double g = 3.14)

    Что произойдет после следующего примера?

    cout << "nnn" << "Hello World" << "a";

    (1) прозвенит звонок и выйдет сообщение "Hello World"

    (2) в четвертой строке выйдет сообщение "Hello World" и прозвенит звонок

    (3) выйдет сообщение "Hello Worlda"

    Если имеется код

    class A { public: int a; };
    A obj;

    как обратиться к переменной a?

    (1) obj.a

    (2) obj-a

    (3) obj::a

    Определите размер структуры

    struct {
    char fio[30];
    unsigned char date:4;
    unsigned char code:4;
    };

    (1) 9 байт

    (2) 38 байт

    (3) 31 байт

    (4) 39 байт

    Какой будет результат ?

    int f(int& x) {
    static int a = 0; if (!a) a = ++x; return a;}
    int y = 6; f(y); cout << f(y) << y;

    (1) 77

    (2) 78

    (3) 70

    (4) 79

    После компиляции программы

    (1) ее можно выполнять многократно без перекомпиляции

    (2) перед каждым последующим запуском ее нужно перекомпилировать

    (3) ее можно выполнять только с одним набором исходных данных

    Если имеется класс с двумя атрибутами

    class Complex {
         double real;
         double img;
    . . .
    };

    какой у него должен быть деструктор?

    (1) виртуальный деструктор

    (2) деструктор, вызывающий деструкторы атрибутов

    (3) деструктор не обязателен

    Какой тип будет у следующего выражения ?

    void* + reinterpret_cast<int*>1000

    (1) void*

    (2) int*

    (3) int

    (4) ошибка компиляции

    (5) ошибка выполнения

    Что произойдет, если определение функции будет находиться в файле в двух местах?

    (1) будет использоваться второе определение

    (2) второе определение будет проигнорировано

    (3) зависит от других факторов

    (4) ошибка компиляции

    (5) ошибка произойдет при сборке программы из нескольких файлов

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

    (1) не могут иметь аргументов по умолчанию

    (2) не существуют до создания первого объекта класса

    (3) не могут объявляться со словом const в конце объявления

    Если заданы классы

    class A {... } A1;
    class B : public A { ... } B1;
    class C : public A { ... } C1;

    то что будет выведено при выполнении оператора

    throw (A1);

    а обработка исключительной ситуации записана

    catch (B& b) { cout << 1; }
    catch (C& c) { cout << 2; }
    catch (A& a) { cout << 3; }
    catch (...) { cout << 4; }

    (1) 1

    (2) 2

    (3) 3

    (4) 4

    (5) 3 4

    (6) 2 3 4

    Какой из стандартных классов используется для вывода строк на терминал:

    (1) strstream

    (2) ostream

    (3) ofstream

    (4) istream

    (5) ifstream

    Какой правильный заголовок шаблона

    (1) template <class t1, class t2>

    (2) template <class t1,t2>

    (3) template <class t, class t>

    (4) template <class t,t>

    Какое из приведенных имен является недопустимым в Си++?

    (1) x03488erJJJ___

    (2) xb___@

    (3) r13

    (4) OOP

    Каково будет значение переменной k после выполнения следующего оператора

    k = ++k;

    если до его выполнения k равнялось 6?

    (1) 6

    (2) 7

    (3) 8

    Если функция вычисления факториала n имеет прототип int fact(int n), отметьте код, использующий рекурсию и правильно вычисляющий значение факториала:

    (1)

    {if (n == 0 || n==1) return 1;
    else return n * fact (n -1);}

    (2)

    {if (n == 0 || n==1) return 1;
    else return fact(n);}

    (3)

    {if (n == 0 || n==1) return 1;
    else return fact(n) * fact(n-1);}

    (4)

    {if (n == 0 || n==1) return 1;
    else return fact(n)*(n-1);}

    Какой из наборов перечисляемых значений записан правильно?

    (1) enum { a, b, 3, 4 };

    (2) enum { a, b = 3, c = 4, 3 };

    (3) enum {a, b = 3, c, d };

    Какой будет результат выполнения следующего кода?

    class A {
    public:
         int inc(int x) { return ++x; };
         int inc(short x) { return x + 2; };
    };
    A obj; int y = 5;
    cout << obj.inc(y);

    (1) 6

    (2) 7

    (3) 8

    Какой результат следующего выражения ?

    int *a; int b[2]; a = b;
    b[0] = 7; b[1] = 10; *a++; cout << *a;

    (1) 7

    (2) 8

    (3) 10

    (4) 11

    Что будет напечатано в результате выполнения следующего кода?

    char x[] = "Hello, world";
    char* p = x; p += 4; cout << *p;

    (1) o

    (2) o, world

    (3) Hello, world

    Комментарий в программе на Си++

    (1) содержит указания компилятору по настройке программы

    (2) содержит пояснения к тексту и не оказывает влияния на выполнение программы

    (3) должен содержать допустимые аргументы программы

    Абстрактный класс – это класс, в котором

    (1) есть виртуальный конструктор

    (2) есть виртуальный деструктор

    (3) есть хотя бы один чисто виртуальный метод

    Произойдет ли ошибка при использовании следующей конструкции:

    class A { const int f() { . . .}; };
    g(const& A a) { a.f(); . . . }?

    (1) не произойдет

    (2) да, ошибка компиляции

    (3) да, ошибка выполнения

    Если в классе операция delete переопределена как

    void operator delete(int x, void* addr)

    то при вызове

    A* ptr; . . . delete(10, ptr);

    (1) ошибка компиляции

    (2) освободится массив из 10 элементов типа a

    (3) аргумент 10 будет передан в оператор delete

    Какие операции поддаются перегрузке?

    (1) только унарные

    (2) только бинарные

    (3) унарные и бинарные

    Для чего предназначен фрагмент текста из заголовочного файла:

    //Пример файла test.h
    #ifndef TEST
    #define TEST
    //прочие строки кода
    #endif

    (1) для целей отладки

    (2) для определения символьной константы Test

    (3) для защиты от повторного включения файла test.h

    (4) для защиты от копирования

    (5) для защиты от удаления

    Могут ли контексты быть вложенными?

    (1) могут

    (2) не могут

    (3) могут, при определенных условиях

    Оператор throw без аргументов

    (1) повторно вызывает обрабатываемую исключительную ситуацию

    (2) вызывает последнюю необработанную исключительную ситуацию

    (3) вызывает исключительную ситуацию типа Exception

    Результат работы программы:

    #include <iostream.h>
    int main()
    {
    char A[] = "ABC ";
    char *U = &A[2];
    cout << "n" << *U--;
    cout << *U-- ;
    cout << *U;
    return 1;
    }

    (1) ABC

    (2) BC

    (3) CBA

    (4) BCA

    Отметьте все неверные утверждения:

    (1) генерация класса из шаблона создает объект этого класса

    (2) память, занимаемая объектом класса, сгенерированного из шаблона, освобождается автоматически

    (3) шаблон не является классом

    (4) все классы-шаблоны наследуют класс Template

    (5) объекты классов, сгенерированных из шаблонов, занимают в памяти меньше места, чем объекты других классов

    Является ли x переменной или константой?

    y = 12 + x;

    (1) является переменной

    (2) является константой

    (3) определить нельзя

    Нелогической операцией является

    (1) &&

    (2) ==

    (3) ||

    (4) !

    (5) =

    Что означает запись while (false);?

    (1) бесконечный цикл

    (2) цикл, который не выполняется ни разу

    (3) ошибка компиляции

    (4) аварийный выход из программы

    Если функция имеет тип void, то неверно, что

    (1) она не может содержать оператор return

    (2) оператор return в теле функции необязателен

    (3) функция не возвращает никакого значения

    Укажите в каких строках кода произойдет ошибка компиляции?

    1: class A
    2: { public:
    3:      int x;
    4: int GetX() { return x; };};
    5: int main()
    6: {    A b;
    7:      b.SetX(2);
    8:      b.GetX();
    9:      A b1(2);
    10:}

    (1) в восьмой

    (2) в четвертой

    (3) в седьмой

    (4) в девятой

    Какая операция позволяет получить значение, записанное по адресу, который содержится в указателе?

    (1) *

    (2) ?

    (3) ^

    (4) &

    (5) .

    Что произойдёт при использовании неправильного адреса в операции delete?

    (1) произойдёт аварийное завершение программы

    (2) программа выдаст сообщение,что память освобождается по неправильному адресу

    (3) результат непредсказуем

    При выходе из функции main

    (1) программа повторяется с теми же аргументами

    (2) программа заканчивается

    (3) выполняется функция finish, определенная программистом

    Произойдет ли ошибка при компиляции этого кода?

    class Channel
    { public:
    void SetNumber (int n) { number = n;};
    int GetNumber() const { return number;};
    int number;};
    int main()
    { private : int n;} Channel ch;
    {Channel ch;
    ch.number = 9;
    ch.SetNumber(10);
    Channel ch2(2);}

    (1) нет, не произойдет

    (2) да, произойдет, запись неверна

    (3) произойдет при обнулении n

    (4) произойдет при n=10

    Какое из следующих объявлений является объявлением неизменяемого указателя?

    (1) int const* ptr;

    (2) const int* ptr;

    (3) int * ptr const;

    (4) int* const ptr;

    Какой класс может использоваться в качестве типа атрибута класса?

    (1) базовый класс данного класса

    (2) производный от данного класса

    (3) пользовательский класс

    (4) произвольный класс

    Если определена операция вычитания для двух объектов класса A, а операция преобразования к int не определена, что будет вызвано при

    A a1,a2,a3=5;
    a3 = a1 – a2;

    (1) преобразование к целому

    (2) операция вычитания, а затем преобразование к целому

    (3) только операция вычитания

    (4) произойдет ошибка

    Какова последовательность создания исполняемого файла:

  • Создать файл с исходным текстом программы, который будет иметь расширение .cpp.
  • Скомпоновать объектный файл с необходимыми библиотеками.
  • Скомпилировать исходный код.
  • (1) 1-3-2

    (2) 2-3-1

    (3) 1-2-3

    (4) 3-2-1

    (5) 2-1-3

    Может ли нестатический метод иметь доступ к статическим методам и атрибутам?

    (1) может

    (2) не может

    С помощью какого метода можно изменить текущую позицию в файле?

    (1) put

    (2) open

    (3) seekp

    Сколько параметров может быть у шаблона при определении шаблона функции ?

    (1) 1

    (2) столько, сколько аргументов у функции

    (3) столько, сколько типов используется для параметризации

    Отметьте все неправильные определения констант:

    (1) const int 12X = 12;

    (2) const int DAYS_OF_WEEK = 7;

    (3) int const x = 2i+56;

    Чему равно значение целой переменной при вычислении выражения 21/5*3?

    (1) 13.02

    (2) 1.47

    (3) 12

    (4) 1

    (5) другое значение

    Что выведет следующая программа?

    #include <iostream>
    int main() {
        int i;
        for(i = 0; i < 9; i++)
        std::cout << i+1;
        return 0;
    }

    (1) цифры от 0 до 8

    (2) цифры от 1 до 9

    (3) программа не будет построена из-за ошибок

    Как называется функция, которая вызывает саму себя?

    (1) конструктором

    (2) деструктором

    (3) подставляемой

    (4) рекурсивной

    Укажите в каком выражении правильно определена переменная в шестнадцатеричной системе счисления?

    (1) short x = 0x1244;

    (2) char c = 9340x;

    (3) unsigned long l = 0x84GAF;

    (4) int k = 0xCDeF;

    При определении метода запись this-> говорит о том, что:

    (1) атрибут принадлежит объекту, получившему сообщение

    (2) атрибут принадлежит классу, определенному в заголовочном файле

    (3) атрибут не принадлежит какому-либо классу

    Укажите какой результат будет у следующего примера?

    float array[5]; array[1] = 10; array[2] = 3;
    float array2[5]; array2 = array;
    cout << array2[1];

    (1) 10

    (2) 3

    (3) ошибка компиляции

    (4) 0

    В каком случае программа выведет строку на консоль

    (1)

    #include <iostream.h>
    using namespace std;
    void main()
    {
    cout < "Hello, world!" < endl;
    return;
    }

    (2)

    #include <iostream.h>
    using namespace std;
    void main()
    {
    cout >> "Hello, world!" >> endl;
    return;
    }

    (3)

    #include <iostream.h>
    using namespace std;
    int main()
    {
    cout << "Hello, world!" << endl;
    return 1;
    }

    Укажите какое из выражений будет правильным?

    class A
    { public : int a, b;
    protected : int z;
    private : short i; } A1;
    class B : public A
    { public : int c, d;
    private : int k;} B1;

    (1) A1.i = 10;

    (2) B1.k = A1.i;

    (3) B1.c = A1.a;

    Какой правильный вызов функции базового класса из объекта производного класса, если в производном классе эта функция была замещена?

    (1) FunctionName();

    (2) Base::FunctionName();

    (3) Base.FunctionName();

    (4) такую функцию вызывать нельзя.

    Отметьте правильный вариант освобождения всей памяти, выделенной для трехмерного массива для следующей программы

    long (*lp)[2][4];
    lp = new long[3][2][4];

    (1) delete [] lp;

    (2) delete lp;

    (3) delete [][] lp;

    (4) delete [][][] lp;

    Какое приведение типов используется в следующем выражении?

    int a = 0; float f = 3.4; f += (int)a;

    (1) неявное приведение типов

    (2) явное приведение типов

    (3) стандартное приведение типов

    В чем различие использования следующих выражений #include <...> и #include "..."

    (1) нет различий

    (2) различие заключается в методе поиска препроцессором включаемого файла

    (3) в различии использования заголовочных и исходных файлов

    Для чего предназначен оператор namespace?

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

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

    (3) для использования классов, переменных и функций из других модулей программы без использования заголовочных файлов

    Какие требования предъявляются к классу исключительных ситуаций?

    (1) он должен быть наследован от специального класса exception

    (2) он не может использовать множественное наследование

    (3) он должен содержать атрибуты только встроенных типов

    (4) он может быть произвольным классом

    Что означает cout << setw(3) ?

    (1) ширина поля вывода устанавливается равной 3

    (2) выводимые строки сокращаются до 3 символов

    (3) выводимые строки дополняются до 3 символов

    (4) нельзя ввести больше 3 символов за один раз

    Какой правильный вариант описания шаблона семейства классов?

    (1)

    template <class T>
    class Array
    {. . . };

    (2)

    template (class T)
    class Array
    {. . . };

    (3)

    template {class T}
    class Array
    {. . . }

    В каких выражениях используются унарные арифметические операции?

    (1) c1 + d2

    (2) s2 % d % 2

    (3) --b

    (4) d++

    Укажите каким будет результат вычисления цикла?

    int m = 2, n = 5;
    while (m <= 3)
    {
    while (m <= n)
    {
    n = m;
    break;
    }
    break;
    m++;
    }

    (1) m = 2; n = 10;

    (2) m = 5; n = 2;

    (3) m = 2; n = 2;

    (4) m = 120; n = 30;

    (5) m = 0; n = 2;

    Укажите правильный вызов функции, объявленной следующим образом: void Iterat(int a, float b, short c);

    (1) int res = 0; res = res + Iterat(2,4,34);

    (2) int k = 1; float l = 3; short m = 4; Iterat(k,l,m);

    (3) float d = Iterat(2,3.2,6);

    Укажите правильный доступ к членам класса:

    class my
    { public: double Z;
    int f(int c, int d) {return c+d;}
    char s;
    } T1, T2;

    (1) T1->Z = 23.1;

    (2) T2.f(4,1);

    (3) my.T2->s = 'L';

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

    ...
    // Необходимые библиотеки подключены
    struct my
    {
    int a, b;
    } m1;
    int func(my *f)
    {
    return f->a + f->b++;
    }
    int main()
    {
    m1.a = 2; m1.b = 5;
    cout << func(&m1);
    return 1;
    }

    (1) 7

    (2) 8

    (3) 1

    (4) ошибка компиляции

    Определите результат выполнения следующего кода:

    float *thingPtr = new float (3.14159)

    (1) возникнет ошибка компиляции, поскольку нельзя задавать значение переменной в процессе её создания

    (2) возникнет ошибка компиляции, поскольку при создании объекта не указан размер выделяемой памяти

    (3) данная строка задает значение объекту типа float

    Файл имеющий имя "test_file.cpp" это:

    (1) исполняемый файл

    (2) заголовочный файл

    (3) файл исходного текста языка C++

    (4) динамически загружаемая библиотека

    В чем заключается принцип полиморфизма?

    (1) в наличии виртуальных методов

    (2) в наличии множественного наследования

    (3) в использовании виртуального наследования

    Укажите правильное использование оператора friend

    (1)

    class A { friend int CountPass(); private: short i;};

    (2)

    class A { public : friend int A::CountPass(); private: short i;};

    (3)

    class A { public : int A1::CountPass(); friend: short i;};

    Известно, что в классе A определен один публичный конструктор A(int);. Выберите из предложенных выражений компилируемые:

    (1) A *a(4);

    (2) A a;

    (3) A *a = new A(4);

    (4) A a(4);

    Какой результат будет у следующего выражения?

    int m = 1,n=2;
    int *p= &n;
    p=static_cast<int*> (m);
    cout << *p;

    (1) 1

    (2) адрес переменной m

    (3) 2

    (4) ошибка компиляции

    Какой результат будет у следующего выражения?

    #define CIRC(x) (3 * (x) * (x))
    #include <iostream.h>
    int main()
    {
    int a = 1, b = 2;
    std::cout << CIRC(a + b);
    }

    (1) 9

    (2) 27

    (3) 16

    (4) 28

    Что будет на экране после выполнения программы

    func() { int a = 10; }
    int main()
    { int x2 = a + 1;
    cout << x2;
    return 0;
    }

    (1) 11

    (2) 1

    (3) 12

    (4) ошибка компиляции

    Что будет на экране после выполнения программы

    #include <iostream.h>
    short x = 4, i = 0;
    void fun1()
    { if (i == 5) throw 2; }
    void fun2()
    { --x; fun1(); x++; }
    int main()
    { try
    { fun2(); }
    catch (int)
    { cout << "Exception "; }
    cout << x << " " << i;
    }

    (1) Exception

    (2) Exception 4 0

    (3) Exception 3 0

    (4) 4 0

    Укажите основные используемые манипуляторы потоков.

    (1) flush

    (2) endl

    (3) printf

    (4) setw

    (5) <<

    (6) setprecision

    В каких случаях код будет работать правильно

    ...

    vector <int> IntVector;//объект класса вектор
    /*запись значений в IntVector*/
    int d=IntVector[0];
    ...
    }

    (1) если оператор [] переопределен соответствующим образом

    (2) если существует элемент IntVector[0]

    (3) ничего, так как эта запись ошибочна

    Укажите, где происходит объявление констант или переменных

    (1) float dD;

    (2) float sd2 = 3.2;

    (3) bType = 3;

    (4) int k; k = 89;

    В каких выражениях возвращаемое значение будет логическим?

    (1) int x = 2, y = 3, z; z = x + y;

    (2) (i > 3);

    (3) (c == b);

    В чем заключается назначение оператора перехода goto?

    (1) изменяет последовательность выполнения операторов в программе

    (2) необходим для передачи аргумента в функцию

    (3) для наглядного отображения листинга программы

    (4) для перехода из одной функции в другую

    Если функция вычисления факториала n имеет прототип int fact(int n), то как будет выглядеть запись определения функции с использованием рекурсии?

    1.{if (n == 1 || n == 0) return 1;
    else return n * fact (n -1);}
    2.{if (n == 1 || n == 0) return 1;
    else return fact(n);}

    (1) первый вариант

    (2) второй вариант

    (3) оба верны

    (4) оба ошибочны

    Укажите в каком выражении произойдет потеря точности

    (1) int i; float x = 2.134, y = 3.14; i = x/y;

    (2) float M = 235.2; double Z = 3; Z *= M;

    (3) short i = 0x3; float x = 2.7, v; v = i + x;

    Что будет выведено в стандартный поток вывода в результате исполнения следущей программы?

    class Add
    { public: short S1;
    int f(int x)
    { return S1 + ++x;}
    int A(short a, short b);
    } K1;
    int Add::A(short a, short b)
    {
    this->S1 += a*b;
    return this->S1;
    };
    int main()
    {
    K1.S1 = 2;
    K1.f(2);
    K1.A(0, 1);
    cout << K1.S1;
    return 0;
    }

    (1) 0

    (2) 10

    (3) 2

    (4) 4

    Какой массив имеет самый большой размер?

    char *s1= "QWERTY";
    char s2[7]= "qwerty";
    char *s3= "127*27";

    (1) Все массивы имеют одинаковый размер

    (2) s1

    (3) s2

    (4) s3

    Какой будет результат у данного выражения?

    int f1(int & x1, int *x2) { return ++x1 + ++(*x2); }
    int main()
    { int a = 7, k = 1;
    k = f1(a, &k);
    cout << a << " " << k;
    }

    (1) 7 10

    (2) 8 1

    (3) 8 10

    Укажите какие компиляторы языка Си++ могут быть использованы в системе Unix.

    (1) Microsoft© Visual C++

    (2) Emacs

    (3) GNU C++, g++, c++

    (4) любой компилятор языка С++

    Какой результат будет у следующего выражения?

    class A
    { public : int a, b;
    protected : int z;
    private : short i; } A1;
    class B : protected A
    { private : int k;} B1;
    int main()
    { B1.z = 20;
    cout << B1.z;
    }

    (1) 20

    (2) ошибка

    (3) 0

    На какой строке произойдет ошибка компиляции?

    1:class A
    2: { public: void f1(int &a){val+=a++;};//val инициализируется в конструкторе
    3: int f2() {return val+1;};
    4: int val;
    5: } A1;
    6:int main()
    7: {
    8: A1.f1();
    9: A1.f2();
    10: }

    (1) 3

    (2) 5

    (3) 8

    (4) 9

    В какой строчке данного кода сработает заданный в нем копирующий конструктор?

    1 monstr::monstr(const monstr &М){
    2 if (M.name){
    3 name = new char [strlen(M.name) + 1];
    4 strcpy(name, M.name);}
    5 else name = 0;
    6 health = M.health; ammo = M.ammo; skin = M.skin;
    7 }

    8 monstr Vasia (blue);
    9 monstr Super = Vasia;
    10 monstr *m = new monstr ("Orc");
    11 monstr Green = *m;

    (1) в шестой

    (2) в пятой

    (3) в четвертой

    (4) в одиннадцатой

    Если в арифметическом выражении участвуют короткое целое и длинное целое, то:

    (1) длинное приводится к короткому

    (2) короткое приводится к длинному

    (3) ошибка компиляции

    Какой результат будет у следующего выражения?

    // файл File1.h
    #ifndef __File_h__
    #define myconst 35
    #else
    #define myconst 30
    #endif

    // файл Test.cpp
    #include <iostream.h>
    #include "File1.h"
    int main()
    { cout << myconst + 10;
    }

    (1) 40

    (2) 45

    (3) 35

    (4) ошибка компиляции

    Что будет на экране после выполнения программы

    int m = 5;
    namespace space1
    { int x1 = 3;
    namespace space2
    { int x1 = 2 + ::m + space1::x1; }
    }
    int main()
    { int x3 = space1::space2::x1 * 2;
    { int x3 = 10; }
    cout << x3;
    return 0;
    }

    (1) 10

    (2) 20

    (3) 13

    (4) ошибка компиляции

    Какой результат будет у следующего выражения?

    int main()
    {
    try
    {
    try
    {
    try{ throw 1; }
    catch (float) { cout << "Exception 1"; }
    }
    catch (int){ cout << "Exception 2"; }
    }
    catch (int){ cout << "Exception 3"; }
    return 0;
    }

    (1) Exception 1Exception 2Exception 3

    (2) Exception 1Exception 2

    (3) Exception 1

    (4) Exception 2

    (5) Exception 3

    Существует файл "test.dat" в котором записано "Hello World".Каково будет содержимое файла после выполнения кода:

    ofstream outfile("c:test.dat",ios::in);
    if (!outfile)
    { cout << "Ошибка создания файла";
    return 1; }
    outfile << "!!!";
    outfile.close();

    (1) Hello World!!!

    (2) !!!lo World

    (3) !!!

    Что будет выведено на экран?

    template <class T> class A
    { public : T x1; T x2;
    T func(T x3)
    { x3 += x1 + x2;
    return x3;
    }
    };
    int main()
    { A <int> A1;
    A1.x1 = 3; A1.x2 = 10;
    cout << A1.func(1);
    ....
    }

    (1) 10

    (2) 14

    (3) 11

    (4) ошибка компиляции

    Отметьте истинные высказывания:

    (1) переменная объявляется, потом изменяется

    (2) переменная инициализируется, потом объявляется

    (3) переменная объявляется, потом инициализируется и изменяется

    Чему будет равен результат вычисления выражения: int d=5; bool b = true, c; c = (!b||(d>3));

    (1) true

    (2) Ошибка компилятора

    (3) false

    Что будет выведено на экран в результате выполнения кода?

    int a=3;
    if (a>1) cout << "а>1";
    if (a>2) cout << "a>2";
    if (a>3) cout << "a>3";

    (1) a>1a>2a>3

    (2) a>1a>2

    (3) a>1

    Произойдет ли ошибка компиляции если функцию

    int sum(int a, int a1, int a2, int a3, int a4=2)
    {return a+a1+a2+a3+a4;}

    вызвать в функции main следующим образом

    int main()
    {
    int z=1,b=1,c=1,d=1,e=1;
    sum(z,b,c,d,e);
    ....
    }

    (1) нет

    (2) да, потому что значение возвращаемое функцией не присваивается никакой переменной

    (3) да, потому что мы пытаемся изменить значение аргумента по умолчанию

    Если имеется объявление float f=4;double d=4;
    что будет выведено на экран в результате выполнения кода

    if(f>d)
    cout << "f>d";
    if (f==d)
    cout << "f=d";
    if(f<d)
    cout <<"f<d";

    (1) f>d

    (2) f=d

    (3) f<d

    Вызовет ли следующее объявление ошибку компиляции

    class A{
    public: void sum(double s1,double s2);
    int sum (int s1,int s2);
    };

    (1) да

    (2) нет

    В какой строке(строках) ошибка?

    1 int f1() {
    2 char ch, ch1='a', ch2='b', ch3='c';
    3 ch=ch1+ch2+ch3;
    4 f1=int(ch);
    5 return f1;
    6 }

    (1) 1 и 3

    (2) 1, 3, 4 и 5

    (3) 4 и 5

    Можно ли в шаблоне класса определить статический метод?

    (1) да

    (2) нет, будет ошибка компиляции

    (3) да, но результат работы программы непредсказуем

    Выберите правильное утверждение:

    (1) язык Си++ включает богатые средства разработки для Internet

    (2) разработка распределенных систем часто ведется на языке Си++

    (3) программные интерфейсы к операционной системе чаще всего написаны на языке Java

    Какими по умолчанию объявляются элементы объединения?

    (1) private

    (2) public

    (3) protected

    (4) по умолчанию не объявляются

    Вызовет ли данный код ошибку компиляции?

    class Rectangle
    {
    public:
    int a,b;

    int sum();
    int square();
    ~rect();
    };

    (1) нет, все записано верно

    (2) да, имя деструктора не может начинаться с маленькой буквы

    (3) да, имя деструктора должно совпадать с именем класса

    : Выберите наиболее правильный вариант объявления оператора += двух объектов класса A:

    (1) A& operator+=(const A& a);

    (2) A& operator+=(A& a) const;

    (3) const A& operator+=(A& a) const;

    С какой целью производится вынесение функций в отдельный заголовочный файл?

    (1) для более наглядного и удобного представления функций

    (2) для возможности использования в нескольких модулях программы

    (3) для возможности использования в других программах

    Сколько блоков catch может быть после блока try?

    (1) количество блоков catch зависит от количества блоков try

    (2) ни одного

    (3) минимум один

    Отметьте правильный вариант описания функции шаблона:

    (1)

    template (class T)
    void change(T *p1, T *p2) { . . . };

    (2)

    template <class T>;
    void change<T *p1, T *p2> { . . . };

    (3)

    template <class T>
    void change(T *p1, T *p2) { . . . };

    (4)

    template {class T}
    void change(T *p1, T *p2) ( . . . );

    Укажите все ключевые слова в приведенном примере?

    int calc(int a, int b, bool f)
    {

    if (f==1)
    return a+b;
    else
    return a*b;

    }

    (1) int,calc,bool,return,if,else

    (2) int,if,else,return

    (3) int,bool,if,else,return

    Если после выражения стоит точка с запятой, то

    (1) это оператор-выражение, действие которого заключается в вычислении выражения

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

    (3) выражение вычисляется только если первой стоит операция присваивания

    При выполнении фрагмента кода

    int x = 3, y = 2, z = 1;
    if(x >= y)
         if(y <= z)
           cout << "Вариант 1";
         else
           if(x <= z)
             cout << "Вариант 2";
           else
             cout << "Вариант 3";

    будет напечатано:

    (1) Вариант 1

    (2) Вариант 2

    (3) Вариант 3

    (4) ничего не будет напечатано

    (5) программа не откомпилируется

    Отметьте истинное высказывание, если вызываются подряд несколько функций:

    (1) все функции выполняются одновременно

    (2) после выполнения одной функции управление переходит к следующей

    (3) последовательность выполнения функций определяется компилятором

    Что будет на экране после выполнения данного кода

    {
    int ar[4];
    ar[0]=1;
    ar[1]=2;
    ar[2]=3;
    ar[3]=4;
    for (int i = 0; i<=4; i++)
    cout << ar[i];

    }

    (1) 1234

    (2) код не выполнится из-за ошибки компиляции

    (3) 1234 и случайное число

    (4) результат невозможно предсказать

    Что нужно сделать для освобождения памяти после выполнения такого кода ?

    char *a; a = new char[20];

    (1) delete [] a;

    (2) delete a[];

    (3) delete a;

    Для того чтобы вывести символ новой строки, надо:

    (1) закончить оператор точкой с запятой

    (2) воспользоваться специальным манипулятором endl

    (3) при выводе строки символов перевод строки добавляется автоматически

    Функция объявлена как friend класса. Отметьте верное.

    (1) функция имеет доступ к внутренним атрибутам класса

    (2) функция-оператор должна иметь в качестве первого аргумента объект данного класса

    (3) ключевое слово friend не оказывает влияния на функции и операторы

    Есть ли преимущество выноса определения функции в отдельный файл

    (1) да,при использования функции в нескольких файлах

    (2) нет,это затрудняет работу программы

    (3) да,можно определить функцию только один раз

    Если имеется программа

    int a;  //в глобальном пространстве
    void f() { extern int a; a = 8; }

    и в функции main() выполняется

    a = 3; f(); cout << a;

    то какой будет результат?

    (1) 0

    (2) 8

    (3) 3

    (4) другой

    Если имеется код double x; cin >> x; и вводится "12-3", то что будет в переменной x?

    (1) 9.0

    (2) 9

    (3) 12.0

    (4) 3.0

    (5) другое

    (6) произойдет ошибка

    Исходя из данного кода какое высказывание верно?

    int main()
    {
    int a,b,c,d;
    a=1;
    b=2;
    c=a+b+p;
    cout << p;
    ...
    }

    (1) код не верен, потому что переменным c и d не присвоены значения

    (2) код верен, потому что по умолчанию все переменные имеют целочисленный тип

    (3) код не верен, потому что переменная p не объявлена

    Чему равен результат вычисления выражения

    b – x * 3 + b

    при x = 12 и b = 8 ?

    (1) -20

    (2) -4

    (3) -124

    Если i=3, какой будет результат ?

    if (i == 4) cout << "aaa";
    else if (i == 3) cout << "bbb";
    else if (i != 3) cout << "ccc";

    (1) aaa

    (2) bbb

    (3) ccc

    (4) aaaccc

    (5) bbbccc

    (6) ошибка компиляции

    Отметьте допустимые имена функций:

    (1) _This_Function_12_x_

    (2) ax%u7

    (3) fffffffAAAAAA

    (4) 3_pi

    (5) calculateIt

    (6) thisname_is_too_long_forafunction

    Найдите недопустимую запись символьной константы:

    (1) 'F'

    (2) "22"

    (3) 'Ю'

    (4) 'a'

    Если имеется код

    class A { public: int a, b, c; };
    A obj;

    как обратиться к члену класса c?

    (1) obj.c

    (2) obj->c

    (3) c.obj

    (4) obj.public.c

    Если объявлен тип

    struct Value {
    double tt; //Размер переменной типа double 64 бита
    union number {
    short sx; // Размер переменной типа short 16 битов
    long lx; // Размер переменной типа long 32 бита
    double dx; // Размер переменной типа double 64 бита
    } val;
    };

    сколько байтов занимает один объект такого типа?

    (1) 8

    (2) 16

    (3) 28

    (4) 176


    Какой будет результат вывода?

    static int a; a = 9;
    {
    static int a = 8;
    }
    cout << a;

    (1) 9

    (2) 8

    (3) 0

    Текст программы можно набирать:

    (1) используя только редактор Emacs

    (2) только в интегрированной среде программирования

    (3) используя любой текстовый редактор или в интегрированной cреде программирования

    Отметьте все правильные варианты продолжения предложения: виртуальный деструктор

    (1) может использоваться с абстрактными классами

    (2) не нужен, если класс не имеет производных классов

    (3) должен быть определен как чисто виртуальный в абстрактном классе

    Если в классе A определены методы

    A(int x);
    operator int();
    operator++(int);

    то какие из них будут вызваны в следующем выражении ?

    A b; static_cast <int> (b + 1);

    (1) operator int(), конструктор А(int x)

    (2) operator++(int), operator int(), конструктор А(int x)

    (3) только конструктор А(int x)

    (4) только operator int()

    (5) operator++(int), конструктор А(int x)

    Определение класса это

    (1) объявление всех его методов и полей

    (2) вызов конструктора

    (3) инициализация всех его полей

    Можно ли создать объект класса, у которого все атрибуты и методы – статические?

    (1) да, можно

    (2) вызовет ошибки компиляции

    (3) вызовет ошибку выполнения

    Если заданы классы

    class A {... } A1;
    class B : public A { ... } B1;
    class C : public B { ... } C1;

    то что будет выведено при выполнении оператора

    throw (C1);

    а обработка исключительной ситуации записана

    catch (B& b) { cout << 1; }
    catch (C& c) { cout << 2; }
    catch (A& a) { cout << 3; }
    catch (...) { cout << 4; }

    (1) 1

    (2) 2

    (3) 3

    (4) 4

    (5) 1 2 3 4

    (6) 2 3 4

    Для того чтобы выполнить чтение из файла с произвольной позиции, надо использовать объект класса

    (1) strstream

    (2) ostream

    (3) ofstream

    (4) istream

    (5) ifstream

    (6) filestream

    Отметьте правильный вариант описания функции шаблона:

    (1)

    template (class T)
    void change(T *p1, T *p2) { . . . };

    (2)

    template <class T>;
    void change<T *p1, T *p2> { . . . };

    (3)

    template <class T>
    void change(T *p1, T *p2) { . . . };

    (4)

    template {class T}
    void change(T *p1, T *p2) { . . . };

    Отметьте ложные высказывания:

    (1) идентификатор - это ключевое слово языка С++

    (2) ключевые слова не могут быть именами переменных, но могут быть использованы в качестве идентификаторов

    (3) ключевое слово может быть создано в процессе написания программы

    Если i = 5, какой будет результат вывода

    do
    {
    cout << (++i)++ << " ";
    }
    while ( i>=5 && i < 8 ) ;

    (1) 6

    (2) 6 8

    (3) 6 7

    (4) 6 7 8

    Если функция вычисления суммы целых чисел, находящихся между двумя заданными (начальное и конечное включительно), имеет прототип int sum(int start, int end), запишите определение функции, используя рекурсию:

    (1)

    {if (end == start) return 1;
    else return end + sum(start, end -1);}

    (2)

    {if (end == start) return start;
    else return end + sum(start, end -1);}

    (3)

    {if (end >= start) return 0;
    else return end + sum(start, end -1);}

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

    (1) enum { day = -1, night = +1 };

    (2) enum { const start = 0, const end = 1 };

    (3) enum { a, b = 2, c, d };

    Какой будет результат выполнения следующего кода?

    class A {
    public:
         int y;
         int inc(int x) { return ++y; };
         int inc(short x) { return x + y; };
    };
    A obj; int y = 5; obj.y = 6;
    cout << obj.inc(y);

    (1) 6

    (2) 7

    (3) 11

    (4) ошибка компиляции

    Какой результат следующего выражения ?

    int *a; int b; a = &b; b = 7; (*a)++; cout << b;

    (1) 7

    (2) 8

    (3) не определено

    (4) ошибка компиляции

    Что будет выведено в результате выполнения следующего кода?

    int a[4] = { 1,2,3,4};
    int* p = a; cout << (*p+2) + *p;

    (1) 4

    (2) 6

    (3) адрес памяти

    (4) 10

    Отметьте истинные высказывания

    (1) функция main может иметь несколько аргументов

    (2) функция main может не иметь аргументов

    (3) функция main должна иметь как минимум один аргумент

    Что содержится в записи минимального по своим возможностям класса?

    (1) только конструктор

    (2) только деструктор

    (3) только конструктор и деструктор

    (4) по крайней мере, один метод

    (5) по крайней мере, один атрибут

    (6) не менее одного атрибута и хотя бы один метод-конструктор класса

    (7) не содержится ничего

    Если указатель объявлен как

    char sss = 't';
    char ddd;
    const char* ptr = &sss;

    какое из следующих выражений верно:

    (1) *ptr++;

    (2) (*ptr)++;

    (3) *ptr = 'k';

    (4) ptr = &ddd;

    Какая из перечисленных функций не может быть конструктором?

    (1) String();

    (2) void String()

    (3) String(String &s)

    (4) const String(int a)

    Какое из следующих определений представляет собой правильную запись операции сложения целого числа и объекта:

    (1) friend A operator+(int a1, const A& a2);

    (2) friend A operator+(int a1, int a2);

    (3) friend void operator+(const A& a1, int a1, A res);

    Каково преимущество использования ключевого слова const вместо директивы #define?

    (1) константу, определенную с помощью const, можно изменять во время работы

    (2) к константе, определенной с помощью const, можно применить операции инкремента и декремента

    (3) константа, определенная с помощью const, доступна в других модулях программы

    (4) константа, определенная с помощью const, имеет тип, и компилятор может проследить за ее использованием в соответствии с объявленным типом

    Какие из перечисленных операций не являются операциями доступа к атрибуту класса?

    (1) "<-"

    (2) "."

    (3) ":"

    Отметьте те средства языка Си++, которые относятся к диагностике ошибок

    (1) возвращаемое значение функции

    (2) исключительные ситуации

    (3) создание объектов

    (4) глобальные переменные, хранящие состояние

    (5) флаг состояния объекта

    (6) преобразование типов переменной

    Существует файл "test.dat" в котором записано "Hello World".Каково будет содержимое файла после выполнения кода:

    ofstream outfile("c:test.dat");
    if (!outfile)
    { cout << "Ошибка создания файла";
    return 1; }
    outfile << "!!!" << endl;
    outfile.close();

    (1) Hello World!!!

    (2) Hello World

    (3) !!!

    При определении класса-шаблона

    (1) он должен быть включен в общий контекст (namespace) STL

    (2) он должен быть наследован от класса Template

    (3) он должен быть отмечен ключевым словом template

    (4) он должен включать в себя, по крайней мере, один параметр-тип

    Отметьте правильное определение константы:

    (1) const long BITS = 32;

    (2) const bit ZERO = 0x0;

    (3) const float 0Ora = 5.9787;

    Чему равно значение выражения 54 << 3 ?

    (1) 432

    (2) 440

    (3) 413

    (4) 47

    (5) 523

    (6) 556

    (7) 623

    (8) другое значение

    Что выведет следующая программа ?

    #include <iostream.h>
    int main() {
         int 1_i ;
         for( 1_i = 0; 1_i < 9; 1_i++)
         cout << 1_i +1;
         return 1;
    }

    (1) цифры от 0 до 8

    (2) цифры от 1 до 9

    (3) программа не будет построена из-за ошибок

    В чем разница между фактическими и формальными параметрами?

    (1) формальные параметры могут использоваться только вне тела функции, а фактические - используются как вне функции, так и внутри ее

    (2) формальные параметры определены в заголовке функции, а фактические - значения, с которыми функция вызывается

    (3) нет различий

    Укажите в каком выражении правильно применяются операции к целочисленным типам

    (1) int a = 2, b = 0x24, c; c = a + b%a;

    (2) long int c <<= 2;

    (3) const char mychar = 2; long s = 3; mychar /= s;

    Имеется класс:

    class Team
    {
    public:
    int score;
    class Player
    {
    public:
    void GoGoGo(){ printf("Player is going to a goal.n"); }
    };
    void SetScore(int x){ score = x;}
    void ShowScore(){printf("%dn",score);}
    };

    Выберите из нижеприведенных записей корректные коды для этого класса:

    1. Team::Player p;
    p.GoGoGo();

    2. Team t;
    t.Player p;
    p.GoGoGo();

    (1) первый

    (2) второй

    (3) оба верны

    (4) оба ошибочны

    Укажите какой результат будет у следующего примера?

    float arr[3] = {10, 20, 30};
    float *a = arr;
    cout << a[1];

    (1) 10

    (2) 20

    (3) 30

    (4) ошибка компиляции

    Какой будет результат у данного выражения?

    long *L = new long;
    *L = 4;
    delete L;
    cout << *L;

    (1) 4

    (2) 5

    (3) ошибка компиляции

    (4) непредсказуемое поведение программы, возможно, случайное число

    В каком файле заголовков определён объект cout:

    (1) iostream.h

    (2) stream.h

    (3) sysutils.hpp

    Укажите какое из выражений будет правильным?

    class A
    {
    public : int a, b;
    protected : int z;
    private : short i;
    } A1;
    class B : protected A
    {
    public : int c, d;
    private : int k;
    } B1;

    (1) A1.i = 10;

    (2) B1.k = A1.i;

    (3) B1.c = A1.z;

    (4) A1.a = B1.d;

    Какой правильный вызов функции базового класса из объекта производного класса, если в производном классе эта функция не была замещена?

    (1) FunctionName();

    (2) FunctionName.Base();

    (3) Base.FunctionName();

    (4) такую функцию вызвать нельзя

    Сопоставьте:

    1. Конструктор –
    2. Деструктор –
    3. Дружественная функция –
    4. Переопределение операций -
    A - вызывается автоматически, как только объект класса уничтожается.
    B – имеет доступ к защищенным и собственным компонентам класса, не являясь его компонентом.
    C – возможность распространения действия стандартных операций на операнды, для которых эти операции первоначально в языке не предполагались.
    D – используется для инициализации объектов класса.

    (1) 1-D, 2-A, 3-B, 4-C

    (2) 1-B, 2-A, 3-D, 4-C

    (3) 1-C, 2-B, 3-A, 4-D

    Какие операции используются для контроля за приведением типов?

    (1) reinterpret_cast

    (2) static_cast

    (3) const_cast

    (4) basic_cast

    Возможно ли использование подобного выражения?

    #define myincl "D:ProjectCodingCodingU.h"
    #include myincl

    (1) нет

    (2) да

    Контекст пространства имен может содержать:

    (1) только объявления переменных

    (2) объявления и определения переменных

    (3) другие контексты

    Что происходит при попытке выполнить оператор return внутри блока catch?

    (1) аварийная остановка программы

    (2) повторное создание обрабатываемой исключительной ситуации

    (3) выход из функции

    (4) ошибка компиляции

    (5) ошибка выполнения

    Что означает cout << flush ?

    (1) вывести перевод строки

    (2) вывести возврат каретки

    (3) вывести символ новой строки

    (4) вывести нулевой байт

    (5) произвести вывод и очистку буферов

    (6) закрыть выводной поток

    Какой правильный вариант создания экземпляра объекта?

    template <class T> class Matrix
    { public : Matrix(int, int);
    ~Matrix() { }
    }

    (1) Matrix x(4, 5)

    (2) Matrix <float> x(4, 5);

    (3) Matrix :: <float> x(4, 5)

    Отметьте правильные объявления переменных:

    (1) int sd1K_k;

    (2) char float = 53.5;

    (3) int x; int y; int X;

    (4) float; float = y;

    В сложных выражениях последовательность выполнения операций определяется:

    (1) только приоритетом операций

    (2) только скобками

    (3) скобками, приоритетом операций, а при одинаковом приоритете ассоциативностью операций

    Чему будет равна переменная k в результате вычисления цикла?

    int k = 0;
    do
    {
    k++;
    if (k == 1) continue;
    else break;
    ++k;
    }
    while (k < 5);

    (1) k = 2;

    (2) k = 0;

    (3) k = 5;

    (4) k = 4;

    Укажите правильное объявление функции

    (1) int MyFunc(double x, y, int i);

    (2) void correct(double d = 3.14, double a);

    (3) int sum(int j, int k, int x = 0);

    Укажите какому классу принадлежит атрибут Z1

    class t
    { public: double sum::Z1;} C;
    class sum
    { public: double t::Z1;} D;

    (1) классу t

    (2) обоим классам

    (3) запись неверна

    Что будет выведено на экран в результате следующего выражения?

    struct my
    {
    int a, b;
    } m1;
    int func(my f)
    {
    return f.a + f.b++;
    }
    int main()
    {
    m1.a = 5; m1.b = 10;
    cout << func(m1);
    return 0;
    }

    (1) 15

    (2) 16

    (3) 5

    В чём заключаются недостатки при использовании передачи аргумента в функцию по значению?

    (1) затраты времени на копирование значений и затраты памяти для хранения копии при передаче больших объёмов данных

    (2) невозможность изменения данных в их источнике

    (3) невозможность передачи больших объёмов данных

    Имеется запись: monster - базовый класс, demon - производный:

    // Описываются указатели:
    monster *p;
    demon *d;

    При выполнении какого выражения всегда можно говорить,что потери информации не будет?

    (1) d=p

    (2) d=(demon*)p

    (3) p=d

    (4) (monster*)d=p

    Допустима ли следующая конструкция?

    class A {
         int x;
         int f(const A& a)
         {
               x = 0;
               x += a.x;
               return x;
         }
    };

    (1) допустима

    (2) произойдет ошибка компиляции

    (3) произойдет ошибка при выполнении

    Если в классе определяется операция delete, то

    (1) только одна операция delete может быть определена для класса

    (2) стандартная операция delete всегда доступна для этого класса

    (3) все виды операций delete должны быть определены

    Переопределение операции сложения приведет к(отметьте все правильные варианты)

    (1) ее вызову при выполнении операции ++ с объектом класса

    (2) ее вызову при выполнении операции сложения с объектом класса

    (3) преобразованию целых чисел к объекту данного класса при выполнении сложения

    (4) возможному преобразованию объектов других классов к данному при выполнении операции сложения

    Что будет выведено на экран в результате выполнения приведенной ниже программы:

    #include "iostream.h"
    #include "conio.h"
    #define N=10
    int main()
    {
    int x=N;
    cout<<x;
    getch();
    return 0;
    }

    (1) компилятор выдаст ошибку компиляции

    (2) N

    (3) 10

    (4) x

    Запись ::func(x) означает, что

    (1) функция func написана на ассемблере

    (2) вызывается функция из анонимного контекста

    (3) вызывается функция из глобального контекста

    (4) вызывается функция, определенная в другом файле

    (5) функция func имеет тип void

    Блок try catch

    (1) должен стоять в функции main

    (2) заключает участок кода, в котором может сложиться исключительная ситуация

    (3) должен заканчиваться catch (...)

    (4) может быть повторен несколько раз в одной функции

    (5) не может быть вложенным

    Правильный вариант программы, выводящей "Hello World":

    (1)

    #include <iostream.h>
    int main()
    {
    cout<<"Hello World";
    return 0;
    }

    (2)

    #include <iostream.h>
    int main()
    {
    cout>>"Hello World";
    return 0;
    }

    (3)

    #include <iostream.h>
    {
    cout<<"Hello World";
    return 0;
    }

    Что будет делать функция find(arr+2,arr+ARR_SIZE,5)?

    (1) искать число 5 на интервале от 2 до ARR_SIZE

    (2) искать 5 в массиве arr начиная от второго элемента

    (3) ничего, эта запись ошибочна

    Отметьте константу в следующем фрагменте кода:

    int k = 3;
    const int a = 2;
    int m = k + a – 3;

    (1) k

    (2) m

    (3) a

    Битовой операцией является

    (1) =

    (2) !=

    (3) ||

    (4) +

    (5) &

    Отметьте ошибочное утверждение:

    (1) Си++ обеспечивает строгий контроль типов

    (2) строгое согласование по типам между формальными и фактическими параметрами требует, чтобы в модуле до первого обращения к функции было помещено либо ее описание, либо определение

    (3) при обращении к функции фактические параметры заменяются формальными

    Каким будет результат следующей программы:

    int a = 5*3;
    float b = 1.5f;
    b += --a/2;
    cout << b;

    (1) 8.0

    (2) 9.0

    (3) 8.5

    (4) 9.5

    (5) 7.5

    В каких выражениях правильно определен метод класса Ping?

    class Ping
    { public: float f, d;
    int a;
    void MathFunc(double D);
    };

    (1) Ping::MathFunc(double D) {f = D;}

    (2) void Ping::MathFunc(double D) {f = D;}

    (3) void Ping::MathFunc(double D) {this->f = D;}

    Что будет выведено на экран в результате выполнения приведенной ниже программы?

    #include "iostream.h"
    #include "conio.h"
    int main()
    {
    int *a;
    int b=7;
    a = &b;
    b+= 7;
    (*a)++;
    cout << a;
    getch();
    return 0;
    }

    (1) 14

    (2) адрес переменной b

    (3) компилятор выдаст ошибку компиляции

    (4) адрес указателя a

    (5) нет правильного ответа

    Что произойдёт если операция выделения памяти new завершится неудачно?

    (1) произойдёт аварийное завершение программы

    (2) программа выдаст сообщение о невозможности выделения памяти под данный объект и вернёт ненулевой указатель

    (3) выделение памяти под объект не произойдёт, и операция new вернёт нулевой указатель или будет сгенерировано исключение

    Отметьте истинное утверждение для абстрактного класса.

    (1) класс, у которого есть хотя бы один чисто виртуальный метод, называется абстрактным

    (2) абстрактный базовый класс навязывает определенный интерфейс всем производным из него классам

    (3) невозможно создать объект абстрактного класса

    (4) в абстрактном классе нельзя определять методы

    Какой результат будет у следующего выражения?

    class A
    { CountPass();
    private: short i;
    }A1;
    friend A::CountPass()
    { A1.i = 23;}

    (1) объявление дружественной функции для всех производных класса A

    (2) инициализация атрибута класса

    (3) ошибка компиляции

    В каком порядке происходит вызов деструкторов при уничтожении объекта производного класса?

    (1) вызывается деструктор производного класса, затем деструкторы атрибутов производного класса и, потом, деструктор базового класса

    (2) вызывается деструктор базового класса, затем деструкторы атрибутов базового класса и, потом, деструктор производного класса

    (3) вызывается деструктор базового класса, затем деструктор производного класса и, потом, деструкторы атрибутов базового класса

    Какой результат будет у следующего выражения?

    int m = 10, n = 4;
    double A = static_cast <double> (m)/n;
    cout << A;

    (1) 2

    (2) 2.5

    (3) 1

    (4) ошибка компиляции

    Отметьте истинные высказывания в отношении использования макросов и функций:

    (1) в большинстве случаев функции позволяют сокращать объем выполняемого файла

    (2) в большинстве случаев макросы позволяют сокращать время выполнения

    (3) недостатком макросов является отсутствие встроенного контроля согласования типов аргументов и формальных параметров

    Что будет на экране после выполнения программы

    namespace t
    { char * c = "Hi"; }
    int main()
    {
    char * x2 = c;
    cout << x2;
    return 0;
    }

    (1) Hi

    (2) H

    (3) ошибка компиляции

    Что будет на экране после выполнения программы

    #include <iostream.h>
    short x = 4, i = 0;
    void fun1()
    { double p=2;
    if (!i) throw p; }
    void fun2()
    { --x; fun1(); x++; }
    int main()
    { try
    { fun2(); }
    catch (double)
    { cout << "Exception "; }
    cout << x << " " << i;
    }

    (1) Exception

    (2) Ошибка компиляции

    (3) Exception 3 0

    (4) 4 0

    Укажите правильное объявление шаблона функции, если в программе производится вызов double х = zero<double>();

    (1) template <class TT> TT zero() { return 0;}

    (2) template TT zero() { return 0;}

    (3) template <class TT> TT zero { return 0;}

    В каком случае компилятор выдаст ошибку:

    (1) int int iCeloe;

    (2) const float fL = 32; float e23 = 1; fL = e23;

    (3) bool LD1LW;

    В каком случае выражение вычислится быстрее:

    (1) X = X + Y

    (2) X += Y

    (3) X = Y + X

    В каких случаях произойдет ошибка компиляции?

    (1)

    void f(int i)
    { return i++;}

    (2)

    int ZZ(float i, int w = 2)
    { i += w;
    return;
    }

    (3)

    int x(int w)
    { int x = w;
    return w;
    return x++;
    }

    Укажите в каких выражениях неправильно применяются операции к вещественным типам

    (1) double a = 2.3, b = 242, c; c = 23 + b%a;

    (2) float xx = 0, z = 2; if (z >= xx) {...}

    (3) float k = 44; k >>= 2;

    (4) double dd = 2; long double s = 3; if (s ^ dd) {...};

    Какое второе число будет выведено в результате выполнения следующего кода?

    class t
    { public: int sum;
    float f(int a, short m)
    {
    sum++;
    return sum * a - m;
    }
    } v;
    int main()
    {
    v.sum = 5;
    cout << v.sum << " " << v.f(5, 2);
    }

    (1) 27

    (2) 28

    (3) 29

    (4) 5

    (5) 6

    Вернет ли фукция strcmp("qwerty","QWERTY"); 0?

    (1) нет

    (2) да

    (3) результат предсказать невозможно

    Что будет выведено в результате выполнения данного кода?

    int f1(int x1, int &x2) { return ++x1 + (++x2); }
    int main()
    { int a = 7, k = 1;
    k = f1(a, k);
    cout << a << " " << k;
    }

    (1) 7 1

    (2) 8 10

    (3) 7 10

    Что выведет программа в стандартный поток вывода?

    class A
    {
    public : int a, b;
    protected : int z;
    private : short id;
    } A1;

    class B : protected A
    { public : short t;
    void ff(short a)
    { id = ++a; }
    } B1;

    int main()
    {
    B1.ff(20);
    cout << B1.id;
    }

    (1) 20

    (2) 21

    (3) 0

    (4) ошибка

    В какой строке будет ошибка компиляции?

    1:class A
    2: { public: void f1(int &a){val+=a++;};//val инициализируется в конструкторе
    3: int const f2() {return val+1;};
    4: int val;
    5: void f3(int f, const char ch);
    6: } A1;

    7: void A::f3(int f, const char ch){
    8: int d=5;
    9: f1(*d);
    10: f2();
    11: }

    (1) 3

    (2) 6

    (3) 9

    Отметьте истинное высказывание для данного примера:

    Item::Item() : taken(false), invNumber(0)
    { }

    (1) метод класса Item выделяет память под атрибуты класса taken и invNumber

    (2) происходит инициализация атрибутов класса taken и invNumber

    (3) происходит инициализация методов класса taken и invNumber

    Какой результат будет у следующего выражения?

    const char* str1 = "hello";
    char* str2 = const_cast ( char* ) str1;
    cout << str2;

    (1) hello

    (2) h

    (3) hellohello

    (4) ошибка компиляции

    Если задано

    #define f(x) x##4

    то какой будет результат после препроцессора ?

    #if 4
    int d4;
    f(d) = 5;
    #endif

    (1) d4 = 5;

    (2) x*4 = 5;

    (3) пусто

    (4) f(d4) = 5;

    (5) d = x;

    (6) = 5;

    Что будет на экране после выполнения программы

    int m = 5;
    namespace space1
    { int x1 = 1;
    namespace space2
    { int x1 = 3 + ::m + space1::x1; }
    }
    int main()
    { int x3 = space1::space2::x1 * 2;
    { int x3 = 20; }
    cout << x3;
    return 0;
    }

    (1) 20

    (2) 18

    (3) 4

    (4) ошибка компиляции

    Что будет выведено на экран после выполнения программы?

    int main()
    {
    try{
    try
    { throw 3.14; }
    catch (int) { cout << "Exception 1"; }
    }
    catch (...)
    { cout << "Exception 2"; }
    return 0;

    }

    (1) Exception 1Exception 2

    (2) Exception 1

    (3) Exception 2

    Существует файл "c:test.dat" в котором записано "Hello World".Каково будет содержимое файла после выполнения кода:

    ofstream outfile("c:test.dat",ios::in);
    if (!outfile)
    { cout << "Ошибка создания файла";
    return 1; }
    outfile << "!!!";
    outfile.close();

    (1) Hello World!!!

    (2) !!!lo World

    (3) !!!

    Отметьте верное утверждение:

    (1) шаблон может быть членом класса или шаблона класса

    (2) шаблон может быть членом только шаблона класса

    (3) шаблон может быть членом только класса

    (4) шаблон не может быть членом класса или шаблона-класса

    Укажите в каких выражениях переменная или константа объявляется и ей присваивается значение:

    (1) const bool b;

    (2) float dasf3, s2, d34w;

    (3) static int iINTEGER; iINTEGER = 2;

    Чему будет равен результат вычисления выражения: float A = 2, B = 20, C; C = (B = A = 5) + 1;

    (1) 6

    (2) 20

    (3) 2

    В какой строке будет ошибка компиляции

    1 int sum(int a,int b,int c,int d=4){
    2 int result;
    3 result=128*a+b-c*d;
    4 d=25;
    5 a=d;
    6 return res;
    7 }

    (1) ошибок нет,все верно

    (2) в 4

    (3) в 5

    (4) в 6

    Вызовет ли следующее объявление ошибку компиляции

    class A{
    public: double sum(int double,double s2);
    int sum (int s1,int s2);
    }

    (1) да

    (2) нет

    Что будет выведено на экран, если вызвать данную функцию последовательно три раза?

    void f1() {
    static int flag=0;
    if (!flag) {
    cout << "false ";
    flag=5;
    }
    else {
    cout <<"true ";
    flag=0;
    }
    }

    (1) false false false

    (2) false true false

    (3) true false true

    Исходя только из назначения шаблонов имеет ли смысл делать из данного кода функцию-шаблон

    if(a){
    a=a%b;
    }
    else
    cout << error;

    (Желательно ответить на этот вопрос не глядя на варианты ответов)

    (1) нет, данный код можно использовать только для целых чисел

    (2) нет, данный код можно использовать только для величин логического типа

    (3) да, данный код можно использовать для переменных типа int и char


    Если в процессе компиляции программы возникла ошибка то:

    (1) будет создан исполняемый файл

    (2) компилятор выдаст сообщение об ошибке и создаст исполняемый файл

    (3) компилятор выдаст сообщение об ошибке, с возможным указанием её места

    Если задано

    #define foo(x,y) x##y

    то какой будет результат после препроцессора ?

    foo(a,c)

    (1) ac

    (2) "ac"

    (3) a c

    (4) a#c

    Какое правильное объявление виртуальной функции, которая принимает одно целочисленное значение и возвращает void:

    (1) void SomeFunction(int);

    (2) virtual SomeFunction(int);

    (3) virtual SomeFunction();

    (4) virtual void SomeFunction(int);

    Отметьте верное утверждение:

    (1) типизированная переменная может изменять свой тип во время исполнения программы

    (2) среда разработки использует эту переменную для своего внутреннего использования

    (3) любая переменная в программе должна принадлежать какому-то определенному типу

    Об ошибке в конструкторе класса может сигнализировать:

    (1) возвращаемое значение

    (2) исключительная ситуация

    (3) вызов деструктора сразу в конструкторе

    (4) установленный атрибут-флаг объекта

    Каким будет результат следующей программы:

    int a = 5/3;
    float b = 1.5f;
    b += --a/2;
    cout << b;

    (1) 2.5

    (2) 1.5

    (3) 6.0

    (4) 1.0

    (5) 0.0

    (6) -0.5

    Если i = 5, какой будет результат?

    while (i <=5)
    {
    cout << (--i)-- << " ";
    if ( i < 2) break;
    }

    (1) ошибка компиляции

    (2) цикл ни разу не будет выполнен

    (3) цикл будет выполняться бесконечно

    (4) 4 3 2 1

    (5) 4 3 2

    (6) 4 2 1

    (7) 4 2

    Какой результат вычисления следующего выражения?

    0xFF & 5 >> 1 + 1

    (1) 1

    (2) 2

    (3) 256

    Какой из ниже перечисленных вариантов не является формой записи вещественного числа?

    (1) 12.3e+2

    (2) 1.23

    (3) .1f

    (4) 0x3F

    Двумерный массив Ar представленный в виде матрицы имеет вид

    _ _
    | 1 2 3 |
    | 5 6 7 |
    |_ 9 1 2 _|

    Что будет на экране в результате выполнения кода

    int flag=7;
    for (int i = 0; i<3; i++) {
    if (flag)
    cout << Ar[1][i];
    else
    cout << "Error";
    }

    (1) 159

    (2) код не выполнится из-за ошибки компиляции

    (3) 567

    (4) Error

    Какое выражение верно с точки зрения целесообразности использования динамического распределения памяти?

    (1) char* c = new char[5]; c = "aabb";

    (2) char* c = "aabb";

    (3) char c; c = new string("aabb");


    Обработка ошибок в языке Си

    24.11.2021 |

    Категория Программирование

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

    квадратные уравнения

    Оказывается, этот подход очень распространен в библиотечном коде проекта GNU. Например, если мы посмотрим на документацию функции `fopen(…)`

    документацию функции

    то есть, функция `fopen()` вернет:

    • Указатель типа `FILE*`, если программа отработала верно 
    • `NULL`, если во время выполнения возникла ошибка (например, файл который мы пытаемся открыть, не существует). Часть про `errno` пока что игнорируем.

    А это значит, что обработка ошибок функции fopen должна происходить примерно вот так:

    обработка ошибок функции fopen

    Файл `blabla.txt` я специально не создал, а значит, моя программа ожидаемо закончится с сообщением `something went wrong with fopen()!`. Но бывают (почти всегда) ситуации, в которых об ошибке хочется знать чуть больше, чем просто то что она произошла. Тут и вступает в дело переменная `errno`, которая глобальная и определена во всех больших хедерах (в том числе в `stdlib.h` и `stdio.h`, но не в `string.h` — подумайте, почему). Где-то в недрах `errno.h` есть специальный массив строк под названием `sys_errlist`, к которому у вас, как у программиста, строго говоря, нет доступа. Строки в этом массиве — сообщения об ошибках, а `errno`, которая выставилась в результате ошибки — индекс в этом массиве, который указывает на описание ошибки, которая возникла. Существуют специальные функции, которые помогают вам выводить ошибки, например `perror()`. Перепишем наш код с обработкой ошибок через `perror()`

    специальные функции

    Теперь при ошибке, при открытии файла, вызовется функция `perror()`, она проверит чему равна переменная `errno`(которая, повторюсь, глобальная и хитро определена в заголовке как `stdio.h`, так и `errno.h`), и она найдет нужное сообщение об ошибке, то есть `main: No such file or directory`. В данном случае, я посчитал, что ошибка критическая, поскольку любое продолжение программы приведет к Segmentation fault, поэтому после вывода сообщения об ошибке, программа завершается с кодом errno (что, в принципе, необязательно). Узнать что делает аргумент функции `perror()` остается заданием для читателя. Узнать это можно в man-страницах ;)

    Более лаконично этот код записывается так:

    код записывается так

    а ЕЩЕ более лаконично

    более лаконичный код

    (Это решение плохо тем, что иногда в случае ошибки функции возвращают `NULL`, а иногда `-1` в случае ошибок, такие штуки ВСЕГДА надо проверять в man-страницах.)

    Важные замечания:

    • НЕ стоит проверять значение `errno` в случае успешного выполнения функции, потому что оно, строго говоря, не определено в этом случае (хотя, в большинстве случаев оно будет значения `Success`)
    • Если у вас несколько функций подряд, которые используют errno, то проверять наличие ошибки нужно после КАЖДОЙ из них.

    проверка на наличие ошибок кода

    Есть ли альтернативы? Ну, пока не придумали. Даже язык Go, по идеологии внук языка Cи, страдает от постоянных

    язык Go страдает от ошибок

    А этот язык сейчас один из самых популярных! (Хотя в Go2 придумали безумно интересный механизм обработки ошибок, кардинально отличающийся от того, что написано здесь и других прочих объектно-ориентированных exception-handling).

    Существует механизм `SJLJ`, пользоваться которым без надобности не рекомендуется.

    Берегите себя и пишите безопасный код ;)

  • Имеется объект класса CreditCard -MyCard, который
    описывает вашу кредитную карту. Класс
    CreditCard содержит следующие методы и поля

  • Абстрактный класс – это класс, в котором

  • Битовой
    операцией является

  • Блок try
    catch

  • Будет ли вызываться конструктор, если
    в программе встретится следующaя
    конструкция:

  • В каких выражениях используются бинарные
    арифметические операции?

  • В каких выражениях используются унарные
    арифметические операции?

  • В каких выражениях правильно определен
    метод класса Ping?

  • В каких выражениях произойдет зацикливание
    программы?

  • В каких выражениях результатом будет
    логическое значение?

  • В каких случаях возможность прокрутки
    окна фрейма будет предоставляться при
    необходимости?

  • В каких случаях код будет работать
    правильно

  • В каких случаях произойдет ошибка
    компиляции?

  • В каких строках ошибка?

  • В какой строке будет ошибка компиляции

  • В какой строке(строках) ошибка?

  • В какой строчке данного кода сработает
    заданный в нем копирующий конструктор?

  • В каком порядке происходит вызов
    деструкторов при уничтожении объекта
    производного класса?

  • В каком случае выражение вычислится
    быстрее:

  • В каком случае компилятор выдаст ошибку:

  • В каком случае описание класса верно?

  • В каком случае программа выведет строку
    на консоль

  • В каком файле заголовков определён
    объект cout:

  • В программе на языке Си++ обязательно
    имеется функция

  • В сложных выражениях последовательность
    выполнения операций определяется:

  • В чем заключается назначение оператора
    перехода goto?

  • В чем заключается принцип полиморфизма?

  • В чем заключается суть компоновки
    программы?

  • В чем недостаток использования шаблонов

  • В чем различие использования следующих
    выражений #include <…> и #include «…»

  • В чем разница между фактическими и
    формальными параметрами?

  • В чём заключаются недостатки при
    использовании передачи аргумента в
    функцию по значению?

  • Верен ли код

  • Вернет ли фукция strcmp(«qwerty»,»QWERTY»);
    0?

  • Возможно ли использование подобного
    выражения?

  • Возможно ли использовать механизм
    исключительных ситуаций в деструкторах

  • Возможно ли использовать механизм исключительных ситуаций в деструкторах

  • Выберите наиболее правильный вариант
    объявления оператора присваивания в
    классе A:

  • Выберите наиболее правильный вариант
    объявления оператора сложения и
    присваивания двух объектов класса A:

  • Выберите правильное объявление константы
    pi:

  • Выберите правильное утверждение:

  • Вызовет ли данный код ошибку компиляции?

  • Вызовет ли следующее объявление ошибку
    компиляции

  • Двумерный массив Ar представленный в
    виде матрицы имеет вид

  • Для переопределенного оператора верно:

  • Для получения адреса переменной
    используется операция

  • Для создания исполняемого файла в
    системе Unix необходимо

  • Для того чтобы вывести символ новой
    строки, надо:

  • Для того чтобы выполнить чтение из
    файла с произвольной позиции, надо
    использовать объект класса

  • Для чего нужны классы?

  • Для чего предназначен оператор namespace?

  • Для чего предназначен фрагмент текста
    из заголовочного файла:

  • Для чего предназначены манипуляторы
    потоков ввода-вывода?

  • Допустима ли следующая конструкция?

  • Если i = 5, какой будет результат

  • Если i=3, какой будет результат ?

  • Если int n=3, какой будет результат ?

  • Если int n=45, какой будет результат?

  • Если в арифметическом выражении
    участвуют короткое целое и длинное
    целое, то:

  • Если в арифметическом выражении
    участвуют целый и вещественный операнды,
    то:

  • Если в классе A определены методы

  • Если в классе операция delete переопределена
    как

  • Если в классе операция new переопределена
    как

  • Если в классе определяется операция
    delete, то

  • Если в конструкторе класса … произойдет
    исключительная ситуация, будет ли
    потеряна память при откате по стеку?

  • Если в массиве A 132 элемента, каким будет
    правильное обращение к последнему
    элементу массива?

  • Если в программе объявлен макрос #define
    CIRC(x) (3.14159 * (x) * (x)), то как будет подставлен
    этот макрос в тексте программы при
    следующем вызове:

  • Если в программе уже имеется функция
    с прототипом int func(int k, double f), то какое
    из следующих объявлений не вызовет
    ошибки компиляции?

  • Если в производном классе переопределена
    операция new то:

  • Если в процессе компиляции программы
    возникла ошибка то:

  • Если в функции main() выполняется … то
    что будет выведено?

  • Если есть два объявления int qwerty; int
    QWERTY; какое из утверждений верно

  • Если задано #define f(x) x##4 то какой будет
    результат после препроцессора ?

  • Если задано #define foo(x,y) x##y то какой будет
    результат после препроцессора ?

  • Если заданы классы … то что будет
    выведено при выполнении оператора

  • Если записано … то что будет напечатано
    в результате выполнения кода?

  • Если имеется абстрактный класс А и
    производный от этого класса класс А1
    то какая из записей заведомо неверна?

  • Если имеется класс с двумя атрибутами

  • Если имеется код

  • Если имеется код char a[8]; cin >> a; и
    вводится текст «Hello world», то что
    будет в массиве a?

  • Если имеется код double x; cin >> x; и вводится
    «12-3», то что будет в переменной x?

  • Если имеется код int x; cin >> x; и вводится
    «1.2», то что будет в переменной x?

  • Если имеется объявление char
    ch1=’a’,ch2=’b’,ch3=’c’; допустима ли запись
    ch1=ch2+ch3;

  • Если имеется объявление float f=4;double d=4;
    что будет выведено на экран в результате
    выполнения кода

  • Если имеется объявление int a[15], какой
    фрагмент кода выводит все элементы
    массива?

  • Если имеется программа … то какой будет
    результат?

  • Если объявлен тип … сколько байтов
    занимает один объект такого типа?

  • Если определена операция вычитания
    для двух объектов класса A, а операция
    преобразования к int не определена, что
    будет вызвано при

  • Если
    определена операция умножения для двух
    объектов класса A и операция преобразования
    к int, что будет вызвано при

  • Если ошибки в алгоритме программы, на
    каком этапе они обнаружатся?

  • Если после выражения стоит точка с
    запятой, то

  • Если указатель объявлен как … какое
    из следующих выражений верно:

  • Если функция вычисления суммы целых
    чисел от 1 до n имеет прототип int sum(int n),
    запишите определение функции, используя
    рекурсию:

  • Если функция вычисления суммы целых
    чисел от 1 до n имеет прототип int sum(int n),
    то как будет выглядеть запись определения
    функции с использованием рекурсии?

  • Если функция вычисления суммы целых
    чисел, находящихся между двумя заданными
    (начальным и конечным), имеет прототип
    int sum(int start, int end), запишите определение
    функции, используя рекурсию:

  • Если функция вычисления факториала n
    имеет прототип int fact(int n), запишите
    определение функции, используя рекурсию:

  • Если функция имеет тип void, то неверно,
    что

  • Есть ли преимущество выноса определения
    функции в отдельный файл

  • Запись ::func(x) означает, что

  • Известно, что в классе A определен один
    публичный конструктор A(int);. Выберите
    из предложенных выражений компилируемые:

  • Имеется запись: monster — базовый класс,
    demon — производный:

  • Имеется класс:

  • Имеется объявление char ch1=’A’;. Что будет
    выведено на экран при выполнения кода
    cout <<ch1+1;

  • Имеется три объекта класса А: A a1,a2,a3 в
    каком выражении возможен неявный вызов
    конструктора копирования

  • Имеется функция
    int sum(int a,int a1=2,int a2,int a3,int a4) {return a+a1+a2+a3+a4;}

  • Имеется функция шаблон … Верен ли код

  • Исходя из данного кода какое высказывание
    верно?

  • Исходя только из назначения шаблонов
    имеет ли смысл делать из данного кода
    функцию-шаблон

  • Как вы понимаете смысл типизированной
    переменной? Укажите правильное
    высказывание.

  • Как называется функция, которая вызывает
    саму себя?

  • Какая из записей соответствует обращению
    к атрибуту m_arg класса AC в определении
    метода этого же класса?

  • Какая из записей является правильной
    записью абстрактного класса?

  • Какая из перечисленных функций не может
    быть конструктором?

  • Какая операция позволяет получить
    значение, записанное по адресу, который
    содержится в указателе?

  • Какая строка данного кода производит
    специализацию шаблона?

  • Какая функция класса, не являясь его
    компонентом, имеет доступ к его защищенным
    и внутренним компонентам?

  • Какие бывают конструкторы? (Выберите
    ниболее полный ответ)

  • Какие виды наследования бывают(Выберете
    наиболее полный ответ)?

  • Какие из перечисленных операций не
    являются операциями доступа к атрибуту
    класса?

  • Какие из перечисленных типов являются
    встроенными типами языка С++?

  • Какие из следующих выражений являются
    константами типа double?

  • Какие из следующих объявлений метода
    func синтаксически правильны?

  • Какие из следующих символов являются
    правильными экранированными
    последовательностями?

  • Какие из следующих утверждений о
    копирующем конструкторе правильны?

  • Какие ключевые слова используются для
    создания и обработки исключительных
    ситуаций?

  • Какие операторы не могут быть
    переопределены пользователем:

  • Какие операции используются для контроля
    за приведением типов?

  • Какие операции поддаются перегрузке?

  • Какие основные области применения
    языка Си++?

  • Какие требования предъявляются к классу
    исключительных ситуаций?

  • Каким будет результат следующей
    программы:

  • Каким будет результат следующей программы:

  • Каким может быть аргумент деструктора?

  • Какими по умолчанию объявляются методы
    класса?

  • Какими по умолчанию объявляются элементы
    объединения?

  • Какими по умолчанию объявляются элементы
    структуры?

  • Какова последовательность создания
    исполняемого файла:

  • Каково будет значение переменной k
    после выполнения следующего оператора

  • Каково преимущество использования
    ключевого слова const вместо директивы
    #define?

  • Какое выражение верно с точки зрения
    целесообразности использования
    динамического распределения памяти?

  • Какое значение будет выведено в
    стандартный поток в результате выполнения
    следующей программы?

  • Какое из представленных выражений
    выводит на экран

  • Какое из приведенных выражений верно?

  • Какое из приведенных имен является
    недопустимым в Си++?

  • Какое из приведенных ниже прототипов
    операции сложения для класса

  • Какое из следующих объявлений является
    объявлением неизменяемого указателя?

  • Какое из следующих определений
    представляет собой правильную запись
    операции сложения целого числа и
    объекта:

  • Какое из следующих утверждений об
    операторе return является верным?

  • Какое определение функции является
    правильным?

  • Какое правильное объявление виртуальной
    функции, которая принимает одно
    целочисленное значение и возвращает
    void:

  • Какое приведение типов используется
    в следующем выражении?

  • Какое слово из списка не относится к
    зарезервированным словам Си++?

  • Какой будет результат ?

  • Какой будет результат выполнения cout
    << a::A::a; в функции main?

  • Какой будет результат выполнения
    следующего кода?

  • Какой будет результат выполнения следующего кода?

  • Какой будет результат следующего
    выражения?

  • Какой будет результат следующей
    программы?

  • Какой будет результат у данного
    выражения?

  • Какой длины может быть идентификатор

  • Какой из наборов перечисляемых значений
    записан правильно?

  • Какой из ниже перечисленных вариантов
    не является формой записи вещественного
    числа?

  • Какой из стандартных классов используется
    для вывода строк на терминал:

  • Какой класс используется для вывода
    данных во внутреннюю область памяти?

  • Какой класс может использоваться в
    качестве типа атрибута класса?

  • Какой массив имеет самый большой размер?

  • Какой правильный вариант описания
    шаблона семейства классов?

  • Какой правильный вариант создания
    экземпляра объекта?

  • Какой правильный вызов функции базового
    класса из объекта производного класса,
    если в производном классе эта функция
    была замещена?

  • Какой правильный вызов функции базового
    класса из объекта производного класса,
    если в производном классе эта функция
    не была замещена?

  • Какой правильный заголовок шаблона

  • Какой результат будет у следующего
    выражения?

  • Какой результат будет у следующего выражения?

  • Какой результат будет у следующего выражения?

  • Какой результат вычисления следующего
    выражения?

  • Какой результат следующего выражения
    ?

  • Какой результат у следующего выражения?

  • Какой статус международного стандарта
    языка Си++?

  • Какой
    тип будет у следующего выражения ?

  • Какой тип преобразования типов
    используется в следующем выражении?

  • Класс B наследован от класса A. Отметьте
    верное для класса B.

  • Ключевое слово void обозначает что функция

  • Комментарий в программе на Си++

  • Компилятор языка Си++:

  • Конструктор класса — это метод, который
    вызывается при создании объекта для
    …(перечислить )

  • Контекст пространства имен может
    содержать:

  • Могут ли контексты быть вложенными?

  • Может ли нестатический метод иметь
    доступ к статическим методам и атрибутам?

  • Может ли статический метод класса быть
    объявлен как friend?

  • Можно ли в шаблоне класса определить
    статический метод?

  • Можно ли перегружать оператор разрешения
    области видимости -«::»

  • Можно ли создать объект класса, у
    которого все атрибуты и методы –
    статические?

  • На какой строке произойдет ошибка
    компиляции?

  • Найдите недопустимую запись символьной
    константы:

  • Найдите неправильную запись набора
    перечисляемых значений:

  • Нелогической операцией является

  • Нужно ли учитывать при перегрузке
    бинарных операций порядок следования
    операндов?

  • Об ошибке в конструкторе класса может
    сигнализировать:

  • Объявление extern int f; означает:

  • Оператор throw без аргументов

  • Операции в выражениях могут быть

  • Операция «.» обозначает

  • Операция ++

  • Определение класса это

  • Определите
    размер структуры

  • Определите результат выполнения
    следующего кода:

  • Отметьте верное утверждение:

  • Отметьте все верные утверждения о
    статических атрибутах класса:

  • Отметьте все верные утверждения о
    статических методах класса:

  • Отметьте все неправильные определения
    констант:

  • Отметьте все правильные варианты
    продолжения предложения: виртуальный
    деструктор

  • Отметьте все утверждения, которые
    считаете верными:

  • Отметьте допустимые имена функций:

  • Отметьте истинное высказывание для
    данного примера:

  • Отметьте истинное высказывание, если
    вызываются подряд несколько функций:

  • Отметьте истинное утверждение для
    абстрактного класса.

  • Отметьте истинные высказывания

  • Отметьте истинные высказывания в
    отношении использования макросов и
    функций:

  • Отметьте истинные высказывания в
    отношении использования макросов и
    функций:

  • Отметьте истинные высказывания в
    отношении потоков, представленные
    классом strstream:

  • Отметьте истинные высказывания:

  • Отметьте константы в следующем фрагменте
    кода:

  • Отметьте ложные высказывания:

  • Отметьте ошибочное утверждение:

  • Отметьте правильное определение
    константы:

  • Отметьте правильное определение
    константы:

  • Отметьте правильные объявления
    переменных

  • Отметьте правильные объявления
    переменных:

  • Отметьте правильный вариант описания
    функции шаблона:

  • Отметьте правильный вариант освобождения
    всей памяти, выделенной для трехмерного
    массива для следующей программы

  • Отметьте правильный заголовок шаблона
    функции:

  • Отметьте свойства языка Си++, которые
    могут быть источниками возможных ошибок
    программирования

  • Отметьте фрагменты кода, которые можно
    назвать выражениями:

  • Отметьте, какие возможности языка Си++
    помогают предупреждать ошибки:

  • Отметьте, какому определению функции
    может соответствовать вызов func(5.98):

  • Переопределение операции сложения
    приведет к(отметьте все правильные
    варианты)

  • После компиляции программы

  • Правильно ли написан данный код:

  • Правильный вариант программы, выводящей
    «Hello World»:

  • Представление и диапазоны значений
    вещественных чисел соответствуют
    стандарту:

  • При выполнении фрагмента кода … будет
    напечатано:

  • При выходе из функции main

  • При использовании копирующего
    конструктора:

  • При определении класса-шаблона

  • При определении метода запись this->
    говорит о том, что:

  • Программа на языке Си++ начинает
    выполняться с:

  • Произойдет ли ошибка компиляции если
    функцию

  • Произойдет ли ошибка при использовании
    следующей конструкции:

  • Произойдет ли ошибка при компиляции
    этого кода?

  • Прототип функции задает

  • Процесс компиляции программы

  • Результат работы программы:

  • С какой целью производится вынесение
    функций в отдельный заголовочный файл?

  • С помошью какой директивы происходит
    подключение других модулей программы?

  • С помощью какого метода можно изменить
    текущую позицию в файле?

  • С помощью механизма friend можно разрешить
    обращение к внутренним элементам
    класса:

  • Сколько блоков catch может быть после
    блока try?

  • Сколько вызовов системных функций
    всегда будет в программе, независимо
    от ее реализации?

  • Сколько параметров может быть у шаблона
    при определении шаблона функции ?

  • Сколько производных классов можно
    получить из базового класса?

  • Сколько функций может быть в программе
    С++?

  • Совокупность формальных параметров
    определяет

  • Сопоставьте:

  • Существует ли в С++ готовый набор
    шаблонов:

  • Существует файл «test.dat» в котором
    записано «Hello World».Каково будет
    содержимое файла после выполнения
    кода:

  • Текст программы можно набирать:

  • У какой переменой в данном коде самое
    длинное «время жизни»?

  • У какой переменой в данном коде самое
    короткое «время жизни»?

  • Укажите в каких выражениях используются
    ключевые слова?

  • Укажите в каких выражениях неправильно
    применяются операции к вещественным
    типам

  • Укажите в каких выражениях переменная
    или константа объявляется и ей
    присваивается значение:

  • Укажите в каких строках кода произойдет
    ошибка компиляции?

  • Укажите в каком выражении используется
    операция с наивысшим приоритетом?

  • Укажите в каком выражении правильно
    определена переменная в шестнадцатеричной
    системе счисления?

  • Укажите в каком выражении правильно
    определена целочисленная переменная?

  • Укажите в каком выражении правильно
    применяются операции к целочисленным
    типам

  • Укажите в каком выражении произойдет
    потеря точности

  • Укажите все ключевые слова в приведенном
    примере?

  • Укажите какие компиляторы языка Си++
    могут быть использованы в системе Unix.

  • Укажите каким будет результат вычисления
    цикла?

  • Укажите какое из выражений будет
    правильным?

  • Укажите какой будет результат вычисления
    k?

  • Укажите какой результат будет у
    следующего примера?

  • Укажите какому классу принадлежит
    атрибут Z1

  • Укажите неправильный идентификатор:

  • Укажите основные используемые
    манипуляторы потоков.

  • Укажите правильное использование
    оператора friend

  • Укажите правильное объявление функции

  • Укажите правильное объявление шаблона
    функции, если в программе производится
    вызов double х = zero<double>();

  • Укажите правильное объявление?

  • Укажите правильные присваивания
    значений переменным и константам

  • Укажите правильный доступ к членам
    класса:

  • Укажите правильный идентификатор для
    имени переменной:

  • Укажите, где происходит объявление
    констант или переменных

  • Файл имеющий имя «test_file.cpp» это:

  • Функция вычисляет произведение двух
    чисел. Исходные данные вводятся с
    клавиатуры. Какие проверки целесообразно
    ввести в программе?

  • Функция объявлена как friend класса.
    Отметьте верное.

  • Чему будет равен результат вычисления
    выражения: float A = 2, B = 20, C; C = (B = A = 5) + 1;

  • Чему будет равен результат вычисления
    выражения: int d=5; bool b = true, c; c = (!b||(d>3));

  • Чему будет равен результат вычисления:

  • Чему будет равна переменная k в результате
    вычисления цикла?

  • Чему равен результат вычисления
    выражения

  • Чему равен результат вычисления выражения

  • Чему равен результат вычисления выражения

  • Чему равно значение выражения !((1 || 0)
    && 0) ?

  • Чему равно значение выражения 54 <<
    3 ?

  • Чему равно значение целой переменной
    при вычислении выражения 21/5*3?

  • Что будет в результате выполнения
    следующей программы?

  • Что будет выведено в результате

  • Что будет выведено в результате
    выполнения данного кода?

  • Что будет выведено в результате
    выполнения следующего кода?

  • Что будет выведено в стандартный поток
    в результате выполнения программы

  • Что будет выведено в стандартный поток
    вывода в результате исполнения следущей
    программы?

  • Что будет выведено на экран в результате
    выполнения данного кода?

  • Что будет выведено на экран в результате
    выполнения кода?

  • Что будет выведено на экран в результате выполнения кода?

  • Что будет выведено на экран в результате
    выполнения приведенной ниже программы:

  • Что будет выведено на экран в результате выполнения приведенной ниже программы?

  • Что
    будет выведено на экран в результате
    следующего выражения?

  • Что
    будет выведено на экран после выполнения
    программы?

  • Что будет выведено на экран, если вызвать
    данную функцию последовательно три
    раза?

  • Что будет делать функция
    find(arr+2,arr+ARR_SIZE,5)?

  • Что будет на экране после выполнения
    данного кода

  • Что будет на экране после выполнения
    программы

  • Что будет напечатано в результате
    выполнения следующего кода?

  • Что выведет программа в стандартный
    поток вывода?

  • Что выведет следующая программа ?

  • Что выполняет операция «delete [] v;»
    в данном ниже коде:

  • Что выполняется в первую очередь при
    компоновке программы?

  • Что вычисляет эта функция:

  • Что из себя представляет динамическое
    выделение памяти?

  • Что может быть аргументом оператора
    throw?

  • Что нужно сделать для освобождения
    памяти после выполнения такого кода ?

  • Что означает cout << flush ?

  • Что означает cout << setw(3) ?

  • Что означает запись for (;;)?

  • Что означает запись while (false);?

  • Что описывает данная строка программы:
    float mas=new int[3][2]?

  • Что описывает данный программный код?

  • Что понимается под потоком в языке C++

  • Что произойдет после объявления в
    программе данного набора перечисляемых
    значений: enum{N=0, E=1, S=2, W=3};?

  • Что произойдет после следующего примера?

  • Что произойдет при выводе в файл,
    открытый с помощью

  • Что произойдет при выполнении ?

  • Что произойдет, если определение функции
    будет находиться в файле в двух местах?

  • Что произойдёт если операция выделения
    памяти new завершится неудачно?

  • Что произойдёт при использовании
    неправильного адреса в операции delete?

  • Что происходит при попытке выполнить
    оператор return внутри блока catch?

  • Что содержится в записи минимального
    по своим возможностям класса?

  • Что такое cout?

  • Что целесообразно определять в public
    разделе класса?

  • Что является минимальной областью
    видимости имен?

  • Что является результатом компоновки
    программы?

  • Шаблон A и его специализации объявлены
    следующим образом:

  • ПРАВИЛО #1 – ФИГУРНЫЕ СКОБКИ

       Блок программы, идущий после ключевых слов  if, else, switch, while, do и for следует всегда окружать фигурными скобками ({}), даже если он содержит только одиночные или пустые операторы. 

    // Не следует так делать…
    if (timer.done)
       // Одиночному оператору нужны скобки!       
       timer.control = TIMER_RESTART;

    // А вот так правильно …
    while (!timer.done)
    {
       // Даже пустой оператор должен быть окружён скобками. 
    }

    ПРАВИЛО #2 – Ключевое слово «const»

    Ключевое слово const следует использовать:

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

    const unsigned char

    dirPort = 0xff;

    Аргументация: Переменные объявленные с ключевым словом const будут защищены компилятором от непреднамеренных  изменений.

    ПРАВИЛО #3 – Ключевое слово «static»

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

    static void InnerFunction(void)
    {
       ….
    }

    Аргументация: Ключевое слово Си – static, имеет несколько значений. На уровне модуля глобальные переменные и функции, объявленные с ключевым словом static защищены от случайного доступа из других модулей. Это уменьшает связанность и способствует инкапсуляции.

    ПРАВИЛО #4 – Ключевое слово «volatile»

    Ключевое слово volatile нужно использовать везде, где уместно, включая:

    — Объявление глобальной переменной доступной любому обработчику прерываний,
    — Объявление глобальной переменной доступной двум или более задачам,
    — Объявление указателя к отображаемым в памяти периферийным регистрам ввода/вывода

    volatile unsigned int timer;

    Аргументация: Правильное использование спецификатора volatile устраняет целый класс трудных для определения ошибок. Это ключевое слово сообщает компилятору, что переменная может быть изменена в любой момент без какого-либо действия со стороны ближайшего кода и компилятор не должен выполнять на ней некоторые оптимизации. {1}

    ПРАВИЛО #5 – Комментарии

       Комментарии не должны быть вложенными и их не следует использовать для отключения блока программы, даже временно. Для временного отключения блока программы используйте директивы условной компиляции (например, #if 0 … #endif).

    // Так делать нельзя…
    /*
     a = a + 1;
     /* comment */
     b = b + 1;
    */

    // Так правильно…
    #if 0
     a = a + 1;
     /* comment */
     b = b + 1;
    #endif

    Аргументация: Вложенные комментарии и закомментированный код могут позволить неожиданным отрывкам кода быть прокомпилированными.
    (Первый пример, кстати, не будет компилироваться. Компилятор принимает за комментарий все, что находится между первыми символами /* и первыми встреченными символами */. Pashgan)

    ПРАВИЛО #6 – Тип данных с фиксированной разрядностью

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

    Стандарт для знаковых и беззнаковых целочисленных типов
    с фиксированной разрядностью

    Аргументация: Разрядность фундаментальных типов данных языка Си — char, short, int, long, и long long  зависит от реализации (от конкретного компилятора и аппаратной платформы), а это приводит к проблемам переносимости программ.  Стандарт 1999 года не решил эту проблему, но ввел одинаковые имена типов, показанных в таблице. Они заданы в новом заголовочном файле <stdint.h>. Эти имена надо использовать, даже если придется создать typedef`ы вручную.

    ПРАВИЛО #7 – Поразрядные операторы       

       Ни один из поразрядных операторов (другими словами, &, |, ~, ^, <<, и >>) не должен использоваться для управления целочисленными данными со знаком.

    // Так делать нельзя…
    int8_t  signed_data = -4;
    signed_data >>= 1;  // не обязательно -2

    Аргументация: Си-стандарт не определяет основной формат данных со знаком и оставляет определение эффекта некоторых поразрядных операторов на усмотрение авторов компилятора.

    ПРАВИЛО #8 – Целые числа со знаком и без

       Целые числа со знаком не должны сочетаться с целыми числами без знаков в сравнениях  или выражениях. Десятичные константы, не подразумевающие наличие знаков, должны быть описаны с ‘u’ на конце.

    // Так делать нельзя…
    uint8_t  a = 6u;
    int8_t   b = -9;

    if (a + b < 4)
    {
       // если бы -9 + 6 было -3 < 4 как ожидалось.
       // была бы выполнена эта ветвь

    }
    else
    {
       //но поскольку -9 + 6 это  (0x100 — 9) + 6 = 253
       //то будет выполнена эта ветвь

    }

    Аргументация: Некоторые детали управления целочисленными данными со знаком зависят от реализации (то есть от от конкретного компилятора). Кроме того, в результате смешивания знаковых и беззнаковых данных могут возникать информационно-зависимые ошибки.

    ПРАВИЛО #9 – Параметризированные макросы против inline функций

    Не следует использовать параметризированный макрос, если для выполнения той же задачи может быть написана inline функция. {2}

    // Так делать нельзя…
    #define MAX(A, B)   ((A) > (B) ? (A) : (B))
    // … если вместо этого вы можете сделать так.
    inline int Max(int a, int b)

    Аргументация: С использованием директивы препроцессора #define ассоциируется множество рисков, и многие из них связаны с созданием параметризированного макроса. Систематическое применение круглых скобок (как показано в примере) важно, но это не устраняет вероятность двойного инкремента — MAX(i++, j++). Другие опасности неправильного использования макроса включают сравнение данных со знаком и без, или любой тест данных с плавающей запятой.

    Оператор запятая (,) не должен использоваться внутри описания переменной.

    // Так делать нельзя…
    char * x, y; // вы хотите, чтобы «y» был указателем, или нет?

    Аргументация: Цена размещения каждого объявления  в отдельной строке низкая, а риск, что компилятор неправильно «поймет» ваши намерения высокий.

    СНОСКИ

    1.  Исходя из опыта консультирования множества компаний, я подозреваю, что подавляющее большинство встроенных систем содержит ошибки из-за нехватки ключевых слов volatile. Такие виды ошибок обычно обнаруживают себя как «глюки».
    2.  Ключевое слово Си++ inline было добавлено в Си-стандарт в 1999-м году.

    Michael Barr  «Bug-Killing Coding Standard Rules for Embedded C». Вольный перевод ChipEnable.Ru

    Ошибки,
    допускаемые при написании программ,
    разделяют на синтаксические и логические.

    Синтаксические
    ошибки

    нарушение формальных правил написания
    програм­мы на конкретном языке,
    обнаруживаются на этапе трансляции и
    могут быть легко исправлены.

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

    Логические
    ошибки

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

    Причина
    ошибки алгоритма — несоответствие
    построенного алгоритма ходу получения
    конечного результата сформулированной
    задачи.

    Причина
    семантической ошибки — неправильное
    понимание смысла (сема­нти­ки)
    операторов языка.

    Программа,
    содержащая логическую ошибку, может
    быть запущена. Однако она либо выдает
    неверный результат, либо даже завершается
    «аварийно» из-за попытки выполнить
    недопустимую операцию (например, деление
    на 0) — в таком случае выдается сообщение
    об ошибке
    времени выполнения
    .
    Поиск места в программе, содержащего
    логическую ошибку, является непростой
    задачей; он носит название отладки
    программы.

    2. Переменные и константы. Типы данных

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

    Род
    информации, которую способна хранить
    ячейка, определяется ее типом.

    2.1. Основные типы данных

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

    Тип
    данных определяет:

    • внутреннее
      представление данных в оперативной
      памяти;

    • совокупность
      значений (диапазон), которые могут
      принимать данные этого типа;

    • набор
      операций, которые допустимы над такими
      данными.

    Основные
    типы базовых данных: целый – int,
    вещественный с одинарной точностью –
    float
    и
    символьный – char.

    В
    свою очередь, данные целого типа могут
    быть короткими – short,
    а также длинными – long
    и еще более длинными — long
    long
    .
    Кроме того, при любой длине данные целых
    и символьного типов могут быть знаковыми
    signed
    либо беззнаковыми – unsigned
    (по
    умолчанию они считаются
    знаковыми,
    поэтому слово
    signed
    необязательно и обычно опускается
    ).
    Вещественные же данные могут иметь
    удвоенную точность – double
    или
    еще большую точность — long
    double.

    В
    языке С++ введен также логический тип
    bool.
    Данные этого
    типа могут принимать лишь два значения:
    true
    (истина) и false
    (ложь).

    Сложные
    типы данных – массивы, структуры –
    struct,
    объединения или смеси – union.

    Данные
    целых и вещественных типов находятся
    в определенных диапазонах, т.к. занимают
    разный объем оперативной памяти.
    Вещественные типы при этом обладают
    еще конечной
    точностью хранения данных
    ,
    т.е. верно хранят лишь первые несколько
    цифр числа; для хранения остальных (чье
    количество бывает даже бесконечным —
    например, у числа π , или у числа ⅓ ) не
    хватает места. В Табл. 1. приведены
    свойства различных типов для системы
    программирования C++
    Builder.
    В других системах программирования,
    поддерживающих язык Си, может отличаться
    размер того или иного конкретного типа,
    например int,
    и соответственно будет отличаться его
    допустимый диапазон значений (например,
    тип long
    int
    может превосходить по размеру тип int).
    Однако последовательность возрастания
    размеров и точности для каждой группы
    типов всегда одинакова:

    char

    short int ≤
    int ≤
    long int ≤
    long long int

    float

    double ≤
    long double

    Таблица
    1.

    Тип
    данных

    Размер
    (байт)

    Диапазон
    значений

    Точность,
    десятичных знаков

    сhar

    1

    -128
    … 127

    unsigned
    сhar

    1

    0
    … 255

    short
    int

    2

    -215
    215–1
    (-32768…32767)

    int

    4

    -231…231–1

    (-2147483648…2147483647)

    long
    int

    4

    -231…231–1

    (-2147483648…2147483647)

    long
    long int

    8

    –263
    263–1

    (примерно
    )

    unsigned
    short int

    2

    0…216–1
    (0…65535)

    unsigned
    int

    4

    0…232–1
    (0…4294967295)

    unsigned
    long int

    4

    0…232–1
    (0…4294967295)

    unsigned
    long long int

    0…264–1

    (примерно
    0…)

    float

    4

    ±3,14*10-38…±3,14*1038

    7-8

    double

    8

    ±1,7
    *10-308
    ±1,7 *10308

    15-16

    long
    double

    10

    ±
    1,1 * 10-4932
    ± 1,1 * 104932

    19-20

    Заметим,
    что для целочисленных типов данных, чье
    название содержит слово
    int
    и еще какое-либо слово перед ним, слово
    int
    можно не писать, и обычно оно опускается
    (в Табл.1 необязательное
    int
    указано мелким шрифтом).

    Аннотация: Попытка классификации ошибок. Сообщение об ошибке с помощью возвращаемого значения. Исключительные ситуации. Обработка исключительных ситуаций, операторы try и catch.

    Виды ошибок

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

    Ошибки компиляции пропустим:пока все они не исправлены,
    программа не готова, и запустить ее нельзя. Здесь мы будем рассматривать
    только ошибки, происходящие во время выполнения программы.

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

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

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

    1. Контроль типов. Случаи использования недопустимых операций
      и смешения несовместимых типов будут обнаружены компилятором.
    2. Обязательное объявление имен до их использования. Невозможно
      вызвать функцию с неверным числом аргументов. При изменении определения
      переменной или функции легко обнаружить все места, где она
      используется.
    3. Ограничение видимости имен, контексты имен. Уменьшается возможность
      конфликтов имен, неправильного переопределения имен.

    Самым важным средством уменьшения вероятности ошибок является
    объектно-ориентированный подход к программированию, который поддерживает
    язык Си++. Наряду с преимуществами объектного программирования,
    о которых мы говорили ранее, построение программы из классов позволяет
    отлаживать классы по отдельности и строить программы из надежных
    составных «кирпичиков», используя одни и те же классы многократно.

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

    Лучше исходить из того, что идеальных программ не существует, это
    помогает разрабатывать более надежные программы. Самое главное –
    обеспечить контроль данных, а для этого необходимо проверять в программе
    все, что может содержать ошибку. Если в программе предполагается
    какое-то условие, желательно проверить его, хотя бы в начальной
    версии программы, до того, как можно будет на опыте убедиться, что это
    условие действительно выполняется. Важно также проверять указатели,
    передаваемые в качестве аргументов, на равенство нулю; проверять, не
    выходят ли индексы за границы массива и т.п.

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

    Второй вид ошибок – «предусмотренные», запланированные ошибки.
    Если разрабатывается программа диалога с пользователем, такая
    программа обязана адекватно реагировать и обрабатывать неправильные
    нажатия клавиш. Программа чтения текста должна учитывать возможные
    синтаксические ошибки. Программа передачи данных по телефонной линии
    должна обрабатывать помехи и возможные сбои при передаче. Такие ошибки – это, вообще говоря, не ошибки с точки зрения программы, а
    плановые ситуации, которые она обрабатывает.

    Третий вид ошибок тоже в какой-то мере предусмотрен. Это исключительные
    ситуации
    , которые могут иметь место, даже если в программе
    нет ошибок . Например, нехватка памяти для создания нового объекта.
    Или сбой диска при извлечении информации из базы данных.

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

    Возвращаемое значение как признак ошибки

    Простейший способ сообщения об ошибках предполагает использование возвращаемого значения функции или метода. Функция сохранения
    объекта в базе данных может возвращать логическое значение: true в
    случае успешного сохранения, false – в случае ошибки.

    class Database
    {
    public:
    	bool SaveObject(const Object& obj);
    };

    Соответственно, вызов метода должен выглядеть так:

    if (database.SaveObject(my_obj) == false ){
    	//обработка ошибки
    }

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

    int main()
    {
       if (fun1()==false )  //обработка ошибки
       return 1;
    }
    bool
    fun1()
    {
    	if (fun2()==false )
    		return false ;
    	return true ;
    }
    bool
    fun2()
    {
    	if (database.SaveObject(obj)==false )
    		return false ;
    	return true ;
    }

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

    Иногда невозможно вернуть признак ошибки в качестве возвращаемого значения.
    Примером является конструктор объекта, который не может вернуть значение. Как же сообщить о том, что во время инициализации объекта что-то было не так?

    Распространенным решением является дополнительный атрибут
    объекта – флаг, отражающий состояние объекта. Предположим, конструктор
    класса Database должен соединиться с сервером базы данных.

    class Database
    {
    public :
      Database(const char *serverName);
    	...
      bool Ok(void) const {return okFlag;};
    private :
      bool okFlag;
    };
    Database::Database(const char*serverName)
    {
      if (connect(serverName)==true )
        okFlag =true ;
      else
        okFlag =false ;
    }
    int main()
    {
      Database database("db-server");
      if (!database.Ok()){
        cerr <<"Ошибка соединения с базой данных"<<endl;
      return 0;
      }
      return 1;
    }

    Лучше вместо метода Ok, возвращающего значение флага okFlag,
    переопределить операцию ! (отрицание).

    class Database
    {
    public :
       bool operator !()const {return !okFlag;};
    };

    Тогда проверка успешности соединения с базой данных будет выглядеть так:

    if (!database){
      cerr <<"Ошибка соединения с базой 
                              данных"<<endl;
    }

    Следует отметить, что лучше избегать такого построения классов,
    при котором возможны ошибки в конструкторе. Из конструктора можно
    выделить соединение с сервером базы данных в отдельный метод Open:

    class Database
    {
    public :
    	Database();
    	bool Open(const char*serverName);
    }

    и тогда отпадает необходимость в операции ! или методе Ok().

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

    Время на прочтение
    10 мин

    Количество просмотров 8K

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

    Все типы данных, которые используются в исходных кодах, могут различаться размером в зависимости от архитектуры целевой машины, на которой компилируют программный код (см. заголовок О РАЗМЕРЕ ТИПОВ ДАННЫХ)

    О РАЗМЕРЕ ТИПОВ ДАННЫХ

    В данной статье предполагается, что машина, на которой компилируется исходный код и запускается программа на языке Си, поддерживает тип данных long int с размером ровно 4 байта, тип int — размером 4 байта и тип char — ровно 1 байт. Указатели имеют размер 4 байта. Тип long long int имеет размер 8 байт. Размер типа данных, используемых в настоящей статье, зависят от реализации и архитектуры платформ. За подробностями, следует ознакомиться с соответствующей документацией. Минимальный допустимый диапазон значений типов данных, который должна поддерживать реализация, указана в стандарте ISOIEC 9899 в редакциях от 1990, 1999, 2011 а также 2018 года, в пункте 5.2.4.2.1 в которой перечислены основные имена констант, и их значения. Эти константы определены в заголовочном файле <limits.h>

    Неправильное использование символа конца строки (нулевого символа »)

    Рассмотрим следующий код:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    int main(int argc, char **argv){
      char *str = "Hello  world!n";
      int l = strlen(str);
      printf("Str: %snLength: %dn", str, l);
    }

    В данном коде мы получим следующие результаты

    Str: Hello 
    Length: 6

    Т.к. маркер конца строки помещён между подстроками «Hello » и «world!n» , а большинство библиотечных функций используют для проверки достижения конца строки равенство просматриваемого символа с нулевым символом, т. е. :

    while((cur_symbol = *str++) != '') process_symbol(cur_symbol);

    то после прочтения пробела и достижения символа '' функция strlen вернёт значение равное 6. Она не учитывает нулевой символ. Аналогично, функция printf будет подставлять вместо %s символы строки str в стандартный поток вывода до тех пор, пока не прочтёт нулевой символ.

    Конечно, никто так явно не вставляет нулевой символ посередине строки. Но что если мы решили разработать свой протокол со своим форматом сообщений для обмена данными между удалёнными хостами? Мы вполне могли хранить в качестве первых четырех символов строки байты числа, представляющего длину сообщения, которое бы следовало за ним. Т.к. тип char занимает в памяти ровно один байт, то логично, что для хранения длины сообщения, представленного типом long int мы бы зарезервировали для него первые четыре символа в массиве символов char[], или четыре ячейки блока данных, на которые указывает указатель char * ptr. Но проблема в том, что некоторые байты 32-битного числа могут оказаться равны нулю, из-за разных величин длины сообщения (например, если длина сообщения равна 232 символам). Такие байты при приведении к типу char могут создать нулевой символ в начале строки, или где-то ещё.

    Решением данной проблемы будет использование библиотечной функции memcpy которая имеет следующий вид:

    memcpy(void *dest, const void *source, size_t n);

    Данная функция копирует ровно первые n-байтов из места, указанные через указатель source, в начало блока, указанное адресом dest.

    Покажем на примере, как сформировать сообщение:

    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>
    int main(int argc, char **argv){
        char *msg = malloc(20);
        long int l = 16;
        memcpy(msg, &l, 4);
        char *c1 = "Hello world!!!n"; /* length 15 symbols + 1 '' symbol */
        memcpy(msg + 4, c1, 16);
        
        
        long int l2 = 0;
        memcpy(&l2, msg, 4);
        char *c2 = malloc(l2);
        memcpy(c2, msg + 4, l2);
        
        
        printf("Str: %snLength: %dn", c2, l2);
        
        free(msg);
        free(c2);
    }

    В данной программе происходит упаковка и распаковка данных сообщения. В начале мы выделяем блок памяти в размере 20 байт для хранения сообщения. Его первые 4 байта будут хранить длину сообщения, равную 16 байтам. Для этого создали переменную l и записали в неё длину сообщения. Затем с помощью функции memcpy скопировали её полностью в блок msg. Теперь 4 байта msg хранят длину сообщения. Затем создали переменную c1, которая хранит фактическое сообщение. Количество символов в строковом литерале 16, поскольку компилятор неявно добавил один нулевой символ '' к строке. После этого, вызываем memcpy, передавая ей в качестве места назначения адрес 5 ячейки блока памяти переменной msg в качестве места назначения, и указатель на строку c1, в качестве адреса источника, а также длину сообщения с учётом нулевого символа, т.е. значение переменной l.

    Далее, чтобы извлечь длину сообщения и сами данные, были определены две переменные: l2 и c2. C помощью memcpy копируем в переменную l2 первые четыре байта блока msg. Затем в переменную c2 копируем само сообщение из msg, которое начинается с пятого байта msg, и имеет длину, равную l2, которую мы получили ранее.

    Наконец, с помощью printf, выводим содержимое переменных l2 и c2. Нетрудно убедиться, что мы получим на выводе следующие строки

    Str: Hello world!!!
    
    Length: 16

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

    Отметим одно важное ограничение на функцию memcpy: блоки данных, на которые указывают первые два параметра функции, НЕ ДОЛЖНЫ ПЕРЕКРЫВАТЬСЯ. Это значит, что если они указывают на один и тот же блок ячеек памяти, то возможны ошибки.

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

    Рассмотрим следующий код:

    char *mystr = "This is my stringn";
    mystr = mystr + 13;
    *mystr = 'u';

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

    Для решения данной проблемы можно было воспользоваться массивом или функцией malloc, которая бы выделила память в динамической изменяемой куче, где данные доступны и для чтения и для записи. Единственное ограничение — это количество выделяемой памяти, как для массива, так и для блока ячеек данных в куче. В приведенном выше примере для строки mystr потребуется 19 байт.

    Неправильное освобождение памяти через функцию free

    При выделении памяти в куче с помощью функций malloc или calloc необходимо позаботиться об освобождении ресурсов, т.е. выделенной памяти, после того, как работа с выделенными блоками была завершена. Когда была выполнена последняя команда в функции main, или была вызвана одна из функции семейства exit то процесс автоматически известит ядро системы о завершении работы, а ядро позаботится о том, чтобы освободить память, которую процесс больше не использует, а также закроет все открытые файлы данным процессом, (если, конечно, не была вызвана функция _exit, которая не закрывает файловые дескрипторы, открытые процессом).

    Но что если мы больше не используем данные блоки, а работа программы ещё не окончена? Конечно, размер блока может быть небольшим, но он также может быть и достаточно великим, чтобы просто так занимать память процесса. С помощью функции free мы можем освободить блок памяти, передав указатель (т.е. адрес начала блока) ей следующим образом:

    char *s1 = malloc(255);
    process(s1);
    free(s1);

    Данный код работает правильно, несмотря на изменения функцией process с указателем s1. Функции process передаётся копия значения, т.е. адрес начала блока из 255 байтов, на который указывает переменная s1. Эта копия сохранится в локальной переменной функции s1, которая является также её формальным параметром. Описание же функции process выглядит так:

    process(char *s);

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

    Чтобы вызвать ошибку, достаточно перед функции free добавить следующую строчку:

    s1 = s1 + 1;

    Данная инструкция сохранит новый адрес в переменную s1, который является лишь смещением относительно адреса, хранимого в s1 на 1 байт. Это приведёт к ошибке при вызове free, поскольку теперь s1 указывает не на начало блока ячеек данных, а на вторую ячейку блока.

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

    char sptr* = (char*)calloc(14, sizeof(char));
    strcpy(sptr, "Hello world!n");
    long int i = 0;
    char c;
    while((c = *sptr++) != ''){
    	printf("s[%ld] = %cn", i, c);
    }
    free(sptr);

    Здесь, функция strcpy копирует содержимое второй строки в начало блока ячеек, на который указывает указатель sptr. Но она не копирует нулевой символ. Функция выделения памяти в куче calloc выделяет память с указанным количеством ячеек указанного размера. Её первый аргумент указывает на число элементов в блоке, а второй — на размер ячейки в блоке. Кроме того, функция calloc дополнительно вызывает функцию memset, которая заполнит все ячейки блока нулями (т.е. в конце строки в любом случае окажется нулевой символ, если, конечно, мы его не затрём чем-нибудь другим). Функция memset принимает три параметра: адрес блока ячеек памяти, значение, которое надо присвоить ячейкам данного блока, и число ячеек, которые необходимо заполнить данным значением.

    После выполнения цикла while, указатель будет ссылаться на последнюю ячейку в блоке. И мы получим вышеописанную ошибку. Конечно, если знать, что функция free должна принимать адрес начала блока, то проблем можно легко избежать. Всего-навсего, надо лишь сохранить адрес начала блока выделенной памяти в другую переменную, и работать с ней. А исходную переменную не трогать, её-то и надо будет передать функции free, после того как работа с блоком будет завершена.

    В предыдущем куске кода, для этого надо сделать две вещи:

    1) Добавить строчку кода перед циклом while

    char *sptr_p = sptr;

    2) Заменить имя переменной sptr на sptr_p во всех выражениях и инструкциях, где изменяется адрес sptr (то есть содержимое переменной sptr).

    Совет: всегда проверяйте, что указатели, переданные функции free, указывают на НАЧАЛО БЛОКА. И всегда сохраняйте адрес начала блока выделенной памяти.

    Использование локальных переменных функции за её пределами после завершения работы функции

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

    void process_person(struct Person *p){
    	char name[] = "El Barto";
      p->name = name;
      printf("Person name: %s was initiatedn", p->name);
    }

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

    struct Person {
    	char *name;
    };

    Предположим, что в коде функции main, выполняется следующий код, который создаёт новую переменную типа Person, и инициирует её имя (name) через функцию process_person:

    /* in main() body */
    struct Person p1;
    process_person(&p1);
    sleep(2);
    printf("Person name is: "%s"n", p1.name);

    В коде функции main, выделяется память под переменную типа структуры Person в кадре стека, соответствующему вызову функции main. Далее вызывается функция process_person, которая получает адрес, где хранится структура. Далее, в стеке создаётся новый кадр, который соответствует вызову функции process_person. В данном кадре выделяется память под переменную массива символов name. Далее адрес начала (первой ячейки массива) копируется в поле структуры name, и выводится содержимое данного поля структуры. После того, как завершится работа функции process_person, происходит приостановка выполнения следующей строчки кода, на 2 сек. (вызов функции sleep). Функция sleep определена в заголовочных файлах <unistd.h> или <windows.h> или в других файлах в зависимости от целевой платформы и операционной системы. Для приложений, работающих с операционной системой Windows следует подключать заголовок <windows.h>, если же вы работаете с Linux, то надо подключать <unistd.h>. Прототипы (заголовки) функции отличаются. Ниже приведён прототип функции из заголовочного файла <unistd.h>.

    unsigned int sleep(unsigned int seconds);

    После 2 секунд, выполняется последняя строчка кода, которая должна вывести содержимое поля структуры, хранимой в переменной p1. Но т.к. память может быть уже очищена, то в данном поле ничего не будет. В итоге мы можем получить следующий вывод:

    Person name: El Barto was initiated 
    Person name is: "

    Вторая двойная кавычка и всё, что за ней следует, не появилось в стандартном выводе, так как после очистки памяти из-за удаления кадра стека функции process_person поле структуры p1.name приняло значение по умолчанию, равное нулевому символу '0'.

    Стоит отметить, что данное поведение неопределенно, поскольку память может не успеть очиститься, и мы можем получить что-то вроде такого:

    Person name: El Barto was initiated
    Person name is: "ElBar2#1"

    Чтобы избежать подобных ситуации, рекомендую придерживаться принципа Тараса Бульбы:

    «Я тебя породил, я тебя и убью.»

    Т.е. выделять и освобождать один и тот же ресурс необходимо на одном уровне. Т.е. если выделили память внутри функции f, то освободить её надо именно в пределах (внутри) функции f. Внутри, значит в её теле. Причём, если мы вызываем другую функцию g внутри f, и g освобождает память, выделенную в f, то мы не можем считать, что мы освободили ресурс на одном уровне, поскольку выделение происходит в функции f, а освобождение в функции g.

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

    Отсутствие проверок на NULL при работе с указателями.

    Наконец, разберём последний тип ошибок, отсутствие проверок на NULL. Вообще говоря, это распространенная ошибка любого языка программирования, допускающего ссылочный тип и выражающего отсутствие значения такого типа (т. е. ссылки) в виде литерала null. Все вышеприведённые куски кода, в которых использовались указатели подвержены данной ошибке. Вообще говоря, у компьютера не бесконечная память. Попытка выделить память под новую переменную динамически с помощью функций, таких как malloc, calloc, realloc может обернуться неудачей. В этом случае, указанные функции вернут нулевой адрес, который является недопустимым для обращения и использования в системе. Нулевой адрес представлен литералом NULL, который может быть оформлен в следующем виде:

    #define NULL (void*)0;

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

    P.S. В данном посте были рассмотрены лишь 5 типа ошибок. Существуют и другие ошибочные ситуации, которые могут возникать чаще, чем вышеописанные.

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

    Возможно, вам также будет интересно:

  • Свойства системы произошла ошибка
  • Свойства сетевого подключения произошла непредвиденная ошибка
  • Свойства сетевого адаптера произошла непредвиденная ошибка
  • Свойства виртуального коммутатора ошибка применения изменений hyper v
  • Свойства виртуального коммутатора ошибка применения изменений 0x80070005

  • Понравилась статья? Поделить с друзьями:
    0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии