Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Информатика Экзамен

.pdf
Скачиваний:
10
Добавлен:
02.05.2015
Размер:
738.97 Кб
Скачать

else

printf(“Введенное число %f является неотрицательным.\n”,x);

return 0;

}

До сих пор рассматривались простые условия типа x<0. Вместе с тем оператор if позволяет реализовывать более сложные условные переходы. В языке С++ имеются три логические операции:

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

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

! – логическое НЕТ

На основе этих трех логических операций можно сформировать более сложные условия. Например, если имеются три переменные exp1, exp2 и exp3, то они могут составлять логические конструкции, представленные в табл. 2.2.

Таблица 2.2. Пример составных логических выражений if(exp1 > exp2 && exp2 < exp3 )

Истинно, если значение переменной exp1

больше значения переменной exp2 и значение переменной exp2 меньше значения переменной exp3.

if(exp1 <= exp2 || exp1 >= exp3 )

Истинно, если значение переменной exp1 меньше либо равно значения переменной exp2 или

значение переменной exp2 больше либо равно значения переменной exp3.

 

if(exp1 && exp2 && !exp3) Истинно, если истинное значение exp1 и истинно значение exp2 и ложно значение exp3.

if(!exp1 || !exp2 && exp3) Истинно, если ложно значение exp1 или ложно значение exp2 и истинно значение exp3.

Подобно операциям умножения и сложения в математике, логические операции И ИЛИ НЕТ, также имеют свои приоритеты. Самый высокий приоритет имеет операция НЕТ, т.е. такая операция выполняется в первую очередь. Более низкий приоритет у операции И, и наконец самый малый приоритет у операции ИЛИ. Данные приоритеты необходимо учитывать, при составлении сложных условий. Например, условие

if(4 < 6 && 5 > 6 || 5 < 6)

проверяется таким образом. Если 4 < 6 И 5 > 6 ИЛИ 5 < 6, то выполняется переход по условию. При необходимости изменения порядка проверки следует операции с более низким приоритетом поместить в круглые скобки, как показано ниже

if(4 < 6 && (5 > 6 || 5 < 6))

Условная операция if облегчает написание программ, в которых необходимо производить выбор между небольшим числом возможных вариантов. Однако иногда в программе необходимо осуществить выбор одного варианта из множества возможных. Формально для этого можно воспользоваться конструкцией if else if … else. Однако во многих случаях оказывается более удобным применять оператор switch языка С++. Синтаксис данного оператора следующий:

switch(переменная)

{

case константа1:

<операторы>

case константа2:

<операторы>

...

default:

<операторы>

}

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

На листинге 2.4 представлен пример программирования условного оператора switch.

Листинг 2.4. Пример использования оператора switch.

#include

int main()

{

int x;

printf(“Введите число: ”);

scanf(“%d”,&x);

switch(x)

{

case 1 : printf(“Введено число 1\n”);break;

case 2 : printf(“Введено число 2\n”); break;

default : printf(“Введено другое число\n”);

}

char ch;

printf(“Введите символ: ”);

scanf(“%с”,&сh);

switch(ch)

{

case ‘a’ : printf(“Введен символ а\n”); break;

case ‘b’ : printf(“Введен символ b\n”); break;

default : printf(“Введен другой символ\n”);

}

return 0;

}

Данный пример демонстрирует два разных варианта использования оператора switch. В первом случае выполняется анализ введенной цифры, во втором – анализ введенного символа. Следует отметить, что данный оператор может производить выбор только на основании равенства своего аргумента одному из перечисленных значений case, т.е. проверка выражений типа x<0 в данном случае невозможна.

11,12.

13.

3.1. Массивы

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

<тип данных> <имя массива>[число элементов];

Например,

int array_int[100]; //одномерный массив 100 целочисленных элементов

double array_d[25]; //одномерный массив 25 вещественных элементов

Как видно из примеров, объявление массивов отличается от объявления обычных переменных наличием квадратных скобок []. Также имена массивов выбираются по тем же правилам, что и имена переменных. Обращение к отдельному элементу массива осуществляется по номеру его индекса. Следующий фрагмент программы демонстрирует запись в массив значений линейной функции f(x)=kx+b и вывода значений на экран:

double k=0.5, b = 10.0;

double f[100];

for(int x=0;i < 100;i++)

{

f[i] = k*x+b;

printf(“%.2f ”,f[i]);

}

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

int powers[4] = {1, 2, 4, 6};

В этом случае элементу powers[0] присваивается значение 1, powers[1] – 2, и т.д. Особенностью инициализации массивов является то, что их размер можно задавать только константами, а не переменными. Например, следующая программа приведет к ошибке при компиляции:

int N=100;

float array_f[N]; //ошибка, так нельзя

Поэтому при объявлении массивов обычно используют такой подход:

#include

#define N 100

int main()

{

float array_f[N];

return 0;

}

Следует отметить, что при инициализации массивов число их элементов должно совпадать с его размерностью. Рассмотрим вариант, когда число элементов при инициализации будет меньше размерности массива.

#define SIZE 4

int data[SIZE]={512, 1024};

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

printf(“%d, \n”,data[i]);

Результат работы программы будет следующим:

512, 1024, 0, 0

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

int data[] = {2, 16, 32, 64, 128, 256};

В результате инициализируется одномерный массив размерностью 6 элементов. Здесь остается последний вопрос: что будет, если значение индекса при обращении к элементу массива превысит его размерность? В этом случае ни программа, ни компилятор не выдадут значение об ошибке, но при этом в программе могут возникать непредвиденные ошибки. Поэтому программисту следует обращать особое внимание на то, чтобы индексы при обращении к элементам массива не выходили за его пределы. Также следует отметить, что первый элемент массива всегда имеет индекс 0, второй – 1, и т.д.

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

int array2D[100][20]; //двумерный массив 100х20 элементов

Нумерация элементов также начинается с нуля, т.е. array2D[0][0] соответствует первому элементу, array2D[0][1] – элементу первой строки, второго столбца и т.д. Для начальной инициализации двумерного массива может использоваться следующая конструкция:

long array2D[3][2] = {{1, 2}, {3, 4}, {5, 6}};

или

long array2D[][] = {{1, 2}, {3, 4}, {5, 6}};

В общем случае можно задать массив любой размерности и правила работы с ними аналогичны правилам работы с одномерными и двумерными массивами.

14. Указатель — элемент программы хранящий адреса памяти некоторого объекта (например, переменной) определённого типа.

Общая схема объявления указателя:

type* name; // объявили указатель с именем name на объект типа type

type *another; // объявили указатель на объект того же типа, звёздочку можно ставить и перед именем указателя

Примеры:

int* p;

int *p2;

double* pd;

Указателям p и p2 можно будет присвоить адреса переменных типа int, но нельзя будет присвоить адреса переменных другого типа или объектов какого-нибудь класса. Аналогично, указателю pd можно будет присвоить только адреса переменных типа double.

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

Примеры:

int a = 15;

cout << &a << endl; // вывели адрес переменной a в памяти (не её значение), увидим шестнадцатиричное число

int ar[] = {728, 3, 402, -1};

/*

Далее выведем адреса элементов массива. Они будут отличаться на размер в байтах

базового типа массива (в данном случае, int). Ещё раз убедимся, что в памяти

все элементы массива расположены последовательно друг за другом.

*/

for (int i=0; i<=3; i++) {

cout << &ar[i] << ' ';

}

cout << endl;

int* p;

p = &a; // скопировали адрес переменной a в указатель p

cout << p << endl; // вывели адрес, хранимый в указателе (совпадёт с ранее виденным адресом)

cout << &p << endl; // вывели адрес самого указателя (он же тоже хранится где-то в памяти, потому имеет адрес)

Вывод мог бы быть примерно таким:

0x22ff08

0x22fef8 0x22fefc 0x22ff00 0x22ff04

0x22ff08

0x22fef4

Для перехода по известному адресу — используется оператор *, размещаемый перед адресом или указателем хранящем адрес. Под переходом по адресу понимается, что от адреса мы переходим к действиям над значением, хранимом по данному адресу. Это операция называется иногда разыменованием.

int a = 15; // переменная a со значением

int* p = &a; // указатель с адресом переменной a

cout << *p << endl; // увидим 15, т.е. значение переменной

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

Справедливо тождество: выражение a == *(&a), где a любого типа — всегда истинно.

При создании любого массива в C++, вместе с ним естественным образом создаётся указатель. Имя этого указателя совпадает с именем массива. Тип этого указателя — «указатель на базовый тип массива». В появившемся указателе хранится адрес начального элемента массива. Чтобы начало массива не было потеряно этот указатель является контсантным, т.е. его нельзя направить на какой-то другой элемент массива или записать туда адрес другой переменной даже подходящего типа. Но зато можно скопировать этот адрес в какой-то другой указатель, не являющимся контсантным.

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

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

int ar[] = {-72, 3, 402, -1, 55, 132};

int* p = ar;

for (int i=101; i<=106; i++) {

cout << *p << ' ';

p++;

}

Над множеством указателей в C++ определён ряд операций:

p+n, где p — указатель, n — целое положительное число. Результат — некоторый указатель, полученный смещением p на n позиций вправо.

p-n, где p — указатель, n — целое положительное число. Результат — некоторый указатель, полученный смещением p на n позиций влево.

p-q, где p и q — указатели на один и тот же тип. Результат — целое число, равное количеству шагов, на которое нужно сместить q вправо, чтобы он достиг указателя p, также этот результат можно называть “расстоянием” между указателями, оно может быть и отрицательным, если элемент, на который направлен указатель q расположен правее (то есть, далее), чем элемент, на который направлен указатель p.

p++ (инкремент), p-- (декремент), где p — указатель. Операции эквивалентны действиям p=p+1 и p=p-1, соответственно.

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

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

int ar[] = {-72, 3, 402, -1, 55, 132};

cout << *ar; // -72

int* p = ar+3; // указатель на 4-ый по счёту элемент массива (со значением -1)

p--; // переместили указатель влево на 1 элемент

cout << *p; // выведется 402

Справедливо тождество: выражение a[i] == *(a+i) всегда истинно (т.е. слева и справа записаны эквивалентные выражения), где a указатель на массив любого типа и i допустимый индекс этого массива. Пользуясь этим тождеством легко переходить от индексов к указателям и обратно при работе с массивом.

15. Указатели и массивы очень тесно связаны между собой. Как объяснялось ранее, имя массива без индекса - это указатель на первый элемент массива. Пусть имеется массив

char р [10];

тогда следующие операторы идентичны:

р

&р [0]

Выражение

р == &р [0]

выдает истину, поскольку адрес первого элемента и адрес массива совпадают.

Справедливо и обратное. Любой указатель может быть проиндексирован, как будто это массив. Например:

int *р, i [10];

p = i;

р[5] = 100; /* присвоение с помощью индекса */

*(р+5) = 100; /* присвоение с помощью арифметики с указателями */

Оба оператора присваивания помещают значение 100 в шестой элемент i. Первый оператор использует индексацию с р, а второй - арифметику указателей. Так или иначе, результат одинаков.

Данные способы индексации совершенно справедливы для массивов размерности 2 и более. Предположим, что а - это целочисленный массив 10 на 10. Тогда нижеприведенные операторы эквивалентны:

а

&а [0] [0]

Более того, к элементу 0, 4 массива а можно обратиться или с помощью индексации массива -а[0][4], или с помощью указателя — *((int *) а + 4). Аналогично к элементу 1, 2 можно обратиться или с помощью индексации массива - а [1] [2], или с помощью указателя - *((int *) а + 12). В целом, для любого двумерного массива справедливо

а[j][k] эквивалентно *((тип *) a + (j * длина строки) + k)

где тип - это базовый тип массива.

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

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

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

int num [10][10];

void pr_row (int j )

{

int *p, t;

p = num [ j]; /* получение адреса первого элемента строки j */

for(t=0; t<10; ++t) printf("%d ",(p+t) );

}

Данный код может быть обобщен, если передавать в качестве аргументов строку, длину строки и указатель на первый элемент массива:

/* общий */

void pr_row(int j, int row_dimension, int *p)

{

int t;

p = p + (j * row_dimension);

for(t=0; t<row_dimension; ++t)

printf("%d ", *(p+t));

}

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

16.

cin>> - для ввода.

cout<< - - для вывода.

Пример.

int a ;

cin>>a; //вводим a

int b = 20;

cout<<b; //Выводим b

Cin - это объект входного потока пространства имен std: std::cin >> x; В данном коде программы используется оператор cin, операция взятия из потока >> чтобы получить от пользователя введенное им значение. Объект std::cin забирает вводимую пользователем информацию из стандартного потока ввода, который обычно является клавиатура. Функция Cin достаточно интелектуальна, чтобы понять, какая информация введена с клавиатуры. Ведь мы можем ввести целое число, а также можем ввести дробное, сивмольное или текст. Cout - это объект выходного потока пространства имен std::. Это необъявленный идентификатор. Его не нужно объявлять. Его нужно только подключать к программе при помощи слова include: #include std::cout <<"Выходной поток"; В данном коде программы используется оператор cout, операция поместить в поток <<, чтобы вывести на экран пользователю определенную информацию. В данном случае на экран выведится Выходной поток. cout достаточно умный, чтобы определить, что нужно вывести на экран, то есть это будет переменная дробного числа или целого или сивмольного. Напишим программу, которая объявляет две переменных целочисленного числа. В них вы вводим с клавиатуры 2 числа и выводим полученный результат.

#include <iostream>//директива препроцесора

#include <conio.h> //директива препроцесора

void main ()

{

int x,y; //объявляем переменный целого типа

std::cout<<"X = "; //На экран выводится 'X = '

std::cin>>x; //вводим с клавиатуры число, например: 5

std::cout<<"Y = "; //На экран выводится 'Y = '

std::cin>>y; //вводим с клавиатуры число, например: 8

std::cout<<"x+y = "<<(x+y)<< std::endl;

_getch();

}

заголовочный файл <iostream>включает объекты cin, cout, которые нам нужны. Если #include убрать, то при компиляции выйдет ошибка. #include необходимо для того, чтобы мы видели результат на экране, благодаря функции _getch(). Если ее не будет, то программа выполнится и закроется. И мы не успеем увидеть результат работы программы. Если откомпилировать код, написанный выше, то результат будет следующий:

Итак, мы видим, что в std::cout<<"x+y = "<<(x+y)<< std::endl; выводится 'x + y = '. После этого вычисляется результат суммы и сразу же выводится результат. Мы могли сделать по-другому:

#include <iostream>//директива препроцесора