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]


 

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

47366. ИССЛЕДОВАНИЕ ХАРАКТЕРИСТИКИ ЦЕНТРОБЕЖНОГО НАСОСА. Исследование кавитационных свойств центробежного насоса 306.5 KB
  В ходе выполнения данной лабораторной работы мы исследовали и построили характеристики центробежного насоса, выбрали оптимальный режим работы, были построены характеристики
47367. Специфика проникновения информационно-коммуникативных технологий в жизнь современного российского общества 386.65 KB
  По мере накопления в обществе различных видов информации невиданными темпами возрастает интенсивность её потребления во всех сферах жизнедеятельности общества. Постепенно на первое место выдвигается содержательный аспект информации, её релевантность (значимость, существенность, важность) по отношению к деятельности людей
47368. Операционные системы (ОС) 35.42 KB
  Основная задача операционной системы (ОС) — это обеспечение управления процессом обработки информации взаимодействие между аппаратными, программными средствами и пользователем. В большинстве современных вычислительных систем ОС является основной, наиболее важной (иногда единственной) частью системного программного обеспечения.
47369. Проектирование маршрутных ТП механической обработки деталей 605.5 KB
  Имеется большое количество поверхностей, которые обладают несколькими направлениями доступа и могут обрабатываться или действительно обрабатываются с различных сторон. Для удобства проектирования рекомендуется пронумеровать обрабатываемые поверхности по их направлениям доступа
47370. Основные сведения о работе с программами обработки текстов, таблиц, презентаций, макетирования и верстки 66.3 KB
  В работе специалиста по СсО и рекламе с ПК значительная доля времени расходуется на создание, редактирование и печать документов. Привычные современному пользователю программы для работы с документами редко выпускаются на рынок как отдельные продукты.
47371. Компьютерная графика. Графическое представление информации 57.87 KB
  Графическое представление информации используется во многих областях визуальной коммуникации: от произведений изобразительного искусства, которое призвано будить у человека эмоции и вызывать чувство прекрасного, до всевозможных символов, например дорожных знаков, которые предназначены только для информативных целей и у опытных водителей могут даже не достигать области осознаваемого восприятия.
47372. Системы оптического распознавания текста 25.01 KB
  Системы оптического распознавания текста (Optical Character Recognition — OCR-системы) предназначены для автоматического ввода печатных документов в компьютер.
47373. Системы машинного перевода 24.33 KB
  Традиционный перевод текстов, т.е. без применения средств автоматизации, многих перестает устраивать, поскольку требует больших затрат времени и, главное, знаний и навыков. Благодаря возможностям компьютера появилась возможность перевода текстов без знания языка и достаточно быстро.
47374. Расчет внутрицеховой сети и электрофикация участка 112.21 KB
  Электрическая энергия - самый распространенный вид энергии, который используется человеком. На протяжении истории республики Беларусь электроэнергетика была важнейшим рычагом в осуществлении структурного и технического производства в народном хозяйстве.