85889

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

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

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

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

Русский

2015-03-31

169 KB

4 чел.

Лабораторная работа №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]


 

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

48983. Проект установки для наплавлення 844.5 KB
  ВИБІР СПОСОБУ НАПЛАВЛЕННЯ РОЗРАХУНОК ОСНОВНИХ ПРИСТРОЇВ ОБЛАДНАННЯ ДЛЯ НАПЛАВЛЕННЯ Наплавлення це процес нанесення за допомогою зварювання шару металу на поверхню виробу. Шляхом наплавлення можна отримати вироби зі зносостійкими жароміцними антифрикційними властивостями.
48984. Розрахунок структури симетричних стаціонарних електромагнітних полів 146 KB
  Симетричне тіло радіуса R перебуває в однорідному зовнішньому електричному полі E0, що перпендикулярне до його осі. Задано матеріальні характеристики навколишнього середовища. Одержати аналітичні вирази для потенціалів й і для полів Ei й Ee відповідно всередині та поза тілом.
48985. Розрахунок структури змінних електромагнітних полів у хвилеводі 550 KB
  Порожнина хвилеводу заповнена діелектриком, електрична проникність якого овжина хвилеводу в напрямку осі z не обмежена. Процес поширення електромагнітних хвиль у порожнині прямокутного хвилеводу розглядаємо, думаючи, що стінки хвилеводу виконані з надпровідного матеріалу ( = ).
48988. Стрільба і управління вогнем. Методичні вказівки 12.85 MB
  Курсова робота призначена для поглиблення знань з дисципліни Стрільба і управління вогнем вміння вирішувати самостійно задачі з проведення заходів підготовки стрільби і управління вогнем проведення пристрілювання цілі із спостереженням за знаками розривів. При проведенні пристрілювання цілі із спостереженням за знаками розривів пропонується виконати наступні етапи: Визначити за даними розвідки топографічні дані по цілі а використовуючи ГРП обчислені дані по цілі; Визначити установки для стрільби коефіцієнти і подати команду...
48989. Проектування установки дугового наплавлення 2.84 MB
  В даній курсовій роботі потрібно спроектувати установку для одного з способів дугового наплавлення. Використання механізованого і автоматизованого обладнання і установок дає можливість підвищити продуктивність виробництва та в основному виводить людський фактор безпосередньо від формування наплавленого шару. Також оператор виводиться з зони безпосереднього впливу дуги і аерозолів на людський організм, що підвищує якість охорони праці на виробництві.
48990. Приготування новітніх страв з перепелиних яєць 318.5 KB
  Користь перепелиних яєць, безумовно, дуже велика. Вживання перепелиних яєць зміцнює кістки, нормалізує роботу всього організму (зокрема серця, печінки, нирок, шлунка і підшлункової, передміхурової залози та ін органів).
48991. Основные проблемы теоретической фонетики 156.5 KB
  Британские типы произношения и их фонетические особенности. Фонетические особенности американского произношения G в сопоставлении с британским RP. Современные тенденции английского произношения. Классификация диалектов и вариантов произношения английского языка.