Консультация № 187140
29.01.2013, 20:55
149.46 руб.
0 10 2
Здравствуйте! Прошу помощи в следующем вопросе:

Мне принесли программу, в которой написано (M$ Visual C++ 2005):

int *mas=new int[];

Она успешно компилируется. Объясните, что это значит? Сколько как и сколько тут выделяется памяти?

Ещё один связанный вопрос. Раньше -- на Паскале -- я бы просто сделал memavail. Про С я где-то прочитал, что такой функции нет. А как же в C/C++ контролируют утечки памяти?

Обсуждение

Неизвестный
29.01.2013, 21:07
общий
29.01.2013, 21:16
Адресаты:
запись с таким выделением памяти - явно ошибочна. в 2010 студии такую запись скомпилировать не удалось.
Попробуйте,очистить весь проект и собрать заново. Может он собирает старые значение, где не было такой ошибки.
Посмотреть сколько памяти "кушает" эта переменная можно в дебагере. Даже интересно, сколько она занимает места:).
В С/С++ утечку памяти контролирует только сам программист(то есть как он пишет).Среда утечку памяти не контролирует. Поэтому в С/С++ утечка памяти- частая и очень сложно вылавливаемая ошибка.
Код:

int *mas=new int[5];

...

delete[] mas;

mas = 0;



Было бы более логично:)
давно
Профессор
399103
482
30.01.2013, 00:25
общий
это ответ
Здравствуйте, Сергей Бендер!

Именно в VS2005 это действительно компилируется, хотя, по хорошему, не должно.
Осмелюсь предположить, что данный компилятор трактует new int[] как new int[0]. Последнее допустимо, хотя и бесполезно.

Для контроля утечек памяти можно собирать проект с отладочной информацией и использовать Valgrind под Linux или Dr. Memory под Windows. И, да, работа с голыми указателями в C++ без очень уважительной причины -- дурной тон. Используют обёртки над ними -- т.н. умные указатели.
Неизвестный
30.01.2013, 07:12
общий
В 6-й студии скомпилировалось, выделило 0 памяти.

Есть пара функций, IsBadReadPtr, IsBadWrritePtr - но я ими никогда не пользовался, не знаю, насколько хорошо они работают. А при правильной организации программы память обычно не течёт .
Про дурной тон ещё добавлю: если пользуетесь указателями, то лучше это делать в отдельном классе, где доступ к этоту указателю закрыт, и сам класс следит, чтобы память освобождалась на выходе(в деструкторе/в чистилке). Т.е. примерно так:

Код:
class CMy
{
protected:
int *m_piMas;
public:
CMy();
virtual ~CMy();
bool Create(unsigned int iSize);
void Clear();
bool Get(int idx, int &iData);
bool Set(int idx, int iData);
};

CMy::CMy()
{
m_piMas = NULL;
}

CMy::~CMy()
{
Clear();
}

bool CMy::Create(unsigned int iSize)
{
Clear();
m_piMas = new int[iSize];
return (NULL != m_piMas);
}

void CMy::Clear()
{
if(m_piMas)
{
delete[]m_piMas;
m_piMas = NULL;
}
}

bool CMy::Get(int idx, int &iData)
{
if(m_piMas)
{
iData = m_piMas[idx];
return true;
}
return false;
}

bool CMy::Set(int idx, int iData)
{
if(m_piMas)
{
m_piMas[idx] = iData;
return true;
}
return false;
}


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

Код:

int *mas=new int[iSize];
delete[]mas;



но, "лучше день потерять" честное слово, потому что в проектах такой код-обёртка:
1. будет смотреться очень маленьким по сравнению с остальным
2. сбережёт кучу времени и нервов
3. его легко модифицировать, не затрагивая остальную программу

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




Неизвестный
30.01.2013, 13:09
общий
это ответ
Здравствуйте, Сергей Бендер!
Если Вы пользуетесь продуктами от MS, утечку памяти детектировать можно следующим образом.
Код:
#ifdef _DEBUG
#include <crtdbg.h>
#endif

int main()
{
#ifdef _DEBUG
_CrtSetDbgFlag(_CRTDBG_CHECK_ALWAYS_DF|_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
#endif
const int SIZE=10;
// Выделяем память
int* arr=new int[SIZE];

// Что-то делаем
for(int i=0;i<SIZE;++i)
{
arr[i]=i;
}

// Забыли освободить
// delete[] arr;
}


В данном случае не освобождается память под массив. Имеем такой вывод отладки:
Код:
...
Detected memory leaks!
Dumping objects ->
{68} normal block at 0x00757DD8, 40 bytes long.
Data: < > 00 00 00 00 01 00 00 00 02 00 00 00 03 00 00 00
Object dump complete.
Программа "[4880] 187140.exe" завершилась с кодом 0 (0x0).

5
давно
Профессионал
304622
583
31.01.2013, 21:45
общий
Осмелюсь предположить, что данный компилятор трактует new int[] как new int[0]. Последнее допустимо, хотя и бесполезно.


Хм-м. Смешно. Т.е. я так понимаю в диспетчере памяти создаётся запись, но длина у него нулевая.

Я тут эксперимент сделал. Строка
int *mas=new int[-1];
тоже успешно откопилировалась. Странно. Хотя компилятор мог просто превратить знаковое число в беззнаковое.

Для контроля утечек памяти можно собирать проект с отладочной информацией и использовать Valgrind под Linux или Dr. Memory под Windows.


Пока то да сё, поставлю, научюсь пользоваться. Могу я вас попросить: посмотрите сами (например с помощью этого Dr. Memory) сколько же выделяется памяти при new int[]? А сколько при new int[-1]?

И, да, работа с голыми указателями в C++ без очень уважительной причины -- дурной тон. Используют обёртки над ними -- т.н. умные указатели.


Это, наверно, так. Но мне поручили вести C/C++ первокурсникам (и сам многое для себя открываю). Не знаю, можно ли "голые" указатели обойти. Боюсь "умных" сразу не осилят.
давно
Профессионал
304622
583
31.01.2013, 22:04
общий
Спасибо.

А такого чтобы в самой программе, по ходу работы, а не после окончания всё-таки нет?
(Я всё думаю об эквиваленте паскалевского memavail.)
давно
Профессионал
304622
583
31.01.2013, 22:10
общий
Цитата: 390430
запись с таким выделением памяти - явно ошибочна. в 2010 студии такую запись скомпилировать не удалось.


Хм-м. У меня версия 2005. У студентов -- 2008. В них копилируется.

А в старом Borland C -- ошибка.

Цитата: 390430
Попробуйте,очистить весь проект и собрать заново. Может он собирает старые значение, где не было такой ошибки.


Нет. Это абсолютно чистый новый проект, типа Win32 Console.
давно
Профессионал
304622
583
31.01.2013, 22:15
общий
Цитата: 181465
выделило 0 памяти.


Как узнали?

Цитата: 181465
но, "лучше день потерять" честное слово, потому что в проектах такой код-обёртка:


"Если бы у нас был ротвейлер! Если бы у нас была перчатка той женщины! Если бы собака сразу взяла след!"
(Мюллер, "17 мнгновений весны")

Для первокурсников, многие из которых, плохо понимают даже if и циклы -- это IMHO неподъёмно.
давно
Профессор
399103
482
01.02.2013, 00:13
общий
01.02.2013, 11:17
Адресаты:
Цитата: Сергей Бендер
Т.е. я так понимаю в диспетчере памяти создаётся запись, но длина у него нулевая.

Вроде того. Т.е. удалить её можно(и нужно, т.к. метаданные -- информация о размере массива, например, -- в памяти, в общем случае, лежат), а писать в неё -- нет. Стандарт(пункт 5.3.4/7) позволяет:
When the value of the expression in a noptr-new-declarator is zero, the allocation function is called to allocate an array with no elements.


Цитата: Сергей Бендер
Я тут эксперимент сделал. Строка
int *mas=new int[-1];
тоже успешно откопилировалась. Странно. Хотя компилятор мог просто превратить знаковое число в беззнаковое.

Это и происходит. Ведь соответствующий аргумент имеет тип std::size_t. А
std::cout << static_cast<std::size_t>(-1);
даёт
4294967295

Попробуйде запустить программу со строкой
new int[-1];
Должно бросить исключение -- выделить памяти во всё адресное пространство не дадут. А какое-нибудь -static_cast<std::size_t>(-1) должно работать. Т.е.
new int[-4294967295];
создаст массив в 1 элемент. Но это уже рассуждение за пределами языка, разумеется.

Цитата: Сергей Бендер
Могу я вас попросить: посмотрите сами (например с помощью этого Dr. Memory) сколько же выделяется памяти при new int[]? А сколько при new int[-1]?

Прямо сейчас, увы, не могу посмотреть. Но вообще new int[-1] должен создать массив в std::size_t(-1) интов, плюс (вообще говоря, зависящие от реализации) метаданные. new int[0], соответственно, лишь метаданные.

Цитата: Сергей Бендер
Но мне поручили вести C/C++ первокурсникам (и сам многое для себя открываю). Не знаю, можно ли "голые" указатели обойти. Боюсь "умных" сразу не осилят.

А, я-то думал, вам проект дали поддерживать. Тут, конечно, без голых не обойтись. Тем более, что умные -- не более чем ~бустовские(Boost library) классы. Т.е. они введены в язык посредством стандартной библиотеки(их в каком-нибудь <memory> можно посмотреть(а то и поправить :))), а не "на уровне языка".
Неизвестный
01.02.2013, 07:21
общий
Цитата: Сергей Бендер
int *mas=new int[-1];
тоже успешно откопилировалась. Странно. Хотя компилятор мог просто превратить знаковое число в беззнаковое.


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

Цитата: Сергей Бендер
Как узнали?

В дамп памяти посмотрел в отладке.

Цитата: Сергей Бендер
Для первокурсников, многие из которых, плохо понимают даже if и циклы -- это IMHO неподъёмно.

Можно ещё упростить. Можно начать вообще с простейшего варианта, а потом на примерах обшивать код классом и новыми методами. Кстати, в жизни ровно так и происходит, у меня например. За пять минут делаю базовый класс. Потом постепенно в нём что-то правится по ходу разработки (многим покажется неправильным такой подход, но я работаю несколько в другой плоскости, поэтому там это наиболее правильный вариант разработки).

Цитата: Сергей Бендер
Но мне поручили вести C/C++ первокурсникам

Да, этому не позавидуешь...
Форма ответа