Студопедия

КАТЕГОРИИ:


Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748)

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

Лекция 6

Массивы и указатели довольно тесно связаны между собой. Имя массива можно разыменовывать, как указатель. В свою очередь, указатель можно индексировать, как массив, если это имеет смысл.

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

int myArray[8];

int mas[2][4];

Массив, как и переменную, можно инициализировать при объявлении.

int iArray[8] = {7, 4, 3, 5, 0, 1, 2, 6};

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

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

Char code[]={‘a’,’b’,’c’};

Обращение к отдельным элементам массива:

myArray[3] = 11;

myArray[i] = iArray[7-i];

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

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

Программа выполняет “пузырьковую сортировку” введенных пользователем чисел в порядке возрастания.

** Процедура сортировки

void DoSort(int array[ ], int n)

{

int i, j, temp;

for (i = n-1; i > 0; i--)

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

if (array[j] > array[j+l])

{

temp = array[j];

array[j] = array[j+l];

array [j+1] = temp;

}

}

int main(int argc, char* argv[])

{

const int N = 8;

int i, iArray[8];

char s[80], *endPtr;

printf("Enter %d integers separated by spaces:\n", N);

gets(s); // Прочитать строку пользователя.

endPtr = s; // Инициализировать указатель строки.

for (i =0; i < N; i++) // Преобразование чисел.

iArray[i] = strto1(endPtr, &endPtr, 10);

DoSort(iArray, N); // Вызов программы сортировки.

printf("Sorted array:\n");

for (i =0; i < N; i++) // Вывод отсортированного массива.

printf("%8d ", iArray[i]);

printf("\n\nPress a key...");

getch();

return 0;

}

Строка, содержащая все введенные пользователем числа, считывается целиком в символьный массив s. Для преобразования символьного представления чисел в данные целого типа в цикле вызывается функция strtol (). Ее прототип (в stdlib.h) имеет вид

long strtol(const char *str, char **endptr, int radix);

Эта функция преобразует строку str в значение типа long. Параметр radix задает основание системы счисления (8, 10 или 16). В параметре endptr функция возвращает указатель на непреобразованную часть строки str (т. е. на строку, оставшуюся после исключения из нее первого из чисел). Если при вызове функции происходит ошибка, например, строка содержит меньше чисел, чем их должно быть, возвращаемый в endptr указатель будет совпадать с аргументом str. Ключевое слово const в объявлении первого параметра говорит компилятору, что функция не должна изменять элементы строки, на которую указывает str. Попытка модифицировать строку в теле функции вызовет ошибку при ее компиляции.

 

Указатель — это переменная, которая содержит адрес другого объекта. (в отличие от операции - &а, с помощью которой определяется адрес ячейки памяти, содержащий переменную а). Поэтому операция * является обратной операции &. Таким объектом может быть некоторая переменная, динамический объект или функция. Хотя адрес, по существу — 32-битное целое число, определяющее положение объекта в виртуальной памяти программы, указатель является не просто целым числом, а специальным типом данных. Он “помнит”, на какого рода данные ссылается.

Объявление указателя выглядит так:

тип_указываемого_объекта *имя_указателя [= значение];

Примеры объявлений:

int *pIntVar; // Указатель на целое.

double *pDouble = SdoubleVar; // Инициализация указателя на double.

char *arrStr[16]; // Массив указателей на char.

char (*arrStr) [16][16]; // Указатель на матрицу char. - Подобные конструкции применяются в объявлении параметров функций, передающих многомерные массивы неопределенного размера.

Чтобы получить доступ к объекту, на который указатель ссылается, применяют операцию-звездочку. Например. *px будет представлять значение переменной, на которую ссылается px:

double x = 3.14159265;

double *px =&x;

printf("Значение самого указателя (адрес): %р", p);

printf("Число, на которое он ссылается: %f", *px);

 

Другой пример:

int a=10,b;

int *px=&a; // указатель инициируется адресом переменной а

px=&b; //указатель указывает на переменную b.

 

Ссылка – это видоизмененная форма указателя, которая используется в качестве псевдонима (другого имени). переменной. Ссылки используются при создании функции с параметрами передаваемыми по ссылке.

void kvadr(int &x);

int main()

{

int t;

printf(“введите переменную -”);

scanf(“%d”,&t);

kvadr(t);

printf(“результат - %d”,t);

return 0;

}

void kvadr(int &x)

{

x*=x;

}

При объявлении параметра-ссылки в функцию будет передаваться адрес переменной. Это позволяет:

ü изменять аргумент (фактический параметр), не указывая в явном виде при вызове функции передачу в нее адреса.

ü не использовать в теле функции операцию разыменования *.

Если изменять аргумент, передаваемый по ссылке в функцию, не требуется, то нужно использовать модификатор const, например:

Void kvadr(const int &x);

Ссылки можно использовать в качестве возвращаемого значения

Int &func(int I); // прототип функции

Int a[3]={3,4,5};

void main()

{

func(1)=23;

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

printf(“%5d”,a[I]);

}

int &func(int i)

{

return a[i];

}

Программа заменяет значение элемента массива с индексом 1 на число 23.

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

Int &func()

{ int I;

return I;

}

Итог! На применение переменных ссылочного типа накладываются ограничения:

  1. Нельзя взять адрес переменной ссылочного типа;
  2. Нельзя использовать массивы ссылок;
  3. Нельзя использовать ссылки на битовые поля;
  4. Нельзя создать указатель на ссылку.

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

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

int mas[4];

int *pmas;

pmas =mas; // pmas указывает на начальный элемент mas.

Последнее эквивалентно

pmas = &mas[0];

И наоборот, указатель можно использовать подобно имени массива, г. е. индексировать его. Например, pmas [3] представляет четвертый элемент массива mas[ ]. К указателю можно прибавлять или отнимать от него целочисленные выражения, применять операции инкремента и декремента. При этом значение указателя изменяется в соответствии с размером объектов, на которые он указывает. Так, (pmas + 2) указывает на третий элемент массива. Это то же самое, что и &mas [2]. Когда мы прибавляем к указателю единицу (pmas++), адрес, который в нем содержится, в действительности увеличивается на 2 — размер типа int и происходит переход к следующему элементу массива.

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

{

int mas[4], *pmas, i;

pmas =&mas[0];

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

printf(“\n %7x”, (pmas+i))

}

Если изменить тип массива на float, то к каждому указателю будет прибавляться по 4.

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

{

int mas[3][3]={{1,2,3},{4,5,6},{7,8,9}}, *pmas, i;

pmas = &mas[0][0];

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

for (j=0; j<3; j++)

printf(“%5d”,*(pmas+i*3+j));

}

*(pmas+3*i) – элементы первого столбца матрицы.

Динамические массивы.

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

В языке С можно выделить память под некоторый объект не только с помощью оператора объявления, но и динамически, во время исполнения программы. Объект создается в свободной области виртуальной памяти функцией malloc (). Для использования функции необходимо подключить файл <alloc.h>.

int *pint; //объявили переменную типа-указатель

pint = (int*)malloc(sizeof(int)); //выделили память под переменную целого типа

*pint=123; // по выделенному адресу разместили число.

Аргументом malloc () является размер области памяти, которую нужно выделить; для этого можно применить операцию sizeof, которая возвращает размер (в байтах) переменной или типа, указанного в качестве операнда. Функция malloc () возвращает значение типа void* — “пустой указатель”. Это указатель, который может указывать на данные любого типа. Такой указатель нельзя разыменовывать, поскольку неизвестно, на что он указывает — сколько байтов занимает его объект и как их нужно интерпретировать. Поэтому используют приведение к явному типу указателя.

pint = (int*)malloc(sizeof(int));

Если выделение памяти по какой-то причине невозможно, malloc() возвращает NULL, нулевой указатель. На самом деле эта константа определяется в stdlib.h как целое — “длинный нуль”:

#define NULL OL

Поэтому, выделяя память, можно проверить: возможна ли данная операция?

pint = (int*)malloc(sizeof(int));

if ((pint)!=NULL)

Память, выделенную malloc(), следует освободить функцией free (), если динамический объект вам больше не нужен.

free(pint);

Перед тем, как разыменовывать указатель, его нужно обязательно инициализировать, либо при объявлении, либо путем присвоения ему адреса какого-либо объекта, возможно, динамического — как в последнем примере. Аналогично, если к указателю применяется функция free(), он становится недействительным и не ссылается больше ни на какие осмысленные данные. Чтобы использовать его повторно, необходимо снова присвоить ему адрес некоторого объекта.

Разыменование нулевого указателя также приводит к ошибке. Поэтому при работе с объектами, создаваемыми с помощью malloc (), обычно всегда проверяют, не возвратила ли эта функция нулевое значение.

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

randomize();

int *pint, i;

pint=(int*)malloc(15*sizeof(int)); // отводим память под 15 элементов

for (i=0; i<15; i++) // заполнение массива. Функция Random подключается //директивой <stdlib.h>

pint[i]=random(10)+15;

for (i=0; i<15; i++) // вывод на печать

printf(“%5d”,*(pint++))

free(pint);

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

int *pendi;

pendi=pint+15;

while(pint<pendi)

printf(“%5d”,*(pint++))

free(pint);

Особенно эффективно применение указателей для работы с большими массивами данных. Пусть требуется разместить в памяти массив 300х200. Целесообразно не выделять память под каждый элемент массива, а использовать массив указателей и динамическое выделение памяти для каждой строки матрицы.

int *p[300],i,j;

randomize();

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

p[i]=(int*)malloc(200*sizeof(int)); //резервируется память для массива //указателей.

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

for (j=0; j<200; j++)

{ *(p[i]+j)=random(1000);

printf(“%3d”,*(p[i]+j));

if ((j+1)%20==0) //числа выводятся по 20 в ряд.

printf(“\n”);

}

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

free(p[i]);

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

void main()

{

float mas[6];

float *pmin[6], * pmax[6]; //определяем массивы указателей, переставляя значения //которых будет осуществляться просмотр //отсортированного массива

float *p;

int i,j;

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

pmin[i]=pmax[i]=&mas[i]; //инициализация массивов указателей

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

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

{

if (*pmin[i]<*pmin[j])

{

p=pmin[i];

pmin[i]=pmin[j];

pmin[j]=p;

}

if (*pmax[i]>*pmax[j])

{

p=pmax[i];

pmax[i]=pmax[j];

pmax[j]=p;

}

}

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

 

void fun(int n, int m, int **p);

void main(void)

{

clrscr();

int **pmas; //pmas – указатель на массив указателей.

int n, m;

printf (“\n введите количество строк - ”);

scanf(“%d”,&n);

printf (“\n введите количество столбцов - ”);

scanf(“%d”,&m);

pmas=(int**)malloc(*pmas[n]); //выделяется память для массива указателей на //строки матрицы

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

pmas[i]=(int*)malloc(m*sizeof(int)); //выделяется память под очередную строку //массива

fun(n, m, pmas);

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

{

for(j=0;j<m1;j++)

printf(“%5d”,pmas[i][j]);

printf(“\n”);

}

getch();

}

void fun(int n, int m, int **p)

{

randomize();

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

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

p[i][j]=random(20);

}

<== предыдущая лекция | следующая лекция ==>
Функции. Функция является основным структурным элементом языка С | Указатель на функцию
Поделиться с друзьями:


Дата добавления: 2014-01-06; Просмотров: 670; Нарушение авторских прав?; Мы поможем в написании вашей работы!


Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет



studopedia.su - Студопедия (2013 - 2024) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! Последнее добавление




Генерация страницы за: 0.008 сек.