Программирование вложенных циклов



 

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

Задача №7: Напечатать N простых чисел.

Разделим эту задачу на две подзадачи:

1. определить является ли число простым;

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

Простым называется число, которое делится только само на себя и на единицу (сама единица простым числом не является). Тогда, чтобы определить является число простым или нет, нужно проверить есть ли у него другие делители. Для этого будем делить число К на все числа от 2 и до К, используя цикл с постусловием. Если К разделится на d без остатка только, когда d станет равно К, значит, число простое:

       d=1;//начальное значение делителя

do

{

    d++;//увеличиваем делитель

}

/*цикл выполняется пока остаток от деления К на d не равен 0 (не делится)*/

while(К%d!=0);

if(К==d)//делитель равен К, т. е. число простое

    cout<<a<<" ";

 

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

K=1;//присваиваем начальное значение числу

//выполняем цикл пока не будет напечатано n чисел

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

{

K++;//берем следующее число

if (K простое число)

i++;//увеличиваем счетчик простых чисел

}

Объединив эти два фрагмента вместе, получим решение поставленной задачи

 

#include<iostream.h>

void main()

{

int K=1,n,d;

cout<<"\nEnter N";

cin>>n;

//неправильно задано число n

if(n<1) {

cout<<”\nerror in data”;

return;//завершение программы

}

for(int i=0;i<n;)//внешний цикл

{

K++;d=1;

do //внутренний цикл

{

    d++;

}

while(K%d!=0);//конец внутреннего цикла

if(K==d){

    cout<<K<<" ";

    i++;}

 

}//конец внешнего цикла

}

Тесты

n=0 Error in data
n=1 2
n=5 10

 

Массивы

 

В языке C/C++, кроме базовых типов, разрешено вводить и использовать производные типы, полученные на основе базовых. Стандарт языка определяет три способа получения производных типов:

· массив элементов заданного типа;

· указатель на объект заданного типа;

· функция, возвращающая значение заданного типа.

 

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

 

12.1. Определение массива в C/C++

 

Массивы определяются следующим образом:

 

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

 

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

Элементы массива всегда нумеруются с 0.

 

45 352 63   124 значения элементов массива  
0 1 2 ….. 99 индексы элементов массива

Рис. 4. Размещение массива в памяти

Чтобы обратиться к элементу массива, надо указать имя массива и номер элемента в массиве (индекс):

a[0] – индекс задается как константа,

a[55] – индекс задается как константа,

a[i] – индекс задается как переменная,

a[2*i] – индекс задается как выражение.

 

Элементы массива можно задавать при его определении:

 

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

 

Операция sizeof(a) даст результат 40, т. е. 10 элементов по 4 байта.

 

int a[10]={1,2,3,4,5};

 

Операция sizeof(a) даст результат 40, т. е. 10 элементов по 4 байта. Если количество начальных значений меньше, чем объявленная длина массива, то начальные элементы массива получат только первые элементы.

 

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

 

Операция sizeof(a) даст результат 20, т. е. 5 элементов по 4 байта. Длина массива вычисляется компилятором по количеству значений, перечисленных при инициализации.

 

Примеры решения задач и использованием массивов

 

Задача 1. Формирование массива с помощью датчика случайных чисел.

Для того чтобы сформировать массив необходимо организовать цикл для перебора элементов массива. Для формирования каждого элемента вызывается функция rand(),которая возвращает псевдослучайное число из диапазона от 0 до 32767. Чтобы получить значения из диапазона от 0 до 100 используется операция получения остатка от деления %.  Если нужно получить не только положительные, но и отрицательные числа вычитается число k равное половине от требуемого диапазона.

a[I]=rand()%100-50;//числа из диапазона -50..49.

Чтобы использовать функцию rand(), надо подключить библиотечный файл <stdlib.h>

 

#include <iostream.h>

#include <stdlib.h> //файл с описанием функции rand()

void main()

{

int a[100];

int n;

cout<<”\nEnter the size of array:”;cin>>n;

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

{

a[I]=rand()%100;// числа из диапазона 0..100

cout<<a[I]<<” ”; //вывод элемента массива

}

}

Задача 3. Найти максимальный элемент массива.

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

 

#include <iostream.h>

#include <stdlib.h>

void main()

{

int a[100];

int n;

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

cin>>n;

for(int I=0;I<n;I++)   //заполнение массива

{

a[I]=rand()%100-50;

cout<<a[I]<<” ”;

 }

 

int max=a[0]; //задаем начальное значение max

for(I=1;I<n;I++)

//сравниваем элементы массива с max

if(a[I]>max)max=a[I];

 

cout<<”\nMax=”<<max;

}

 

Задача 3. Найти сумму элементов массива с четными индексами.

Для решения этой задачи можно предложить несколько способов.

  1. Массив перебирается с шагом 2 и все элементы суммируются.
  2. Массив перебирается с шагом 1 и суммируются только элементы, имеющие четные индексы. Для проверки на четность используется операция получения остатка от деления на 2.

 

//Первый способ

#include <iostream.h>

#include <stdlib.h>

void main()

{

int a[100];

int n;

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

cin>>n;

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

{

//заполнение массива случайными числами

a[I]=rand()%100-50;

cout<<a[I]<<” ”;

}

int Sum=0;

for(I=0; I<n; I+=2)

//суммируются элементы с индексами 0, 2, 4, и т. д.

Sum+=a[I];

 

cout<<”\nSum=”<<Sum”;

}

 

 

//Второй способ

#include <iostream.h>

#include <stdlib.h>

void main()

{

int a[100];

int n;

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

cin>>n;

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

{

//заполнение массива случайными числами

a[I]=rand()%100-50;

cout<<a[I]<<” ”;

}

int Sum=0;

for(I=0; I<n; I++)

if(I%2==0)

//суммируются элементы с индексами 0, 2, 4, и т. д.

Sum+=a[I];

 

cout<<”\nSum=”<<Sum”;

}

 

Указатели

 

Понятие указателя

 

Указатели являются специальными объектами в программах на C/C++. Указатели предназначены для хранения адресов памяти.

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

Рис. 5 Значение переменной и ее адрес

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

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

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

 

тип* имя;

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

 

int* i;

double *f, *ff;//два указателя

char* c;

 

Тип может быть любым, кроме ссылки.

 

Размер указателя зависит от модели памяти. Можно определить указатель на указатель: int** a;

Указатель может быть константой или переменной, а также указывать на константу или переменную.

 

int i;   //целая переменная

const int ci=1; //целая константа

int* pi; //указатель на целую переменную

const int* pci; //указатель на целую константу

 

Указатель можно сразу проинициализировать:

 

//указатель на целую переменную

int* pi=&i;  

//указатель на целую константу

const int* pci=&ci;

 

//указатель-константа на переменную целого типа

int* const cpi=&i;

 

//указатель-константа на целую константу

const int* const cpc=&ci;

 

Если модификатор const относится к указателю (т. е. находится между именем указателя и *), то он запрещает изменение указателя, а если он находится слева от типа (т. е. слева от *), то он запрещает изменение значения, на которое указывает указатель.

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

· с помощью операции получения адреса

int a=5;

int* p=&a; или int p(&a);

· с помощью проинициализированного указателя

int* r=p;

· адрес присваивается в явном виде

char* cp=(char*)0х В800 0000;

где       0х В800 0000 – шестнадцатеричная константа,

(char*) – операция приведения типа.

· присваивание пустого значения:

int* N=NULL;

int* R=0;

 

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

 

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

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

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

 

указатель = new имя_типа[инициализатор];

где инициализатор – выражение в круглых скобках.

 

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

 

int* x=new int(5);

 

Для удаления динамических переменных используется операция delete, определенная в C++:

 

delete указатель;

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

 

delete x;

 

Операции с указателями

 

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

· разыменование (*);

· присваивание;

· арифметические операции (сложение с константой, вычитание,
инкремент ++, декремент --);

· сравнение;

· приведение типов.

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

 

int a; //переменная типа int

int* pa=new int; //указатель и выделение памяти под //динамическую переменную

*pa=10;//присвоили значение динамической
//переменной, на которую указывает указатель

a=*pa;//присвоили значение переменной а

 

Присваивать значение указателям-константам запрещено.

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

 

int a=123;

int* pi=&a;

char* pc=(char*)&a;

float* pf=(float*)&a;

printf("\n%x\t%i",pi,*pi);

printf("\n%x\t%c",pc,*pc);

printf("\n%x\t%f",pf,*pf);

 

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

 

66fd9c 123

66fd9c {

66fd9c 0.000000

 

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

В примере при инициализации указателя была использована операция приведения типов. При использовании в выражении указателей разных типов, явное преобразование требуется для всех типов, кроме void*. Указатель может неявно преобразовываться в значения типа bool, при этом ненулевой указатель преобразуется в true, а нулевой в false.

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

· Инкремент увеличивает значение указателя на величину sizeof(тип).

 

char* pc;

int* pi;

double* pd;

. . . . .

pc++;  //значение увеличится на 1

pi++;  //значение увеличится на 4

pd++;  //значение увеличится на 8

 

· Декремент уменьшает значение указателя на величину sizeof(тип).

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

int a=123, b=456, c=789;

int* pi1=&a;

int* pi2=&b;

int* pi3=&c;

printf("\n%x",pi1-pi2);

printf("\n%x",pi1-pi3);

 

Результат выполнения программы:

 

1

2

 

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

 

· Можно суммировать указатель и константу:

 

pi3=pi3+2;

pi2=pi2+1;

printf("\n%x\t%d",pi1,*pi1);

printf("\n%x\t%d",pi2,*pi2);

printf("\n%x\t%d",pi3,*pi3);

 

Результат выполнения программы:

 

66fd9c 123

66fd9c 123

66fd9c 123

 

Ссылки

Ссылка – это синоним имени объекта, указанного при инициализации ссылки.

Формат объявления ссылки

 

тип & имя =имя_объекта;

 

int x;  // определение переменной

int& sx=x; // определение ссылки на переменную х

const char& CR=’\n’; //определение ссылки на константу

 

Правила работы со ссылками:

· Переменная-ссылка должна явно инициализироваться при ее описании, если она не является параметром функции, не описана как extern или не ссылается на поле класса.

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

· Не существует указателей на ссылки, массивов ссылок и ссылок на ссылки.

· Операция над ссылкой приводит к изменению величины, на которую она ссылается.

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

 

#include<iostream.h>

void main()

{

int i=123;

int& si=i;

cout<<”\ni=”<<i<<” si=”<<si;

i=456;

cout<<”\ni=”<<i<<” si=”<<si;

i=0;

cout<<”\ni=”<<i<<” si=”<<si;

}

 

Выведется:

i=123 si=123

i=456 si=456

i=0 si=0

 

 

Указатели и массивы


Дата добавления: 2018-04-04; просмотров: 209;