Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

читает исходный текст программы находит строки начинающиеся с символа и обрабатывает их перед началом к

Работа добавлена на сайт samzan.net:


Структура программы на С++. Консольный ввод и вывод.

Пространство имен.

Пример.

#include <iostream>

int main( )

{

std ::cout << "Hello World! \n";

return 0;

}

Построчно рассмотрим назначение инструкций программы.

#include <iostream>

  •  Подключение к файлу программы внешней библиотеки iostream.

При каждом запуске компилятора запускается препроцессор, он «читает» исходный текст программы, находит строки, начинающиеся с символа # и обрабатывает их перед началом компиляции программы. Файл iostream (Input-Output-Stream – поток вода/вывода1) используется объектом cout, который обслуживает процесс вывода данных на экран.

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

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

 int main( )

  •  Вызов функции main( ).

Основной код программы начинается с вызова функции main(), ее содержит каждая программа на языке С++. Функция main() имеет тип int, что значит, а это означает, что по окончании работы функция возвратит операционной системе целое число. В данном случае будет возвращено число «0» (инструкция return 0). Возвращение значения в ОС не столь важно и самой системой почти не используется, указанная строка – всего лишь требование стандарта языка С++ к корректному объявлению функции   main( ).

Все функции начинаются открывающей «{» и заканчиваются закрывающей «}» фигурной скобкой, между ними располагается тело функции.

std ::cout << "Hello World! \n";

  •  Инструкция вывода на экран строки символов.

Каждый оператор в С++ должен заканчиваться точкой с запятой, «;» иначе называется признаком конца оператора.

Объект cout используется для вывода сообщений либо данных на консоль (экран) – Console Output. Этот объект содержится в стандартной библиотеке. Для указания же компилятору, что будет использован объект именно из нее, используется спецификация пространства имен std.

Пространство имен (namespace) – это некая объявляемая область, необходимая для того, чтобы избежать конфликта имен идентификаторов, объектов. Существует вероятность приобрести аналогичные объекты с тем же именем от другого поставщика программных компонентов, и чтобы компилятор «понял», частью какого пространства является используемый объект, перед словом cout помещается спецификация std в сопровождении двух  двоеточий:

std :: cout.

За словом cout следует оператор перенаправления потока вывода ( символ << 2), все, что следует за этим оператором (правый операнд) выводится на экран. Например, следующая инструкция выводит на экран компьютера число, хранящееся в переменной с:

 cout << c ;

Два заключительных символа строки – управляющие символы «\n»,  означают, что после строки текста необходимо выполнить переход на новую строку (обратный слэш «\» называется знаком перехода), т.е. все, что следует после символов  «\n», будет показано с новой строки, сами символы на экран не выводятся.

Рассмотрим следующий фрагмент программного кода:

int a=5;

int b=16;

int c=a+b;

std::cout<< "There is variable c:   "<<c<<"\n";

В последней строке объекту cout передаются три значения, и каждое отделяется оператором вывода: строка символов "There is variable c:    "; значение переменной с; символы новой строки \n. Поскольку после первого значения нет символа начала новой строки, следующее значение выводится вслед за предыдущим:

There is variable c: 21_

К управляющим символам относятся так же:

  •  оператор  endl (сокращение от end line – конец строки), действие аналогично символу   \n. Так можно записать тело функции main( ):

{

std::cout<< "Hello!";

std::cout<< std::endl;

    std::cout<<3+6<< std::endl;

}

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

 Hello!

 9

Press any key to continue_

Типы данных в С++. Определение переменных. Особенности использования некоторых типов данных, переполнение регистров переменных.

Переменная – место в оперативной памяти компьютера (RAMRandom Access Memory), где можно размещать хранимое значение, а затем извлекать его для дальнейшего использования. Значение сохраняется в переменной до выключения компьютера.

Определяя переменную в С++, необходимо предоставить компилятору информацию о ее типе. Тогда компилятор будет знать, сколько место (какой объем оперативной памяти) нужно зарезервировать для хранения переменной, и какого рода значения будут в ней храниться.

В оперативной памяти любой тип занимает определенный объем, но у различных типов компьютеров он может оказаться разным. Тип integer (целое число) может иметь размер в два байта на одной машине и четыре на другой, но в пределах одного компьютера он будет одним и тем же. Размер целого числа определяется системой компьютера (16- или 32-разрядная) и используется компилятором.

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

Таблица

Тип данных

Размер в байтах

Диапазон

char

1

 -128  - 127 (256 значений символов)

unsigned char

1

 0 - 255

bool

1

true или false

unsigned short int

(unsigned short)

2

  0 - 65 535

short int (short)

2

  -32 768  -  32 767

unsigned long int

(unsigned long)

4

 0 - 4 294 967 295

long int (long)

4

  -2 147 483 648  -  2 147 483 647

unsigned int

(unsigned) 

2 или 4

(16/32 байта)

 0  -  65 535 или

 0  -  4 294 967 295

int

2 или 4

(16/32 байта)

 -32 768  -  32 767 или

 -2 147 483 648  -  2 147 483 647

float

4

  1, 2е-38  - 3,4е38

double

8

  2, 2е-308  -  1,8е308

Представление целых чисел

Все целочисленные типы существуют в двух вариантах: signed (знаковые – положительные и отрицательные) и unsigned (беззнаковые - положительные).  Такое деление связано с тем, что отрицательные числа иногда нужны, а иногда нет.

Данные, имя типа которых включает модификатор signed, являются данными со знаком, имя типа которых содержит модификатор  unsigned –  данными без знака. При отсутствии модификатора по умолчанию принимается модификатор  signed.

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

Например, переменные типа unsigned short int, как правило, имеют размер, равный двум байтам, и могут хранить значение, не превышающее 65 535. Диапазон же типа signed short int (short) поделен между положительными и отрицательными числами, поэтому их максимальное значение вдвое меньше, чем у беззнаковых (от -32 768 до 32 767).

Определение переменных

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

Тип  ИмяПеременной;

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

Тип ИмяПеременной_1, ИмяПеременной_2, …, ИмяПеременной_n;

Для присвоения значений переменным используется следующая запись:

Тип ИмяПеременной;

ИмяПеременной = значение;

Эти две строки можно объединить в одну и инициализировать переменную в процессе ее определения:

Тип ИмяПеременной = значение;

Инициализация переменных напоминает присвоение. Существенное отличие инициализации от присвоения состоит в том, что первая происходит в момент создания переменной. Подобно тому, как можно определять сразу несколько переменных, можно и инициализировать сразу несколько переменных. Например:

// создаем две переменных типа long и инициализируем их

long а = 5, b = 7;

 Здесь переменная а типа long int инициализируется значением 5, а переменная b того же типа – значением 7. Можно комбинировать определение и инициализацию:

int a = 42, b, c = 41;

Созданы три переменные, а инициализированы две – первая и третья.

Особенности использования некоторых типов данных:

переполнение регистров переменных

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

Пример. Поместим слишком большое число в переменную типа unsigned short.

#include <iostream>

int main( )

{

using std::cout;

using std::endl;

unsigned short int smallNumb;

smallNumb=65535; // максимально 65 535

cout<<"Значение smallNumb:"<<smallNumb<<endl;

 //значение переменной должно стать 65 536

 smallNumb++;

//переменная сбрасывается в 0

 cout<<" Значение smallNumb:"<<smallNumb<<endl;

//переманная увеличивается на 1 но уже с 0

 smallNumb++;

cout<<" Значение smallNumb:"<<smallNumb<<endl;

return 0;

}

Результат:

Значение smallNumb: 65 535

Значение smallNumb:0

Значение smallNumb:1

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

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

Пример. Добавим единицу к максимально возможному числу, хранящемуся в переменной типа short int:

#include <iostream>

int main() {

using std::cout;

using std::endl;

short int Numb;

Numb=32767; // диапазон -32 768 до 32 767

cout<<"Значение Numb:"<<Numb<<endl;

 //значение переменной увеличим на 1

Numb++;

//переменная имеет значение максимального отрицательного

 cout<<" Значение Numb:"<<Numb<<endl;

 Numb++;

cout<<" Новое значение Numb:"<<Numb<<endl;

 return 0;

}

Результат:

Значение Numb: 32 767

Значение Numb: -32 768

Значение Numb: -32 767

Операторы С++. Базовые конструкции структурного программирования, условный оператор.

Объединение оператора присвоения и арифметических операторов. Любая инструкция типа Sum = Sum + I, смысл которой состоит в увеличении переменной  Sum на величину  i, и присвоении результата переменной Sum, может быть записана:

Sum + = i

Оператор присвоения с суммой («+ =») добавляет r-значение к l-значению, а затем присваивает результат l-значению. Если бы до начала выполнения выражения значение переменной Sum было равно 21, а i рано 1, то результат оказался бы равным 22.

Все арифметические операторы  позволяют использовать такую же запись:

x = x * i       x * = i

x = x / i       x / = i

x = x % i       x % = i

Инкремент и декремент. Если оператор «+ =» применить в инструкции увеличения  переменной на 1 (инкремент) или уменьшения  переменной на 1 (декремент), что в программах встречается очень часто, то можно записать вместо выражения i + = 1выражение i ++ , а вместо выражения i - = –  i --.

Пример. Рассмотрим программу:

#include <iostream>

using namespace std;

int main( ) {

int N=2000000;

int sum=0, i=1;

while (i<N) {

sum = sum + i;

i=i+1; }

cout<<"sum="<<sum<<endl;

return 0;}

Тело функции main( ) в этой программе может быть записано:

int main( ){

int N=2000000;

int sum=0, i=1;

while (i<N)

sum += i++;

cout<<"sum="<<sum<<endl;

return 0;}

Операторы инкремента и декремента существуют в двух вариантах: префиксном и постфиксном. Префиксный вариант записывается перед именем переменной (+=sum), а оператор увеличения значения вычисляется до присвоения. Постфиксный записывается после имени переменной (sum +=), оператор выполняется после присвоения.

Другими словами смысл префиксного оператора: изменить значение, а затем присвоить его. Смысл постфиксного: присвоить значение, а затем изменить его оригинал.

#include <iostream>

using namespace std;

int main( ){

int  i=1, j=1;

int sumPr =++i;

int sumPst = j++;

cout<<" Prefics sum="<<sumPr<<endl;

cout<<" Postfics sum="<<sumPst<<endl;

 return 0;}

Выражение int sumPr =++i; сообщает компилятору, что переменную i необходимо увеличить на 1, сделав ее равной 2, а затем присвоить это значение переменной sumPr. Выражение же int sumPst = j++; предписывает компилятору сначала присвоит переменной sumPst текущее значение переменной j (j=1) и только после этого увеличить j на единицу. Следовательно результат работы программы:

Prefics sum= 2

Postfics sum= 1

Логические операторы для программирования сложных условий. Для обозначения результатов логических выражений в стандарте ANSI применяется тип bool, имеющий только два возможных значения: true (истина – для определения равенства двух значений) и false (ложь – для определения неравенства двух значений). Тогда результатом любого логического выражения может быть либо истина, либо ложь. Выражение, содержащее операторы отношения (выражения сравнения) всегда возвращают значения true либо false. Математические выражения, возвращающие ноль, можно рассматривать как значение false, а любой другой результат как true.

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

0 <x<1. Поэтому в языке С++ есть специальные операторы, помогающие записывать сложные условия. Например, условие попадания числа  x в указанный интервал запишется как x >9 && x < 10. В нем сначала проводятся сравнения x >9 и x < 10, потому, что приоритет операторов <>,  выше, чем оператора &&, а затем с результатом сравнений работает оператор. Оператор && называется логическим (логическое AND). Кроме него существуют еще два логических оператора, они приведены в Таблице 3.

Таблица 3

Оператор

Символ

Пример

AND (И)

&&

выражение1   &&   выражение2

OR   (ИЛИ)

| |

выражение1   | |   выражение2

NOT  (НЕ)

!

! выражение

 Оператор AND (И) двухаргументный (оценивает два операнда). Результат оператора:

  •  (истина) AND (истина) есть истина;
  •  (истина) AND (ложь) есть ложь;
  •  (ложь) AND (истина) есть ложь;

Например, логическое выражение в составе оператора условия

 if ( (x = = 5)  && (y = = 5) )

возвратит значение true только в том случае, если x и y равны числу 5 и false, если хотя бы одна из переменных не равна 5.

Оператор OR ( ИЛИ) двухаргументный. Результат оператора:

  •  (истина) OR (истина) есть истина;
  •  (истина) OR (ложь) есть истина;
  •  (ложь) OR (истина) есть истина;
  •  (ложь) OR (ложь) есть ложь

Например, логическое выражение в составе оператора условия

 if ( (x = = 5)  | | (y = = 5) )

возвратит значение true в том случае, если хотя бы одна их переменных x и y равна числу 5 или они обе равны 5, и false, если обе переменные не равны 5.

Оператор NOT является одноаргументным (оценивает только один операнд). Результат противоположен значению операнда:

  •  NOT  (истина) есть ложь;
  •  NOT  (ложь) есть истина

Например, логическое выражение в составе оператора условия

 if ( ! (x = = 5))

возвратит значение true только в том случае, если x не равен числу 5. Это выражение можно записать и по-другому:

 if (x != 5)

Базовые конструкции структурного программирования

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

Линейной называется конструкция, представляющая собой последовательное соединение двух или более операторов. Ветвление – задает выполнение одного из двух операторов, в зависимости от выполнения какого либо условия. Цикл – задает многократное выполнение оператора.

Целью использования базовых конструкций является получение программы простой структуры. Такую программу легко читать, отлаживать и при необходимости вносить в нее изменения. Структурное программирование также называют программированием без go to, т. к. частое использование операторов перехода затрудняет понимание логики работы программы. Но иногда встречаются ситуации, в которых применение операторов перехода, наоборот, упрощает структуру программы.

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

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

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

Первая форма оператора If

if  (выражение)

оператор;

следующий оператор;

Выражение в круглых скобках является условием и содержит, как правило, операторы отношения. Если это выражение возвращает false, то следующий за выражением в скобках оператор пропускается (игнорируется) и выполняется следующий оператор. Если же оно возвращает true, то оператор выполняется, а за ним выполняется следующий оператор.

Пример 1.

#include <iostream>

 int main( ) {

 using namespace std;

 int a,b;

 cout<<"Enter meaning variable a:";

 cin>>a;

 cout<<"Enter meaning variable b:";

 cin>>b;

 if (a>b)

    a=a+b;

    cout<<"Variable a is:   "<<a<<endl;

 cout<<"Variable b is:   "<<b<<endl;

 return 0; }

Здесь вводимые значения переменных a и b определяют возвращаемое выражением значение. Результаты работы программы при различных значениях переменных:

Enter meaning variable a: 5

Enter meaning variable b: 3

Variable a is:   8

Variable b is:   3

Enter meaning variable a: 5

Enter meaning variable b: 8

Variable a is:   5

Variable b is:   8

Вторая форма оператора If

Довольно часто в программах требуется, чтобы при выполнении условия (т.е., когда оно возвратит true) программа выполняла один блок кода, а при его невыполнении – другой.

if  (выражение)

  оператор1;

else

  оператор2;

 следующий оператор;

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

Пример 2.

#include <iostream>

 int main( ) {

  using namespace std;

 int a,b;

 cout<<"Enter meaning variable a:";

 cin>>a;

 cout<<"Enter meaning variable b:";

 cin>>b;

 if (a>b)

  a=a+b;

 else

  a=a*b;

    cout<<"Variable a is:   "<<a<<endl;

 return 0;}

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

Enter meaning variable a: 5

Enter meaning variable b: 3

Variable a is:   8

Enter meaning variable a: 5

Enter meaning variable b: 7

Variable a is:   35


В случае, когда число операторов, следующих за условием больше одного, используется блочная форма записи оператора if. Блоком называется составной оператор, начинающийся открывающей фигурной скобкой «{» и заканчивающийся закрывающей скобкой «}». Каждый оператор отделяется в блоке от другого точкой с запятой, сам же блок ею не заканчивается – после закрывающей фигурной скобки точка с запятой не ставится:

{

   оператор1;

   оператор2;

    …

   операторN;

}

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

Третья форма оператора If

В операторе If может находиться  любой оператор, в том числе и IfElse. Тогда форма записи условного оператора примет вид:

if  (выражение1)

  {

    if (выражение2)

оператор1;

        else

{

             if (выражение3)

оператор2;

                 else

оператор3;

                     }

   }

else

оператор4;

  

Порядок работы этого оператора:

  •  если выражение1 истинно и истинно выражение2, то следует выполнять оператор1;
  •  если выражение1 истинно, но выражение2 ложно, то в случае истинности выражения3, следует выполнять оператор2;
  •  если выражение1 истинно, но выражение2 и выражение3 ложны, то выполняется оператор3;
  •  если выражение1 ложно, выполняется оператор4.

 Пример. Решим систему уравнений:

#include <iostream>

int main( ) {

 using namespace std;

 float x, y;

 cout<<"Enter number: \n";

 cin>>x;

 cout<<"\n";

 if (x>1)

  y=x*x+x*x*x;

  else

  { if (x>=0 && x<=1)

      y=x*x+2*x;

  else

       y=x*x+2*x*x; }

 cout<<"y: "<<y<<endl;

 return 0;}

Троичный условный оператор

Троичный условный оператор «? :» – единственный оператор языка С++, который работает с тремя операндами, он получает три выражения и возвращает значение:
 (выражение1) ? (выражение2) : (выражение3)

Смысл записи: если выражение1 истинно, возвратить значение выражения2, в противном случае возвратить значение выражения3.

Пример. Вот как будет выглядеть решение задачи о нахождении большего из двух значений.

#include <iostream>

int main( ){

 using namespace std;

 int x, y,z;

 cout<<"Enter two number: ";

 cin>>x;

 cin>>y;

 z =(x>y)? x : y;

 cout<<"z: "<<z<<endl;

 return 0;}

Ветвление процесса выполнения программ

 

Для решения многих задач требуется многократное повторение одних и тех же действий. На практике это реализуется либо с помощью рекурсии либо с помощью итерации. Итерация – это повторение одних и тех же действий определенное количество раз. Основным методом итерации является цикл. Циклы в С++ могут быть трех видов:

  •  цикл с предусловием –  while
  •  цикл с постусловием – do…while
  •  цикл с управляющей переменной – for

Оператор цикла While

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

Синтаксис оператора цикла с предусловием следующий:

while (выражение условия)

{

операторы тела цикла;

}

 Выражение условия – это любое выражения языка С++ , а оператор – любой допустимый оператор или блок операторов. Если выражение истинно – возвращает значение 1, выполняется тело цикла (блок операторов), после чего выражение условия проверяется снова. Так будет продолжаться до тех пор, пока выражение не станет ложным (0); тогда цикл while завершится, а выполнение программы продолжится со следующей строки.

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

Пример. В приведенном ниже листинге программы на экран выводятся все значения переменной х, начиная с начального (х=0), увеличиваясь на единицу с каждой итерацией, до тех пор, пока х < 5.

  1.  #include <iostream>
  2.  
  3.  int main()
  4.  {
  5.  int x=0; //присвоение начального значения
  6.  
  7.  while(x<5) // проверка, истинно ли еще условие
  8.    {
  9.       x++; // тело цикла
  10.       std::cout<<"x= "<<x<<"\n";
  11.     }
  12.   std::cout<<" Complete. x: "<<x<<"\n";
  13.   return 0;
  14.    }

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

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

x= 1

x= 2

x= 3

x= 4

x= 5

Complete x: 5

Условие, проверяемое в цикле while может состоять из нескольких выражений, объединенных логическими операторами: &&, | |, ! . Например, приведенная ниже программа демонстрирует реализацию логической задачи. Вводятся два положительных числа – одно меньше, другое больше, их значения размещаются соответственно в переменных small и large. Далее меньшее начинает увеличиваться на единицу, а большее уменьшаться на два, процесс продолжается до тех пор, пока значения чисел не «встретятся». Цель задачи – определить эти числа. Для решения необходимо проверить три условия продолжения цикла:

- меньшее число меньше большего (small < large);

- большее больше нуля (0 < large);

- меньшее число меньше максимально допустимого.

  1.  #include <iostream>
  2.  using namespace std;
  3.  int main() {
  4.      unsigned short small; // от 0 до 65 535
  5.      unsigned long large;   // от 0 до 4 294 967 295
  6.      const unsigned short MAX= 65535; //объявление константы МАХ и
  7.      //присвоение ей значения3
  8.      cout<<"Enter a small number:  ";
  9.      cin>>small;
  10.      cout<<"Enter a large number:  ";
  11.      cin>>large;
  12.      // при каждой итерации проверяются три условия
  13.      while (small<large && large > 0 && small < MAX)
  14.      {
  15.         small++;
  16.         large -=2;
  17.       }
  18.       cout<<"\nSmall: "<<small<<" Large:  "<<large<<endl;
  19.       return 0;  }

Результат

Enter a small number: 2

Enter a large number:  100000

Small: 33335  Large:  33334

Оператор цикла dowhile

Цикл с постусловием является противоположностью цикла с предусловием. Возможен вариант, когда тело цикла while вовсе не будет выполнено. Оператор цикла while проверяет выражение условия цикла до того, как приступит к выполнению операторов тела цикла, и если условие ложно с самого начала, то тело цикла будет пропущено.

// Пример игнорирования цикла при заведомо ложном условии

#include <iostream>

#include <windows.h>

using namespace std;

int main()

{

char str[20];

int x;

CharToOem("Сколько приветов? : ", str);

cout<<str;

cin>>x;

while (x>0)

{

 CharToOem("Привет!\n", str);

 cout<<str;

 x--;

}

return 0;

}

Тема: Указатели и ссылки

 

Вопрос №1 Понятие указателя

Адреса переменных. Оператор взятия адреса

Задача. Создать функцию, предназначенную для обмена значений двух однотипных переменных.

// Решение

#include <iostream>

using namespace std;

void  Obmen(int x, int y)

{

int temp;

temp=x;

x=y;

y=temp;

// обмен выполнен

cout<<"В Obmen () x =  "<<x<<endl; // здесь x = 21

cout<<"В Obmen () y =  "<<y<<endl; // здесь y = 7

}

void main()

{

int a = 7, b=21;

cout<<"В main() до вызова Obmen ()…  a =  "<<a<<endl;

cout<<"В main() до вызова Obmen ()…  b =  "<<b<<endl;

Obmen (a,b); //вызов 

cout<<"В main() после вызова Obmen ()  a =  "<<a<<endl;

cout<<"В main() после вызова Obmen ()  b =  "<<b<<endl;

}

Результат:

В main()до вызова Obmen ()  a =  7

В main()до вызова Obmen ()  b =  21

В Obmen () x =  21

В Obmen () y =  7

В main() после вызова Obmen ()  a =  7

В main() после вызова Obmen ()  b = 21

Задача не решена, функция Obmen () не влияет на значение переменных a и b в основной программе. Переменные a, b и параметры функции Obmen (int x, int y) «соприкасаются» лишь в момент ее вызова, и внутри функции x = 21,  y = 7 (обмен выполнен). Но когда происходит возврат в основную программу, переменные x и y внутри функции пропадают, а переменные a и b основной программы остаются теми же.

Решение данной проблемы – использование в качестве передаваемых параметров функции Obmen () аргументов не переменных a и b, а их адресов.

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

// Фрагмент программы.

// Использование оператора обращения к адресу переменной

 unsigned short a = 5;

//получаем значение адресов переменных

 cout<<"Adres a:  "<<&a<<"\n";

Результат:

Adres a:   0012FF7C

Указатели

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

Указатель (pointer) – это переменная, значением которой является адрес другой переменной, другого объекта или функции в памяти компьютера и позволяющая косвенно манипулировать ими.

Для объявления указателя используется символ звездочка:

 тип_переменной * имя_указателя;

Здесь тип_переменной – это тип, на который ссылается указатель. Например:

int * ptr ; //переменная ptr объявляется указателем на тип int

Эта строка означает, что ptr – указатель, который будет содержать адрес переменной типа int.

Инициализация указателей

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

тип_переменной * имя_указателя = инициализатор;

Существуют следующие способы инициализации указателя:

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

  •  с помощью операции получения адреса. Чтобы присвоить указателю адрес переменной, перед ее именем ставится оператор взятия адреса (&):

тип_переменной * имя_указателя = & имя_переменной;

 

Пример

// фрагмент кода 

int a = 7;

cout<<"Znachenie a =  "<<a <<endl;

int *ptr=&a; // объявляется указатель и инициализируется

        // значением адреса переменной а

*ptr = 5;   //значение переменной – корректное значение указателя

cout<<" Znachenie a =  "<<a <<endl;

Результат:

Znachenie a =  7

Znachenie a =  5

Связь между значением переменной и указателем

Обратите внимание, что как только объявляется указатель и в нем оказывается адрес переменной a, возникает «тайная тропа» к ней и после инструкции *ptr = 5; переменная становится равной 5.

  1.  Итак, указатели – это переменные, которые содержат в качестве своих значений адреса ячеек памяти.
  2.  С другой стороны указатель содержит адрес переменной, которая в свою очередь имеет определенное значение. В этом смысле переменная отсылает к своему значению прямо, а указатель – косвенно.

Ссылка на значение, содержащееся в переменной, посредством указателя называется косвенной адресацией.

Когда компилятор обрабатывает оператор определения переменной, (например, int i=10;), то в памяти выделяется участок в соответствии с типом переменной (для int 4 байта) и записывает в этот участок указанное значение.

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

Оператор разыменования

Чтобы возвратить значение по адресу, содержащемуся в указателе, перед его именем устанавливается оператор разыменования или косвенного обращения к объекту – звездочка (*):

тип_переменной имя_переменной = *имя_указателя;

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

// Пример_2.  (фрагмент программы)

void main()

{

  int a = 10; // определение переменной a

  int *p=&a; // инициализация указателя

 *ptr=*ptr+11; // косвенный доступ к а и неявное увеличение ее

// значения

  cout<<"Znachenie a =  "<< a <<endl;

}

Результат:

Znachenie a =  21

Как видно действие оператора  *ptr=*ptr+11; аналогично оператору а =а+11;, а запись *ptr в контексте оператора *ptr=*ptr+11; по существу обозначает саму переменную a.

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

Форматы использования оператора разыменования

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

*указатель

ведет себя по-разному, в зависимости от своего местоположения по отношению к оператору присваивания:

  •  

справа от оператора присваивания, что означает «взять значение переменной, адрес которой содержит указатель, и присвоить его какой-либо переменной». Например,

//Пример.

#include <iostream>

using namespace std;

int main()

{

int a,b,c;

 int *ptr=NULL; //указатель инициализирован пустым значением

 a=7;

b=21;

ptr=&a;

c=*ptr;

cout<<"Value c: "<<c<<endl<<endl;   //c=7

          //конструкция c = *ptr  означает c = *(&a) или

    //что то же самое c=a – т.е. (*) и (&) взаимно стираются

 c=*ptr+21;

cout<<"Value c: "<<c<<endl<<endl;  //c=28

ptr=&b;

c=*ptr;

cout<<"Value c: "<<c<<endl<<endl;  //c=21

 return 0;

}

  •  слева от оператора присваивания, что означает «поместить значение правой части от оператора присваивания в переменную, адрес которой содержит указатель».

// Пример.

#include <iostream>

using namespace std;

int main()

{

int b;

int *ptr;

ptr=&b;

*ptr=21;

          //конструкция *ptr = 21  означает *(&b) = 21 или

    //что то же самое b=21 – т.е. (&) и (*) взаимно стираются

 cout<<"Value b: "<<b<<endl<<endl;  //b=21

 return 0;

}

Динамически распределяемая память

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

Реально указатели применяются в следующих случаях:

  •  управление данными в свободной (динамической) области памяти;
  •  передача данных между функциями по ссылке.

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

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

Ячейкам свободной памяти нельзя присвоить имя, можно лишь зарезервировать определенное количество ячеек и запомнить их адрес в указателе. С помощью указателей и осуществляется доступ к участкам динамической памяти.

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

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

указатель = new выражение;

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

Например, чтобы создать в динамической памяти переменную типа unsigned short необходимо записать:

unsigned short int *ptr = new unsigned short int;

  1.  Выражение new unsigned short int выделяет два байта динамической памяти.

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

  1.  Кроме того, ptr указывает на переменную типа unsigned short int, размещенную в динамически распределяемой памяти. Строку

*ptr = 21;

можно прочитать: «разместить число 21 в той области динамически распределяемой памяти, на которую указывает ptr».

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

delete указатель;

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

Например,

delete p;

где p ранее объявлен как указатель.

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

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

Ниже приведен пример создания и удаления указателей.

//Пример_9

#include <iostream>

using namespace std;

int main()

{

int Var = 7; // объявление и инициализация переменной Var

int *ptrVar =&Var; // объявление указателя и присвоение ему адреса

int *ptrHeap=new int; // объявлен еще один указатель, а в динамической

                 // памяти выделено пространство для переменной типа int

*ptrHeap=21; // участку динамической памяти присвоено значение

cout<<"Value Var: "<<Var<<endl; // вывод содержимого переменной Var

cout<<"Value *ptrVar: "<<*ptrVar<<endl; // вывод значения,

                                                                      //на которое указывает ptrVar

cout<<"Value *ptrHeap: "<<*ptrHeap<<endl; // вывод значения, на

 //  которое указывает ptrHeap

delete ptrHeap; // освобождение участка динамической памяти,

                         // указатель ptrHeap пуст и пригоден для записи

                         // адреса другого   участка памяти

ptrHeap = new int; // в динамической памяти вновь выделено

             // пространство для переменной типа int

*ptrHeap=3; // участку динамической памяти присвоено новое значение

 cout<<"Value *ptrHeap: "<<*ptrHeap<<endl; // вывод значения, на

  //  которое указывает ptrHeap

     delete ptrHeap; // освобождение участка динамической памяти

     return 0;

}

Указатели на объекты

Создание и удаление объектов в динамической памяти

Выделяют два основных отличия между статическими и динамическими объектами.

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

Выделение и освобождение памяти под статические объекты производится компилятором автоматически. Выделение и освобождение памяти под динамические объекты целиком и полностью возлагается на программиста. Для манипуляции динамически выделяемой под объекты памятью служат операторы new и delete.

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

имя_класса  *указатель = new тип_объекта;

 

Здесь имя_класса – имя класса, адрес экземпляра которого, размещенного в динамической памяти будет хранить объявляемый указатель, а тип_объекта – спецификатор типа объекта (экземпляра класса), для размещения которого выделяется память.

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

Сar *ptrCar = new Car;

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

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

 

Рассмотрим пример:

#include <iostream>

using namespace std;

//описание класса 

class Car

{

public:

     Car();

    ~Car();                                 

private:                                                    

     unsigned int itsYear;                           

};

Car::Car()

{

itsYear=5;

cout<< "Constructor called\n";

}

Car::~Car ( )

{

cout<< "Destructor called\n";

}

int main( )

{

 cout<<"Static memory. Creation object…"<<endl;

Car myZaparozec;  // в стеке создается объект myZaparozec

cout<<"Dynamic memory. Creation object…"<<endl;

Car *ptrCar=new Car; // в динамической памяти

//резервируется место под объект класса Car, адрес

// участка сохраняется в указателе ptrCar

 cout<<"Delete object..."<<endl;

 delete ptrCar; //освобождение указателя и вызов деструктора

 cout<<"Function main() exit..."<<endl;

return 0;

}

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

Static memory. Creation object…

Constructor called

Dynamic memory…Creation object

Constructor called

Delete object...

Destructor called

Function main() exit...

Destructor called

Доступ к членам объекта в динамической памяти

Для доступа к переменным-членам и функциям объекта, созданного в динамической памяти, необходимо использовать оператор косвенного доступа – стрелка (- >).

#include <iostream>

class myclass

{

int a;

public:  

myclass ( ) {a = 10;}

   ~myclass();

   int Show ( );

};

int myclass:: Show ( )

{

return a;

}

int main ()

{

myclass *Object=new myclass;   //  объект в динамической памяти

 std::cout<<"\nBy way of Pointer..."<<Object->Show ()<<"\n";

   return 0;

}

Результат:

By way of Pointer...10

Перегрузка функций

Важной возможностью С++ является перегрузка функций (function overloading). Перегрузка функций не только обеспечивает механизм, посредством которого в С++ реализуется механизм, называемый полиморфизмом (многообразие форм), но и формирует то ядро, вокруг которого развивается вся среда программирования на С++. Под полиморфизмом функций понимают существование в программе нескольких перегруженных функций разного назначения. Перегруженные функции позволяют упростить программы, допуская обращение к одному имени для выполнения близких по смыслу действий.

Язык С++ позволяет создать несколько разных функций с одинаковым именем (функции-тезки), при этом функции отличаются:

  •  либо типом;
  •  либо числом своих аргументов;
  •  либо типом и числом аргументов.

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

Существует условие, которому обязаны удовлетворять функции-тезки – функции должны отличаться друг от друга списком параметров, а именно:

  •  типом или количеством параметров;
  •  тем и другим одновременно.

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

Int myFunction (int, int);

Int myFunction (long, long);

Int myFunction (long);

Функция  myFunction перегружена с тремя различными списками параметров: первая и вторая отличаются типами параметров, а третья – типом и количеством.

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

 

Механизм перезагрузки функций необходим, например, тогда, когда требуется создать функцию, которая удваивает любое передаваемое ей значение. При этом необходимо иметь возможность передавать ей значения типа int, long, float или double. Без перегрузки функций пришлось бы создавать четыре различные функции:

int DblInt (int);

long DblLongt (long);

float DblInt (float);

double DblDouble (double);

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

int Dbl (int); // объявление функции удвоения целых чисел

long Dbl (long); // объявление функции удвоения чисел типа long

float Dbl (float); // объявление функции удвоения чисел типа float

double Dbl (double); // объявление функции удвоения чисел типа double

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

Вышесказанное рассмотрим на примере. Здесь функция Dbl() перегружается для приема параметров четырех типов.

// полиморфизм функций

#include <iostream>

using namespace std;

// прототипы перегруженных функций

int Dbl (int);

long Dbl (long);

float Dbl (float);

double Dbl (double);

int main ()

{// инициализация переменных

 int myInt = 6500;

 long myLong=65000;

 float myFloat=6.5F;

double myDouble=6.5e20;

// объявление переменных для хранения удвоенного значения

 int doubledInt;

long doubledLong;

float doubldedFloat;

double doubledDouble;

// вывод на экран значений переменных

 cout <<"myInt:  "<<myInt<<"\n";

cout<<"myLong:  "<<myLong<<"\n";

cout<<"myFloat:  "<<myFloat<<"\n";

cout<<"myDouble:  "<<myDouble<<"\n";

// вызов перегруженных функций и

// присвоение переменным возвращаемых ими значений

 doubledInt = Dbl (myInt);

doubledLong = Dbl (myLong);

doubldedFloat = Dbl (myFloat);

doubledDouble = Dbl (myDouble);

//вывод значений

cout<<"doubleInt:  "<<doubledInt<<"\n";

cout<<"doubleILong:  "<<doubledLong<<"\n";

cout<<"doubleFloat:  "<<doubldedFloat<<"\n";

cout<<"doubleDouble:  "<<doubledDouble<<"\n";

return 0;

}

//определение функций

int Dbl (int original)

{

return 2*original;

}

long Dbl (long original)

{

return 2*original;

}

float Dbl (float original)

{

return 2*original;

}

double Dbl (double original)

{

 return 2*original;

}

Результат:

 myInt:  6500

myLong:  65000

myFloat:  6.5

myDouble:  6.5е+020

doubleInt:  13000

doubleILong:  130000

doubleFloat:    13

doubleDouble:   1.3е+021

Значения параметров функции, используемые по умолчанию

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

Из этого правила существует одно исключение, которое вступает в силу, если в объявлении функции для параметра объявляется значение по умолчанию. Значение по умолчанию – это значение, которое используется в том случае, если при вызове функции для параметра не установлено значение.

Рассмотрим пример объявления функции:

 long myFunc (int x = 21);

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

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

long myFunc (int param1, int param2, int param3);

Параметру param2 может быть назначение значение по умолчанию только в том случае, если значение по умолчанию назначено и параметру param3. Параметру param1– когда установлены значения по умолчанию и param2 и param3.

Рассмотрим сказанное выше подробно.

// использование значений по умолчанию для параметров функций

  1.  #include <iostream>
  2.  using namespace std;
  3.  //объявление функции
  4.  int Cube (int l, int w=25, int h=1);
  5.  
  6.  int main()
  7.  {
  8.  // инициализация параметров, хранящих значения размеров куба
  9.  int l=100;
  10.  int w=50;
  11.  int h=2;
  12.  int v;
  13.  // вызов функции со всеми ее параметрами; используются значе-
  14.  // ния, определенные в строках 9-11
  15.  v=Cube(l,w,h); 
  16.  cout<<"First:  "<<v<<"\n";
  17.  // вызов функции с двумя ее параметрами; значение высоты ис
  18.  //пользуется по умолчанию
  19.  v=Cube(l,w);
  20.  cout<<"Second:  "<<v<<"\n";
  21.  // вызов функции с одним параметром
  22.  v=Cube(l);
  23.  cout<<"Thrid:  "<<v<<"\n";
  24.  return 0;
  25.  }
  26.  //определение функции
  27.  Cube (int l, int w, int h)
  28.  {
  29.  return (l*w*h);
  30.  }

Результат.

First:  10000

Second:  5000

Thrid: 2500

Анализ. В прототипе функции Cube() в строке 4 объявляется, что функция принимает три параметра, причем последние два имеют значения, устанавливаемые по умолчанию. Функция вычисляет объем параллелепипеда на основании передаваемых параметров: если значение ширины w не передано, то по умолчанию оно принимается равным 25 , а высоты h в том же случае – 1. В строке13 в качестве параметров передаются функции Cube() переменные, инициализированные в строках 9-11 и в строке 16 выводится первое значение объема. В строке 19 функция вызывается вновь, но без параметра, содержащего высоту, и в этом случае используется значение по умолчанию(h=1). При третьем вызове функции Cube() нет w и h, поэтому уже два значения используются определенными по умолчанию.

Массивы в С++

Определение массива

Массив – это упорядоченная последовательность переменных одного типа. Каждому элементу массива отводится одна ячейка памяти. Элементы одного массива занимают последовательно расположенные ячейки памяти. Все элементы имеют одно имя – имя массива и отличаются индексами – порядковыми номерами в массиве. Количество элементов в массиве называется его размером. Чтобы отвести в памяти нужное количество ячеек для размещения массива, надо заранее знать его размер. Резервирование памяти для массива выполняется на этапе компиляции программы.

Определение массива состоит из спецификатора типа, имени массива и размера в квадратных скобках:

тип_элемента имя_массива [размер]

Размер задает число элементов массива (не менее 1) и должен быть константным выражением.

Например.

int myArray [100]; // массив из 100 элементов целого типа

Операция sizeof(myArray), примененная к объявленному массиву даст результат 400, т. е. 100 элементов по 4 байта.

const int buf_size=512, max_files=20;

int staff_size = 27;

char buffer [buf_size];     //правильно

char Table[max_files - 3];   //правильно

double salary[staff_size];   //ошибка

bool flag[10];            //правильно

Нумерация элементов начинается с 0, поэтому для массива размером 10, правильными значениями индексов будут 0..9. К каждому элементу массива можно обратиться с помощью операции индексирования: flag[2] = true; .

Инициализация массива

Небольшие  массивы переменных встроенных типов (например, int или char) можно инициализировать  при объявлении массива:

тип_элемента имя_массива [размер] = { список_значений}

здесь список_значений – перечисление через запятую значений элементов массива

Например.

int a[10]={1,2,3,4,5,6,7,8,9,10} ; //массив из 10 целочисленных значений

или

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

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

int iArray[3] = {1,2,3}

int v1[ ] = {1,2,3,4};

char v2[ ] = {‘a’,’b’,’c’};

char v3[2] = {‘a’,’b’,’c’};   //ошибка (см. ниже)

int v4[8] = {1,2,3,4}

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

Рассмотрим пример объявления и заполнения массива.

#include <iostream>

int main()

{

 int myArray[3]; //объявление массива

 int i; 

 for (i=0; i<3; i++) //заполнение массива значениями

 {

std::cout<<"Value for array [ "<< i <<" ]: "; //ввести значение

std::cin>>myArray[ i ]; //сохранение значения в соответствующем                            //элементе

 }

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

    std::cout<<i<<":"<<myArray[i]<<"\n"; // вывод массива

return 0;

}

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

const int array_size=7;

int ia1[ ]= {0,1,2,3,4,5,6};

int main()

{

int ia2[array_size];

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

 ia2[i] = ia1[i];

return 0;

}

Язык С++ не обеспечивает контроля индексов массива – ни на этапе компиляции, ни на этапе выполнения. Программист сам должен следить за тем, чтобы индекс не вышел за пределы массива.

Массивы символов удобно инициализировать не списком символьных значений в фигурных скобках, а строковым литералом.  Строковый литерал – это последовательность символов, заключенная в двойные кавычки. В строковом литерале на один символ больше, чем используется при записи. Он всегда заканчивается нулевым символом ‘\0’.

const char ch1[ ] = {‘C’,’+’,’+’};    //размер массива ch1 - 3

const char ch2[ ] = ”C++”;            //размер массива ch2 – 4

const char ch3[6] = ”Daniel”;        //ошибка

const char ch4[ ] = ”abcdefghsdkjsdkskeuifhshabavbscaatsfdatd”

             “SDJDKJDHKFSDJHVM,C,BNMSIERHOERHSDKLFHBNASMBF”; //правильно

Пустая строка записывается “ ” и имеет тип const char[1].

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

В С++ есть функция int rand() возвращающая псевдослучайное число из диапазона 0..RAND_MAX=32767, описание функции находится в файле <stdlib.h>.

В общем случае псевдослучайное n определяется по формуле:

n = a + rand ( ) % b

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

Пример.

#include<iostream.h>

#include<stdlib.h>

void main()

{

int array[100];

int n;

cout<<"\nEnter the size of array:";

cin>>n;

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

 {

    array[ i ]=rand()%100-50;// диапазон значений от -50 до 50

    cout<<array[ i ]<<"\n";

}

}

В этой программе используется перебор массива по одному элементу слева направо с шагом 1.

Приведенная программа генерирует появление одной и той же случайной последовательности. Рандомизация достигается путем использования инструкции

srand(time(NULL));  

, где функция srand(), содержащаяся в файле stdlib.h, принимает в качестве аргумента текущее календарное время  в секундах, это значение преобразуется в беззнаковое целое и используется как начальное значение в генераторе случайных чисел. Прототип функции находится в файле time.h.

#include<iostream.h>

#include<stdlib.h>

#include<time.h>

void main()

{

int array[100];

int n;

cout<<"\nEnter the size of array:";

cin>>n;

srand(time(NULL));

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

 {

    array[ i ]=-5+rand()%10; // диапазон значений от -5 до 5

    cout<<array[ i ]<<"\n";

}

}

Многомерные массивы

Многомерные массивы описываются как массивы массивов. Например:

int d2[10] [20];  //массив из десяти массивов по 20 целых

Многомерные массивы могут быть инициализированы:

int ia [2][3] = {

{0,1,2},

(3,4,5}

};

int ib[2][3] = {0,1,2,3,4,5};

int ic[2] [3]= { {1}, {3} }; //инициализирует только первые элементы каждой //строки, остальные – 0

int id[2][3] = {1,2,3,4}; //два последних элемента второй строки – 0

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

В программных кодах высокого уровня использования многомерных массивов надо избегать. Лучше пользоваться типом vector из стандартной библиотеки С++.

Структуры С+

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

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

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

 В языке С++ структура является видом класса и обладает всеми его свойствами

Структуры определяются:

struct [имя типа]

{

тип_1 элемент_1;

тип_2 элемент_2;

} [список описателей];

Т.о. возможно следующее объявление структуры:

#include <iostream.h>

void main()

{

struct address

{

char name[10];         

char street[30];

char town[10];

long number;

} my={"Petrov", "Green Street","Moscow",33};

}

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

Рассмотрим пример.

#include <iostream.h>

#include <string.h>// используется функция strcpy()

// объявление структуры

struct MYSTRUCTURE

{ 

char sName[100];

int iAge;

};

/*Это означает, что MYSTRUCTURE состоит из строки с именем sName и целого с именем iAge. sName и iAge называются элементами данных структуры.*/

/* Код в main() объявляет переменную с именем MyStructure типа MYSTRUCTURE: */

void main()

{

 MYSTRUCTURE MyStructure;

/*Обратите внимание, что по традиции имя переменной составлено из символов нижнего регистра или в нем смешаны символы нижнего и верхнего регистров, MyStructure, но в имени типа структуры используются только символы верхнего регистра, как, например, в MYSTRUCTURE. */

 strcpy (MyStructure.sName, "Andy" ); /*функция strcpy() копирует строку 'Andy' в элемент данных MyStructure.sName */

/*Следующий оператор присваивает значение 13 элементу данных iAge cтруктуры*/

 MyStructure.iAge = 13;

cout << "My name is ";

cout << MyStructure.sName;

cout << " and I am ";

cout << MyStructure.iAge;

cout << " years old." << endl;

}

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


My name is Andy and I am 13 years old.

Тема: Классы в С++

Конструкторы и деструкторы

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

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

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

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

  •  деструктору присваивается имя класса с символом тильды «~» впереди;
  •  деструкторы не имеет параметров (не принимают аргументов) и не возвращают значений;
  •  должен быть открытым (public) методом;
  •  никогда не вызывается явно, вызывается автоматически для каждого объекта при выходе из области видимости объекта;
  •  если деструктор не определен в классе явно, то он создается компилятором автоматически, по умолчанию освобождая память, занимаемую объектом.

#include <iostream>

class Car

{

public:

Car(unsigned int initialYear);

~Car();                        

unsigned int GetYear( );                    

void SetYear (unsigned int Year);     

void Start( );                                       

private:                                                    

unsigned int itsYear;                        

};

using namespace std;

Car::Car(unsigned int initialYear)

{

 itsYear=initialYear;

}

Car::~Car ( )  { }

int unsigned Car::GetYear ( )              

{

return itsYear;

}

void Car::SetYear (unsigned int Year)

{

 itsYear=Year;

}

void Car::Start ()

{

 cout<<"Forward!!!\n";

}

int main( )

{

unsigned int Old;

Car myZaparozec(19);

 

Old=myZaparozec.GetYear ( );

cout<<"The year back to my machine was "<<Old<<"  years"<<endl;

myZaparozec.SetYear (20);

Old=myZaparozec.GetYear ( );

cout<<"Now to my machine "<<Old<<"  years"<<endl;

 myZaparozec.Start ();

return 0;

} 

1 С++ не имеет встроенных средств ввода/вывода подобно другим языкам. Даже простейший исходный текст программы располагается в нескольких файлах: например, файл iostream является заголовочным файлом, в котором на языке С++ содержится описание стандартных средств ввода/вывода, в частности консольных потоков cin и cout. Эти потоки в терминах С++ являются объектами классов.

2 Символ << называют так же операцией помещения в поток.

3

4 В прототипе функции имена параметров не обязательны и имеет право существовать следующий вариант объявления: long myFunc (int = 21);




1. реферат дисертації на здобуття наукового ступеня кандидата економічних наук3
2. Гігієнічні норми житла
3. Електронні видання
4. Доходы и расходы правительства муниципальных образований России на примере города Казань
5. Контрольная работа- Расчет работы лесовозных автопоездов на лесозаготовках
6. Товарищи рабочие крестьяне трудовая интеллигенция учащаяся и рабочая молодёжь Челябинская орган
7. Организация воспитательной деятельности 050702 Преподавание в начальных классах 050709 Учебная дисципли
8. ТЕМА- ИСТОРИЯ БОЛЕЗНИ заключительное занятие Учебнометодическая разработка
9. Пермский институт Федеральной службы исполнения наказаний далее ~ институт обучение по программам высше
10. Элементы экономической теории в пространстве
11. тематизация знаний и практических навыков студентов полученных в процессе изучения дисциплин Экономика от
12. РЕФЕРАТ Проблемы внедрения ИС и способы их решения Выполнила- ст
13. директора по УМР Т
14. прежнему упрямец и анархист так же независим с начальством и никто из сослуживцев не знает что выкинет Ребу
15. Легенды о Сафо
16. Принцип разделения властей и структура государственной власти Российской Федерации
17. ЛЕКЦИЯ 21 ИССЛЕДОВАНИЕ БОЛЬНЫХ С ЗАБОЛЕВАНИЯМИ ЖЕЛУДКА Расспрос больного В диагностике заболеваний же
18. темами монологічність унормованість мови
19.  Стратегия и тактика диалога Стратегия диалога определяется основной целью которая преследуется учас
20. Учет изделий из железобетона