Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual_Studio_2010

.pdf
Скачиваний:
110
Добавлен:
03.03.2016
Размер:
5.94 Mб
Скачать

const double *pexp = &e; const int base = 10;

const int *const pbase = &base; const double *ptr_pi = π int i;

time_t t;

printf("\n Enter a real number: "); scanf_s("%lf", px);

printf("\n The value of the entered number is \"%g\"\n", x);

printf("\n The base of natural logarithms \ is \"%0.14f\"\n", *pexp);

printf("\n The base of the decimal logarithm is \"%d\"\n",\ *pbase);

srand((unsigned) time(&t)); // рандомизация for (i = 0; i < rand(); i++)

{

rand();

}

//Случайное вещественное число из интервала [-100.0; 100.0]

x= -100.0 + (100.0 - (-100.0))* (double)rand() / RAND_MAX;

printf("\n The modified value of x: %g\n \ Pointer to the variable x: %g\n", x, *px);

printf("\n The value of pi through the pointer \ and the function acos(0): %0.14f\n", *ptr_pi * 2);

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

return 0;

}

В программе для получения числа используется функция acos(0), так как косинус /2 равен нулю. Затем полученный результат умножается на два. Дополнительная рандомизация осуществляется в цикле, одним из параметров которого является случайная функция rand(), возвращающая целое число. При этом предусмотрено приведение типов. Число х изменяется по равномерному случайному закону из интервала [–100.0; 100.0].

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

121

Рис. 7.7. Пример работы программы с указателями

Задание 7

1.Выполните инкрементирование указателей на константы и константных указателей. Объясните полученный результат.

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

3.В качестве константы примите некоторое шестнадцатеричное число (с буквами). Определите указатель на константу и в цикле от 1 до 16 измените значение указателя с последующим выводом результатов на консоль.

4.Выполните возможные арифметические операции с константными указателями и с указателями на константы.

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

Контрольные вопросы

1.Каково общее назначение указателей в языке С?

2.Какие арифметические операции допускаются для указателей?

3.Какие унарные операторы используются с указателями? Как они называются?

4.Для каких типов данных может быть использован указатель?

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

122

6.С помощью какого формата осуществляется вывод на консоль адресов переменных заданного типа?

7.Что такое многоуровневая адресация? Как она организуется в языке С?

8.Как осуществляется инициализация указателей на вещественные типы данных?

9.Как осуществляется инициализация указателей на символьный тип данных?

10.Какой смысл имеет значение указателя NULL?

11.Что произойдет, если применить к указателю со значением NULL операцию разыменования?

12.Как следует определять и инициализировать указатель на константу?

13.Как следует определять и инициализировать константный указатель?

14.В чем отличие константного указателя от указателя на константу?

БИБЛИОГРАФИЧЕСКИЙ СПИСОК

1.Керниган Б. У. Язык программирования С : пер. с англ./Б. У. Керниган, Д. М. Ритчи. – 2-е изд. – М. : Вильямс, 2007. – 304 с.

2.Кочан С. Программирование на языке С : пер. с англ./С. Кочан. –

3-е изд. – М. : Вильямс, 2007. – 496 с.

3.Подбельский В. В. Программирование на языке Си : учеб. пособие/ В. В. Подбельский, С. С. Фомин. – 2-е изд., доп. – М. : Финансы и стати-

стика, 2007. – 600 с.

4.Шилдт Г. Полный справочник по С : пер. с англ./Г. Шилдт. – 4-е изд. –

М. : Вильямс, 2007. – 704 с.

5.Подбельский В. В. Практикум по программированию на языке Си : учеб. пособие/В. В. Подбельский. – М. : Финансы и статистика, 2004. – 576 с.

6.Horton Ivor. Beginning C : From Novice to Professional / Ivor Horton. – 4 th ed. – N. Y. : Apress, 2006. – 640 p.

123

Тема 8

УКАЗАТЕЛИ И МАССИВЫ В ЯЗЫКЕ С

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

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

Одной из наиболее распространенных конструкций с использованием указателей являются массивы [1]. В результате применения указателей для массивов требуется меньшее количество используемой памяти и обеспечивается высокая производительность [1].

В языке С массивы – это упорядоченные данные (элементы) одного типа. Компилятор языка С рассматривает имя массива как адрес его первого элемента. Например, если имя массива Arr с десятью элементами, то компилятор преобразует i-й элемент (0 i < 10) по правилам работы с указателями с операцией разыменования: *(Arr + i). Здесь Arr как бы указатель, а i – целочисленная переменная. Сумма (Arr + i) указывает на i-й элемент массива, а операция разыменования (оператор раскрытия ссылки *) дает его значение.

Имя массива без индекса образует указатель на начало этого массива.

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

Пусть, например, массив Arr содержит 10 целочисленных переменных: int Arr[10];

Тогда можно объявить указатель ptr, который будет указывать на элементы массива Arr:

int *ptr;

Тип указателя (в примере это int) должен соответствовать типу объявленного массива.

Для того чтобы указатель ptr ссылался на первый элемент (с нулевым индексом) массива Arr, можно использовать утверждение

ptr = Arr;

В то же время можно применить прямую адресацию: ptr = &Arr[0];

Обе формы записи эквивалентны.

Аналогичные утверждения будут справедливы для других типов масси-

вов: char, float, double и пр.

Если указатель ptr указывал на первый элемент (с нулевым индексом) массива Arr, то для обращения к следующему элементу массива допустимы такие формы утверждений:

124

ptr = &Arr[1]; ptr += 1;

Соответственно выражение *(ptr+1) будет ссылаться на значение, содержащееся в элементе Arr[1].

Утверждение ptr += n;

заставит указатель *ptr ссылаться на элемент массива, находящийся на расстоянии n от того, на который ранее ссылался указатель, независимо от типа элементов, содержащихся в массиве [1]. Разумеется, значение n должно быть в допустимых пределах для данного объема массива.

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

В языке программирования С вполне корректной операцией является сравнение указателей. К указателям применяются операции сравнения >, >=, !=, ==, <=, < [3]. Сравнивать указатели допустимо только с другими указателями того же типа или с константой NULL, обозначающей значение условного нулевого адреса. Константа NULL – это особое значение переменной-указателя, присваиваемое ей в том случае, когда она не должна иметь никакого значения. Его можно присвоить переменной-указателю любого типа. Оно представляет собой целое число нуль. Особое значение имеет сравнение двух указателей, которые связаны с одним и тем же массивом данных.

Рассмотрим инициализацию указателей типа char: char *ptr = "hello, world";

Переменная *ptr является указателем, а не массивом. Поэтому строковая константа "hello, world" не может храниться в нем. Тогда возникает вопрос, где она хранится. Для этого следует знать, что происходит, когда компилятор встречает строковую константу. Он создает так называемую таблицу строк, где сохраняет строковые константы, которые попадаются ему по ходу чтения текста программы [4]. Следовательно, когда встречается объявление с инициализацией, то компилятор сохраняет "hello, world" в таблице строк, а указатель *ptr записывает ее адрес.

Указатели сами по себе являются переменными, поэтому их можно хранить в массивах, как и переменные других типов [2]. Получается массив указателей.

Массив указателей фиксированных размеров вводится одним из следующих определений [4]:

тип *имя_массива [размер]; тип *имя_массива [ ] = инициализатор;

тип *имя_массива [размер] = инициализатор;

125

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

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

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);

126

В результате в выходной поток (на дисплей пользователя) будет выведено число 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 – число столбцов в матрице.

127

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

Пример 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. Пример считывания строк различной длины

128

Задание1

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

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

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

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];

129

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], где Х – номер компьютера, на котором выполняется лабораторная работа.

130

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