85889

Указатели. Одномерные массивы и в языке программирования Си++

Лабораторная работа

Информатика, кибернетика и программирование

Цель работы: Освоить работу с указателями и операциями над указателями ознакомиться с основными принципами работы с одномерными массивами 1. Существует следующие способы инициализации указателя: Присваивание указателю адреса существующего объекта: С помощью операции получения адреса например...

Русский

2015-03-31

169 KB

3 чел.

Лабораторная работа №4

“ Указатели. Одномерные массивы и в языке программирования Си++”

  1.  Теоретические сведения

Цель работы: Освоить работу с указателями и операциями над указателями, ознакомиться с основными принципами работы с одномерными массивами

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

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

Адрес –это местоположение ячейки памяти. Этикетка адреса –это имя переменной.

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

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

Указатель не является самостоятельным типом, он всегда связан с каким либо другим конкретным типом /например, int, float, double или массив, функция  т.п./.

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

Общий формат объявления указателя на объект:

                                                         тип  <*имя_указателя> ;  

здесь тип – это название типа, *- унарная операция обращения по адресу / или операция разыменования, операция доступа по адресу, операция раскрытия ссылки и т.д./, имя_указателя – идентификатор переменной. Например:

                                                              int *ukaz_a;

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

                                                              int *ukaz_a, *ukaz_b, *ukaz_c   ;

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

  1.  *- унарная операция обращения по адресу / или операция разыменования, операция доступа по адресу, операция раскрытия ссылки, операция разадресации/ предназначена для доступа к величине , адрес которой хранится в указателе;
  2.  присваивание :

                                 char *ukaz_ch=’ю’;

  1.  операция приведение типов /Подбельский, 117 стр./:  

   unsigned long L=0x12345678L;

   char *ukaz_ch=(char *)&L; //приведение типа long  к типу char*

   int *ukaz_int=(int *)&L; // приведение типа long  к типу int *

                                  

  1.  Операции инкремент (++) и декремент (--):  *ukaz_int ++    или  *ukaz_int --    
  2.  & -унарная операция получения адреса /или операция взятия адреса/, предназначена к величинам имеющим имя и размещенным  в оперативной памяти, т.е . это означает, что нельзя получить адрес скалярного выражения, неименованной константы и т.д.

int a=45; // объявление переменной а

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

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

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

int a=45; // существующий объект –объявленная  переменная-  а

                     int *ukaz_a=&a;

                    int *ukaz_a (&a);

  •  С помощью значения другого инициализированного указателя, например:

int a=45;

                      int *ukaz_a=&a; // инициализированный указатель

              int *ukaz_a2= ukaz_a;

  •  С помощью имени массива или функции /имя массива или функции считается как адрес/, например:  

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

int *ukaz_mas=massiv; // присваивание  адреса начала массива или имени массива

…         …    

void  funk(int arg1, int arg2); // объявление функции

void  (*ukaz_funk) (int); // объявление указателя на функцию

ukaz_funk= funk; // присваивание  адреса функцции

 

  1.  Присваивание указателю пустого или нулевого значения, например:    int *ukaz1=NULL; или    int *ukaz2=0;  

  1.  Присваивание указателю адреса области памяти в явном виде, например:

char  *ukaz_adr=(char *) 0xB8000000;

здесь,   0xB8000000- шестнадцатеричная константа, (char *)- операция приведения типа, т.е. константа преобразуется к типу  «указатель на char » ;

Применение указателей.  Указатели чаще всего  используются при работе с динамической памятью, иногда называемой «кучей» / куча в переводе с английского означает heap/.  Динамическая  память –это свободная память, в которой можно во время выполнения программы выделять место в соответствии с потребностями. Доступ к выделенным участкам динамической памяти , так называемым динамическими переменными производится только через указатели. Время жизни динамических переменных  от точки их  создания и до конца выполнения программы или до явного освобождения памяти. Для работы с динамической памятью в С++ чаще всего  используются две  операции  new /выделяет место в памяти/  и delete /освобождает или вернет  место в памяти выделенной операцией new/  . Например:

int *n= new int; // операция new выполняет выделение достаточного для размещения величины типа int участка  

                            динамической памяти и  записывает адрес начала этого участка в переменную  n.

int *m= new int(10); // выделение участка под int , записывает адрес начала этого участка в переменную  m и

                                     производится иницализация динамической переменной значением 10.

int *massiv= new int[10]; // // операция new выполняет выделение памяти под 10 величин  типа int  /или массива из 10 элементов/  и  записывает адрес начала этого участка в переменную  massiv, к оторая может считаться как имя массива.

delete n; //освобождение выделенного участка памяти для n или уничтожение динамической переменной n

delete m; // освобождение выделенного участка памяти для m или уничтожение динамической переменной m

delete massiv;// освобождение выделенного участка памяти или уничтожение динамического массива massiv

Пример1:

#include "iostream.h"

#include "conio.h"

#pragma argsused

int main()

{

 int a=45; // объявление переменной а

 int *ukaz_a=&a; // объявление и иницализация указателя для переменной а

 cout<<"\n a="<<a;

 cout<<"\n ukaz_a="<<*ukaz_a;

 getch();

       //return 0;

}

РЕЗУЛЬТАТ ПРОГРАММЫ:

1.2. Массивы

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

Массивы объявляются так же, как и переменные. Например:

int a[100];

float c[10][20];

В первой строке объявляем массив а из 100 элементов целого типа: а[0],a[1], … ,a[99] (индексация всегда начинается с нуля). Во второй строке объявлен двумерный массив вещественного типа. Двумерный массив представляется как одномерный, элементы которого являются тоже массивами. В первых квадратных скобках указывается количество строк в массиве, во вторых – количество столбцов.

Пример 1. Задан одномерный массив S, состоящий из десяти элементов вещественного типа. Вывести на экран дисплея значения элементов этого массива в обратном порядке.

#include "stdio.h"

#include "conio.h"

#include "iostream.h"

main()

{

float s[10];

int i;

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

scanf("%f", &s[i]); /*ввод элементов массива*/

for (i=9; i>=0;i--)

printf("%f", s[i]); /* вывод элементов в обратном порядке*/

getch();

return 0;

}

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

int a[5]= { 0, 0, 0, 0, 0};

Это значит, что все элементы массива получают нулевое значение.

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

int a[] = {10,20,30,40,50};

1.3.  Указатели и одномерные массивы

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

Объявление

int a[10];

определяет массив а размера 10, т.е. блок из десяти последовательных объектов, представленных на рисунке, с именами a[0], a[1], … ,a[9].

Запись a[i] отсылает нас к i-му элементу массива. Если ра есть указатель, т.е. определен как

int *pa; , то в результате присваивания    pa = &a[0];

pa будет указывать на нулевой элемент массива а; иначе говоря, ра будет содержать адрес элемента a[0] (см. рис.). Теперь присваивание

x=*pa;

будет копировать содержимое а[0] в х.

Если ра указывает на некоторый элемент массива, то ра+1 по определению указывает на следующий элемент (см. рис.).Таким образом, если ра указывает на a[0], то *(ра+1) есть содержимое a[1], ра+1 – адрес a[1], *(ра+i) – содержимое a[i].

Поскольку имя массива есть не что иное как адрес его начального элемента, присваивание

pa=&a[0];

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

pa = a;

Так как ра – указатель , то в выражениях его можно использовать с индексом, то есть запись pa[i] эквивалентна записи *(pa+i). Элемент массива одинаково разрешается изображать и в виде указателя со смещением, и в виде имени массива с индексом.

Между именем массива и указателем, выступающим в роли имени массива, существует одно различие. Указатель – это переменная, поэтому можно написать pa=a или pa++. Но имя массива не является переменной, и запись типа a=pa не допускается.

Следует также различать выражения *(a+2) и *a+2:

*(а+2) – значение третьего элемента массива а;

*а+2 – добавление числа 2 к значению первого элемента массива.

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

#include "stdio.h"

#include "conio.h"

#include "iostream.h"

int a[6]={10,20,30,40,50,60};

main()

{

int  i, *p;

/*объявление и инициализация массива*/

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

printf("%d",a[i]); /*вывод массива обычным способом*/

printf("\n");

p=a;

for (p=&a[0];p<=&a[5];p++)

printf("%d", *p); /*вывод массива с использованием указателя*/

printf("\n");

for (p=&a[0],i=0;i<6;i++)

printf("%d", p[i]); /*еще один вариант с использованием указателя*/

getch();

return 0;

}

Дадим еще некоторые пояснения. Операция р++ увеличивает значение указателя на единицу. Если p=&a[i], то после операции р++ в р содержится адрес элемента a[i+1].

Пример 3. Найти среднее арифметическое массива, состоящего из шести элементов, с использованием указателя.

#include "stdio.h"

#include "conio.h"

#include "iostream.h"

int a[]={10,20,30,40,50,60};

main()

{

int  i, *p;

float s;

p=a; /*указатель получает значение адреса нулевого элемента массива*/

for (s=0,i=0;i<6;i++)

s+=*(p+i); /*получение суммы элементов массива*/

s=s/6; /*среднее арифметическое массива*/

printf("%f", s);

getch();

return 0;

}

Пример 4. Решить задачу, приведенную в примере 1, с использованием указателя.

#include "stdio.h"

#include "conio.h"

#include "iostream.h"

main()

{

int s[10];

int *p, i;

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

scanf("%d", &s[i]);

p=&s[9]; /*указатель получает значение адреса последнего элемента массива*/

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

printf ("%d",*(p-i)); /*вывод элементов в обратном порядке*/

for (p=&s[9];p>=&s[0];p--)/*еще один способ вывода элементов в обратном порядке*/

printf("\n%d", *p);

getch();

return 0;

}

Пример5. Сортировать массив из 10 целых чисел по убыванию методом прямого выбора.

#include<conio.h>

#include<stdlib.h>

 #include<time.h>

#pragma argsused

void main()

{   int massiv[10];

int i,j,k, max,Nmax,bufPerem;

time_t t;

srand ((unsigned)time(&t)); //иниццализация генератора случайных чисел

// формирование массива

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

{

massiv[i]=rand()%19+2; //присваивание элементу массива случайного значения которое выдает  функция  rand

cout<<"\t" <<massiv[i];

}

//сортировка массива

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

{ max=massiv[i]; Nmax=i;

 for (j=i+1; j<=9; j++)

 if (max<massiv[j])

{

max= massiv[j]; Nmax=j;

} ;

bufPerem=massiv[i];

massiv[i]=max;

massiv[Nmax]=bufPerem;

//вывод промежуточного состояния массива

/*cout<<"\n "<<i<<"- shag  :";

 for (k=0; k<=9; k++)

cout<<"\t" <<massiv[k];*/

}

// вывод  отсортированного массива

cout<<"\n ";

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

cout<<"\t" <<massiv[i];

getch();

       //return 0;

}

2. Задание

Задание взять из таблицы согласно заданному варианту. Написать два варианта программы: без применения указателей и с указателями.

Массив

Действия

Условия и ограничения

1.

X(N)

Вычислить сумму и количество элементов Х

0<=Xi, N<100  

2.

A(80)

Вычислить среднее арифметическое значение элемента массива А

Ai >0

3.

X(70)

Переписать элементы массива Х в массив Y и подсчитать их количество

-1<=Xi <=1

4.

B(50)

Определить максимальный элемент массива В и его порядковый номер

Bi>0

5.

C(40)

Определить минимальный элемент массива С и его порядковый номер

Ci <0

6.

D(80)

Найти максимальный и минимальный элемент массива и поменять их местами

7.

Y(20)

Вычислить среднее геометрическое элементов массива

Yi >0

8.

Z(30)

Расположить в массиве R сначала положительный а затем отрицательный элементы массива Z

9.

N(50)

Определить сумму элементов массива N кратных трем

INT(Ni/3)*3=Ni

10.

X(N)

Вычислить сумму и количество элементов массива Х

Xi>0,N<=30

11.

X(N)

Вычислить среднее геометрическое элементов массива

Xi>0,N<=50

12.

X(N)

Переписать в массив Y подряд положительные элементы массива Х

Xi>0,N<=40

13.

X(N)

Переписать в массив Y положительные элементы а в массив Z отрицательные элементы массива Х

N<=40

14.

B(K)

Определить максимальный элемент массива В и его порядковый номер

Xi>0,K<=40

15.

С(К)

Определить минимальный элемент массива С и его порядковый номер

-1<=Xi<=1,

K<=20

6

PAGE  5


Ра

a+1

Pa+2

a:

a[0]

a[1]

a[9]


 

А также другие работы, которые могут Вас заинтересовать

1645. Санитарно-гигиенические требования к приготовлению сред и компонентов 19.34 KB
  Все компоненты для приготовления разбавителей должны быть химически чистыми, проверенными на токсичность и соответствовать ГОСТу, указанному в инструкции по искусственному осеменению.
1646. Серозный и катаральный маститы: причины, особенности течения, диагностика и профилактика 20.65 KB
  Серозное воспаление вымени характеризуется гиперемией, большим выпотом серозного экссудата и эмиграцией лейкоцитов, преимущественно в междольковую ткань. Характеризуется перерождением железистого и покровного эпителия, его отторжением.
1647. Слабые и бурные схватки и потуги как причина патологических родов 19.78 KB
  Сильные потуги. Причиной бурных потуг могут быть неправильное расположение плода, его уродливость, раннее отхождение околоплодных вод. Слабые схватки и потуги.
1648. Сперма и её видовые особенности 19.51 KB
  Сперма – смесь спермиев (половых клеток самца) и плазмы(сыворотки). Сыворотка спермы – секрет придатков семенников и придаточных половых желез.
1649. Сперматогенез. Физиологическое значение придатков семенников, мошонки, придатков половых желез 20.39 KB
  По достижении животным половой зрелости в семеннике его происходят сложные процессы, сводящиеся к созреванию и формированию спермиев — сперматогенезу.
1650. Спермии, их строение, скорость и виды движения спермиев. Энергетика спермиев 21.39 KB
  Строение В спермии с/х. животных различают головку, шейку, тело и хвост. Скорость и виды движения спермиев. Энергетика спермиев.
1651. Способы определения концентрации спермиев в эякуляте: подсчет в счетной камере при помощи ФЭК по стандартам 20.59 KB
  Концентрацию спермиев определяют с помощью фотоэлектроколориметров (ФЭК), калибровочную кривую для которых составляют по результатам подсчета спермиев в камере Горяева.
1652. Способы искусственного осеменения кобыл 20.49 KB
  При осеменении кобыл сперму вводят в матку (маточный метод осеменения). В практике применяют два способа введения спермы: мануальный и визуальный.
1653. Способы искусственного осеменения коров и телок 20.59 KB
  Для осеменения коров и телок используется цервикальный метод осеменения, т. е. в шейку матки. Существуют три принципиально различающихся по технике исполнения способа введения спермы в цервикальный канал.