Выберите правильный вариант выделения динамической памяти под переменную x типа float
Теги: Си память, malloc, calloc, realloc, free, Ошибки выделения памяти, Висячие указатели, Динамические массивы, Многомерные динамические массивы.
Ошибки при выделении памяти
1. Бывает ситуация, при которой память не может быть выделена. В этом случае функция malloc (и calloc) возвращает NULL. Поэтому, перед выделением памяти необходимо обнулить указатель, а после выделения проверить, не равен ли он NULL. Так же ведёт себя и realloc. Когда мы используем функцию free проверять на NULL нет необходимости, так как согласно документации free(NULL) не производит никаких действий. Применительно к последнему примеру:
Хотелось бы добавить, что ошибки выделения памяти могут случиться, и просто выходить из приложения и выкидывать ошибку плохо. Решение зависит от ситуации. Например, если не хватает памяти, то можно подождать некоторое время и после этого опять попытаться выделить память, или использовать для временного хранения файл и переместить туда часть объектов. Или выполнить очистку, сократив используемую память и удалив ненужные объекты.
2. Изменение указателя, который хранит адрес выделенной области памяти. Как уже упоминалось выше, в выделенной области хранятся данные об объекте - его размер. При удалении free получает эту информацию. Однако, если мы изменили указатель, то удаление приведёт к ошибке, например
Таким образом, если указатель хранит адрес, то его не нужно изменять. Для работы лучше создать дополнительную переменную указатель, с которой работать дальше.
3. Использование освобождённой области. Почему это работает в си, описано выше. Эта ошибка выливается в другую – так называемые висячие указатели (dangling pointers или wild pointers). Вы удаляете объект, но при этом забываете изменить значение указателя на NULL. В итоге, он хранит адрес области памяти, которой уже нельзя воспользоваться, при этом проверить, валидная эта область или нет, у нас нет возможности.
Эта программа отработает и выведет мусор, или не мусор, или не выведет. Поведение не определено.
Если же мы напишем
то программа выкинет исключение. Это определённо лучше, чем неопределённое поведение. Если вы освобождаете память и используете указатель в дальнейшем, то обязательно обнулите его.
4. Освобождение освобождённой памяти. Пример
Здесь дважды вызывается free для переменной a. При этом, переменная a продолжает хранить адрес, который может далее быть передан кому-нибудь для использования. Решение здесь такое же как и раньше - обнулить указатель явно после удаления:
5. Одновременная работа с двумя указателями на одну область памяти. Пусть, например, у нас два указателя p1 и p2. Если под первый указатель была выделена память, то второй указатель может запросто скомпрометировать эту область:
Рассмотрим код ещё раз.
Теперь оба указателя хранят один адрес.
А вот здесь происходит непредвиденное. Мы решили выделить под p2 новый участок памяти. realloc гарантирует сохранение контента, но вот сам указатель p1 может перестать быть валидным. Есть разные ситуации. Во-первых, вызов malloc мог выделить много памяти, часть которой не используется. После вызова ничего не поменяется и p1 продолжит оставаться валидным. Если же потребовалось перемещение объекта, то p1 может указывать на невалидный адрес (именно это с большой вероятностью и произойдёт в нашем случае). Тогда p1 выведет мусор (или же произойдёт ошибка, если p1 полезет в недоступную память), в то время как p2 выведет старое содержимое p1. В этом случае поведение не определено.
Два указателя на одну область памяти это вообще-то не ошибка. Бывают ситуации, когда без них не обойтись. Но это очередное минное поле для программиста.
calloc
Ф ункция calloc выделяет n объектов размером m и заполняет их нулями. Обычно она используется для выделения памяти под массивы. Синтаксис
Табличный вывод
При указании формата можно явным образом указать общее количество знакомест и количество знакомест, занимаемых дробной частью:
Результат выполнения
В приведенном примере 10 — общее количество знакомест, отводимое под значение переменной; 5 — количество позиций после разделителя целой и дробной части (после десятичной точки). В указанном примере количество знакомест в выводимом числе меньше 10, поэтому свободные знакоместа слева от числа заполняются пробелами. Такой способ форматирования часто используется для построения таблиц.
Ввод информации
Функция форматированного ввода данных с клавиатуры scanf() выполняет чтение данных, вводимых с клавиатуры, преобразует их во внутренний формат и передает вызывающей функции. При этом программист задает правила интерпретации входных данных с помощью спецификаций форматной строки.
Общая форма записи функции scanf( ) :
Строка форматов аналогична функции printf() .
Для формирования адреса переменной используется символ амперсанд ‘&’ :
адрес = &объект
Строка форматов и список аргументов для функции обязательны.
Результат работы программы:
Функция scanf( ) является функцией незащищенного ввода, т.к. появилась она в ранних версиях языка Си. Поэтому чтобы разрешить работу данной функции в современных компиляторах необходимо в начало программы добавить строчку
Другой вариант — воспользоваться функцией защищенного ввода scanf_s( ) , которая появилась несколько позже, но содержит тот же самый список параметров.
Примеры
1. Простое скользящее среднее равно среднему арифметическому функции за период n. Пусть у нас имеется ряд измерений значения функции. Часто эти измерения из-за погрешности "плавают" или на них присутствуют высокочастотные колебания. Мы хотим сгладить ряд, для того, чтобы избавиться от этих помех, или для того, чтобы выявить общий тренд. Самый простой способ: взять n элементов ряда и получить их среднее арифметическое. n в данном случае - это период простого скользящего среднего. Так как мы берём n элементов для нахождения среднего, то в результирующем массиве будет на n чисел меньше.
Это простой пример. Большая его часть связана со считыванием данных, вычисление среднего всего в девяти строчках.
2. Сортировка двумерного массива. Самый простой способ сортировки - перевести двумерный массив MxN в одномерный размером M*N, после чего отсортировать одномерный массив, а затем заполнить двумерный массив отсортированными данными. Чтобы не тратить место под новый массив, мы поступим по-другому: если проходить по всем элементам массива k от 0 до M*N, то индексы текущего элемента можно найти следующим образом:
j = k / N;
i = k - j*M;
Заполним массив случайными числами и отсортируем
3. Бином Ньютона. Создадим треугольную матрицу и заполним биномиальными коэффициентами
Если Вы желаете изучать этот материал с преподавателем, советую обратиться к репетитору по информатике
Всё ещё не понятно? – пиши вопросы на ящик
1. Функция вычисляет произведение двух чисел. Исходные данные вводятся с клавиатуры. Какие проверки целесообразно ввести в программе:
а) проверка, что исходные данные являются числами +
б) проверки не нужны, все возможные ошибки отловит компилятор
в) проверка исходных данных на равенство нулю
2. Для чего предназначен оператор namespace:
а) для использования классов, переменных и функций из других модулей программы без использования заголовочных файлов
б) для заключения в группу объявлений классов, переменных и функций в отдельный контекст со своим именем +
в) для заключения в группу объявлений классов, переменных и функций для использования только в текущем модуле
3. Какой из компонентов может входить в интегрированную среду программирования:
а) наладчик
б) доводчик
в) отладчик +
4. Какой из компонентов может входить в интегрированную среду программирования:
а) текстовый редактор +
б) текстовый директор
в) текстовый модератор
5. Какой из компонентов может входить в интегрированную среду программирования:
а) регулятор
б) доминатор
в) компилятор +
6. Если определена операция вычитания для двух объектов класса A, а операция преобразования к int не определена, что будет вызвано при:
A a1,a2,a3=5;
a3 = a1 – a2;
а) только операция вычитания
б) произойдет ошибка +
в) преобразование к целому
7. Какой из наборов перечисляемых значений записан правильно:
а) enum < a, b = 3, c = 4, 3 >;
б) enum < a, b, 3, 4 >;
в) enum ; +
9. Чему будет равен результат вычисления выражения: int d=5; bool b = true, c; c = (!b||(d>3)):
а) Ошибка компилятора
б) false
в) true +
10. Если в арифметическом выражении участвуют целый и вещественный операнды, то:
а) ошибка компиляции
б) целый тип приводится к вещественному +
в) вещественный тип приводится к целому
11. Укажите в каком выражении произойдет потеря точности:
а) int i; float x = 2.134, y = 3.14; i = x/y; +
б) short i = 0x3; float x = 2.7, v; v = i + x;
в) float M = 235.2; double Z = 3; Z *= M;
12. Если после выражения стоит точка с запятой, то:
а) выражение вычисляется, а его значение запоминается в специальной переменной, которую можно использовать в следующем операторе
б) это оператор-выражение, действие которого заключается в вычислении выражения +
в) выражение вычисляется только если первой стоит операция присваивания
13. Что из себя представляет динамическое выделение памяти:
а) память под объект (переменную) может выделяться не сразу, а в процессе работы программы, освобождение памяти производится вручную +
б) память под объект (переменную) может выделяться не сразу, а в процессе работы программы, освобождение памяти производится автоматически после завершения программы
в) память под объект (переменную) выделяется каждый раз при обращении к переменной
14. Отметьте истинное высказывание:
а) переменная инициализируется, потом объявляется
б) переменная объявляется, потом инициализируется и изменяется
в) переменная объявляется, потом изменяется +
15. Какие операции поддаются перегрузке:
а) унарные и бинарные +
б) только бинарные
в) только унарные
16. Переменная типа signed char может принимать значения:
а) только символов английского алфавита, цифр и символа подчеркивания
б) из первой половины кодовой таблицы +
в) только из алфавита языка C++
17. Переменная типа signed char может принимать значения:
а) только из алфавита языка C++
б) только символов английского алфавита, цифр и символа подчеркивания
в) от -128 до 127 +
18. В переменной типа unsigned char можно хранить число:
а) -213
б) 213 +
в) 1213
19. В переменной типа unsigned char можно хранить число:
а) -13
б) 1213
в) 13 +
20. Чему равно числовое значение выражения e/2*a-abs(e)*1e0 при e = 4, a = 2:
а) 3
б) 0 +
в) 1
21. Выберите правильное утверждение:
а) целой переменной можно присвоить вещественную константу +
б) целой константе можно присвоить целую переменную
в) целой константе можно присвоить вещественную переменную
22. Выберите правильное утверждение:
а) целой константе можно присвоить целую переменную
б) целой переменной можно присвоить целую константу +
в) целой константе можно присвоить вещественную переменную
23. Чему равно значение выражения (a && ! b || c), где a, b и с -величины типа bool, имеющие значения false, true и true соответственно:
а) false
б) yes
в) true +
24. Какое выражение не содержат синтаксических ошибок:
а) sin(abs(0.6(e*3))
б) ((cos(3*a+1.*abs(x)))) +
в) a*exp(t)\(2t)
25. Какое выражение не содержат синтаксических ошибок:
а) a*exp(t)\(2t)
б) sin(abs(0.6(e*3))
в) 0XCC00*.34E-4/_do/k-2 +
26. Чему равно числовое значение выражения sqrt(4)+142/20*2:
а) 5
б) 16 +
в) 9
27. Какая из следующих операций языка C выполняется справа налево:
а) = +
б) ->
в) []
28. Какое выражение не содержит синтаксических ошибок:
а) (-0.18)*a)/(r-0.2*t)
б) (-0.18)*a)\(r-0.2*t))
в) -0.18*a/r-0.2*t +
29. При использовании в программе функции scanf требуется разделять числовые значения величин при вводе при помощи:
а) нажатия клавиш TAB или ENTER +
б) клавиш курсора
в) пробела или запятой
30. При использовании в программе функции scanf требуется разделять числовые значения величин при вводе при помощи:
а) пробела или запятой
б) по крайней мере одного пробела +
в) клавиш курсора
Очень часто возникают задачи обработки массивов данных, размерность которых заранее неизвестна. В этом случае возможно использование одного из двух подходов:
- выделение памяти под статический массив, содержащий максимально возможное число элементов, однако в этом случае память расходуется не рационально;
- динамическое выделение памяти для хранение массива данных.
Для использования функций динамического выделения памяти необходимо описать указатель, представляющий собой начальный адрес хранения элементов массива.
Начальный адрес статического массива определяется компилятором в момент его объявления и не может быть изменен.
Для динамического массива начальный адрес присваивается объявленному указателю на массив в процессе выполнения программы.
Различные аргументы realloc и malloc.
При вызове функции malloc, realloc и calloc с нулевым размером поведение не определено. Это значит, что может быть возвращён как NULL, так и реальный адрес. Им можно пользоваться, но к нему нельзя применять операцию разадресации.
Вызов realloc(NULL, size_t) эквиваленте вызову malloc(size_t).
Однако, вызов realloc(NULL, 0) не эквивалентен вызову malloc(0) :) Понимайте это, как хотите.
Вывод информации
Функция printf() предназначена для форматированного вывода. Она переводит данные в символьное представление и выводит полученные изображения символов на экран. При этом у программиста имеется возможность форматировать данные, то есть влиять на их представление
на экране.
Общая форма записи функции printf() :
СтрокаФорматов состоит из следующих элементов:
- управляющих символов;
- текста, представленного для непосредственного вывода;
- форматов, предназначенных для вывода значений переменных различных типов.
Объекты могут отсутствовать.
Управляющие символы не выводятся на экран, а управляют расположением выводимых символов. Отличительной чертой управляющего символа является наличие обратного слэша ‘\’ перед ним.
Основные управляющие символы:
- ‘\n’ — перевод строки;
- ‘\t’ — горизонтальная табуляция;
- ‘\v’ — вертикальная табуляция;
- ‘\b’ — возврат на символ;
- ‘\r’ — возврат на начало строки;
- ‘\a’ — звуковой сигнал.
Форматы нужны для того, чтобы указывать вид, в котором информация будет выведена на экран. Отличительной чертой формата является наличие символа процент ‘%’ перед ним:
- %d — целое число типа int со знаком в десятичной системе счисления;
- %u — целое число типа unsigned int ;
- %x — целое число типа int со знаком в шестнадцатеричной системе счисления;
- %o — целое число типа int со знаком в восьмеричной системе счисления;
- %hd — целое число типа short со знаком в десятичной системе счисления;
- %hu — целое число типа unsigned short ;
- %hx — целое число типа short со знаком в шестнадцатеричной системе счисления;
- %ld — целое число типа long int со знаком в десятичной системе счисления;
- %lu — целое число типа unsigned long int ;
- %lx — целое число типа long int со знаком в шестнадцатеричной системе счисления;
- %f — вещественный формат (числа с плавающей точкой типа float );
- %lf — вещественный формат двойной точности (числа с плавающей точкой типа double );
- %e — вещественный формат в экспоненциальной форме (числа с плавающей точкой типа float в экспоненциальной форме);
- %c — символьный формат;
- %s — строковый формат.
Строка форматов содержит форматы для вывода значений. Каждый формат вывода начинается с символа % . После строки форматов через запятую указываются имена переменных, которые необходимо вывести.
Количество символов % в строке формата должно совпадать с количеством переменных для вывода. Тип каждого формата должен совпадать с типом переменной, которая будет выводиться на это место. Замещение форматов вывода значениями переменных происходит в порядке их следования.
Пример на Си
Результат работы программы
Тот же самый код может быть представлен с использованием одного вызова printf :
realloc
Е щё одна важная функция – realloc (re-allocation). Она позволяет изменить размер ранее выделенной памяти и получает в качестве аргументов старый указатель и новый размер памяти в байтах:
Функция realloc может как использовать ранее выделенный участок памяти, так и новый. При этом не важно, меньше или больше новый размер – менеджер памяти сам решает, где выделять память.
Пример – пользователь вводит слова. Для начала выделяем под слова массив размером 10. Если пользователь ввёл больше слов, то изменяем его размер, чтобы хватило места. Когда пользователь вводит слово end, прекращаем ввод и выводим на печать все слова.
Хочу обратить внимание, что мы при выделении памяти пишем sizeof(char*), потому что размер указателя на char не равен одному байту, как размер переменной типа char.
Динамическое выделение памяти для двумерных массивов
Пусть требуется разместить в динамической памяти матрицу, содержащую n строк и m столбцов. Двумерная матрица будет располагаться в оперативной памяти в форме ленты, состоящей из элементов строк. При этом индекс любого элемента двумерной матрицы можно получить по формуле
index = i*m+j;
где i - номер текущей строки; j - номер текущего столбца.
Рассмотрим матрицу 3x4 (см. рис.)
Индекс выделенного элемента определится как
index = 1*4+2=6
Объем памяти, требуемый для размещения двумерного массива, определится как
n·m·(размер элемента)
Однако поскольку при таком объявлении компилятору явно не указывается количество элементов в строке и столбце двумерного массива, традиционное обращение к элементу путем указания индекса строки и индекса столбца является некорректным:
Правильное обращение к элементу с использованием указателя будет выглядеть как
- p - указатель на массив,
- m - количество столбцов,
- i - индекс строки,
- j - индекс столбца.
Пример на Си Ввод и вывод значений динамического двумерного массива
Результат выполнения
Возможен также другой способ динамического выделения памяти под двумерный массив - с использованием массива указателей. Для этого необходимо:
- выделить блок оперативной памяти под массив указателей;
- выделить блоки оперативной памяти под одномерные массивы, представляющие собой строки искомой матрицы;
- записать адреса строк в массив указателей.
Графически такой способ выделения памяти можно представить следующим образом.
При таком способе выделения памяти компилятору явно указано количество строк и количество столбцов в массиве.
Пример на Си
Результат выполнения программы аналогичен предыдущему случаю.
С помощью динамического выделения памяти под указатели строк можно размещать свободные массивы. Свободным называется двухмерный массив (матрица), размер строк которого может быть различным. Преимущество использования свободного массива заключается в том, что не требуется отводить память компьютера с запасом для размещения строки максимально возможной длины. Фактически свободный массив представляет собой одномерный массив указателей на одномерные массивы данных.
Для размещения в оперативной памяти матрицы со строками разной длины необходимо ввести дополнительный массив m , в котором будут храниться размеры строк.
Пример на Си : Свободный массив
Работа с двумерными и многомерными массивами
Д ля динамического создания двумерного массива сначала необходимо создать массив указателей, после чего каждому из элементов этого массива присвоить адрес нового массива.
Для удаления массива необходимо повторить операцию в обратном порядке - удалить сначала подмассивы, а потом и сам массив указателей.
- 1. Создавать массивы "неправильной формы", то есть массив строк, каждая из которых имеет свой размер.
- 2. Работать по отдельности с каждой строкой массива: освобождать память или изменять размер строки.
Создадим "треугольный" массив и заполним его значениями
Чтобы создать трёхмерный массив, по аналогии, необходимо сначала определить указатель на указатель на указатель, после чего выделить память под массив указателей на указатель, после чего проинициализировать каждый из массивов и т.д.
Освобождение памяти с помощью free
Т еперь рассмотри, как происходит освобождение памяти. Переменная указатель хранит адрес области памяти, начиная с которого она может им пользоваться. Однако, она не хранит размера этой области. Откуда тогда функция free знает, сколько памяти необходимо освободить?
- 1. Можно создать карту, в которой будет храниться размер выделенного участка. Каждый раз при освобождении памяти компьютер будет обращаться к этим данным и получать нужную информацию.
- 2. Второе решение более распространено. Информация о размере хранится на куче до самих данных. Таким образом, при выделении памяти резервируется места больше и туда записывается информация о выделенном участке. При освобождении памяти функция free "подсматривает", сколько памяти необходимо удалить.
Перераспределение памяти
Если размер выделяемой памяти нельзя задать заранее, например при вводе последовательности значений до определенной команды, то для увеличения размера массива при вводе следующего значения необходимо выполнить следующие действия:
- Выделить блок памяти размерности n+1 (на 1 больше текущего размера массива)
- Скопировать все значения, хранящиеся в массиве во вновь выделенную область памяти
- Освободить память, выделенную ранее для хранения массива
- Переместить указатель начала массива на начало вновь выделенной области памяти
- Дополнить массив последним введенным значением
Все перечисленные выше действия (кроме последнего) выполняет функция
- ptr - указатель на блок ранее выделенной памяти функциями malloc() , calloc() или realloc() для перемещения в новое место. Если этот параметр равен NULL , то выделяется новый блок, и функция возвращает на него указатель.
- size - новый размер, в байтах, выделяемого блока памяти. Если size = 0 , ранее выделенная память освобождается и функция возвращает нулевой указатель, ptr устанавливается в NULL .
Размер блока памяти, на который ссылается параметр ptr изменяется на size байтов. Блок памяти может уменьшаться или увеличиваться в размере. Содержимое блока памяти сохраняется даже если новый блок имеет меньший размер, чем старый. Но отбрасываются те данные, которые выходят за рамки нового блока. Если новый блок памяти больше старого, то содержимое вновь выделенной памяти будет неопределенным.
Пример на Си Выделить память для ввода массива целых чисел. После ввода каждого значения задавать вопрос о вводе следующего значения.
С++ использует новые методы работы с динамической памятью при помощи операторов new и delete :
- new — для выделения памяти;
- delete — для освобождения памяти.
Оператор new используется в следующих формах:
- new тип; — для переменных
- new тип[размер]; — для массивов
Память может быть распределена для одного объекта или для массива любого типа, в том числе типа, определенного пользователем. Результатом выполнения операции new будет указатель на отведенную память, или исключение std::bad_alloc в случае ошибки.
int *ptr_i;
double *ptr_d;
struct person *human;
…
ptr_i = new int ;
ptr_d = new double [10];
human = new struct person;
Память, отведенная в результате выполнения new , будет считаться распределенной до тех пор, пока не будет выполнена операция delete .
Освобождение памяти связано с тем, как выделялась память – для одного элемента или для нескольких. В соответствии с этим существует и две формы применения delete :
- delete указатель; — для одного элемента
- delete[] указатель; — для массивов
Например, для приведенного выше случая, освободить память необходимо следующим образом:
Освобождаться с помощью delete может только память, выделенная оператором new .
Пример Создание динамического массива
Указатель dan – базовый адрес динамически распределяемого массива, число элементов которого равно size . Операцией delete освобождается память, распределенная при помощи new .
Пример неудачного выделения памяти (в случае очень большого требуемого объема):
Зачем вы вводите людей в заблуждение? Сами не разбираетесь в вопросе и ещё чему-то других учите. Оператор new при обычном использовании __никогда__ не возвращает нулевой указатель, чтобы заставить оператор new вернуть NULL требуется другой синтаксис вызова, но это явно не ваш случай. Учите матчасть и морочьте людям голову.
Можно ли изменить размер выделенной памяти по new? Для malloc есть realloc, а для new ничего подобного нет :-(
Можно попробовать через обработку исключений. Но я, честно сказать, не знаю, как заставить память не выделиться.
Эм, да очень просто! Попробуйте выделить памяти у системы на гигов 5. И система вернёт не указатель на участок памяти, а NULL. На равенство указателя этому NULL и можно определить, выделилась память или нет.
Не верно. Запроси ты у системы хоть 20 Гб, она не выделит (см.виртуальная память, система подкачки памяти). Но даже если системы не сможет выделить память для процесса, она бросит badalloc. Система не может вернуть нуль-указатель. Если было бы так, то это спровоцировало бы undefined behaviour.
Елена, скажите, а если динамическая память выделяется в функции, очищать её где надо там же в функции или в int main() ?
Зависит от того, будете ли Вы ее в main использовать. Например, если Вам в функции нужно сформировать массив, и указатель на него вернуть в main() , чтоб потом с ним работать, очевидно очищать память нужно в main .
А если это вспомогательный массив был, который после выхода из функции больше не нужен, то память нужно очистить в функции.
Все решается, может неоптимально по скорости, зато неоправданно память не загаживаем. А то современные программы привыкли оперативку жрать тоннами )))
Возводить в степень можно гораздо быстрее, чем за n умножений! Для этого нужно воспользоваться следующими рекуррентными соотношениями: an = (a2)n/2 при четном n, an = a × an−1 при нечетном n. Реализуйте алгоритм быстрого возведения в степень с помощью рекурсивной функции. Формат входных данных Вводятся действительное число a и целое неотрицательное число n. Формат выходных данных
Выведите ответ на задачу. Но следующая задача которую надо решить тоже с помощью рекурсии это найти все возможные расстановки ферзей.
Дано число N. Определите, сколькими способами можно расставить на доске N×N N ферзей, не бьющих друг друга.
Вот у меня стоит задача создать функцию, которая будет динамически увеличивать размер строки. Но вот засада, данный код принимает размер строки длинною только в 4094 символа. Почему так происходит? И как это можно обойти?
int main()
char *s;
s= getline();
cout >
// Принимает строку, размер этой строки, новый размер строки, возвращает указатель на новую строку.
char *resize( const char *str, unsigned size, unsigned new_size)
char *ns = new char [ sizeof ( char )*new_size];
for ( unsigned i=0;i
>
delete []str;
return ns;
>
// Получаем строку и увеличиваем ее понеобходимости.
char *getline() //
unsigned j=1; //размер массива
char *temp= new char [j];
char c;
while (std::cin.get(c) && c != '\n' && c != std::cin.eof())
temp[j-1]=c;
temp=resize (temp,j,++j);
>
temp[j-1]='\0';
return temp;
>
Честно говоря, не сталкивалась с подобной проблемой, но она действительно имеет место. Может быть, можно ввести данные в несколько строк?
А вот с этой задачей, видимо предполагается использование динамического программирования. 1,000001 * 1,000001 = 1,000002000001 1,000002000001 * 1,000001 = 1,000003000003000001 и т.д. То есть надо выявить закономерность, как изменяется число при умножении его на 1,000001 и в соответствии с этим сформировать результат. P.S. В результате получится число e.
Так рекурсивная функция должна правильно обработать все возможные возведения в степень, не только 1.000001 в степени 1000000 да и на третем шаге число 1,000003000003000001 уже выйдет за пределы всего что только можно. a^n = (a^2)^(n/2) при четном n, a^n = a × a^(n−1) при нечетном n. Рекурсию то я создал. Но она не может уйти в глубину на 1.000.000 Максимум можно уйти в глубь около 86.674 раза. А по условию задачи необходимо что бы рекурсия была способна войти вглубь на 1.000.000.
PS в 50-60% случаях при вооде защитного кода графическая надпись не обновляется, поэтому происходит неверный ввод кода. И после неверного ввода весь текст что был введен пропадает, очень обидно. То есть можно зайти на одну и туже страницу 10 раз и всегда будет одно и тоже графическое число например: "Шесть тысяч восемьсот восемьдесят два". Но данный код неверный. Перезагрузка страницы F5 не помогает.
А как предлагает решать задачу тот, кто условие придумал? P.S. Отключила на время кэширование сайта, чтобы избежать проблемы с капчей. Посмотрим, будет лучше или хуже.
Знаю что не актуально, но может кто будет это читать
temp=resize (temp,j,++j); - здесь ошибка.
j - в данном случае будет сначала инкрементироваться, а затем отправляться. в итоге будет нечно вроде такого j = 1
resize (temp,j,++j) - вид вызова будет такой resize (temp,2,2)
для решения необходимо вызывать так
resize (temp,j - 1,++j)
или так
resize (temp,j,j++ + 1)
или в две строки
resize (temp,j,j + 1)
j++
А реально ли создать или написать чтобы выложить в интернет чтобы все скачивали и пользовались что я создам на языке c++?
int main() <
do <
system( "CLS" );
//creating the dynamic array[0][0] with only one free space
float **a = ( float **)malloc( sizeof ( float *));
a[0] = ( float *)malloc( sizeof ( float ));
int volume = 0, i = 0; //volume contains used space(bytes) in array, ′i′ need for counting below
do <
cout do <
cin >> a[i][_msize(a[i]) / sizeof ( float ) — 1]; //the ′j′ is memory of *p divided by 4, for first one 4/4 = 1, next 8/4 and etc
if (cin.rdbuf()->in_avail() != 1) a[i] = ( float *)realloc(a[i], _msize(a[i]) + sizeof ( float )); //realocates memory only if buffer contains more then 1 number in stack
> while (cin.get() != EOS); //while not in the end of buffer >′cin′
volume += _msize(a[i]); //increases volume 4/8/16 and etc
a = ( float **)realloc(a, _msize(a) * sizeof ( float *)); //reallocates new pointer
a[i + 1] = ( float *)malloc( sizeof ( float )); //allocates memory for new p[]
i++;
> while (_getch() != ESC); //while you dont press ESC
cout volume /= _msize(a[0]); //strings in array calc
//output on screen
for ( int i = 0; i < volume; i++) <
for ( int j = _msize(a[i]) / sizeof ( float ); j > 0; j—) < //′j′ is a amount of numbers in string, 1 1 1 1 all size is 16, divided by 4 = 4
cout >
cout free(a[i]); //frees memory in *p
>
free(a); //frees memory in **p
> while (_getch() != ESC); //while you dont press ESC
return 0;
>
Вот тут в прототипе все работает корректно. Однако при попытке создании структуры что-то пошло не так.
typedef struct <
float **matrix;
>matrix;
A.matrix = ( float **)malloc( sizeof ( float ));
A.matrix[0] = ( float *)malloc( sizeof ( float ));
Борис, 1) В функции input_matrix(matrix m) Вы работаете с копией матрицы, а не с ее оригиналом, поэтому после возврата из функции введенные данные недоступны. Один из способов решения этой проблемы:
2) Я не поняла назначение цикла do. while в main(), но у меня он работать отказывается. 3) Очистка памяти в конце функции main() производится неверно. Один из вариантов очистки памяти
int h = A.volume / _msize(A.matrix[0]);
// это вычисление обязательно сделать до цикла, поскольку количество строк в цикле будет меняться
for ( int i=0; ifree(A.matrix[i]);
free(A.matrix);
Доброго времени суток! Столкнулся с ситуацией, когда выгодным решением стало затолкать динамик в структуру. После компиляции, которая прошла успешно кстати, при работе с программой не могу взаимодействовать с элементами динамика. Есть мнение, что неправильно обращаюсь к адресу хранения. Подскажите пожалуйста, как правильно нужно это делать? Привожу пример кода:
typedef struct float **matrix;
unsigned int volume;
char name;
>matrix;
//функция ввода.
void input_matrix(matrix m) //если значения вводились ранее, удалить текущую матрицу
if (m.volume != 0) m.volume /= _msize(m.matrix[0]);
for ( int i = 0; i < m.volume; i++) free(m.matrix[i]);
>
free(m.matrix);
m.volume = 0;
>
do cout do cin >> m.matrix[i][_msize(m.matrix[i]) / sizeof ( float ) - 1]; //текущий размер массива / размер типа = кол-во элементов в массиве. -1 это свободная ячейка
if (cin.rdbuf()->in_avail() != 1) m.matrix[i] =
( float *)realloc(m.matrix[i], _msize(m.matrix[i]) + sizeof ( float )); // если в очереди буфера больше 1 символа, записать и увеличить массив m[]
> while (cin.get() != EOS); //считать все из буфера cin, то есть до ‘\n’
m.volume += _msize(m.matrix[i]); //суммирую кол-во затрачиваемой памяти
m.matrix = ( float **)realloc(m.matrix, _msize(m.matrix) * sizeof ( float *)); //увеличить массив m
m.matrix[i + 1] = ( float *)malloc( sizeof ( float )); //выделить память под новые элементы m[]
i++;
> while (_getch() != ESC); //пока не нажал ESC повторять, увеличивая строки массива
>
//функция вывода на консоль
void out_matrix(matrix m) cout for ( int i = 0; i < m.volume / _msize(m.matrix[0]); i++) < // подсчет строк путем деления общего объема на длину строки
for ( int j = _msize(m.matrix[i]) / sizeof ( float ); j > 0; j--) < //подсчет элементов путем деления длины строки на тип памяти(float = 4)
cout >
cout >
cout >
int main() //для примера я создам объект структуры с названием А
matrix A;
A.matrix = ( float **)malloc( sizeof ( float ));
A.matrix[0] = ( float *)malloc( sizeof ( float )); //выделяю 4 байта, 1 ячейка
A.name = 'A'; //имя задам константно
A.volume = 0; //начальный объем
Я, конечно, не уверена на 100%, но по-моему ошибка состоит в том, что когда Вы вводите элементы первой строки матрицы, указатель на эту строку еще не определен - операция
делается позже, после ввода элементов первой строки. В результате первый вызов этой функции создает новую область памяти, игнорируя уже введенные данные для первой строки.
Основной задачей программирования является обработка информации, поэтому любой язык программирования имеет средства для ввода и вывода информации. В языке Си нет операторов ввода-вывода.
Ввод и вывод информации осуществляется через функции стандартной библиотеки. Прототипы рассматриваемых функций находятся в файле stdio.h . Эта библиотека содержит функции
- printf() — для вывода информации
- scanf() — для ввода информации.
malloc
В предыдущей главе уже обсуждалось, что локальные переменные кладутся на стек и существую до тех пор, пока мы не вышли из функции. С одной стороны, это позволяет автоматически очищать память, с другой стороны, существует необходимость в переменных, время жизни которых мы можем контролировать самостоятельно. Кроме того, нам необходимо динамическое выделение памяти, когда размер используемого пространства заранее не известен. Для этого используется выделение памяти на куче. Недостатков у такого подхода два: во-первых, память необходимо вручную очищать, во-вторых, выдеение памяти – достаточно дорогостоящая операция.
Для выделения памяти на куче в си используется функция malloc (memory allocation) из библиотеки stdlib.h
Функция выделяет size байтов памяти и возвращает указатель на неё. Если память выделить не удалось, то функция возвращает NULL. Так как malloc возвращает указатель типа void, то его необходимо явно приводить к нужному нам типу. Например, создадим указатель, после этого выделим память размером в 100 байт.
После того, как мы поработали с памятью, необходимо освободить память функцией free.
Используя указатель, можно работать с выделенной памятью как с массивом. Пример: пользователь вводит число – размер массива, создаём массив этого размера и заполняем его квадратами чисел по порядку. После этого выводим и удаляем массив.
Здесь (int *) – приведение типов. Пишем такой же тип, как и у указателя.
size * sizeof(int) – сколько байт выделить. sizeof(int) – размер одного элемента массива.
После этого работаем с указателем точно также, как и с массивом. В конце не забываем удалять выделенную память.
Теперь представим на рисунке, что у нас происходило. Пусть мы ввели число 5.
Функция malloc выделила память на куче по определённому адресу, после чего вернула его. Теперь указатель p хранит этот адрес и может им пользоваться для работы. В принципе, он может пользоваться и любым другим адресом.
Когда функция malloc "выделяет память", то она резервирует место на куче и возвращает адрес этого участка. У нас будет гарантия, что компьютер не отдаст нашу память кому-то ещё. Когда мы вызываем функцию free, то мы освобождаем память, то есть говорим компьютеру, что эта память может быть использована кем-то другим. Он может использовать нашу память, а может и нет, но теперь у нас уже нет гарантии, что эта память наша. При этом сама переменная не зануляется, она продолжает хранить адрес, которым ранее пользовалась.
Это очень похоже на съём номера в отеле. Мы получаем дубликат ключа от номера, живём в нём, а потом сдаём комнату обратно. Но дубликат ключа у нас остаётся. Всегда можно зайти в этот номер, но в нём уже кто-то может жить. Так что наша обязанность – удалить дубликат.
Иногда думают, что происходит "создание" или "удаление" памяти. На самом деле происходит только перераспределение ресурсов.
Динамическое выделение памяти для одномерных массивов
Форма обращения к элементам массива с помощью указателей имеет следующий вид:
int a[10], *p; // описываем статический массив и указатель
int b;
p = a; // присваиваем указателю начальный адрес массива
. // ввод элементов массива
b = *p; // b = a[0];
b = *(p+i) // b = a[i];
Пример на Си : Организация динамического одномерного массива и ввод его элементов.
Результат выполнения программы:
Стандартные функции динамического выделения памяти
Функции динамического выделения памяти находят в оперативной памяти непрерывный участок требуемой длины и возвращают начальный адрес этого участка.
Функции динамического распределения памяти:
Для использования функций динамического распределения памяти необходимо подключение библиотеки :
Поскольку обе представленные функции в качестве возвращаемого значения имеют указатель на пустой тип void , требуется явное приведение типа возвращаемого значения.
Для определения размера массива в байтах, используемого в качестве аргумента функции malloc() требуется количество элементов умножить на размер одного элемента. Поскольку элементами массива могут быть как данные простых типов, так и составных типов (например, структуры), для точного определения размера элемента в общем случае рекомендуется использование функции
Память, динамически выделенная с использованием функций calloc(), malloc() , может быть освобождена с использованием функции
«Правилом хорошего тона» в программировании является освобождение динамически выделенной памяти в случае отсутствия ее дальнейшего использования. Однако если динамически выделенная память не освобождается явным образом, она будет освобождена по завершении выполнения программы.
Читайте также:
- Точка входа в процедуру deletecriticalsection не найдена в библиотеке dll api ms win core synch
- Статистика разгона памяти ddr3
- Компьютер способен сделать все это только если продолжите а откуда они берутся
- Какие возможности компьютера являются важными при компьютерном математическом моделировании
- Назовите причину появления признака резко снизившаяся скорость работы компьютера