3.9 Методы доступа к элементам массивов
В языке СИ между указателями и массивами тесная связь.
Например, при объявлении массива
int array[25]
выделяется память для 25-ти элементов массива и для указателя с именем array, значение которого равно адресу первого по счету (нулевого) элемента массива, т.е. сам массив остается безымянным, а доступ к элементам массива осуществляется через указатель с именем array.
С точки зрения синтаксиса языка указатель arrаy является константой, значение которой можно использовать в выражениях, но изменить это значение нельзя.
int arrаy[25];
Здесь указатель ptr устанавливается на адрес первого элемента масcива,
присваивание ptr=array можно записать в эквивалентной форме ptr=&arrаy[0].
3.9.1 Два способа для доступа к элементам массива
Первый способ связан с использованием обычных индексных выражений в квадратных скобках, например,
При таком способе доступа записываются два выражения, причем второе выражение заключается в квадратные скобки.
Одно из этих выражений должно быть указателем, а второе — выражением целого типа.
Последовательность записи этих выражений может быть любой, но в квадратных скобках записывается выражение следующее вторым. Поэтому записи array[16] и 16[array] будут эквивалентными и обозначают элемент массива с номером шестнадцать.
Второй способ доступа к элементам массива связан с использованием адресных выражений и операции разадресации в форме *(array+16)=3 или *(array+i+2)=7.
При таком способе доступа адресное выражение равное адресу шестнадцатого элемента массива.
При реализации на компьютере первый способ приводится ко второму, т.е. индексное выражение преобразуется к адресному.
array[16] ипреобразуются в *(array+16).
Для доступа к начальному элементу массива (т.е. к элементу с нулевым индексом) можно использовать просто значение указателя array или ptr.
Любое из присваиваний
присваивает начальному элементу массива значение 2, но быстрее всего выполнятся присваивания *array=2 и *ptr=2, так как в них не требуется выполнять операции сложения.
3.9.2 Указатели на многомерные массивы
Указатели на многомерные массивы в языке СИ — это массивы массивов, т.е. такие массивы, элементами которых являются массивы.
При объявлении таких массивов в памяти компьютера создается несколько различных объектов.
Пример при объявлении двумерного массива
Int arr2[4][3]
в памяти выделяется участок для хранения значения переменной arr2, которая является указателем на массив из четырех указателей. Для этого массива из четырех указателей тоже выделяется память. Каждый из этих четырех указателей содержит адрес массива из трех элементов типа int, и, следовательно, в памяти компьютера выделяется четыре участка для хранения четырех массивов чисел типа int, каждый из которых состоит из трех элементов.
Т.о., объявление arr2[4][3] порождает в программе три разных объекта:
- указатель с идентификатором arr2,
- безымянный массив из четырех указателей;
- безымянный массив из двенадцати чисел типа int.
Работа с массивами в языке Си
На этой странице относительно подробно рассказывается о статических и динамических массивах. Краткое изложение основных моментов и описание методов поиска ошибок доступны при нажатии на кнопки выше. Двумерные массивы описаны на этой странице.
Массив – это линейно упорядоченная совокупность однотипных элементов. Массив определяется типом элементов (int, double, . ) и длиной. Доступ к элементам осуществляется по индексу – порядковому номеру элемента массива. Логически первый элемент массива имеет индекс ноль. В языке Си существуют статические массивы, число элементов в которых должно быть известно в момент компиляции программы, и динамические массивы, размер которых задается в процессе выполнения программы, то есть может зависеть от входных данных. Эти два типа отличаются только методом создания массива, поэтому сначала рассмотрим статические массивы.
Статические массивы
Способы объявления статических массивов
Объявление статического массива отличается от объявления обычной переменной только указанием количества элементов массива. Например, следующее объявление означает, что именем points называется массив из 100 действительных чисел.
double points[100];
В некотором смысле можно считать, что такое объявление переменной points создает 100 переменных, которые называются points[0], points[1], . points[99]. Плюс к этому, «имена» этих переменных можно вычислять: points[1], points[0+1] или points[k-1] имеют одно значение (если k=2).
В реальных программах следует избегать явного использования числовых констант в объявлениях массива (и других частях программы). Если нам нужно объявить два массива, которые теоретически могут иметь разный размер, например,
double points[100]; int students[100];
то в дальнейшем, если возникнет необходимость увеличить один из массивов, будет сложно отличить одну константу от другой. Особенно это верно при обработке элементов массива (см. ниже). Правильным считается использование директив препроцессора для присвоения константам «говорящих» имен. Например:
#define NPOINTS 100 #define NSTUDENTS 100 . double points[NPOINTS]; int students[NSTUDENTS];
Объявление массива может быть совмещено с присвоением значений его элементам. Например,
double points[] = ;
создает массив из четырех действительных чисел с указанными значениями. Заметим, что в данном случае число элементов массива в квадратных скобках не указывается. Компилятор самостоятельно вычисляет длину по списку начальных значений. В программе можно вычислить длину такого массива, разделив его размер на размер одного элемента (пример ниже).
Работа с элементами массива
Для доступа к элементу массива достаточно знать его имя и порядковый номер элемента. В языке Си элементы массива индексируются начиная с нуля, то есть в массиве из двух элементов корректными являются индексы 0 и 1. Если массив имеет имя array, то его k -й элемент записывается как array[k] . Это выражение может использоваться как для получения значения элемента массива, так и для его изменения, если оно стоит в левой части оператора присваивания. Рассмотрим для примера следующую программу.
#define NPOINTS 100 int main() < double points[NPOINTS]; int k; points[0] = 0.1; for(k=1; k < NPOINTS; k++) < points[k] = 0.1 + points[k-1]; >return 0; >
Эта программа заполняет массив действительных чисел значениями 0, 0.1, 0.2 и так далее. Отметим, что макропеременная NPOINTS используется как при объявлении массива, так и в качестве верхней границы цикла по всем его элементам. Если размер массива нужно будет изменить, то достаточно исправить одну строчку в программе (#define).
Пример работы с массивом, который задан с начальными значениями:
int main() < double points[] = ; int k; int npoints = sizeof(points)/sizeof(points[0]); for(k=0; k < npoints; k++) < printf("points[%d] = %lf\n", k, points[k]); >return 0; >
Типичная ошибка при работе с массивами состоит в указании неправильного индекса. Если в приведенной выше программе переменная цикла k будет пробегать значения от 0 до npoints включительно, то поведение программы, вообще говоря, может быть любым. Наиболее вероятным поведением является вывод на экран какого-то значения, но может возникнуть и критическая ошибка, которая приведет к аварийной остановке программы.
Представление массива в памяти и адресная арифметика
В памяти ЭВМ элементы массива записаны последовательно без пропусков. Имя массива является указателем на его начальный элемент (с индексом 0). Поскольку в массиве все элементы имеют одинаковый тип, то зная адрес начала массива (A), размер одного элемента (size) и индекс k можно вычислить адрес размещения k-ого элемента: A + k*size. Если требуется получить значение k-ого элемента массива, то достаточно выполнить одно умножение (k*size), одно сложение (A + k*size) и загрузить значение из памяти по только что вычисленному адресу. Таким образом, обращение к элементу массива очень эффективно и сложность этой операции не зависит от величины индекса k: получение (или изменение) значения нулевого элемента столь же эффективно, как и миллионного.
Хорошо, адрес начала массива мы знаем — это его имя, индекс нам известен, но как узнать size (размер одного элемента)? Чуть ниже мы узнаем как это сделать, но для работы с указателями на элементы массива это не требуется! В языке Си к указателям можно прибавлять целые числа. Например, если есть указатель double *a; , то значением выражения a+9 будет адрес десятого (еще раз вспомним, что массивы индексируются с нуля!) элемента массива, который начинается с адреса a . Компилятор сам понимает, что a является указателем на double и прибавляет нужное значение.
Обратной стороной последовательно хранения элементов в памяти является сложность вставки нового значения с сохранением порядка следования элементов. Например, если в массив нужно добавить новое значение по индексу 0, то чтобы «освободить» место все элементы массива придется сдвинуть на одну позицию. Ясно, что сложность этой операции зависит от длины массива. Чем больше длина, тем дольше выполняется это действие.
Передача массива в функцию
Функция может получать на вход массив. В действительности в функцию передается адрес начала массива и его длина. Прототип функции может быть оформлен либо так:
int print_array(double x[], int len);
int print_array(double *x, int len);
Эти варианты являются эквивалентными. Некоторые программисты предпочитают первый (квадратные скобки показывают, что формальный параметр функции является массивом), другие — второй (имя массива является указателем на нулевой элемент). Естественно, что функция может иметь и другие параметры, в том числе, другие массивы. Это только пример.
Рассмотрим возможную реализацию функции распечатывания массива.
#include int print_array(double x[], int len) < int k; for(k = 0; k < len; k++) < printf("x[%d] = %lf\n", k, x[k]); >return 0; >
При вызове функции в качестве аргумента нужно передавать имя массива и его длину.
int main() < double points[] = ; int npoints = sizeof(points)/sizeof(points[0]); print_array(points, npoints); return 0; >
Внимание! Если функция print_array изменит значение элемента массива x (например, в цикле будет написано x[k]=0; ), то изменятся значения и в массиве points функции main. Элементы массива при вызове функций не копируются! Функция получает на вход адрес памяти, где записаны элементы массива. Эта память «общая» для вызывающей и вызываемой функции.
Динамические массивы: malloc и free
Статические массивы имеют одно существенное ограничение: размер массива должен быть известен в момент компиляции программы. В большинстве задач размер данных становится известным только в момент выполнения программы. Например, вы написали программу для обработки списка друзей или подписчиков в социальной сети. У одного пользователя друзей мало, а у другого — очень много. Какое значение выбрать для длины массива друзей? 200? 1000? Миллион? Если константа будет очень большой, чтобы «заведомо» (посмотрите как росло число пользователей Интернет) устраивать всех пользователей, то для подавляющего числа пользователей это приведет к излишним затратам памяти. Захотите ли Вы поставить на свой телефон программу, которая при запуске займет всю его память с сообщением: «А вдруг у тебя миллион друзей. Нет? Всего 12?! Неплохо, прямо как у Oушена! А y Трампа миллион. «? [Друзей не должно и не может быть так много, но это к делу не относится.] Чтобы избежать таких ситуаций нужно уметь выделять минимально необходимое количество памяти.
- выделение памяти под массив;
- освобождение памяти, когда она больше не требуется.
Стандартная библиотека языка Си содержит несколько функций для работы с динамической памятью. Нам понадобятся две: malloc (memory allocation — выделение памяти) и free (освобождение). Для использования этих удивительных функций нужно в программе подключить заголовочный файл . Пример программы приведен ниже. Сначала посмотрим, что делают эти функции.
malloc: динамическое выделение памяти
Прототип: void *malloc(size_t size); Параметры: size — беззнаковое целое число, размер запрашиваемой памяти в байтах. Возвращает: Адрес начала выделенной памяти или NULL, если не удалось выделить память. Функция malloc возвращает указатель типа void * — это «абстрактный» указатель на память, который может быть приведен к указателю на любой тип. Функция malloc не может сразу возвращать указатель нужного типа, так как она используется для создания разных массивов, а в прототипе нужно указать конкретный тип возвращаемого значения.
Для выделения памяти под массив из n элементов типа T, где в T могут быть стандартные типы int , double и т.п., необходимо знать размер значения T в байтах. Для определения этой величины в языке Си есть специальный оператор sizeof , который в момент компиляции программы вычисляет нужное значение. Например, массив из n целых чисел будет занимать n*sizeof(int) байт памяти.
Таким образом, для создания динамического массива некоторого типа, например с массива целых чисел, нужно использовать команду вида:
int length; int *points; // . получили значение length (длина массива) points = (int *)malloc(length * sizeof(int));
Если нужен другой тип данных, допустим double , то int заменяется на нужное имя ( double ) в трех местах (кроме первой строки, так как длина массива всегда является целым числом).
free: освобождение памяти
Функция free позволяет освободить область памяти, которая ранее была выделена программе при вызове malloc .
Прототип void free(void *ptr); Параметры: ptr — указатель, который был получен в результат вызова malloc.
В качестве аргумента функции free может использоваться только тот адрес, который был получен в результате вызова malloc. Нельзя создать статический массив и «освободить» его функцией free. Адрес может быть освобожден только один раз. Если два раза подряд вызвать функцию free с одним и тем же аргументом, то это приведет к аварийному завершению программы.
Пример программы с динамическим массивом
В качестве иллюстрации описанных методов рассмотрим программу, которая динамически выделяет память под массив и считывает его.
#include #include int main() < int npoints; double *points; int k; scanf("%d", &npoints); /* npoints получает значение в момент выполнения программы */ points = (double *)malloc(npoints*sizeof(double)); /* Выдели память для хранения npoints элементов, каждый размера sizeof(double) */ if(points == NULL) < printf("Произошла ошибка. Запросили слишком много памяти??\n"); return -1; >/* Считываем данные с использованием адресной арифметики */ k = 0; while(k < npoints && scanf("%lf", points+k) == 1) < k++; >/* Работаем с points как с обычным массивом */ /* Например, вызываем функцию print_array(points, npoints) */ free(points); /* Освободили память */ return 0; >
Функции, которые возвращают массив
Иногда бывает удобно сделать функцию, которая возвращает динамически созданный массив. Примером может служить функция считывания массива из файла. Такая функция может получать на вход файловую переменную ( FILE * ) и должна вернуть в вызывающую функцию массив значений. Например, массив действительнах чисел. Попробуем ее реализовать.
Во-первых, нужно понять, какой прототип должна иметь такая функция. Она должна вернуть два значения: адрес выделенной памяти и длину массива. Как мы уже знаем, несколько значений можно вернуть используя указатели. Длина массива имеет тип int . Значит параметр функции будет иметь тип int * (адрес, по которому нужно записать значение). Массив — это адрес нулевого элемента, то есть double * . Значит параметр будет иметь тип double ** — «указатель на указатель». Мы должны передать адрес (одна звездочка), по которому нужно записать результат вызова malloc, который имеет тип double * . В результате получаем следующий прототип:
int read_array(FILE *input, double **array, int *length);
Собственно возвращаемое значение функции ( int ) может быть кодом ошибки. Если функция вернет 0, то это означает успешное выполнение. Любое ненулевое значение означает ошибку.
Теперь можно рассмотреть структуру тела функции (для наглядности в приведенном ниже коде отсутствуют проверки успешности считывания и корректности данных).
int read_array(FILE *input, double **array, int *length) < double *arr; int arr_length, k; /* Считываем массив: сначала длину, потом элементы */ fscanf("%d", &arr_length); arr = (double *)malloc(arr_length * sizeof(double)); for(k = 0; k < arr_length; k++) fscanf("%lf", arr + k); /* Копируем результат по заданным адресам */ *length = arr_length; *array = arr; return 0; >
16. Методы доступа к элементам массивов
Доступ к элементам массива может производиться двумя различными способами.
Первый способ связан с применением обычных индексных выражений в квадратных скобках, например: array[18] = 3 или array[i + 3] = 9. При данном способе доступа записываются два выражения. Второе выражение должно быть заключено в квадратные скобки. Одно из данных выражений должно являться указателем, а второе – выражением целого типа. Последовательность записи данных выражений может быть произвольной, однако в квадратных скобках следует записывать выражение, следующее вторым. Поэтому записи array[16] и 16[array] будут являться одинаковыми и обозначающими элемент массива с номером шестнадцать. Указатель, который используется в индексном выражении, не всегда является константой, которая указывает на какой-либо массив, это может быть и переменная. Например, после выполнения присваивания ptr = array доступ к шестнадцатому элементу массива можно получить, применяя указатель ptr в форме ptr[16] или 16[ptr].
Второй способ доступа к элементам массива связан с применением адресных выражений и операции раза-дресации в виде *(array+16) = 3 или *(array+i+2) = 7. При данном способе доступа адресное выражение соответствует адресу шестнадцатого элемента массива, тоже может быть записано различными способами: *(array+16) или *(16+array).
При работе на компьютере первый способ приводится ко второму, т. е. индексное выражение становится адресным. Для ранее рассмотренных примеров array[16] и 16[array] преобразуются в *(ar-ray+16).
Для доступа к начальному элементу массива, т. е. к элементу с нулевым индексом, можно применять просто значение указателя array или ptr. Любое из присваиваний
array[0] = 2; *(array+0) = 2; *ptr = 2;
присваивает начальному элементу массива значение 2, но быстрее всего выполнятся присваивания *array = 2 и *ptr = 2, так как в них не требуется выполнять операции сложения.
Данный текст является ознакомительным фрагментом.
Продолжение на ЛитРес
Читайте также
Доступ сразу к нескольким элементам Web-страницы
Доступ сразу к нескольким элементам Web-страницы Зачастую приходится выполнять одинаковые манипуляции не с одним, а сразу с несколькими элементами Web-страницы, соответствующие одному критерию (обычно это селектор CSS).Метод select объекта Ext возвращает экземпляр объекта
Доступ к родительскому, дочерним и соседним элементам Web-страницы
Доступ к родительскому, дочерним и соседним элементам Web-страницы Теперь предположим, что мы наконец-то получили нужный нам элемент Web- страницы и хотим найти его родителя, потомка или «соседей» по уровню вложенности. Для этого Ext Core предоставляет нам множество методов
Обращение к элементам страницы
Обращение к элементам страницы Объект document имеет несколько полезных методов, которые можно использовать в скриптах. Но главное его значение — предоставление доступа к отдельным элементам Web-страницы.Как же можно добраться до отдельного элемента страницы?Прежде всего,
Доступ сразу к нескольким элементам Web-страницы
Доступ сразу к нескольким элементам Web-страницы Зачастую приходится выполнять одинаковые манипуляции не с одним, а сразу с несколькими элементами Web-страницы, соответствующие одному критерию (обычно это селектор CSS).Метод select объекта Ext возвращает экземпляр объекта
Доступ к родительскому, дочерним и соседним элементам Web-страницы
Доступ к родительскому, дочерним и соседним элементам Web-страницы Теперь предположим, что мы наконец-то получили нужный нам элемент Web- страницы и хотим найти его родителя, потомка или «соседей» по уровню вложенности. Для этого Ext Core предоставляет нам множество методов
Присвоение команд элементам меню
Присвоение команд элементам меню Выполните команду Вид ? Инспектор. В правой части рабочего окна появится панель инструментов Инспектор. На вкладке Действие этой панели (рис. 11.13) из раскрывающегося списка Тип действия можно выбрать команды, которые будут присвоены
8.1.2. Доступ к элементам массива и присваивание им значений
8.1.2. Доступ к элементам массива и присваивание им значений Получить ссылку на элемент и присвоить ему значение можно с помощью методов класса [] и []= соответственно. Каждый из них принимает один целочисленный параметр — либо пару целых чисел (начало и конец), либо диапазон.
Получение доступа к устаревшим элементам управления
Получение доступа к устаревшим элементам управления Во-первых, отметим, что устаревшие элементы пользовательского интерфейса, о которых здесь идет речь, остаются пригодными для использования в .NET 2.0, а во-вторых, если вы хотите их использовать, то их можно снова добавить
ДОСТУП К ЭЛЕМЕНТАМ СТРУКТУРЫ
ДОСТУП К ЭЛЕМЕНТАМ СТРУКТУРЫ Структура является разновидностью супермассива, в котором один элемент может быть массивом типа char, следующий — float и еще один int. Обычно можно обращаться к отдельным элементам массива, используя индекс. Как это сделать для отдельных
Разграничение доступа к элементам класса
Разграничение доступа к элементам класса Определив класс, вы можете создавать объекты этого класса и манипулировать ими, используя методы. Некоторые данные и методы, объединенные одним классом, можно сделать недоступными вне реализации класса, к другим можно будет
Разграничение доступа к элементам базового класса
Разграничение доступа к элементам базового класса Мы уже рассказывали, что можно управлять доступом к элементам класса, указывая спецификаторы доступа для элементов класса. Элементы класса, объявленные с спецификаторами protected и private доступны только из методов самого
5. КАНАЛЫ И МЕТОДЫ НЕСАНКЦИОНИРОВАННОГО ДОСТУПА К ИНФОРМАЦИИ
5. КАНАЛЫ И МЕТОДЫ НЕСАНКЦИОНИРОВАННОГО ДОСТУПА К ИНФОРМАЦИИ 5.1. Выявление каналов доступа к информацииДля того чтобы осуществить дестабилизирующее воздействие на конфиденциальную информацию, людям, не имеющим разрешенного доступа к ней, необходимо его получить. Такой
1. Объявление массива
Здесь квадратные скобки являются элементом синтаксиса, а не признаком необязательности конструкции.
Объявление массива может иметь одну из двух синтаксических форм, указанных выше. Квадратные скобки, следующие за именем, – признак того, что переменная является массивом. Константное выражение, заключенное в квадратные скобки определяет число элементов в массиве. Индексация элементов массива в языке C++ начинается с нуля. Таким образом, последний элемент массива имеет индекс на единицу меньше, чем число элементов массива.
Во второй синтаксической форме константное выражение в квадратных скобках опущено. Эта форма может быть использована, если в объявлении массива присутствует инициализатор, либо массив объявляется как формальный параметр функции, либо данное объявление является ссылкой на объявление массива где-то в другом месте программы. Однако для многомерного массива может быть опущена только первая размерность.
Многомерный массив, или массив массивов, объявляется путем задания последовательности константных выражений в квадратных скобках, следующей за именем:
[ ][ ] . ;
Каждое константное выражение определяет количество элементов в данном измерении массива, поэтому объявление двумерного массива содержит два константных выражение, трехмерного – три и т.д.
Массив может состоять из элементов любого типа, кроме типа void и функций, т.е. элементы массива могут иметь базовый, перечислимый, структурный тип, быть объединением, указателем или массивом.
Примеры объявлений массивов
2. Инициализация массивов
Как и простые переменные, массивы могут быть инициализированы при объявлении. Инициализатор для объектов составных типов (каким является массив) состоит из списка инициализаторов, разделенных запятыми и заключенных в фигурные скобки. Каждый инициализатор в списке представляет собой либо константу соответствующего типа, либо, в свою очередь, список инициализаторов. Эта конструкция используется для инициализации многомерных массивов.
Наличие списка инициализаторов в объявлении массива позволяет не указывать число элементов по его первой размерности. В этом случае количество элементов в списке инициализаторов и определяет число элементов по первой размерности массива. Тем самым определяется размер памяти, необходимой для хранения массива. Число элементов по остальным размерностям массива, кроме первой, указывать обязательно.
Если в списке инициализаторов меньше элементов, чем в массиве, то оставшиеся элементы неявно инициализируются нулевыми значениями. Если же число инициализаторов больше, чем требуется, то выдается сообщение об ошибке.
Примеры инициализации массивов
Обратите внимание, что не существует присваивания массиву, соответствующего описанному выше способу инициализации.
3. Работа с массивами
3.1. Доступ к элементу массива
Для доступа к конкретному элементу массива используются так называемые индексные выражения:
[ ]
Здесь квадратные скобки являются требованием синтаксисам языка, а не признаком необязательности конструкции.
Индекс массива может быть не только константой, но и выражением, которое имеет целочисленный тип, например, a [ i + 1] (здесь a должно быть именем ранее объявленного массива, а i – переменной целого типа).
Объявление массива и индексное выражение, используемое для доступа к элементу массива, имеют схожий синтаксис. Различаются они по месту в программе. Это особенно важно, когда мы определяем индекс последнего элемента массива. Как было сказано ранее, индексы элементов массива в языке C начинаются с 0, и номер последнего элемента на 1 меньше количества элементов массива. Поэтому если Вы объявили массив x из 10 элементов, Вы не можете написать индексное выражение x [10], т.к. в этом случае Вы пытаетесь обратиться к элементу с индексом 10, которого нет в Вашем массиве. Компилятор не выдаст сообщения об ошибке, но результаты работы такой программы будут непредсказуемы.
Имя массива является адресом его начала! Оно имеет тип константный указатель на . Конструкция a [ i ] эквивалентна *( a + i ) (см. лекцию 5).
Для многомерного массива надо указать соответствующее количество индексов в квадратных скобках.
3.2. Обработка массивов
Для обработки элементов массива обычно используется оператор пошагового цикла for .
Для обработки многомерного массива используется соответствующее количество циклов.
Массивы не самодостаточны в том смысле, что не гарантируется хранение информации о количестве элементов вместе с самим массивом. В большинстве реализаций С++ отсутствует проверка диапазона индексов для массивов. Таков традиционный низкоуровневый подход к массивам. Более совершенное понятие массива можно реализовать при помощи классов.
В С++ массивы тесно связаны с указателями. Имя массива можно использовать в качестве указателя на его первый элемент. Гарантируется осмысленность значения указателя на элемент, следующий за последним элементом массива. Это важно для многих алгоритмов. Но ввиду того, что такой указатель на самом деле не указывает ни на какой элемент массива, его нельзя использовать ни для чтения, ни для записи. Результат получения адреса элемента массива, предшествующего первому, не определён, и такой операции следует избегать.
Неявное преобразование имени массива в указатель на его первый элемент широко используется в вызовах функций.
Неявное преобразование массива в указатель при вызове функции приводит к потере информации о размере массива. Вызываемая функция должна каким-либо образом определить этот размер, чтобы выполнять осмысленные действия.
При объявлении многомерного массива как параметра функции можно опустить только первую размерность.
Это ограничение при желании можно обойти. Правда, при этом возникают другие проблемы (см. пример 3 в конце лекции).
3.3. Ввод/вывод массивов
В языке C нет возможности вводить и выводить весь массив одним оператором ввода/вывода. Можно вводить и выводить только один элемент массива. Следовательно, для того чтобы ввести весь массив, надо использовать цикл.
Вывод также осуществляется в цикле.
В результате на экране мы увидим примерно следующий текст: a[1] = 4 a[2] = 15 a[3] = -2 .
3.4. Пример 1. Обработка одномерного массива
Даны три массива разной размерности. Определить в каком массиве больше сумма элементов. #include #include const int nmax = 100; int ArrayInput( int *n, double x[], char *fname); // Функция ввода массива из файла double Sum( double x[], int n); // Функция поиска суммы элементов массива void main( int argc, char *argv[]) < double a[nmax], b[nmax], c[nmax]; double sa, sb, sc, max; int na, nb, nc; setlocale(LC_ALL, "rus"); // Меняем кодировку для консольного приложения if (argc < 4) < printf("Недостаточно параметров!\n"); return ; >if (!ArrayInput(&na, a, argv[1])) return ; if (!ArrayInput(&nb, b, argv[2])) return ; if (!ArrayInput(&nc, c, argv[3])) return ; sa = Sum(a, na); sb = Sum(b, nb); sc = Sum(c, nc); max = sa; if (sb > max) max = sb; if (sc > max) max = sc; if (sa == max) printf(«Массив А имеет максимальную сумму элементов: %9.3lf\n», max); if (sb == max) printf(«Массив B имеет максимальную сумму элементов: %9.3lf\n», max); if (sc == max) printf(«Массив C имеет максимальную сумму элементов: %9.3lf\n», max); > double Sum( double x[], int n) < double s = 0; for ( int i = 0; i < n; i++) s += x[i]; return s; >int ArrayInput( int *n, double x[], char *fname) < FILE *file; if ((file = fopen(fname, "r")) == NULL) < printf("Невозможно открыть файл '%s'\n", fname); return 0; >if (fscanf(file, «%d», n) < 1) < printf ("Ошибка чтения из файла '%s'\n", fname); fclose(file); return 0; >if (*n < 0 || *n >nmax) < printf("Кол-во эл-тов массива должно быть от 1 до %d! (файл '%s')\n", nmax, fname); return 0; >for ( int i = 0; i < *n; i++) if (fscanf(file, "%lf", &x[i]) < 1) < printf ("Ошибка чтения из файла '%s'\n", fname); fclose(file); return 0; >fclose(file); return 1; >
3.5. Пример 2. Обработка двумерного массива
Для каждой строки матрицы проверить наличие нулевых элементов.
Первый способ
Второй способ
3.6. Пример 3. Суммирование элементов матрицы
Даны две матрицы разного размера. Функция Sum находит сумму элементов матрицы, не зависимо от того, что матрицы имеют разное количество столбцов. Обратите внимание, что функция будет выдавать корректный результат, только если используются все объявленные элементы матриц. #include #include double Sum( double *x, int m, int n); void main( int argc, char *argv[]) < const int na = 4, mb = 3, nb = 5; double a[na][na], b[mb][nb]; double sa, sb; FILE *file; setlocale(LC_ALL, "rus"); if (argc < 3) < printf("Недостаточно параметров!\n"); return ; >if ((file = fopen(argv[1], «r»)) == NULL) < printf("Невозможно открыть файл '%s'\n", argv[1]); return ; >for ( int i = 0; i < na; i++) for ( int j = 0; j < na; j++) if (fscanf(file, "%lf", &a[i][j]) < 1) < printf ("Ошибка чтения из файла '%s'\n", argv[1]); fclose(file); return ; >fclose(file); if ((file = fopen(argv[2], «r»)) == NULL) < printf("Невозможно открыть файл '%s'\n", argv[2]); return ; >for ( int i = 0; i < mb; i++) for ( int j = 0; j < nb; j++) if (fscanf(file, "%lf", &b[i][j]) < 1) < printf ("Ошибка чтения из файла '%s'\n", argv[2]); fclose(file); return ; >fclose(file); sa = Sum(a[0], na, na); // a[0] — указатель на первую строку матрицы // (и, соответственно, на начало всей матрицы). sb = Sum( reinterpret_cast < double *>(b), mb, nb); // Преобразование без проверки с помощью reinterpret_cast. // Просто b нельзя написать — это вызовет сообщение // о невозможности преобразовать матрицу в указатель. printf(«SumA = %6.2lf\nSumB = %6.2lf\n», sa, sb); > double Sum( double *x, int m, int n) < double s = 0; for ( int i = 0; i < m; i++) for ( int j = 0; j < n; j++) s += x[i * n + j]; return s; > Содержание