Студопедия

КАТЕГОРИИ:

АвтоАвтоматизацияАрхитектураАстрономияАудитБиологияБухгалтерияВоенное делоГенетикаГеографияГеологияГосударствоДомЖурналистика и СМИИзобретательствоИностранные языкиИнформатикаИскусствоИсторияКомпьютерыКулинарияКультураЛексикологияЛитератураЛогикаМаркетингМатематикаМашиностроениеМедицинаМенеджментМеталлы и СваркаМеханикаМузыкаНаселениеОбразованиеОхрана безопасности жизниОхрана ТрудаПедагогикаПолитикаПравоПриборостроениеПрограммированиеПроизводствоПромышленностьПсихологияРадиоРегилияСвязьСоциологияСпортСтандартизацияСтроительствоТехнологииТорговляТуризмФизикаФизиологияФилософияФинансыХимияХозяйствоЦеннообразованиеЧерчениеЭкологияЭконометрикаЭкономикаЭлектроникаЮриспунденкция

Многомерные массивы, указатели,




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

 

Цель работы:

Изучить работу с многомерными массивами, освоить возможности динамического размещения данных.

 

Краткие теоретические сведения

Кроме одномерных массивов возможна работа с многомерными массивами. Объявление многомерного массива:

<тип><имя>[<размер 1 >][<размер 2 >]…[<размер N>]={{список начальных значений}, {список начальных значений},…};

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

Например, элементы двухмерного массива b[2][1] размещаются в памяти в следующем порядке:

 b[0][0], b[0][1], b[1][0], b[1][1], b[2][0], b[2][1].

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

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

Если в какой-то группе {…} отсутствует значение, то соответствующему элементу присваивается 0. Предыдущий оператор будет эквивалентен следующему определению:

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

Пример программы

       Создать двумерный массив целых чисел NxM (N и M не более 50), используя функцию rand и вывести на экран в форме матрицы, N,M ввести с клавиатуры:

#include<stdio.h>

       #include<stdlib.h>

       #include<conio.h>

       #define rnd (rand()/ 32768.0) // rand - генератор случайных чисел от 0 до

int,   rnd – от 0 до 1

       void main(void)

       { int i,j,n,m,a[50][50];

       puts(“\n Input n, m:”); scanf(“%d %d”,&n,&m);

           printf(“\n Array a \n”);

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

                   for(j=0; j<m; j++) {

                                          a[i][j]=rnd*10-5;   // диапазон от –5 до 5

                              printf(“%d%c“, a[i][j], (j= =m-1)?’\n’:’ ‘);

                                                      }

                   getch();

}

 

Указатели и операции над адресами

       Обращение к объектам любого типа как операндам операций в языке C может проводиться:

 - по имени, как мы до сих пор делали;

 - по указателю (косвенная адресация).

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

Указатель объявляется следующим образом:

тип *идентификатор;

Например: int *a, *d;

float *f;

Здесь объявлены указатели a, d, которые можно инициализировать адресами целочисленных переменных и указатель f, который можно инициализировать адресами вещественных переменных.

С указателями связаны две унарные операции: & и *. Операция & означает «взять адрес» операнда (т.е. установить указатель на операнд). Данная операция допустима только над переменными. Операция * имеет смысл: «значение, расположенное по указанному адресу» и работает следующим образом:

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

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

- Производится обращение к участку памяти по выделенному адресу для проведения некоторых действий.

Пример 1:

int x,                                   /* переменная типа int */

*y;                                /* указатель на элемент данных типа int */

 y=&x;                   /* y - адрес переменной x */

             *y=1;                     /* косвенная адресация указателем поля x

/* “по указанному адресу записать 1”, т.е. x=1; */

Пример 2:

int i, j=8, k=5, *y;

y=&i;

*y=2;                /* i=2 */

y=&j;                   /* переустановили указатель на переменную j */

*y+=i;               /* j+=i , т.е. j=j+1 -> j=j+2=10 */

y=&k;                  /*переустановили указатель на переменную k */

k+=*y;                /* k+=k, k=k+k = 10 */

(*y)++;               /* k++, k=k+1 = 10+1 = 11 */

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

Отказ от именования объектов при наличии возможности доступа по указателю приближает язык С по гибкости отображения «объект-память» к языку ассемблера.

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

int x[100], *y;

y=x;                  // присваивание константы переменной

...

x=y;                     // Ошибка: в левой части - указатель-константа

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

int i, *x;

    char *y;

x=&i;          /* x -> поле объекта int */

y=(char *)x; /* y -> поле объекта char */

y=(char *)&i;  /* y -> поле объекта char */

Рассмотрим фрагмент программы:

       int a=5, *p, *p1, *p2;

                  p=&a; p2=p1=p;

                  ++p1; p2+=2;

       printf(“a=%d, p=%d, p=%p, p1=%p, p2=%p.\n”,a,p,p,p1,p2);

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

a=5, *p=5, p=FFC8, p1=FFCC, p2=FFD0.

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

 

Операции над указателями (адресная арифметика)

Указатель может использоваться в выражениях вида:

p # ie, ##p, p##, p# = ie,

где p - указатель, ie - целочисленное выражение, # - символ операции '+' или '-'.

Значением таких выражений является увеличенное или уменьшенное значение указателя на величину ie*sizeof(*p). Следует помнить, что операции с указателями выполняются в единицах памяти того типа объекта, на который ссылается этот указатель.

Текущее значение указателя всегда ссылается на позицию некоторого объекта в памяти с учетом правил выравнивания для соответствующего типа данных. Таким образом, значение p#ie указывает на объект того же типа, расположенный в памяти со смещением на ie*sizeof(*p) позиций.

Разрешается сравнивать указатели и вычислять разность двух указателей. При сравнении могут проверяться отношения любого вида (">",">=","<","<=","==","!="). Наиболее важными видами проверок являются отношения равенства или неравенства.

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

Разность двух указателей дает число объектов адресуемого ими типа в соответству­ющем диапазоне адресов. Очевидно, что уменьшаемый и вычитаемый указатель также должны соответствовать одному массиву, иначе результат операции не имеет практической ценности.

Любой указатель можно сравнивать со значением NULL, которое означает недействительный адрес. Значение NULL можно присваивать указателю как признак пустого указателя. NULL заменяется препроцессором на выражение (void *)0.

 

Указатели на указатели

В языке Си можно описать переменную типа «указатель на указатель». Это ячейка оперативной памяти, в которой будет храниться адрес указателя на какую либо переменную. Признак такого типа данных – повторение символа «*» перед идентификатором переменной. Количество символов «*» определяет уровень вложенности указателей друг в друга. При объявлении указателей на указатели возможна их одновременная инициализация.

Например:

int a=5;                                                     Рисунок

int *p1=&a;

int **pp1=&p1;

int ***ppp1=&pp1;

Теперь присвоим целочисленной переменной а новое значение, например 10.

Одинаковое присваивание произведут следующие операции:

a=10; *p1=10;     **pp1=10; ***ppp1=10;

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

*p1 <-> p1[0] **pp1 <-> pp1[0][0] ***ppp1 <-> ppp1[0][0][0]

Таким образом, указатели на указатели – это имена многомерных массивов.

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

float name[][][][];  <-> float ****name;

В последнем случае эквивалентными являются выражения:

name[i][j][k][l]

*(*(*(*(name+i)+j)+k)+l)

*(*(*(name+i)+j)+k)[l]

*(*(name+i)+j)[k][l]

  *(name+i)[j][k][l]

 

Массивы указателей

       В языке Си можно использовать массивы указателей, элементы которых содержат как правило указатели на строковые данные. Объявляется такой массив например так: char *m[5]. Здесь массив m[5] – массив, который может содержать пять адресов данных типа char.

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

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

 

#include <stdio.h>

#include <conio.h>

void main(void)

{

       int i,k;

       char *m[]={

                   "Winter",

                   "Spring",

                   "Summer",

                   "Automn"};

k=sizeof(m)/sizeof(*m);

printf("\n size of array=%d",k);

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

printf("\n string - %d; adress - %p; string: %s", i, m[i], m[i]);

getch();

}

В результате получим:

 size of array=4

 string - 0; adress - 0042007C; string: Winter

 string - 1; adress - 00420074; string: Spring

 string - 2; adress - 0042006C; string: Summer

 string - 3; adress - 00420064; string: Automn

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










Последнее изменение этой страницы: 2018-05-10; просмотров: 218.

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