Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Answers_proga_1sem.doc
Скачиваний:
0
Добавлен:
01.03.2025
Размер:
237.57 Кб
Скачать
  1. Идея алгоритма быстрой сортировки.

Быстрая сортировка использует стратегию «разделяй и властвуй». Шаги алгоритма таковы:

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

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

два индекса — l и r, приравниваются к минимальному и максимальному индексу разделяемого массива соответственно;

вычисляется опорный элемент m;

индекс l последовательно увеличивается до m или до тех пор, пока l-й элемент не превысит опорный;

индекс r последовательно уменьшается до m или до тех пор, пока r-й элемент не окажется меньше опорного;

если r = l — найдена середина массива — операция разделения закончена, оба индекса указывают на опорный элемент;

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

Рекурсивно упорядочиваем подмассивы, лежащие слева и справа от опорного элемента.

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

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

  1. Метод белого ящика: основные положения.

Читаем Главы 6-7 http://www.mediafire.com/download.php?gjyk1jgjtna

При тестировании белого ящика (англ. white-box testing, также говорят — прозрачного ящика), разработчик теста имеет доступ к исходному коду и может писать код, который связан с библиотеками тестируемого ПО. Это типично для юнит-тестирования (англ. unit testing), при котором тестируются только отдельные части системы. Оно обеспечивает то, что компоненты конструкции — работоспособны и устойчивы, до определенной степени.

  1. Метод белого ящика: построение потокового графа.

Читаем Главы 6-7 http://www.mediafire.com/download.php?gjyk1jgjtna

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

1. Граф строится отображением управляющей структуры программы. В ходе отображения закрывающие скобки условных операторов и операторов циклов (end if; end loop) рассматриваются как отдельные (фиктивные) операторы.

2. Узлы (вершины) потокового графа соответствуют линейным участкам программы, включают один или несколько операторов программы.

3. Дуги потокового графа отображают поток управления в программе (передачи управления между операторами). Дуга — это ориентированное ребро.

4. Различают операторные и предикатные узлы. Из операторного узла выходит одна дуга, а из предикатного — две дуги.

  1. Предикатные узлы соответствуют простым условиям в программе. Составное условие программы отображается в несколько предикатных узлов. Составным называют условие, в котором используется одна или несколько булевых операций (OR, AND).

  2. Например, фрагмент программы

if a OR b

then x

else у

end if;

вместо прямого отображения в потоковый граф вида, показанного на рис. 6.4, отображается в преобразованный потоковый граф (рис. 6.5).

Рис. 6.4. Прямое отображение в потоковый граф

Рис. 6.5. Преобразованный потоковый граф

6. Замкнутые области, образованные дугами и узлами, называют регионами.

7. Окружающая граф среда рассматривается как дополнительный регион. Например, показанный здесь граф имеет три региона — Rl, R2, R3.

  1. Метод черного ящика: построение множеств эквивалентности.

Читаем Главы 6-7 http://www.mediafire.com/download.php?gjyk1jgjtna

Разбиение по эквивалентности — самый популярный способ тестирования «черного ящика» [3], [14].

В этом способе входная область данных программы делится на классы эквивалентности. Для каждого класса эквивалентности разрабатывается один тестовый вариант.

Класс эквивалентности — набор данных с общими свойствами. Обрабатывая разные элементы класса, программа должна вести себя одинаково. Иначе говоря, при обработке любого набора из класса эквивалентности в программе задействуется один и тот же набор операторов (и связей между ними).

На рис. 7.2 каждый класс эквивалентности показан эллипсом. Здесь выделены входные классы эквивалентности допустимых и недопустимых исходных данных, а также классы результатов.

Классы эквивалентности могут быть определены по спецификации на программу.

Рис. 7.2. Разбиение по эквивалентности

Например, если спецификация задает в качестве допустимых входных величин 5-разрядные целые числа в диапазоне 15 000...70 000, то класс эквивалентности допустимых ИД (исходных данных) включает величины от 15 000 до 70 000, а два класса эквивалентности недопустимых ИД составляют:

- числа меньшие, чем 15 000;

- числа большие, чем 70 000.

  1. Метод черного ящика: анализ граничных условий.

Читаем Главы 6-7 http://www.mediafire.com/download.php?gjyk1jgjtna

Как правило, большая часть ошибок происходит на границах области ввода, а не в центре. Анализ граничных значений заключается в получении тестовых вариантов, которые анализируют граничные значения [3], [14], [69]. Данный способ тестирования дополняет способ разбиения по эквивалентности.

Основные отличия анализа граничных значений от разбиения по эквивалентности:

1) тестовые варианты создаются для проверки только ребер классов эквивалентности;

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

Сформулируем правила анализа граничных значений.

1. Если условие ввода задает диапазон п...т, то тестовые варианты должны быть построены:

  • для значений п и т;

  • для значений чуть левее п и чуть правее т на числовой оси.

Например, если задан входной диапазон -1,0...+1,0, то создаются тесты для значений - 1,0, +1,0, - 1,001, +1,001.

2. Если условие ввода задает дискретное множество значений, то создаются тестовые варианты:

  • для проверки минимального и максимального из значений;

  • для значений чуть меньше минимума и чуть больше максимума.

Так, если входной файл может содержать от 1 до 255 записей, то создаются тесты для О, 1, 255, 256 записей.

3. Правила 1 и 2 применяются к условиям области вывода.

  1. Чем характеризуется тип данных.

Тип данных (встречается также термин вид данных) — фундаментальное понятие теории программирования. Тип данных определяет 1) множество значений, 2) набор операций, которые можно применять к таким значениям и, возможно, 3) способ реализации хранения значений и выполнения операций. Любые данные, которыми оперируют программы, относятся к определённым типам.

14. Приведите определение одного и того же числа в 10-ной и 16-ной системах счисления.

int dec = 31;

int hex = 0x1f;

15. Приведите примеры различного определения символьной константы

const char c='1'; const char d=49;

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

const float f = 1.2F;

const long double d = -3.3L;

Нет, не обязательно

const double expF = 5.5e-2;

17. Приведите примеры определения строковых литералов.

string str1 = “Это строка”

string str2 = “Первая строка \n Вторая строка”

18. Как разместить длинную строковую константу в нескольких строках?

string longStr = “Это очень длинная строка, которая не умещается”

“на одной строке, поэтому мы разбили её на 3.”

“И не обязательно писать '+' между строками, компилятор сам склеит.”

Длинную строковую константу можно разместить на нескольких строках, используя в качестве знака переноса обратную косую черту. Например, строка

“Никто не доволен своей \

внешностью, но все довольны\

своим умом”

полностью эквивалентна сроке “Никто не доволен своей внешностью, но все довольны своим умом”

19. Что такое «пустая строка»? Ее внутреннее представление

string str = “”;

В строке содержится только нулевой символ ( '\0' ), если строка задана указателем на чар char*

если же как выше через string, то надо смотреть представления класса string, а там думаю просто пустой массив

20. Как определить размер памяти для переменной типа int. От чего он зависит?

оператором sizeof(int)

зависит от компилятора и процессора 21. Зачем используется спецификатор short, long?

для изменения размера типа (short – уменьшение, long – увеличение), правда на современных компах int аналогичен long int

22. Укажите внутреннее представление величины целого типа в зависимости от модификатора unsigned

старший(первый) бит идёт не под знак, а под дополнительную цифру

23. Как явно указать, что число в выражении имеет некоторый требуемый тип?

указать после числа суффикc: 1.22F, 11234L

24. Сравните переменные типов float, double и long double по назначению, объему памяти, внутреннему представлению

все переменный хранят вещественные числа, но с разной точностью

float – 4 байта,

double – 8 байт,

long double – 8 байт (visual studio), 12 байт (gnu c++)

25. С какой целью используется операция sizeof?

sizeof() используется для определения объёма переменной определённого типа, еще можно узнавать размеры массива

sizeof – оператор, поэтому можно и без скобок

26. Может ли идентификатор совпадать с ключевым словом?

нет

27. В чем отличие между 'a' и “a” ?

’a’-символьный литерал, “a”-строковый

28. Корректно ли выражение ++х=f(x)?

да, слева префиксный ++ возвращает lvalue

29. Корректно ли выражение x<<y?

да, это побитовый сдвиг влево (изначально)

но благодаря перегрузке операторов для всего, чего душа пожелает, то<< должен быть перегружен для (тип x, тип y), это более общий и корректный ответ на вопрос

30. Проинтерпретируйте выражение (pointer)?1:0 ?

если pointer на что-то указывает (не NULL), то 1, иначе 0

31. Можно ли указателю присвоить числовое значение?

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

ну или можно, но через приведение, но получается, что присваиваем не числовое значение, а всё равно указатель

32. Как можно использовать базовый тип void?

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

33. Приведите синтаксис объявления функции.

<тип результата> [<область действия>::]<имя функции> (<типы параметров (через запятую)>);

void f();

int getSum(int, int);

34. Приведите синтаксис определения функции.

<тип результата> [<область действия>::]<имя функции> (<параметры>){<тело функции>}

void f(){}

int getSum(int a, int b) { return a + b; }

35. Можно ли определить вложенные функции? Если да, то каким образом и какова допустимая глубина вложенности?

нет, нельзя

36. Укажите назначение классов памяти для переменных.

Укажите назначение классов памяти для переменных. Для того чтобы указывать область видимости и действия переменных, а также того, будут ли они динамическими (будет ли их адрес меняться в процессе выполнения программы). К динамическим относятся auto и register. • auto. Этот спецификатор автоматического класса памяти указывает на то, что объект располагает-ся в локальной (или автоматически распределяемой) памяти. Он используется в операторах объявле-ния в теле функций, а также внутри блоков операторов. Объекты, имена которых объявляются со спецификатором auto, размещаются в локальной памяти непосредственно перед началом выполнения функции или блока операторов. При выходе из блока или при возвращении из функции (о механиз-мах вызова функций и возвращения из них речь ещё впереди), соответствующая область локальной памяти освобождается и все ранее размещённые в ней объекты уничтожаются. Таким образом спе-цификатор влияет на время жизни объекта (это время локально). Спецификатор auto используется редко, поскольку все объекты, определяемые непосредственно в теле функции или в блоке операто-ров и так по умолчанию располагаются в локальной памяти. Вне блоков и функций этот специфика-тор не используется. • register. Ещё один спецификатор автоматического класса памяти. Применяется к объектам, по умолчанию располагаемым в локальной памяти. Представляет из себя "ненавязчивую просьбу" к транслятору (если это возможно) о размещении значений объектов, объявленных со спецификатором register в одном из доступных регистров, а не в локальной памяти. Если по какой-либо причине в мо-мент начала выполнения кода в данном блоке операторов регистры оказываются занятыми, трансля-тор обеспечивает с этими объектами обращение, как с объектами класса auto. Очевидно, что в этом случае объект располагается в локальной области памяти. К статическим относятся static и extern. • static. Спецификатор внутреннего статического класса памяти. Применяется только(!) к именам объектов и функций. В C++ этот спецификатор имеет два значения. Первое означает, что определяе-мый объект располагается по фиксированному адресу. Тем самым обеспечивается существование объекта с момента его определения до конца выполнения программы. Второе значение означает ло-кальность. Объявленный со спецификатором static локален в одном программном модуле (то есть, недоступен из других модулей многомодульной программы) или в классе (о классах - позже). Может использоваться в объявлениях вне блоков и функций. Также используется в объявлениях, располо-женных в теле функций и в блоках операторов. • extern. Спецификатор внешнего статического класса памяти. Обеспечивает существование объекта с момента его определения до конца выполнения программы. Объект, объявленный со спецификато-ром extern доступен во всех модулях программы, то есть глобален.

37. Можно ли описать несколько переменных разных типов в одном определении?

да: int *a, r;

38. Можно ли инициализировать несколько переменных в одном операторе?

да: int x,q;

x = q = 1;

39. Может ли тип инициализирующего выражения не совпадать с типом определяемой переменной?

да: int k = ‘a’;

40. Что такое «область действия идентификатора»? Как она определяется?

Область действия идентификатора - это часть программы, в которой на идентификатор можно ссылаться. Существуют четыре области действия идентификатора: 1) область действия функция; 2) область действия файл; 3) область действия блок; 4) область действия прототип функции. Идентификатор, объявленный вне любой функции (на внешнем уровне), имеет область действия файл. Такой идентификатор «известен» всем функциям от точки его объявления до конца файла. Глобальные переменные, описания функции, прототипы функции, находящиеся вне функции - все они имеют областью действия файл.

41. Доступна ли локальная переменная во вложенных блоках?

да, если не перекрывается или если извращаться как Влад и создавать либо ссылку на неё, либо указатель :)

42. Какую переменную называют глобальной?

Если она объявлена вне любой функции, класса или пространства имён.

43. Как определяется область действия глобальной переменной?

от объявления до конца файла содержащего объявсление ©...

44 Как транслятор определяет класс памяти для переменной?

автоматически, все локальные переменные – auto, глобальные – static, и при частом обращении к какой-либо переменной он может её занести в register вроде бы

45. Совпадает ли область видимости переменной с областью действия?

эээ, вроде бы это одно и то же

46. Как получить доступ к глобальной переменной, если ее идентификатор совпадает с локальной переменной?

с помощью ::, например глобальная переменная int k; к ней можно обратиться ::k

47. Как инициализируются статические переменные?

по умолчанию они инициализируются 0, false, null

48. Определите назначение операций ! ~ & %

! – ЛОГИЧЕСКОЕ отрицание

~ - ПОБИТОВОЕ отрицание (изменяет 0 на 1 и 1 на 0)

& - побитовое И

% - остаток от деления

49. Определите назначение операций / << ^

/ - деление (нацело или дробно, смотря что и на что делят)

<< - побитовый сдвиг влево (x << y - то же самое, что x * 2^y) (ну еще перегружено для записи в поток)

^ - побитовая операция XOR

50. Определите назначение операций >> || &&

>> - побитовый сдвиг вправо (x >> y - то же самое, что x / 2^y) (ну ещё перегружено для чтения из потока)

|| - логическое ИЛИ

&& - логическое И

51. Определите назначение операций ? , (тип)

? - краткая запись эээ… условия: (условие) ? (выражение, которое вернёт, если верно) : (выражение, которое вернёт, если ложь). Можно записать модуль b = abs(a)  b = (a < 0) ? –a : a

, - задание последовательности. Это бинарный оператор, то есть, ему нужны два операнда. Обрабатываются операнды слева направо. Результат работы - второй операнд. Например, если вы напишете i = (1,2); то в переменной i окажется значение 2. Разумеется, можно поставить в выражение и несколько таких операторов. Так, строка i = ( 1, 3, -4 ); зашлет в i значение -4. Обратите внимание на круглые скобки - они, как обычно, помогают бороться с приоритетами операций, и в данном случае они совершенно необходимы. Если вы попытаетесь написать, например, так: i = 1, 3, -4 ; то с удивлением обнаружите, что переменная i стала равна единице, а не -4. А все дело в том, что у оператора присваивания приоритет повыше, так что написать последнюю строку все равно, что написать result = ( (i=1), 3, -4 ); а потом забыть про result и использовать значение i. Зачем понадобился такой необычный оператор? Прежде всего, есть в программах на С такие места, куда можно поставить только одно выражение, а надо ставить несколько. Вот в таких случаях и пишут что-нибудь в таком духе: f(( (i=1), (j=2), i*j ) )); То, что вы видите - вызов функции с одним аргументом. В конечном итоге, в качестве аргумента функция получит i*j. И правила языка не нарушены, и несколько выражений стоит. Эта же строка показывает и второе полезное применение этого оператора - мы прямо в вызове функции присвоили новые значения переменным i и j, и только после этого сосчитали значение аргумента i*j; То есть, с помощью оператора запятая можно гарантировать, что обработаются все операнды, причем обработаются они последовательно - слева направо. Еще один пример, чтобы вам этот оператор получше запомнился. В строке программы f(), g(), h(); последовательно будут вызваны все три функции f(), g(), h();

(тип) - это оператор приведение, позволяющий переменную 1 типа привести к другому типу например double d = 2.0; int i = (int)d;

52. Укажите особенности выполнения операций % <<

% - работает стандартно только для целых, и в математике остаток от деления ­-3 на 2 будет 1, а (-3) % 2 будет равно -1

<< - эээ, а фиг его знает, может то, что при x << y будет на самом деле выполняться x << (y % 32) вроде для интов

Ну и это быстрее будет умножать на 2^y, если важна скорость

53. Вычислите значение char var = 0x1F & 9 >> 1 + 2;

если расставить приоритеты, то это аналогично

char var = 0x1F & (9 >> (1 + 2) ), поэтому var == 1

54. Вычислите значение char hello[] = "moto";

if (sizeof(hello) == strlen(hello) ) printf("true");else printf("false");

false, т.к. sizeof(hello) вернёт 5, т.к. там ещё последний нулевой символ \0

55а. Укажите неверные объявления int func_a()

{

static int a;

int static b;

int extern c; extern static int d; }

лишний extern static int d;

55. Какие операции применимы к указателям?

применимы инкремент и декремент (++ и –-)

также к указателю можно прибавлять величину типа int, что передвинет его на указанное кол-во элементов вперёд, назад

можно вычислять разность между 2 указателями на одинаковый тип, Результат такой операции имеет тип int и равен числу элементов исходного типа между уменьшаемым и вычитаемым

также указатели можно разыменовывать (*) и обращаться к полям и методам, если они есть (->)

также их можно сравнивать на равно/не равно ( ==, !=, <, <=,>, >)

56. Приведите примеры использования операции преобразования типа.

double d = 2.0;

int k = (int)d;

char ch = (char)k;

57. Определите понятие «пустой оператор».

оператор, в котором ничего не выполняется, может использоваться в циклах

int i;

for (i = 0; ar[i] != 0; i++) ;

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

58. Каждый ли оператор заканчивается «точкой с запятой»?

нет, в операторе if: if (a = b) … после оператора ПРИСВАИВАНИЯ = нету точки с запятой

Неплохим примером является составной оператор... Т.е. любое выражение вида { // всякие радости } есть составной оператор, после которого нет точки с запятой

59. Как интерпретировать следующее выражение if (a=b) выражение; ?

сначала a присваивается значение b, а потом если а не null или не 0, то выполняется «выражение»

60. Как интерпретировать следующее выражение    if (a<x<b) выражение;   ?

Вначале выполниться проверка первого условия, а потом bool результат приведёться к int (0 или  1) и произойдет проверка второго условия. Т.е. например if (-5<-3<-1) будет всегда интерпретировано как false.

61. Как интерпретировать следующее выражение    if (int i=fun(t)) выражение;   ?

I=fun(t).  Если fun(t)!=0, то выражение будет интерпретированно как true, иначе false;

62. Как организовать выход из оператора switch?

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

63. Опишите синтаксис оператора цикла с предусловием. Как организовать в этом случае бесконечный цикл?

While (условие выполнения) {список операторов}. Бесконечный цикл – while (true).

64. Опишите синтаксис оператора цикла с постусловием. Как организовать в этом случае бесконечный цикл?

Do {список операторов} while (условие выполнения). Бесконечный цикл –do{} while (true).

65. Опишите синтаксис оператора цикла for.

For (инциализация; условие выполнения; изменение значений) {список операторов}

66. Опишите использование операторов break, сontinue, return.

Break – выход из ближайшего цикла. Continue – переход к следующему шагу ближайшего цикла. Return – выход из функции.

67. Как определить указатель на функцию? Пример использования.

Возвращаемый  тип(*имя указателя)(аргументы). Например:

void(*pf)(string);

void f1(string) {…}

pf = &f1;

Пример: сортировка для произвольных типов (передаётся функция сравнения).

68. Можно ли определить указатель на указатель? Если да, то дайте объяснение, что это за объект.

Да. Например: int **c; Это указатель на область памяти, в которой храниться указатель на необходимый объект.

69. Определите указатель на константу.

Const int *c;

70. Определите константный указатель.

int *const c;

71. Укажите способы инициализации указателей, используя операцию &, через имя функции.

int  *c = &a; // где a - int;

void(*pf)(string) = f1; // где void f1(string);

72. Укажите способы инициализации указателей, используя другой указатель, имя массива.

int  *c = Ip; // где ip – int*;

int *p = M; // где int M[size];

73. Укажите способы инициализации указателей, используя операции new  и   new[].

lala  *pl = new lala;

lala *ppl = new lala [size];

74. Опишите использование операций delete и delete[].

Операции delete и delete[] предназначены для очистки неиспользуемой памяти при ручной работе с динамической памятью. Т.е. для удаления элементов созданных при помощи команд new и new[] соответсвенно.

75. Проинтерпретируйте  выражение   int *(*p[10])().

Массив из 10 указателей на функции которые ничего не принимают и возвращают указатель на int.

76. Проинтерпретируйте  выражение   int *(*p[10])(int *).

Массив из 10 указателей на функции которые принимают указатель на int и возвращают указатель на int.

77. Укажите особенности выполнения операций ++  --.

Оператор ++ увеличивает на 1 значение переменной, которая стоит справа (слева) от него. (Стоит заметить, что при использовании с указателями данный оператор увеличивает значение указателя не на единицу, а на n, где n – размер в байтах того типа данных, на который указывает указатель). Данный оператор имеет две формы: префиксную и постфиксную. Различие в том, что происходит раньше: использование значения переменной в выражении или же инкремент переменной. Наприме: 1) int x, y = 10; x = ++y; // префиксная форма – сначала происходит инкремент переменной y, а затем полученное значение присваивается переменной x cout << x << " " << y; В итоге в консоль будет выведено: 11 11 2) int x, y = 10; x = y++; // постфиксная форма – сначала значение y, равное 10, присваивается переменной х, а затем происходит инкремент переменной у. cout << x << " " << y; В итоге в консоль будет выведено: 10 11 Обе формы возвращают значение того же типа данных, что и операнд. Но важное различие заключается в том, что значение, возвращаемое префиксной формой, является l-value, в то время как значение, возвращаемое постфиксной формой оператора ++, не является l-value. Справка: l-value - это то, чему можно присваивать значение, т.е. выражение, которое может быть расположено слева от оператора присваивания (=). На самом деле это не совсем так, но для понимания проще всего. Формально, l-value - выражение типа, отличного от void, ссылающееся на область памяти. При этом l-value может быть и не модифицируемым. Например, переменная, объявленная как const является l-value, однако ее значение нельзя изменять: const int i = 0; i = 1; // error C3892: 'i' : you cannot assign to a variable that is const l-value может быть: - идентификатор переменной любого из следующих типов: - целочисленного - с плавающей точкой - указатель - структура (struct) - или союз (union) - выражение с индексом ([ ]) не являющееся само массивом - обращение к члену (–> или .) - выражение взятия значения (*) не являющееся массивом - любое l-value в скобках - любое l-value со спецификатором const (немодифицируемое l-value) То есть можно считать, что префиксная форма возвращает ссылку на операнд (имеющий уже измененное значение), а постфиксная форма возвращает просто значение операнда (причем не измененное значение, так как инкремент будет произведен позже). То же самое касается и оператора – с той лишь разницей, что он осуществляет не инкремент, а обратную операцию - декремент (т.е. уменьшение на единицу).

78. Укажите особенности выполнения операций +  - для указателей.

К указателю можно прибавить целое число, причем выражение p = p + i; где p – указатель на переменную типа Type, а i – переменная типа int, не означает, что значение указателя увеличится на i. В такой ситуации значение указатля увеличится на i*sizeof(Type), т.е. произойдет сдвиг указателя на i блоков памяти, какждый из которых по количеству занимаемой памяти равен переменной типа Type. Аналогично для -.

Перегружена также разность для указателей, которая вернёт кол-во объектов типа, на который указывают указатели, находящихся между ними. Пусть p1 , p2 - указатели на type и имеют адреса c1 и с2 (в интах) Тогда значение выражения (p2 - p1) равно (c2 - c1) / sizeof(type).

Сложение двух указателей невозможно!!!

79. Укажите особенности выполнения операций ++  -- для указателей.

Изменение указателя зависит от размера типа на который он указывает. ++i/--I – lvalue, i++/i-- - не lvalue.

80. Укажите особенности присвоения для указателей типа void.

Указатель любого тип можно присвоить переменной типа void*, void* можно присвоить void*, пару void* можно сравнивать на равенство/неравенство, явно приводить к любому типу.

81. Дайте интерпретацию следующим выражениям p++=10      (*p)++=10, где р - указатель.

Ошибка копмпляции, т.к. p++ - не lvalue.

82. Определите понятие «ссылка». Как инициализировать ссылку?

Ссылка – альтернативное имя объекта. Пример инициализации:

Int ii = 1;

Int& rr = ii;

83. Как изменить значение ссылки после инициализации?

Изменить значение ссылки нельзя, т.к. ни один оператор не выполняет действий над ссылкой.

84. Можно ли построить ссылку на ссылку? указатель на ссылку?

Нет этого сделать нельзя, оператор не выполняет действий над ссылкой.

int& rr = ii;

int *p = & rr; // p указывает на ii;

85. Определите понятие «массив». Приведите примеры инициализации массива.

Массив - именованный набор однотипных переменных, расположенных в памяти непосредственно друг за другом. Пример:

Int M[]={1,2,3,4,5};

86. В каких случаях можно опустить указание размерности массива?

Если при инициализации указанны все его элементы.

87. Как создать динамический массив из n элементов?

Int *M;

M = new int* [n];

88. Как описываются многомерные массивы?  Организуется доступ к его элементам?

Int M[10][20]; //массив из 10 массивов по 20 целых;

M[i][j] – доступ к (i,j) элементу

89. Приведите примеры инициализации многомерных массивов.

int M[2][5]={{1,2,3,4,5},{1}}; //недостающие элементы буду дополнены 0;

  1. Приведите пример создания 2-мерного динамического массива.

int **s;

int n = 10;

s = new int *[10];

for (int i=0; i<n; i++)

s[i] = new int [10];

  1. Укажите отличия: char str[]=”qwerty”; char str[10]=”qwerty”; char *str=”qwerty”;

в первом случае памяти будет выделено ровно на 7 элементов – на само слово и на ноль в конце

в втором памяти будет на 10 элементов.

А третий – это вообще С-шный стиль. Оставлен в С++ для того чтобы работало куча старого кода на С. Памяти расходует вроде 7 байт, не считая 4 байта – указатель p.

Стоит ещё добавить, что *str – указатель на константу, так что отдельные символы изменять нельзя, в отличие от str[] и str[10]

  1. Как присвоить один массив другому?

Легко.

  1. void *memcpy(void *dest, const void *src, size_t count)

Прмер:

int s[10], d[7];

memcpy(s, d, sizeof d);

  1. for (int i=0; i<10; i++) s[i] = d[i];

  1. Как присвоить одну строку другой?

  1. Так же, как и массив

  2. #include <string> / #include <string.h>

strcpy(s, d);

  1. Приведите пример использования typedef для определения новых типов.

typedef long double ldouble;

Типа меняем long double на ldouble, чтоб каждый раз не писать long

  1. Можно ли структуру возвратить из функции, присвоить одну структуру другой?

Конечно.

  1. Какие типы возвращаемых значений недопустимы для функций?

Массивы что пудов нельзя. По крайней мере явно.

Может и ещё что-то нельзя, я не знаю..

  1. Может ли массив быть результатом функции? Укажите особенности передачи массивов как параметров.

Ну не может. Сказал же.

Когда передаём массив как параметр…

По ходу массив нельзя передать по значению..

передавать массив можно вот так:

  1. void f(int *s)

{

s[4] = 111;

}

int main()

{

int s[10] = {1,2,3,4,5,6,7,8,9};

f(s);

return 0;

}

  1. void f(int s[]) // Ну зачем такое извращение.

{

s[4] = 111;

}

int main()

{

int s[10] = {1,2,3,4,5,6,7,8,9};

f(s);

return 0;

}

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

  1. Укажите особенности передачи параметров функций по значению, по адресу, по ссылке.

по значению – всё тупо копируется.

по адресу в основном передаются массивы. Можно передавать и обычную переменную, но тогда ее придётся разыменовывать всё время.

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

  1. Можно ли объявить параметры функции с модификаторами?

Да, можно. Например, с модификатором const (можно передать константный указатель – его нельзя будет изменять внутри ф-ции). Void func(const int * p ) { * p = 20; // ERROR! }

100. Опишите особенности задания параметров функций со значениями по умолчанию.

Ф-ция задаётся так:

int func(int a = 0, int b = 12, int c = 666)

{…}

При этом при вызове это ф-ции с кол-вом параметров меньшим, чем перечислено,то параметры по умолчанию будут браться справа налево, т.к. сначала c, потом b, потом a

Поэтому нельзя задавать ф-цию вида:

int func(int a = 0, int b) т.к. в ф-цию всегда должен передваться параметр b, а значит и a

101. Как передать функции переменное число параметров? Как их обработать?

Можно с помощью параметров по умолчанию, как в вопросе выше.

А можно с помощью неуказанного кол-ва аргументов вида:

void func(int a …)

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

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

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

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

103. Укажите особенности функции main.

Программа должны содержать ровно 1 ф-цию main. Выполнение программы начинается с вызова main и заканчивается после выхода из main. main может возвращать int (код ошибки) или void.

Также в main можно передать параметры из командной строки:

int main(int argc, char *argv[])

argc – кол-во строк

argv – массив строк

104. Опишите применение директивы #include.

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

105. Опишите применение директивы #define.

В самом простом варианте (и нужном нам, наверное) эта директива используются для замены одного выражения на другое, это выглядит так:

#define NAME rest of line

При этом во всём файле NAME будет замещено на rest of line

Было

named = NAME

Стало

named = rest of line

106. Опишите синтаксис описания класса. Какой тип могут иметь поля класса?

class Name: public A, private B, …

{

int a;

public:

Name();

~Name();

protected: …

}

В общем, сначала указывается имя, потом от кого наследуется, потом уже идёт описание класса.После метки public, protected, private идут соответственно поля/методы с такими же доступами. По умолчанию (без метки) доступ private. Желательно, чтобы у класса был конструктор/деструктор.

Поля класса могут вроде иметь любой тип, за исключением себя же и наследников данного класса

107. Как инициализировать, изменить поля класса, определенные как константные?

Пусть класс

class A

{

public:

const int b;

A(int val): b(val){};

}

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

Вот код для изменения константы, НО ГЛОБАЛЬНЫЕ константы таким образом НЕ ТРОГАТЬ

#include <iostream>

using namespace std;

class A{

const int val;

public:

A(): val(1){};

void check()

{

if (val == 1)

cout << "Old value" << endl;

else

cout << "New value" << endl;

}

void change()

{

int& z=const_cast<int&>(val);

z=2;

}

};

int main()

{

A a;

a.check();

a.change();

a.check();

}

108. Опишите синтаксис определения метода класса вне описания класса.

class A

{

public:

int calc(int,int);

}

int A:: calc(int a, int b)

{

return a + b;

}

109. Опишите синтаксис конструктора, деструктора.

class A

{

public:

A();

A(int);

~A();

}

A:: A() { La-la-la }

A:: A(int val) { La-la-la2 }

A:: ~A() { La-la-la3 }

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

Деструктор тоже ничего не возвращает и имеет название, совпадающее с именем класса, но перед ним стоит ~

Деструктор всегда ничего не принимает

  1. Укажите способы доступа к полям и методам класса.

Ну я так понимаю это оператор . (точка) (obj.data) или -> (стрелка), если obj – указатель

на объект (obj->data)

  1. Зачем нужны методы с модификатором const? и как они используются.

Когда метод не изменяет поля объекта, для которого он вызван, желательно в конце написать const

void func () const { //lala

}

  1. Как определить константный объект? Какие методы доступа применимы к нему?

Ну как.

int main()

{

const MyClass obj;

}

Что за методы доступа ??, такие же, как и в нормальном объекте.. Точка и стрелка. Или я не понимаю что такое метод доступа.

  1. Какой параметр скрыто передается методу класса?

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

  1. Можно ли построить класс без конструктора? Как в этом случае описать объекты класса? Можно ли определить конструктор в закрытой секции?

Можно построить. Тогда вызовется конструктор по умолчанию. Чтобы описать объекты, надо забодяжить функцию типа SetValue(Type), которая инициализирует какое-то поле или несколько полей.

Определить конструктор в закрытой части можно. Оно компилится. Вот тока ни один объект создать не получится 

  1. Определите понятие «дружественная функция ».

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

  1. Зачем виртуализировать деструктор? Можно ли наследовать деструктор?

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

Наследовать деструктор по ходу нельзя. Что очень даже логично.. Вроде.

  1. Можно ли явно вызвать деструктор?

Можно. Но не нужно ((с) СтраусТруп)

  1. Можно ли явно вызвать конструктор?

Нет…

  1. Все ли операции могут быть перегружены? Можно ли перегрузить операции для базовых стандартных типов данных?

Нет… не все. Тернарный опрератор ?: не перегружается, sizeof и typeid не перегружаются.. Нельзя также определять . (точку) :: (разрешение области видимости) и .* (выбор члена через указатель на член.

Для базовых стандартных типов тоже нельзя ничего перегрузить. Это было бы нагло.

  1. Укажите различные способы перегрузки операторов.

Ну что тут сказать..? Можно перегружать как метод класса. Можно ещё как friend-функцию (для << и >> чаще всего)

Это называется операторы-члены и операторы-не-члены. Вроде как.

  1. Укажите особенности перегрузки ++ --

Ну типа ++ есть постфиксный, а есть префиксный. Так вот если мы перегружаем по-обычному, т.е.

MyClass operator ++ () ;

То вот так считается за префиксный инкримент,

А если написать

MyClass operator ++ (int) ;

Или

MyClass operator ++ (int lalala) ;

То это постфиксный вариант.

  1. Определите понятие «конструктор копирования».

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

  1. Опишите синтаксис описания производного класса.

class A{ //базовый класс

};

class B : public A{ //public наследование

}

class C : protected A{ //protected наследование

}

class Z : private A{ //private наследование

}

  1. Как в производном классе определяется доступность полей базового класса?

Эээ, может то, что private члены класса в его потомках будут недоступны

  1. При порождении класса от базового класса с атрибутом public public-компоненты базового класса становятся ………..-компонентами производного класса.

public

  1. При порождении класса от базового класса с атрибутом private public-компоненты базового класса становятся ………..-компонентами производного класса.

private

  1. Укажите особенности использования указателей базового класса для производного класса.

Неизвестно куда указывает указатель – на базовый класс или на производный. Чтобы узнать, на какой указывает, есть куча способов. Но их за 5 минут не расскажешь. Если указатель указывает на производный класс, а не на базовый и в этих классах есть виртуальный метод, то при вызове его для указателя вызовется именно тот, который описан в производном классе.

  1. Зачем виртуализируются методы класса?

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

  1. Определите понятие «чистый виртуальный метод», «абстрактный класс».

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

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

virtual void Show() = 0; то это будет «чисто виртуальная функция». Если класс водерижт хотя бы одну такую чисто виртуальную функцию, то

компилятор считает такой класс абстрактным и запрещает создание объекта такого класса.

  1. Опишите понятие «множественное наследование». Какие проблемы возникают при множественном наследовании?

Множественное наследование – это такое наследование, когда у класса имеется более одного предка.

Проблемы:

  1. У обоих предков могут оказаться одинаковые имена

  2. Если предки класса сами являются предками одного класса, то может возникнуть неоднозначность

Это то, что я знаю.. Вполне вероятно, есть ещё всякие радости..

  1. Укажите результат class B { public: B(char c) { cout << c;}}; class D {public : B bB, bA, bC; D(void) : bA("a"), bB("b"), bC("c") {}}; int main() { D dInstance;. . . .

Ну если дописать эту прогу, подключить пару требуемых библиотек и запустить, то она не скомпилится  Вот если поправить двойные кавычки на одинарные, то она выведет bac

131.a

Укажите результат class B{

public: virtual void msg () {cout << "classB ";}

};

class D : public B{

public: virtual void msg() {cout << "classD ";}

};

int main() {

B * var = new D;

var->msg();

Она напишет ClassD

  1. Опишите использование ключевых слов try, throw,catch.

Ну вот так выглядит код:

try { // Пробуем выполнить этот код

// Код

// Например:

if (y == 0) throw DivisionZero(); // Генерация исключения

else x /= y;

}

catch (Overflow) {

// Обработка класса Overflow и всех производных от него…

}

catch (DivisionZero) {

// Обработка деления на 0 и всех производных от этого класса..

}

catch(…) {

// Перехват всех оставшихся исключений..

}

  1. Как определяется, какому блоку catch передается исключение для обработки?

Обработчики проверяются по порядку.. как написаны, так и проверяются..

Если текущая ошибка подходит под блок, то он и выполняется.

  1. Куда передается управление после обработки исключения?

После обработки управление передаётся туда, где заканчивается последний catch

  1. Как организуется передача исключения в объемлющий блок?

Объемлющий блок – этот тот, который не вложенный

Вот так не пашет:

for (int y=-3; y<=3; y++) {

try {

x = 5;

if (y == 0) throw DivisionZero();

else x /= y;

}

}

catch (DivisionZero) {

cout << "I caught it!" << endl;

}

Всё что я могу предложить – это написать вот так:

try {

for (int y=-3; y<=3; y++) {

try {

x = 5;

if (y == 0) throw DivisionZero();

else x /= y;

}

catch (DivisionZero) {

throw DivisionZero();

}

}

}

catch (DivisionZero) {

cout << "I caught it!" << endl;

}

Но это бред какой-то. Надо у Дубкова на консультации спросить

  1. Если в блоке try не генерируются никакие исключения, куда передается управление после его завершения?

Ну передаётся как в 134 вопросе. Как будто ничего и не было.

  1. Что произойдет, если исключение сгенерировано вне блока try?

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

error C2318: no try block associated with this catch handler

  1. Что произойдет, если несколько обработчиков соответствуют типу исключения?

Тогда выберется самый первый подходящий в порядке записи этих обработчиков..

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

P.S. Учите джаву, здесь есть ответ на этот вопрос 

140. Исправьте ошибку, если она есть:

include <iostream.h>; int b[10]={1}; for (int i=0; i<=10; i++) b[i]=2;

b[10] – не существует, т.е. надо i<10.

141. Исправьте ошибку, если она есть: include <iostream.h>; int a[2][2]={{1,2},{3,4}}; a[2,2]=4;

a[2,2] – 1)не существует элемента [2][2];

2) в C++ написание [2,2] – не верно, т.к. , - последовательность.

142. Установить область действия для всех идентификаторов: int cube(int y); void main() {int x; for (x=1; x<=10; x++) cout<<cube(x)<<endl;} int cube(int e){return e*e*e;}

Cube – от объявления до конца программы, x от объявления, до конца main, e – вся процедура cube.

143. Найдите ошибки, если они есть: int g(void) {cout<<”function g”<<”\n” Int h ( ) {cout <<”function h”<<endl;}}

1) C++ не поддерживает вложенных функции.

2) Тип Int в C++ не определён по умолчанию (строчные и заглавные буквы – разные буквы).

3) Функции должны возвращать значения.

Т.е правильный код:

int g(void) {cout<<”function g”<<”\n”; return 0;} int h ( ) {cout <<”function h”<<endl; return 0;}

144. Найдите ошибки, если они есть: int sum(int x, int y){int result; result=x+y}

sum не возвращает значения .

145. Найдите ошибки, если они есть: int sum(int n){if(n=0) return 0; else n+sum(n-1);}

1) if(n=0) всегда будет false т.к. присваивание 0.

2)Функция не будет ничего возвращать при n!=0 .

146. Найдите ошибки, если они есть: void p( ) {int a,b,c,result; cin>>a>>b>>c; result=a*b*c; cout<<result;return result;}

p не должна возращать значение ибо p - void.

147. Найдите ошибку, если она есть: class C{public: C(int y=10) {data=y;} int get ( ) const {return data++;} static int count( ) {cout<<data<<endl; return count;} private: int data; static int count; }

1) data++ нельзя, т.к. функция const.

2) data должна быть статик, ибо static функция может обращаться только к статик.

3) Член класса и функция класса не могут иметь одинаковое имя.

148. Исправьте ошибки: class C{public: int C(int); void ~C(int); private: int h=0; int m=0;};

1) Конструктор и деструктор не должны ничего возвращать.

2) Деструктор имеет void параметры.

3) Только статические члены могут быть инициализированы по умолчанию.

149. Исправьте ошибки : class H {

int i;

public:

int H( ) { };};

int main ( ) { H ob ; ob. i = 10 ; }

1) Конструктор не должен ничего возвращать.

2) Мы не имеем доступа к ob.i т.к. i по умолчанию лежит в private.

150. Статические члены класса:

Члены, общие для всех экземпляров класса

151. Что происходит при вызове classA *ptrA = new classA

Выделение места в памяти и размещение там экземпляра объекта  

Вызов конструктора объекта  

Создание статических полей класса  

Присвоение указателю адреса созданного объекта 

1) Выделение места в памяти и размещение там экземпляра объекта.

2) Вызов конструктора объекта.

3) Присвоение указателю адреса созданного объекта.

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

#include <iostream> #define max(a,b) ((a) > (b) ? (a) : (b)) int main()   {   int x = 5;   int y = 3;         std::cout << max(++x, y) << endl;   std::cout << max(--x, y) << endl;   return 0; }

1) Ошибка компиляции endl лежит в std;

2) Если её убрать результат будет:

7

5

Т.к. при применении макроподстановки мы получим ++x в условии и в результате.

153. Как необходимо объявить переменную a, чтобы вызов a.Ouput() выводил результат "Goodbye"?

template<class T, class T2> class A { public:   void Output()   {     std::cout << "Hello";   } }; template<class T2> class A<char, T2> { public:   void Output()   {     std::cout << "Goodbye";   } };

A <char,произвольный_тип> a;

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

#include <iostream> int main() {   char* str = "Wello, World!";   str[0] = 'H';   std::cout << str; }

Ошибка. Строковые литералы нельзя изменять.

155. Выберите верный вариант объявления метода M() класса A другом класса B

friend void A::B(); class B { // объявление членов класса }   class B { friend void A::M(); // объявление членов класса }   class B : friend A::M() { // объявление членов класса }   class A { friend class B void M();

class B { friend void A::M(); // объявление членов класса }

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

class Sample { public:   Sample()   {     cout << "Constructor" << endl;   }   void * operator new(size_t size)   {    cout << "Operator New" << endl;     return ::new Sample();  }   void operator delete(void *p, size_t size)   {    ::delete p;     cout << "Operator Delete" << endl;  }   ~Sample()   {    cout << "Destructor" << endl;  } }; void main() {  Sample *ptr = new Sample;   delete ptr; }

Operator New

Constructor

Constructor

Destructor

Operator Delete

Не знаю почему.

157. Может ли класс иметь несколько конструкторов и деструкторов?

Класс может иметь любое количество конструкторов, но только 1 деструктор. A destructor must have a 'void' parameter list. Т.е. перегрузка с различным параметрами невозможна.

158. Можно ли в С++ объявлять массив объектов следующим образом: classA objects[10];

Разрешено, если classA имеет конструктор по умолчанию.

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

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]