Материал: lectures_3

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

}

В рассматриваемом коде «n» элементов массива x1 копируется в массив x2. Если ввести значение «n», равное MSIZE, то весь массив x1 , будет скопировани в массив x2. Заметим, что инстукция x1 = x2; в связи с тем, x1 – константный указатель.

Пример 2. Обработка двумерных массивов.

Постановка задачи. В двумерном массиве, хранятся данные типа double. Массив состоит из nr строк и nc столбцов. Требуется вычислить две суммы summa_row и summa_col. Здесь summa_row - сумма элементов в каждой строке, а summa_col – сумма элементов для каждого столбца.

Решение

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

Каждая из сумм summa_row и summa_col должна вычисляться отдельно с использованием вложенных циклов. Дело в том, что при вычислении суммы summa_row матрица должна просматриваться по строкам, а при вычислении summa_col матрица должна просматриваться по стобцам.

Алгоритм просмотра по строкам реализуется с помощью вложенных циклов. Причем:

внешний цикл должен фиксировать индекс строки,

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

Валгоритме просмотра по столбцам наоборот:

внешний цикл должен фиксировать индекс столбца,

внутренний цикл – изменять индекс строки.

#define MROW 5 #define MCOL 10

int main(void)

{

double summa_row[MROW]; int nr;

printf(“nr = ”); scanf(“%d”, &nr);

int nc; printf(“nc = ”); scanf(“%d”, &nc);

double m[MROW][MCOL];

// Ввод матрицы по строкам for(int r = 0; r < nr; r++)

{

for(c = 0; c < nc; c++)

91

{

printf(“m[%d][%d]”, c, r); scanf(“%lf”, &m[r][c]);

}

}

// Вычисление суммы для каждой строки

double summa_row[MROW];// массив для хранения суммы по // строкам

for(int r = 0; r < nr; r++)

{

double s = 0;

for(c = 0; c < nc; c++)

{

s += m[r][c];

}

summa_row[r] = s;

}

// Вычисление суммы для каждого столбца

double summa_col[MROW];// массив для хранения суммы по // столбцам

for(int c = 0; c < nc; c++)

{

double s = 0;

for(r = 0; r < nr; r++)

{

s += m[r][c];

}

summa_col[c] = s;

}

// вывод результатов вычислений

}

return 0;

37.3.Размещение массивов в оперативной памяти

Массивы занимают в памяти компьютера непрерывный участок памяти. Структура одномерного массива, как структура данных, хорошо соответствует одномерной природе структуры оперативной памяти компьютера. Иначе дело обстоит с двумерными массивами. Двумерность совместить с одномерностью оперативной памяти можно разными способами. В некоторых языках двумерные массивы хранятся по столбцам. Так двумерные массивы хранятся в языке Фортран. В языке Си двумерные массивы хранятся по строкам. Вначале первая строка, затем вторая и так далее.

92

38. Указатели

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

Целесообразно различать типизированные и нетипизированные указатели. Значением типизированного указателя может быть адрес объекта только определенного типа. Значением нетипизированного указателя может быть объект любого типа.

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

А) Для типизированных указателей

T*

T

 

 

 

 

 

 

 

 

 

Указатель Объект

Б) Для нетипизированных указателей

void*

 

T

 

 

 

 

 

 

Указатель Объект

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

Для дальнейшего необходимо рассмотреть два оператора: разыменования и взятия адреса.

38.1.Операторы разыменования и взятия адреса

Это два основных оператора при работе с указателями. В результате выполнения оператора разыменования возвращается тот объект, на который установлен указатель. Основным оператором разыменования является префиксный унарный оператор “*”. Пусть в программе объявлен типизированный указатель p. Тогда, образовав выражение *p, получим доступ к тому объекту, адрес которого содержится в указателе p. Выражение *p относится к категории lvalue. Такое выражение может использоваться как в левой, так и в правой части оператора присваивания. Если T - тип объекта,

93

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

*p = 10;

Оператор взятия адреса является унарным префиксным оператором, позволяющим получить адрес своего операнда. Использование оператора взятия адреса приводит к образованию указателя-выражения. Приведем пример. Пусть в программе объявлены переменная n типа int и типизированный указатель ptr, значением которого могут быть адреса объектов типа int. Тогда с помощью следующей инструкции можно инициализировать указатель ptr:

ptr = &n;

Приведенная выше инструкция присваивания записывает в указательпеременную ptr значение указателя-выражения &n.

38.2.Области применения указателей

Можно выделить следующие основные области применения указателей:

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

2.Работа с объектами, размещенными в динамической памяти.

3.Замена манипуляций со сложными объектами данных манипуляцией с адресами этих объектов.

38.3.Нулевой указатель

Нулевой указатель – это такое значение указателя, которое не может быть адресом переменной или функции. Наиболее часто используемыми представлениями нулевого указателя как константы являются 0, 0L и NULL. Считается хорошим стилем программирования всем указателям, которые не работают с реальными объектами (переменными) или функциями, было присвоено значение NULL

38.4.Определение указателя

Вопределении указателя используется оператор разыменования “*”. Упрощенный формат определения указателя имеет следующий вид:

type * name;

Здесь type – спецификация типа, определяющая тип объекта, адрес которого будет храниться в указателе; name – имя (идентификатор) указателя. Заметим, что иногда тип объект, на который установлен указатель, называют базовым типом. Как уже отмечалось, в записи определений используется синтаксис выражений. Продемонстрируем это положение на примере. Пусть имеется следующее определение указателя:

int * ptr;

Здесь type – это int, а ptr – name. Приведенное выше определение можно “прочитать”, используя синтаксис выражений, следующим образом.

94

Применение оператора разыменования к имени ptr позволяет получить объект типа int. Отсюда следует тот факт, что ptr является именем указателя

Во время определения указатель может быть инициализирован. В качестве инициализаторов указателя при его объявлении могут использоваться:

Нулевой указатель.

Указатель-переменная

Указатель-выражение.

Приведем примеры инициализации указателей во время их определения.

/* Примеры инициализации указателей при их определении */

int* p = NULL; /*

Инициализация

указателя

p нулевым указателем*/

int* p2

= p;

/* Инициализация

указателя

p2 значением

 

 

 

 

существующего

указателя

p*/

int n = 5;

/* Инициализация

указателя

значением

int* p3

= &n;

/*

 

 

 

указателя-выражения &n */

. . . . . . . . . . . . . . . . . . . . . . . . . . . */

38.5.Недействительный указатель

Недействительный указатель (invalid pointer) – это ненулевой указатель, содержимое которого не является адресом переменной или функции. Любое использование недействительного указателя (разыменование такого указателя, сравнение его со значением NULL, передача его в качестве параметра в функцию) в стандарте языка Си приводит к неопределенному поведению программы. Рассмотрим возможные причины появления недействительных указателей.

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

Отсутствие инициализации указателей,

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

Освобождение динамической памяти.

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

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

/*

Ошибка в программном коде*/

 

double x;

 

double* pn;

 

x = modf(10.2, pn); // Использование неинициализированного

95

Источник: https://studfile.net/preview/16544272/