plzvtl 57 / 43 / 12 Регистрация: 27.10.2018 Сообщений: 452 |
||||||||||||||||||||
1 |
||||||||||||||||||||
MASM32 Ошибка при адресации элементов массива при адресации03.11.2020, 20:52. Показов 1187. Ответов 10 Метки нет (Все метки)
Задание: вычислить значение заданной функции :
но я немогу понять где ошибка и как это пофиксить, помогите пожалуйста, спасибо заранее.
c++ модуль:
0 |
plzvtl 57 / 43 / 12 Регистрация: 27.10.2018 Сообщений: 452 |
||||
03.11.2020, 21:02 [ТС] |
2 |
|||
дизассемблер и вывод :
Миниатюры
0 |
E=m*c^2 160 / 47 / 10 Регистрация: 04.02.2019 Сообщений: 261 Записей в блоге: 5 |
|
04.11.2020, 08:00 |
3 |
ты изменяешь значения Добавлено через 31 секунду Добавлено через 13 минут
0 |
ФедосеевПавел Модератор 8316 / 4215 / 1602 Регистрация: 01.02.2015 Сообщений: 13,125 Записей в блоге: 4 |
||||
04.11.2020, 09:17 |
4 |
|||
plzvtl, думаю, что требуется пояснение — почему функция заполняет массив. Ещё, хоть и не знаю C++, кажется странной такая передача адреса в функцию
ведь A это динамический массив, т.е. указатель на массив (область памяти), т.е. переменная A содержит адрес на массив. Судя по скриншоту, заполнение массива на ассемблере совпало с заполнением оного на C++ кроме одного элемента. Видимо, одна из веток вычисляется некорректно или ошибка в переходе на другую ветку.
0 |
57 / 43 / 12 Регистрация: 27.10.2018 Сообщений: 452 |
|
04.11.2020, 11:01 [ТС] |
5 |
ты изменяешь значения esi edi, этого делать нельзя Почему нельзя?
ты можешь вернуть из функции сразу double, для этого его нужно оставить на вершине стека сопроцесора, это место передачи результата с плавающей точкой, такоеже как eax для целочисленных результатов. это я знаю, только вернуть мне надо массив чисел. Кроме того размерность массива может изменяться. Вызов асм модуля для каждого числа мне неподходит — сказано сделать 1 вызовом.
х32 зло, переходи на векторный х64 Сменить платформу? Увы немогу.
Ещё, хоть и не знаю C++, кажется странной такая передача адреса в функцию Есть переменная — указатель, я сместил на 0 байт указатель, разыменовал, и после взял адрес от результата операции.
0 |
plzvtl 57 / 43 / 12 Регистрация: 27.10.2018 Сообщений: 452 |
||||
04.11.2020, 11:24 [ТС] |
6 |
|||
Кроме того если я в асм модуль, в начало цикла подсчета вставлю это
то пишет результат как надо. Миниатюры
0 |
57 / 43 / 12 Регистрация: 27.10.2018 Сообщений: 452 |
|
04.11.2020, 11:51 [ТС] |
7 |
Кажется я нашел где ошибка, но как пофиксить все еще не понимаю. Миниатюры
0 |
Constantin Cat 3405 / 1824 / 489 Регистрация: 28.02.2015 Сообщений: 3,699 |
||||||||
04.11.2020, 12:19 |
8 |
|||||||
Почему нельзя? Потому, что используются в вызвающем Вашу вставку коде
Правильно так:
0 |
plzvtl 57 / 43 / 12 Регистрация: 27.10.2018 Сообщений: 452 |
||||||||||||
05.11.2020, 15:53 [ТС] |
9 |
|||||||||||
Constantin Cat, хорошо, я понял. Сделал так: Кликните здесь для просмотра всего текста
обостряю внимание на:
…
Но я уже ничего не понимаю как это
работает: Миниатюры
0 |
ФедосеевПавел Модератор 8316 / 4215 / 1602 Регистрация: 01.02.2015 Сообщений: 13,125 Записей в блоге: 4 |
||||||||||||
05.11.2020, 20:45 |
10 |
|||||||||||
plzvtl, сохраните где-нибудь код и начните заново.
2. далее добавляете условие и какое нибудь действие над элементом массива
И так усложняя понемногу создадите правильный код. Затем определите имена параметров в виде констант
и добавите ручное создание фрейма стека и сохранение регистров.
0 |
plzvtl 57 / 43 / 12 Регистрация: 27.10.2018 Сообщений: 452 |
||||
06.11.2020, 21:03 [ТС] |
11 |
|||
Решил проблему. Добавил строки
в код з поста №1 после строки 78 и строки 53.
1 |
Найти абитурненток, получивших одинаковые оценки по всем предметам, но не набравшим проходного балла р. Найти абитуриентов, имеющих полупроходной балл, при наличии р мест на факультете. Найти пассажиров, вес багажа которых отличается от максимального веса монсе чем нар кг. Найти пассажира, средний вес вещей багажа которого отличается не более чем на р кг от среднего веса вещей пассажиров для каждого рейса. Найти пассажиров, имеющих более р вещей. Найти пассажиров, число вещей которых превосходит среднее число вещей не менее, чем нар штук.
Определить, имеются ли два пассажира, багаж которых совпадает по числу вещей и различается по весу нс более чем на р кг. Выяснить, имеется ли пассажир, багаж которого состоит из р, вещей весом не менее ря кг. Дать сведения о пассажирах, число вещей которых не меньше, чем в любом другом багаже, а вес вещей нс больше, чем в любом другом багаже с этим же числом вещей. Выяснить, имеется ли пассажир„багаж которого превышает багаж каждого из остальных пассажиров и по числу вещей н по ассу. Выяснить, имеются ли в школе однофамильцы. Выяснить, имеются лн однофамильцы в каких-либо параллельных классах. Выяснить, имеются ли однофамильцы в каком-нибудь классе.
43.* Выяснить, в каком классе учится максимальное число учениц. 44. Выяснить, на сколько учеников в р-х классах школы больше, чем в десятых. 45.* Найти среднее число учениц в классах школы. 46.в Найти классы, в которых число учеников больше числа учениц. 47. Найти классы, выпускники которых либо поступили в вузы, либо служат в армии. Литература к заданию ‘Л 1. Кристиан К. Введение в операционную систел!и !Л~ПХ. — Л1: Финансы и стапшспшка, 1985.
Беляков ИН., Рабовер ЮИ., Фридман Л.у! Л!обильная операционная система: Справочник..-Л!.: Радио и связь, 1991. 3. Баурн С. Отрационная систеча БЛ1Х Л1: Мир, 1986. 4. СП Снь!ПРазса!. Краткое руководсгпвоззЗайцев В.Е., Лебедев А В., Сошников Д В. Мл МАИ, 1997 2003. 5. Неясен К., Вирт Н. Паскаль. Руководство пользователя и описание языка.
— М: Финансы и тнатистика, !980, 1989. 6. Абралнзв СА. и др. Задачи по програлсиированию. — Л1: Наука, !988. 7. Пичыциков В.Н. Сборник упралснений по языку Паскаль. -.М: Наука, 1989. 8. Зайцев В Е. и др. СТ)-хреспюлгапшя по к~усу Информатика. М: МАИ 1997 2004. 9. Дейт. Введение в сисгпемы баз данных. — Л1: Наука, 1981. !О. Каймин ВА., Титов В.К., и др. Информатика. Учебное пособие и сборник заоач срешениялш (для школьникое). Л!.: Бридлс, 1994 1!. Технология программирования. 1995, Лй! (март). Журнал для профессиональных про ршилшстов с прилозкениел( С!з-КОЛЛ! (имеется в продазюе в киоске Л!АИ ц. 35 рл), 12. Дейтел 1 .
‘Введение в операционные ситпемы. Т.2. — Мл Мир, 1987. Вопросы для самостоятельного изучения к заданию 17П курсового проекта и к лабораторной работе №21 РАЗРЕЖЕННЫЕ МАТРИЦЫ 1. Представление массивов в памяти ЭВМ. 2. Адрес щия элементов массивов и сс использование для представления структур данных. 3. Передача параметров-массивов и параметров-записей.
4. Ошибки адресации массивов и их последствия при выполнении программ в операционных системах с зшцитой памяти и бсэ нее. 5. Приемы обработки и ввода/вывода массивов на скалярных ЭВМ. 6. Представление обычных и вариантных записей в памяти ЭВМ. 7. Приемы обработки и ввода/вывода записей. 8. Разреженные матрицы. Их представление в памяти ЭВМ. 9.
Особенности хранения в памяти ЭВМ треугольных, симметричных и квазидиагональных матриц. 10. Приемы хранения и обработки разреженньж матриц на языках Паскаль и Си. ОС 1)Ь!1Х. ПРОГРАММИРОВАНИЕ НА ИКЯ. !. Понятие о программировании на ИКЯ (бйсП.Сзйс11, Ьавй, рог!, …). Командные файлы и процедуры. 2. ИКЯ ОС )Лч)1Х (бйе11.
Свйе!1, Ьав1ь )гвйе11, хз1ге!1, …). 3. ИКЯ общего назначения (ЙЕХХ. рег1, …). Основные консгпрукции и приемы програлгиирования на одном из ИКЯ, по выбору преподавателя в группе. 1. Переменные. Присваивание. Понятие окружения. 2. Использование параметров в командных файлах и процедурах. 3. Интерпретация команд. Подстановка переменных и комацд. Встроенные документы. 4. Вычисление выражений. 5. Проверки и ветвления. 6. Циклы. 7.
Оператор выбора Задача Ъ’11. Разреженные матрицы. Составить програгял~у на Паскале (на Си) с процедурами и!или функциями для обработки прлмоу. альных разреженных матриц с элементами целого (группы 1. 2, 3. 9). вещественного (группы 4, 5. 7), или комплексного (группы 6, типов, которая: 1. вводит матрицы различного размера представленные во входном текстовом файле в обычном формате (по строкам). с одновременным размещением ненулевых элементов в разреженной матрице в соответствии с заданной схемой: 2. печатает введенные матрицы во внутреннем представлении согласно заданной схеме ра)мещения и в обычном (естественном) виде; 3.
выполняет необходимыс преобразования разреженных матриц (или вычисления над ними) путем обращения к соответствующим процедурам игили функциям; 4. печатает результат преобразования (вычисления) согласно заданной схеме размещения и в обычном виде. В процедурах и функциях предусмотреть проверки и печать сообщсиий в случаях ошибок в задании параметров. Для отладки использовать матрицы. содержащие 5 — 10% ненулевых элементов с максимальным числом элементов 100.
Вариант схемы размещения матрицы определяется по формуле ((ггг з- 3) шо(1 4) ж 1, где Аг — номер студента по списку в группе. Вариант преобразования определяется по формуле ((Х вЂ” 1) шо(1 11) -г 1. Вариант физического предспаления (1— отображение на массив, 2 — отображение на динамические структуры) определяются по формуле (11 5 х((3+Лз) шо(! 9)) +Л) апой 2+1, где ЛХ вЂ” номер группы. В случае использования динамических структур индексы заменяются соответствующими ссылками. Варианты схемы размещения матрицы: все матрицы т х п хранятся по строите, в порядке возрастания индексов ненулевых элементов.
1. епочка не .левых элементов в некто еЛ со ст очным и екси ованием (индексы в массивеМравны О, если соответствующая строка матрицы содержит только нули) М: Индекс начала 1-ой строки Иипекс начала 3-й … Иннекс начала М-ой в массиве А с окн Ст оки А Помер сголбца Значение Иннекс сленующего !1омер столбца Значение ненулевого элемента этой от окн (илло) Индекс следующего ненулевого элемента этой строки (или 0) 0 Номер Номер Значение Номер Значение ст оки столбца столбца 0 Номер Номер Значение ст оки столбца 0 0 г.тр р: Индекс начали 1-ой строки в массивах Р! и ггЕ Индекс начарла 2-ой Ст ки Индекс начала )г(-ой С ки Р1: Номер Номер Номер Номер 0 столбца столбца столбца Столбца г’Е: Значение Значение Значение Значение р.
~~зн )В: )й’ Аг А1 т’Е: Значение Значение Значение Значение где )мэ = (1 — 1) х и ь) — 1 !!о согласованию с преподавателем возможна модификация схемы хранения, например хранение не исходной, а транспонированной матрицы. Дополнительное задание: реализовать функциональную спецификацию АТД Матрица. Индекс, равный нулю. означжзт отсутствие ненулевых элементов в строке (или в ее остатке). Если матрицы нс изменяются программой, возможна экономия памяти за счет отказа от хранения в массиве й индексов следующего элемента столбца (когда элементы идут подряд).
Вставка и удаление при этом способе возможны, но чересчур дороги: число перестановок элементов составит 0(Ж) вместо О(1). 2. Орю р: Ненулевому элементу соответствуют две ячейки: первая содержит номер столбца, вторая содержит значение элемента. Нуль в первой ячейке означает конец строки, а вторая ячейка содержит в этом случае номер следующей хранимой строки. Нули в обеих гщейках являются признаком конца перечня ненулевых элементов разреженной матрицы. Варианты преобразований: 4. 5.
б. 7. 10. 11. Вопросы для самостоятельного изучения к заданию ь>1П курсового проекта и к лабораторным работам № 23 и 24. 180/1ЕС 9899:1999 Ргойгагппггпй!апйпайея — С [С99! (для изучающих С! 1. 2. 3. 5. 1. 2 3. 5. 6. 7. 8. 9. 10. !!. 13. 14. 15. 16. 17. 18. 19. 20. 2!.
22. 23. 74 Определить максимальный по модулю элемент матрицы и разделить на него все элементы строки, в которой он находится. Если таких элементов несколько. обработать каждую строку, содержащую такой элемент. Определить максимальный по модулю элемент матрицы н рагделнть на него все элементы столбца, в котором он находится. Если таких элементов несколько, обработать предпоследний столбец, содержащий такой элемент.
Найти элемент матрицы. ближайший к заданному значению. Разделить на него элементы строки и столбца, на пересечении которых он расположен. Если таких элементов несколько, обработать все. Умножить разреженную матрицу на вектор-столбец и вычислить количество ненулевых элементов результата. Умножить вектор-строку на разреженную матрицу и вычислить количество ненулевых элементов ре.п льтата. Вычислить сумму двух разреженных матриц. Проверить, не является ли полученная матрица симметричной.
Найти строку, содеряшщую наибольшее количество ненулевых элементов, и напечатать ее номер и сумму элементов этой строки. Если таких строк несколько, обработать все. Вычислить произведение двух разракенных матриц. Проверить, не является зи полученная матрица диагональной. Найти столбец, содсрясащий наибольшее количество ненулевых элементов, и напечатать его номер и произведение элементов этого столбца. Если таких столбцов несколько обработать предпоследний. Вычислить матрочлен — многочлен первой степени от разрехсенной матрицы: (а М м Ь Е), где Š— единичная матрица, а и Ь вЂ” числовые константы. транспонировать разреженную матрицу относительно побочной диагонали.
Почему данная запись не работает?
#include <iostream>
using namespace std;
void foo(int *&pa){}
int main()
{
int a[4]{};
foo(a);
}
А когда я записываю так, то всё работает. В двух случаях же адрес указателя пытаюсь передать (значение указателя), т.е. сам указатель, в функцию, не создавая при этом новый, а просто ссылаясь на тот указатель. Имя массива может же служить указателем на его первый элемент, т.е. может хранить адрес первого элемента массива. Просто в первом случае я пропустил промежуточный указатель, т.к. имя массива тоже может служить им, но программа поломалась. Адрес то тот же передаю в функцию.
#include <iostream>
using namespace std;
void foo(int *&pa){}
int main()
{
int a[4]{};
int *x = a;
foo(x);
}
Кстати, если создать динамический массив int *a = new int[4];
и потом передать a
в функцию, то это тоже будет работать, что логично и работает адекватно.
Интересует именно c++.
Первый и второй принт выдадут разные результаты.
Массив размещается в стеке, там же размещается переменная name, где хранится адрес на первый элемент массива. В конец массива char компилятор добавляет символ с кодом 0x00 — признак конца строки. Именно по этому printf выводит правильное количество символов.
Первый принт выдаст адрес переменной name, а второй — содержимое (адрес символа ‘H’).
Третий же выведет все символы начиная с адреса, хранящегося в переменной name, пока не встретится 0x00.
sizeof выполняется на этапе компиляции, в машинном коде вместо него будут константы.
sizeof(name) вернёт размер указателя, а не размер массива, потому что вы определили не массив, а указатель, и присвоили ему адрес массива.
char name_array[] = "Hello World!"
Вот это уже массив, и sizeoff вернёт его размер, потому что он известен на этапе компиляции.
С указателями можно проводить арифметические операции. К примеру *(name+i) равносильно name[i].
К адресу прибавится не i, а i*sizeof(*name), т.е. размер типа указателя помноженный на число. Получаем i-ый элемент. Об этом позаботится компилятор. Для получения произвольного адреса необходимо привести указатель к типу void.
Массивы не являются указателями. Есть массивы, есть указатели. Есть адреса. Берём адрес массива и сохраняем его в указатель. В свою очередь указатель — это переменная, и она тоже располагается по какому-то адресу. Тут появляются указатели на указатели (иерархия не ограничена). К примеру многомерные массивы.
В языке программирования B, который был непосредственным предшественником C,
указатели и целые числа были свободно взаимозаменяемыми. Система будет вести себя как
хотя вся память была гигантским массивом. Каждое имя переменной имело глобальный
или относительный адрес стека
связанный с ним, для каждого имени переменной единственными вещами, которые должен был отслеживать компилятор, была ли глобальная или локальная переменная и ее адрес относительно первой глобальной или локальной переменной.
Учитывая глобальное объявление, подобное i;
[не нужно было указывать тип, поскольку все было целым числом/указателем], обрабатывалось
компилятор как: address_of_i = next_global++; memory[address_of_i] = 0;
, а инструкция типа i++
будет обрабатываться как: memory[address_of_i] = memory[address_of_i]+1;
.
Объявление типа arr[10];
будет обрабатываться как address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;
. Обратите внимание, что как только эта декларация была обработана, компилятор мог сразу забыть о arr
, являющемся массивом. Оператор типа arr[i]=6;
будет обрабатываться как memory[memory[address_of_a] + memory[address_of_i]] = 6;
. Компилятору все равно, будет ли arr
представлять массив и i
целое число, или наоборот. В самом деле, было бы все равно, являются ли они как массивами, так и целыми числами; он вполне с удовольствием генерирует код, как описано, без учета того, будет ли полученное поведение, вероятно, полезным.
Одна из целей языка программирования C была в значительной степени совместима с B. В B имя массива [называемого «вектором» в терминологии B] идентифицировало переменную, содержащую указатель, который был первоначально назначен чтобы указать на первый элемент распределения заданного размера, поэтому, если это имя появилось в списке аргументов для функции, функция получит указатель на вектор. Несмотря на то, что C добавил «реальные» типы массивов, имя которых было жестко связано с адресом распределения, а не с переменной указателя, которая первоначально указывала бы на распределение, имея массивы, разлагающиеся на код, созданный указателями, который объявлял массив C-типа одинаковым к коду B, который объявил вектор, а затем никогда не изменял переменную, содержащую ее адрес.
Сколько ошибок в таком маленьком коде…
#include <iostream>
а дальше весь код на чистом си. Какой смысл — никакого. Добавляем
#include <stdio.h>
и пишем все на чистом си.
setlocale(LC_ALL, "rus");
это исключительно для старых систем. На нормальных OS это не нужно.
float b[100][100];
запомним — двумерный массив вещественных чисел.
scanf("%d", b+i+j);
а здесь сходу сразу минимум две ошибки (а то и все три).
Первое — раз сказали, что массив вещественных чисел, почему вводим целое? scanf наивный и вводит так как сказали. Какой будет результат — неведомо.
Второе — адресация массива. Так как массив размером 100 на 100, то индексы девяти элементов должны быть 0,1,2,100,101,102,200,201,202. А по этой формуле выходит 0, 4, 8, 4, 8, 12, 8, 12, 16. Но я сознательно сделал умолчал об одном. В первом случае это индексы в массиве. А массив имеет тип вещественного (у Вас). А это скорее всего 4 (а может и 6 или байт. А во втором случае это просто адреса. Переформулирую. Если привести все к адресам в байтах, то для массива целых 4 байтовых адреса будут такими:
0, 4, 8, 400, 404, 408, 800, 804, 808
Поэтому, так как вводим непонятно что, то и вывод обсуждать не имеет смысла.
Итак, мой вариант
#include <stdio.h>
void main() {
int i, j,
n = 3, // строки
m = 3; // столбцы
int b[100][100];
// Создаем матрицу
for (i=0;i<n;i++) {
for (j=0;j<m;j++) {
printf("Введите элемент [%i,%i]: ", i+1, j+1);
scanf("%d", &b[i][j]); //явная, нормальная адресация
}
printf("n");
}
// Выводим матрицу
printf("----------------------------n");
for (i=0;i<n;i++) {
for (j=0;j<m;j++) {
printf("%dt", b[i][j]); // 1
}
printf("n");
}
printf("----------------------------n");
}
О динамических массивах в языке Си: что это такое, виды, как с ними работать
Содержание:
-
Что такое динамический массив
- Указатели для динамических массивов
- Ошибки, возникающие при неверном использовании динамических массивов
- Виды динамических массивов
- Стандартные функции динамического выделения памяти
- Выделение памяти под двумерный массив
- Нестандартные функции динамического выделения памяти
- Перераспределение памяти
Что такое динамический массив
Определение
Массив — последовательная группа ячеек памяти, имеющих одинаковое имя и одинаковый тип. То есть это структуры, которые содержат связанные друг с другом однотипные элементы данных.
Они занимают область в памяти. Компилятор резервирует соответствующий объем памяти для каждого типа и количества элементов каждого массива.
Чтобы зарезервировать память при помощи компилятора для статического массива, необходимо указать ему сколько в нем будет элементов целых чисел. Для этого используется объявление int a [] ;
Осторожно! Если преподаватель обнаружит плагиат в работе, не избежать крупных проблем (вплоть до отчисления). Если нет возможности написать самому, закажите тут.
Пример
Пример объявления для 12 элементов:
int a [12] ;
Для динамического массива количество элементов не указывают.
На этапе написания программы размер динамических массивов неизвестен. Ввод количества элементов осуществляется во время работы программы.
Размером при объявлении динамического массива является числовая переменная, а не константа.
Передача динамических массивов в функцию осуществляют через указание на начало массива указателей. То есть объявляют тип указателя, содержащего данные об адресе соответствующего первого элемента массива.
В динамических массивах также не указывают длину строк и их количество. Границы массивов полностью контролируются разработчиком.
Указатели для динамических массивов
Одним из основных понятий языка Си являются указатели. Они представляют собой переменные, хранящие информацию об адресе какого-либо объекта. Их особенностью является то, что иногда только с их помощью можно выполнить некоторые операции, или записать код более компактно и эффективно, чем при использовании других способов.
В такие переменные чаще всего записывают адрес начального элемента динамического массива, однако возможность записи адресов любых участков памяти также существует.
Действия с указателями
- Объявление.
Указатели:
- int *pI;
- char *pC;
- float *pF;
Чтобы объявить указатель, объявляют тип переменной и ставят перед именем символ *. Указанная переменная должна быть одного типа с указателем, который на ее указывает.
- Присвоение адреса.
Указатели:
- pI = &i;
- int i, *pI;
Адрес присваивается указателю с помощью оператора &. Знак &, стоящий перед переменной, будет указывать на ее адрес.
- Получение значения по этому адресу.
Указатели:
- f = *pF;
- float f, *pF;
Для переменной определенного типа, на которую определен указатель, например, pF, указывается число соответствующего типа. Символ * перед pF означает информацию ячейки, указанной pF.
- Сдвижение.
Указатели:
- int *pI;
- pI ++;
- pI -= 4;
- pI += 4;
Подобные операции позволяют сдвигать значение указателя на то или иное целое число или количество байтов.
- Обнуление.
Указатели:
- char *pC;
- pC = NULL;
Указатель указывает на недействительный адрес, который равен нулю, соответственно, указатель тоже недействительный. Запись по нему не может быть осуществлена.
- Выведение на экран.
Указатели:
- int i, *pI;
- printf(«Адр.i =%p», pI);
- pI = &i;
Для вывода указателей на экран применяют формат %p.
Ошибки, возникающие при неверном использовании динамических массивов
- Запись в чужую область памяти.
Причина: Выделение памяти произошло неудачно, при этом массив используется.
Вывод: необходимо всегда осуществлять проверку указателя на NULL. Если значение указателя будет равно нулю при проверке после выделения памяти, то использование такого массива будет приводить к зависанию компьютера.
- Повторное удаление указателя.
Причина: Массив уже удален и теперь удаляется снова.
Вывод: если массив удален из памяти, необходимо обнулить показатель, так можно быстрее выявить ошибку.
- Выход за границы массива
Причина: В массив записан элемент с отрицательным индексом или индексом, выходящим за границу массива.
- Утечка памяти
Причина: Неиспользуемая память не освобождается. Если это происходит в функции, которая вызывается много раз, то ресурсы памяти скоро будут израсходованы. Вывод: необходимо тщательно проверить код, и удалить артефакты.
Виды динамических массивов
Классификация динамических массивов:
- одномерные: int * A;
- двумерные: int ** A;
Одномерный динамический массив представляет собой множество элементов одного типа данных.
Двухмерный массив имеет более сложную структуру и содержит в качестве элементов массива другие массивы. Каждому хранящемуся в массиве А указателю присвоено не одно значение, а одномерный динамический массив таких значений.
Переменная А сама является указателем и объявляется как указатель на указатель на нужный тип. Данный указатель нужно инициализировать, то есть положить ему адрес массива адресов. В памяти программы должен существовать массив адресов указателей на int и сам массив указателей на int. В этом массиве указателей должны быть разложены адреса, возможно лежащие отдельно друг от друга в памяти.
Пример двумерного массива:
- #include <iostream> // здесь указывают заголовок файла, содержащего функции, переменные, классы, для организации операций ввода-вывода
- using namespace std; // объявление пространства имен для ограничения видимости переменных, функций и т.д.
- int main() {
- setlocale(0, «»); “// указывают тип значения, возвращаемого функцией, и задают локаль для программы
- int **dinamic_array2 = new int* [5];
- for (int i = 0; i < 5; i++) {
- dinamic_array2[i] = new int [i + 1];
- } // Создание двумерного массива
- for (int i = 0; i < 5; i++) {
- cout << «Введите числа» << «(» << i + 1 << «)» << «:»;
- for (int j = 0; j < i + 1; j++) {
- cin >> dinamic_array2[i][j];
- }
- } // заполнение массива
- for (int j = 0; j < i + 1; j++) {
- sum += dinamic_array2[i][j];
- }
- cout << «Сумма » << i + 1 << » массива равна » << sum << endl;
- } // проведение различных операций по вводу-выводу
- for (int i = 0; i < 5; i++) {
- delete [] dinamic_array2[i]; // удаление массива
- }
- system(«pause»);
- return 0;
- }
Строки 5-8 — создание динамического массива.
Строки 9-14 — заполнение.
Строки 15-19 — подсчет, сортировка и вывод на экран суммы всех массивов.
Строки 20-22 — удаление массива.
Стандартные функции динамического выделения памяти
В языке программирования Си имеются стандартные функции управления памятью:
- Malloc (от англ. memory allocation, выделение памяти). Функция malloc возвращает указатель на n байт неинициализированной памяти или NULL, если запрос на память нельзя выполнить: void *malloc(size_t n). Значение, которое возвращает malloc, — это адрес начала выделенной области памяти. При этом гарантируется соответствующее выравнивание этого адреса на границу памяти, обеспечивающую хранение в области любого объекта.
- Calloc (от англ. clear allocation, чистое выделение памяти). Функция calloc возвращает указатель на участок памяти, достаточный для размещения п объектов заданного размера size или NULL, если запрос на память невыполним. Память инициализируется нулями; void *calloc(size_t n, size_t size).
- Realloc (от англ. reallocation, перераспределение памяти). Используется для изменения величины выделенной памяти, на которую указывает ptr, на новую величину, задаваемую параметром newsize. Эта функция может перемещать блок памяти на новое место, в этом случае функция возвращает указатель на новое место в памяти.
- Free (англ. free, освободить). Функция free(p) освобождает участок памяти, на который указывает указатель р, первоначально полученный вызовом функции malloc или calloc. Порядок освобождения выделенных участков памяти не регламентируется. Однако если указатель не был получен с помощью malloc или calloc, то его освобождение является грубой ошибкой. Обращение по указателю после его освобождения — также ошибка. Правильным было бы записать все, что нужно, перед освобождением:
for (p = head; p != NULL; p = q) {
q = p->next;
free(p);
}
Выделение памяти под двумерный массив
Для того, чтобы создать двумерный массив, необходимо осуществить множественные системные вызовы по выделению памяти.
Например, есть указатель А: int ** A ;
Сначала создают массив, состоящий из указателей, которые будут отвечать количеству строк:
A = (int **) malloc (N * sizeof(int *)). Здесь важно указать тип указателя, а не int. Потому что в этом массиве, на который указывает A, будут храниться указатели, а не int.
Далее инициализируют уже сами элементы этого массива указателей путем запуска цикла:
for (int i = 0 ; i < N ; ++i).
Для каждого A[i] проводим инициализацию по отдельности. По сути этого разыменование А со смещением i:
A[i] = (int *) malloc (M * sizeof (int)). Указатели A[i] уже указывают на массив int. Если бы был другой тип, то указывали бы на него.
Для освобождения памяти используют схему: сначала освобождают память для указателей, а потом уже для самого указателя A. То есть массив, который выделил массив указателей, тоже сбрасывают.
int ** A ;
A = (int **) malloc (N * sizeof(int *)).
for (int i = 0 ; i < N ; ++i).
free (A[i]) ;
free (A) ;
Нестандартные функции динамического выделения памяти
Когда требуемый размер массива становится известен, то используют оператор new из языка Си ++, который является расширенной версией языка Си. В скобках указывают размер массива:
pI =new int [N] ;
При отрицательном или нулевом N использовать оператор new нельзя.
При динамическом распределении памяти основными операциями являются new и delete.
Операция new принимает в качестве аргумента тип динамически размещаемого объекта и возвращает на него указатель.
Например, оператор Node *newPtr = new NodeA0); используется для выделения области памяти размером sizeof(Node) байтов, а также его применяют для сохранения указателя на данной области памяти в указателе newPtr.
Если планируется использовать память повторно, после того, как динамический массив в какой-то момент работы программы перестанет быть нужным, то ее освобождают с помощью delete[],
При этом не указывают размерность массива. Операция delete освобождает область памяти, выделенную при помощи оператора new. Таким образом, она возвращает системе эту область памяти для дальнейшего распределения. Чтобы освободить память, которая была динамически выделена предыдущим оператором, применяют оператор delete newPtr;
Иногда выделение памяти под динамические массивы осуществляют с помощью двух языков Си и Си++ одновременно: операции new и функции malloc.
Пример
int n = 10;
int *a = new int[n];
double *b = (double *)malloc(n * sizeof (double));
Во второй строке описан указатель на целую величину. Ему присвоен адрес начала непрерывной области динамической памяти, выделенной с помощью операции new. В зависимости от того, сколько присутствует n величин типа int, столько выделяется памяти для их хранения. Величина n может являться переменной.
В третьей строке применяется функция malloc, которая в данном случае выделяет память под n элементов типа double. Для того, чтобы освободить память, выделенную с помощью функции malloc, применяют функцию free. Память не обнуляется при ее выделении. Динамический массив нельзя инициировать.
Перераспределение памяти
Динамическое перераспределение памяти позволяет создавать и поддерживать динамические структуры данных. Оно дает возможность осуществлять увеличение области памяти в процессе выполнения программы, для того чтобы хранить новые узлы и освобождать ресурсы памяти, в которых уже нет необходимости.
Объем доступной физической памяти или доступной виртуальной памяти в системах с виртуальной памятью определяют пределы динамического выделения памяти. При делении свободной памяти при совместном доступе между многими пользователями эти пределы несколько снижены.
Функция realloc осуществляет перераспределение памяти. Она меняет размер динамически выделяемой области памяти, адресуемой указателем ptr, на новый размер size.
Realloc может возвращать адрес новой области памяти:
realloc(void *ptr, size_t size);
Если ptr равен NULL, то realloc ведет себя подобно функции malloc.
Поведение функции realloc будет неопределенным, если:
- значение ptr не было возвращено функциями calloc, malloc или realloc;
- пространство памяти было освобождено функцией free, и ptr указывают на него.
Size указывают в абсолютном значении.
Важные моменты в перераспределении памяти с помощью функции realloc:
- Если существующее пространство меньше по размеру, чем size, то в конце будет выделяться новое неинициализированное непрерывное пространство, при этом предыдущие данные пространства будут сохранены.
- Значение, равное NULL, будет возвращено, если realloc не может выделить запрашиваемое пространство, а в указанном ptr пространстве содержимое остается нетронутым.
- Realloc будет действовать, как функция free, если size=0, а ptr не равен NULL.
- Если первым параметром функции realloc использовать NULL, то можно получить эффект, который достигается с помощью функции malloc с тем же значением size.
Новое пространство может начинаться с отличного от заданного адреса, независимо от того, в какой момент функция realloc изменяла пространство, даже если она усекала память. Поэтому указатели могут адресоваться к перемещаемому пространству, что необходимо учитывать при разработке программы.
Например, при создании связанного списка linked list и применения realloc, чтобы выделить для цепочки пространство памяти, может оказаться, что оно будет перемещаться. В таком случае, указатели будут адресованы не в место их расположения в настоящий момент, а в то место, где были размещены последовательные звенья цепочки.
Пример
Пример использования функции realloc:
p2 = realloc(p1, new_size);
if (p2 1= NULL) p1 = p2;
Необходимо учитывать, что realloc работает с ранее выделенной памятью и старыми строками. Добавление новых строк и выделение памяти осуществляется посредством функций calloc или malloc.