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

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

"Массивы и указатели в языке программирования Си++"




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



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

Массивы

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

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



int a[100];

float c[10][20];



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

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



#include <stdio.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]); /* вывод элементов в обратном порядке*/

}



Пример 2. Задана двумерная матрица а, имеющая пять строк и пять столбцов. Определить номер строки с наибольшим числом единиц в этой строке.



#include <stdio.h>

main()

{int i,j,p,q=0,f=0,a[5][5];

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

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

scanf("%d", &a[i][j]); /*ввод матрицы*/

/*поиск в матрице а строки с наибольшим числом единиц*/

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

{p=0;

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

if (f[i][j]==1) p++;

if (q<p) {q=p; f=i;}

}

/*f – номер строки с наибольшим числом единиц, q – число единиц в f-й строке*/

printf("%d %d",f,q);

}




Пример 3. Переставить местами элементы главной и побочной диагоналей массива D(6,6). Полученную матрицу вывести на экран дисплея.



#include <stdio.h>

main()

{int i,j,a,d[6][6];

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

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

scanf("%d", &d[i][j]); /*ввод матрицы*/

/*перестановка местами элементов главной и побочной диагоналей*/

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

{a=d[i][i];

d[i][i]=d[i][6-i];

d[i][6-i]=a;

}

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

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

printf("%d%c", d[i][j], (j==5)?"\n":" ");

/*вывод по строкам элементов матрицы*/

}



При выводе элементов матрицы по строкам применена тернарная операция. Смысл этой операции сводится к следующему: если j=5 (закончен вывод элементов по строке), то курсор переводится в начало следующей строки (работает символьная константа "\n"), в противном случае выводится один пробел. Так как в операторе вывода используется символьная константа, то применена спецификация %c.

В заключении этого раздела отметим, что массив можно инициализировать, т.е. присвоить его элементам начальные значения. Это делается при объявлении типа массива, например: int a[5]= { 0, 0, 0, 0, 0};

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

Двумерный массив можно инициализировать следующим образом:



int a[3][3] = {{10,20,30},

{40,50,60},

{70,80,90}};



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



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



Указатели

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

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

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

Декларация



int a[10];



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






Ра

Pa+1

Pa+2

a:

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 к значению первого элемента массива.

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



#include <stdio.h>

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

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

main ()

{int i, *p;

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

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

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

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

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

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

}



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

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


#include <stdio.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);

}



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



#include <stdio.h>

main()

{float s[10];

int *p,i;

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

scanf("%f",s[i]);

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

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

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

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

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

}


Указатели и двумерные массивы

Предположим, что у нас есть описания:



int z[4][2]; /**/

int pz; /**/



Тогда pz=z указывает на нулевой столбец нулевой строки, т.е.



pz=&z[0][0];



Пример 7. Вывести на экран значения нулевого, второго и четвертого элементов массива.



#include <stdio.h>

int a[]=(10,20,30,40,50,60); /*объявление и инициализация массива а*/

main()

{

for (p=a,i=0; p+i<=a+4; p++, i++)

printf("%d"; (p+i)); /*вывод на экран значений нулевого, второго и четвертого элементов*/

}



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



pz+1 = &z[0][1], pz+2 = &z[1][0], pz+3 = &z[1][1] и т.д.




Двумерный массив описан как массив массивов. Если z является именем массива, то каковы имена четырех строк, каждая из которых является массивом из двух элементов? Имя первой строки z[0], второй - z[1], и т.д. Однако имя массива является также указателем на этот массив в том смысле, что оно ссылается на первый его элемент. Значит:



z[0]=&z[0][0], z[1]=&z[1][0], z[2]=&z[2][0], z[3]=&z[3][0].



Пример 8. Задана матрица а. Вывести на экран элементы главной диагонали, первой строки и значений первых элементов каждой строки матрицы, применив для этого указатели.



#include <stdio.h>

int a[3][3]={{10,20,30},

{40,50,60},

{70,80,90}};

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

int *pa[3]={a[0],a[1],a[2]};

/*объявление и инициализация указателя ра на строки массива а и присвоение начальных значений : pa[0]=a[0]; pa[1]=a[1]; pa[2]=a[2]*/

int p=a[0]; /*объявление указателя на нулевой элемент нулевой строки массива а*/

main ()

{int i;

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

printf("%d",*(p+i)); /*вывод на экран элементов главной диагонали*/

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

printf("%d",*p[i]); /*вывод на экран элементов первой строки*/

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

printf("%d",pa[i]); /*вывод на экран первых элементов каждой строки матрицы*/

}



Сделаем некоторые пояснения для первого оператора цикла. Представим матрицу в виде одномерного массива, записанного по строкам:

a[0][0],a[0][1],a[0][2],a[1][0],a[1][1],a[1][2],a[2][0],a[2][1],a[2][2]

Тогда элементы, стоящие на главной диагонали, занимают нулевое, четвертое и восьмое места, т.е. интервал между интересующими нас элементами равен четырем, поэтому переменная i изменяется с шагом 4. Соответственно с таким же шагом меняются адреса ячеек, содержимое которых выводится на экран.



2. Задание



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



Таблица 

№ варианта

Задание



Определить, является ли заданная квадратная матрица А(5,5) симметричной относительно главной диагонали.



Задана матрица В(4,4). Определить, отсортированы ли все элементы первого столбца в возрастающем порядке.



Задана матрица С(5,5). Поменять местами максимальный элемент каждой строки с первым элементом соответствующей строки.



Переписать первые элементы каждой строки матрицы D(3,3), которые больше 10, в массив В.



Задана матрица Q(5,5). Заменить последний нуль в каждой строке на 5.



Задана матрица D(4,4). Определить максимальный среди положительных, минимальный среди отрицательных и поменять их местами.



Задана матрица А(4,4). Заменить первый нуль в каждом столбце на количество нулей в этом столбце.



Задана матрица F(9,3). определить, равны ли все элементы первого столбца соответствующим элементам главной диагонали. Если нет, то поменять их местами.



Задана матрица C(5,5). Получить вектор В, каждый элемент которого равен количеству нулей, стоящих в столбце матрицы.



Задана матрица В(4,4). Если в строке есть хотя бы одна единица, то заменить эту строку нулями.



Задана матрица Q(3,3). Если на главной диагонали стоит нуль, то соответствующую строку заменить единицами.



Задана матрица D(4,4). Если максимальный элемент матрицы стоит на главной диагонали, то все элементы главной диагонали сделать равными максимальному.



Задана матрица С(5,5). Если минимальный элемент стоит в первой строке, то все элементы, стоящие в строке за ним, заменить нулями.



Задана матрица А(4,4). Если максимальный элемент матрицы равен сумме элементов первой строки, то поменять местами первую строку с той строкой, где находится максимальный элемент.



Задана матрица А(4,4). Если максимальный элемент матрицы равен сумме элементов первой строки, то поменять местами первую строку с той строкой, где находится максимальный элемент.




Литература



Подбельский В.В. Язык Cu ++: Учебное пособие. - М.: Финансы и статистика,1995, - 560 с.

Страуструп Б. Язык программирования Сг ++. - М.: Радио и связь, 1991. - 352 стр.

Собоцинский В.В. Практический курс Turbo Cu ++. Основы объктно- ориентированного программирования. - М.: Свет, 1993. - 236 с.

Романов В.Ю. Программирование на языке Cu ++. Практический подход. - М.: Компьтер, 1993. - 160 с.

Уинер Р. Язык турбо Cu . - М.: Мир, 1991. - 384 с.

Юлин В.А., Булатова И.Р. Приглашение к Cu. - Мн.: Высш. Шк., 1990,- 224 с.

Котлинская Г.П., Галиновский О.И. Программирование на языке Cu. -Мн.: Высш. Шк., 1991. - 156 с.

1