Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
BOOK_С_INTUIT.doc
Скачиваний:
3
Добавлен:
19.09.2019
Размер:
7.91 Mб
Скачать

Int data[7]; // обычный массив

Int *pd[7]; // массив указателей

int *pi[ ] = { &data[0], &data[4], &data[2] };

Здесь каждый элемент массивов pd и pi является указателем на объекты типа int.

Значением каждого элемента указателей pd[j] и pi[k] может быть адрес объекта типа int. Все 7 элементов указателя pd не инициализированы. В массиве указателя pi три элемента, и они инициализированы адресами конкретных элементов массива data.

При обработке строк текста они, как правило, имеют различную длину, и их нельзя сравнить или переместить одной элементарной операцией в отличие от целых чисел. В этом случае эффективным средством является массив указателей. Например, если сортируемые строки располагаются в одном длинном символьном массиве вплотную – начало одной к концу другой, то к каждой строке можно обращаться по указателю на ее первый символ [2]. Сами же указатели можно поместить в массив, т.е. создать массив указателей.

Приведем пример массива строк о студенте, задаваемого с помощью массива указателей:

char *ptr[ ] = {

"Surname", //фамилия

"Name", // имя

"group", // группа

"ACOUY" // специальность

};

С помощью массива указателей можно инициализировать строки различной длины. Каждый элемент массива ptr[] указывает на одномерный массив символов (строку) независимо от других указателей.

В языке программирования С предусматриваются ситуации, когда указатели указывают на указатели. Такие ситуации называются многоуровневой адресацией. Пример объявления указателя на указатель:

int **ptr2;

В приведенном объявлении **ptr2 – это указатель на указатель на число типа int. При этом наличие двух звездочек свидетельствует о том, что имеется двухуровневая адресация. Для получения значения конкретного числа следует выполнить следующие действия:

int x = 88, *ptr, **ptr2;

ptr = &x;

ptr2 = &ptr;

printf("%d", **ptr2);

В результате в выходной поток (на дисплей пользователя) будет выведено число 88. В приведенном фрагменте переменная *ptr объявлена как указатель на целое число, а **ptr2 – как указатель на указатель на целое. Значение, выводимое в выходной поток (число 88), получается в результате операции разыменования указателя **ptr2.

В многомерных массивах указатели содержат адреса элементов массива построчно. Рассмотрим пример двухмерного целочисленного массива М размера 3×5, т.е. состоящего из 3 строк и 5 столбцов, и определим указатель:

int M[3][5]= {{1,2,3,4,5},{–6,–7,–8,–9,–10},{11,12,13,14,15}};

int *ptr;

Элементы массива (по индексам) располагаются в ячейках памяти по строкам в следующем порядке:

M[0][0], M[0][1], M[0][2], M[0][3], M[0][4], M[1][0], M[1][1], M[1][2], M[1][3], M[1][4], M[2][0], M[2][1], M[2][2], M[2][3], M[2][4].

Сначала запоминается первая строка, затем – вторая, потом – третья. В данном случае двухмерный массив – это массив трех одномерных массивов, состоящих из 5 элементов.

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

ptr == &M[0][0]; //1-я строка, 1-й столбец

ptr + 1 == &M[0][1]; // 1-я строка, 2-й столбец

ptr + 2 == &M[0][2]; // 1-я строка, 3-й столбец

ptr + 3 == &M[0][3]; // 1-я строка, 4-й столбец

ptr + 4 == &M[0][4]; // 1-я строка, 5-й столбец

ptr + 5 == &M[1][0]; // 2-я строка, 1-й столбец

ptr + 6 == &M[1][1]; // 2-я строка, 2-й столбец

ptr + 7 == &M[1][2]; // 2-я строка, 3-й столбец

ptr + 8 == &M[1][3]; // 2-я строка, 4-й столбец

ptr + 9 == &M[1][4]; // 2-я строка, 5-й столбец

ptr + 10 == &M[2][0]; // 3-я строка, 1-й столбец

ptr + 11 == &M[2][1]; // 3-я строка, 2-й столбец

ptr + 12 == &M[2][2]; // 3-я строка, 3-й столбец

ptr + 13 == &M[2][3]; // 3-я строка, 4-й столбец

ptr + 14 == &M[2][4]; // 3-я строка, 5-й столбец

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

ptr = &M[0][0];

*(ptr + i*n + j);

где i – номер строки заданной матрицы, j – номер столбца, n – число столбцов в матрице.

ПРАКТИЧЕСКАЯ ЧАСТЬ

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

Программный код решения примера

#include <stdio.h>

#include <conio.h>

int main (void) {

int i, n;

char *ptr[ ] = {"one", "two", "three", "four", "five",\

"six", "seven", "eight", "nine", "ten"};

n = sizeof(ptr)/sizeof(ptr[0]);

printf("\n\t Strings of various length:\n");

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

printf("\n%12d) %s", i+1, ptr[i]);

printf("\n\n Press any key: ");

_getch();

return 0;

}

В программе использован одномерный массив указателей. Функция printf() и спецификатор преобразования %s допускают применение в качестве параметра указателя на строку. При этом на дисплей выводится не значение указателя, а содержимое адресуемой им строки. Обратный слэш \ служит для переноса содержимого операторной строки на новую строку. Оператор sizeof() вычисляется во время компиляции программы, превращаясь обычно в целую константу, значение которой равно размеру типа или объекта, в данном случае соответствует размеру массива указателей.

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

Р езультат выполнения программы показан на рис.8.1.

Рис.8.1. Пример считывания строк различной длины

Задание1

  1. Вывод заданных строк осуществите на экран дисплея в одной строке.

  2. Вывод результата выполните на основе разыменования массива указателей.

  3. Инициализируйте массив указателей своей фамилией, именем, номером группы, специальности, факультета и номером компьютера (1, 2, ), на котором выполняется лабораторная работа.

  4. В программу введите дополнительный массив указателей, с помощью которого выполните вывод заданных строк.

  5. В программе вместо оператора цикла for примените оператор while.

Пример2. Написать программу сортировки одномерного массива, состоящего из 10 равномерно распределенных случайных чисел из интервала [–8; 8], с помощью указателей.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <time.h>

#define N 10

int main (void) {

double a = -8.0, b = 8.0;

double arr[N], *pmin[N], *temp;

int i, j;

long int T;

T = (long)time(NULL); // использование системного времени

srand((unsigned int) T); // рандомизация случайных чисел

// Заполнение массива случайными числами

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

arr[i] = a + (b - a)*(double)rand()/RAND_MAX;

printf("\n\t The initial array of [%1.4f, %1.4f]:\n", a, b);

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

printf("\n\t%2d) %8.4f", i+1, arr[i]);

// Взятие адресов элементов исходного массива

//в предположении, что они образуют отсортированный массив

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

pmin[i] = &arr[i];

//Сортировка массива по убыванию

for (i = 0; i < N-1; ++i)

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

{

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

{

temp = pmin[i];

pmin[i] = pmin[j];

pmin[j] = temp;

}

}

//Вывод отсортированного массива по убыванию

printf("\n\n\t Assorted array of descending:\n");

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

printf("\n\t%2d) %8.4f", i+1, *pmin[i]);

printf("\n\n Press any key: ");

_getch();

return 0;

}

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

В озможный результат выполнения представлен на рис.8.2.

Рис.8.2. Сортировка массива по убыванию

Задание2

  1. Выполните вывод отсортированного массива и исходного массива в два параллельных столбца.

  2. Напишите программу сортировки массива по возрастанию. Границы интервала равномерно распределенных случайных чисел: [–8; 2X], где Х – номер компьютера, на котором выполняется лабораторная работа.

  3. Сгенерируйте массив целых чисел размера N из интервала кодов строчных букв латинского алфавита, где N – число букв вашей фамилии. Из сформированного массива выведите коды полученных букв и сами буквы. Отсортируйте полученные буквы по возрастанию кодов составляющих букв с помощью указателей.

Пример3. Написать программу заполнения матрицы по спирали натуральными числами с помощью массива указателей.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#define n 15

int main(void) {

int i = 1, I, j, k,

p = n/2;

int M[n][n], *ptr[n*n];

// Обнуление матрицы и инициализация указателя

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

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

{ M[I][j] = 0; ptr[I*n + j] = &M[I][j];}

for (k = 1; k <= p; k++) // Число спиралей

{

// Верхний горизонтальный столбец

for (j = (k-1); j < (n-k+1); j++)

*ptr[(k-1)*n + j] = i++;

// Правый верхний столбец

for (j = k; j < (n-k+1); j++)

*ptr[j*n + (n-k)] = i++;

// Нижний горизонтальный столбец

for (j = (n-k-1); j >= (k-1); --j)

*ptr[(n-k)*n + j] = i++;

// Левый верхний столбец

for (j = (n-k-1); j >= k; j--)

*ptr[j*n + (k-1)] = i++; }

if ( n % 2 ) *ptr[p*n + p] = n*n;

printf("\n\t Spiral matrix of dimention (%d x %d):\n\n",n,n);

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

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

{ if (n*n < 20*20){

printf("%4d", *ptr[i*n + j]);

if (j == (n-1)) printf("\n");}

else if (n*n >= 20*20)

goto mes;

}

mes: if (n > 19)

printf("\n\t It is a large matrix. Can not to see on display.\n");

printf("\n Press any key: ");

_getch();

return 0;

}

Р езультат выполнения программы показан на рис.8.3.

Рис.8.3. Пример заполнения матрицы по спирали

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

Задание3

  1. Проверьте программу при размерности матрицы от 3 до 19 и более.

  2. Выведите на дисплей матрицу M[n][n]. Объясните результат.

  3. Дополните программу: создайте матрицу и заполните ее по спирали натуральными числами с помощью массива указателей, который использовался для взятия адресов элементов исходной матрицы М[n][n]. Объявите матрицу с начальной буквой вашей фамилии (набранной латиницой). Выведите матрицу на дисплей.

  4. Напишите программу заполнения матрицы по спирали и против часовой стрелки натуральными числами с помощью массива указателей.

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

  6. Дополните программу подсчетом суммы элементов каждого столбца матрицы. Осуществите вывод результата на дисплей.

  7. Дополните программу подсчетом суммы элементов каждой строки матрицы. Осуществите вывод результата на дисплей.

  8. Дополните программу подсчетом суммы элементов диагонали матрицы. Осуществите вывод результата на дисплей.

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

Пример4. Написать программу заполнения целочисленной прямоугольной матрицы размером не более 15× 14 из интервала [–12; 12] с помощью операции разыменования.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#include <stdlib.h>

#include <time.h>

#define N 15

#define M 14

const int Left = -12;

const int Right = 12;

int main(void) {

int i, j, n, m;

int matr[N][M];

time_t t; //переменная системного времени

srand((unsigned)time(&t)); // для рандомизации случайных чисел

printf("\n Enter the number of lines of the matrix is not more than %d: ", N);

scanf_s("%d", &n);

printf(" Enter the number of columns of the matrix is not more than %d: ", M);

scanf_s("%d", &m);

// Контроль ввода допустимой размерности

if (n > N || m > M || n < 1 || m < 1)

{

printf("\n\t Data error! Repeat please.\n Press any key: ");

_getch();

return -1;

}

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

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

*(*(matr + i) + j) = 0;

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

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

*(*(matr + i) + j) = (rand() % (2*Right+1)) + Left;

printf("\n The matrix of random whole numbers from the entire [%d, %d]:\n", Left, Right);

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

printf("\n ");

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

printf("%5d", *(*(matr + i) + j));

}

printf("\n\n Press any key: ");

_getch();

return 0;

}

В программе использованы спецификаторы const для объявления неизменяемых переменных.

В озможный результат выполнения программы представлен на рис.8.4.

Рис.8.4. Матрица случайных целых чисел

Задание4

  1. Проверьте режим ошибочного ввода размерности матрицы.

  2. Осуществите перевод значений сформированной матрицы n×m на новую строку на основе оператора условия ?.

  3. Подсчитайте количество отрицательных и положительных чисел. Учтите также подсчет количества нулей.

  4. Предусмотрите изменение интервала последовательности случайных чисел, генерируемой функцией rand(), для чего осуществите ввод границ интервала с клавиатуры.

  5. В сформированной матрице найдите минимальные и максимальные числа с определением их индексов расположения в матрице.

Пример5. Написать программу умножения целочисленных матриц с использованием указателей [5].

Условием перемножения двух матриц А и В является равенство числа столбцов матрицы А и числа строк матрицы В. Если первая матрица А имеет размер n×k, то вторая матрица В должна иметь размер k×m. В результате перемножения получим матрицу С размера n×m. Приведем схему по размерностям:

C  = AB = (m  r) (r  n) = m  n.

Поэлементное перемножение двух матриц в стандартной математической форме имеет вид

С учетом синтаксиса формирования массивов в языке С индексация должна начинаться с нуля, поэтому формулу перепишем в следующем виде:

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#define n 7

#define k 12

#define m 4

int main(void) {

int i, j, p;

int A[n*k], B[k*m];

long int *C[n*m];

//Обнуление матриц

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

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

C[i*m + j] = 0;

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

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

A[i*k + j] = 0;

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

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

B[i*m + j] = 0;

// Заполнение матриц целыми числами

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

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

A[i*k + j] = i + j - 6;

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

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

B[i*m + j] = i + j - 11;

// Циклы перемножения с накоплением суммы

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

for (j = 0; j < m; ++j) { C[i*m + j] = 0;

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

C[i*m + j] += (A[i*k + p] * B[p*m + j]);

}

printf("\n\t The result of multiplying the two matrices:\n\t\t C = A*B (%dx%d = %dx%d * %dx%d)\n", n, m, n, k, k, m);

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

printf("\n\t");

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

printf("%6ld", C[i*m + j]);

}

printf("\n\n Press any key: ");

_getch();

return 0;

}

Обнуление матриц и массива указателей позволяет заранее выделить память. Для вывода результата перемножения предусмотрен спецификатор ld.

Р езультат выполнения программы показан на рис.8.5.

Рис.8.5. Результат перемножения двух матриц

Задание5

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

  2. Вывод результата перемножения выполните на основе операции разыменования.

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

  4. Включите нумерацию строк и столбцов (слева от матрицы и вверху над ней).

  5. Проверьте работу программы без предварительного обнуления.

Пример6. Написать программу формирования трех массивов, состоящих из матриц размерностей 4×8 и заполненных натуральными числами. Заполнение матриц выполнить по строкам последовательно для каждого из массивов. Предусмотреть вывод всех трех массивов на консоль и вывод одного из массивов, номер которого задается пользователем с клавиатуры.

Для решения примера сформируем трехмерный массив размером 3×4×8 с помощью указателя, в котором левый размер будет равен 3, центральный – 4, а крайний правый – 8, например *PTR[3*4*8].

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#define p 3 // количество "подшитых" матриц

#define n 4 // число строк матрицы

#define m 8 // число столбцов матрицы

int main (void)

{

int i, j, k, r;

int *PTR[p*n*m], *M2[n*m];

// Заполнение одномерного массива

for (i = 0; i < p*n*m; ++i)

PTR[i] = (int*)(i + 1);

printf("\n An array of dimension %dx%dx%d:\n", p, n, m);

puts("===========================================");

// Разбивка массива

for (k = 0; k < p; ++k){ printf("\n");

for (i = 0; i < n; ++i) { printf(" ");

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

printf(" %3d ",PTR[k*n*m + i*m + j]);

}

printf("\n");

}

}

puts("===========================================");

printf("\n Select the number of arrays of a series of numbers (%d...%d): ",1, p);

scanf_s("%d", &r);

printf("\n Matrix number %d from three-dimensional array:\n", r);

// Выбор матрицы из 3-мерного массива

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

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

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

if ( k == (r-1))

{

M2[i*m + j] = PTR[k*n*m + i*m + j];

}

for (i = 0; i < n; ++i) { printf("\n ");

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

printf(" %3d ", M2[i*m + j]);

}

printf("\n\n Press any key: ");

_getch();

return 0;

}

Трехмерный массив можно рассматривать как книгу с заданным количеством страниц (p = 3), размер которых определяется как матрица с заданным числом строк (n = 4) и заданным количеством столбцов ( m = 8). Страницы (матрицы) «сшиваются». С помощью указателя *PTR сначала формируется одномерный массив, который затем разбивается на заданное количество матриц. С помощью функций printf("\n") оcуществляется перевод на новую строку с помощью символа форматирования \n.

Р езультат выполнения программы представлен на рис.8.6.

Рис.8.6. Пример вывода данных трехмерного массива

Задание6

  1. В программу включите проверку вводимого номера матрицы, т.е. чтобы число r принадлежало отрезку [1;3].

  2. В программу введите указатели с многоуровневой адресацией, например ***ptr3, **ptr2. Проверьте работу программы.

  3. Количество строк матрицы примите n = 2Х, где Х – номер компьютера, на котором выполняется лабораторная работа.

  4. Трехмерный массив заполните вещественными числами, равномерно распределенными в интервале [–3X; 3X], где Х – номер компьютера, на котором выполняется лабораторная работа.

  5. С учетом предыдущего пункта определите минимальный и максимальный элементы трехмерного массива. Определите также индексы (k,i,j) этих элементов. Результаты выведите на консоль.

Пример7. Написать программу поиска подстроки в строке, сформированной по случайному закону из 15 букв латинского алфавита, с помощью функции strstr(). В качестве подстроки принять первые три буквы своей фамилии.

Для работы с функцией strstr() требуется подключение заголовочного файла string.h.

Программный код решения примера

#include <stdio.h>

#include <conio.h>

#include <string.h>

#include <stdlib.h>

#include <time.h>

int main (void)

{

int i,

k = 0,

n,

in;

int numSTR;

int N = 1000;

int numA, numZ;

char str[16];

char sub[4];

char *ptr;

srand((unsigned)time(NULL));

numA = (int)'a';

numZ = (int)'z';

printf("\n Enter the three letters: ");

in = scanf_s("%s", sub, sizeof(sub));

if (in == 0)

{

printf("\n Error input. Press any key: ");

_getch();

exit(1);

}

printf("\n\t substring is \"%s\"\n", sub);

for (n = 0; n < N; n++) {

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

str[i] = numA + rand() % (numZ - numA) + 1;

str[i] = '\0'; // завершение строки нулевым символом

ptr = strstr(str, sub);

if (ptr != NULL)

{

numSTR = (int)(ptr - str +1);

k++;

break;

}

}

if (k == 0)

printf("\n\t Substring \"%s\" not found", sub);

else

printf("\n\t Substring \"%s\" found at positions %d, %d, %d", \

sub, numSTR, numSTR+1, numSTR+2);

puts("\n");

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

printf(" %3d)", i+1);

puts("");

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

printf(" %3c ", str[i]);

printf("\n\n ... Press any key: ");

_getch();

return 0;

}

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

В озможный положительный результат работы программы показан на рис. 8.7.

Рис. 8.7. Результат поиска подстроки в строке

Задание 7

  1. Объясните процесс вычисления правой части выражения с переменной numSTR.

  2. Примените вместо операторов цикла for операторы цикла while.

  3. Подсчитайте количество итераций формирования строки (из 15 символов), когда в ней будет найдена задаваемая подстрока (например, три латинские буквы Вашей фамилии).

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

  5. Напишите программу имитации взлома пароля, состоящего из трех букв латинского алфавита. Если при ручном трехкратном вводе пароля, он не будет разгадан, то включите автоматический поиск.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]