Здравствуйте, Alexey_VL, Вы писали:
A_V>Теоретически, в g() могли переставить указатели на что-нибудь другое, и не удалить то, на что они указывали. Лечится с помощью void g( char * const, char * const, size_t ); Хотя в реальных проектах пока ни разу не встречал такой защиты.
Наложение константности на данные, передаваемые по значению (собственно указатели) — большого смысла не несёт.
Всё равно наружу любые изменения не просочатся.
А внутри — элементарно снимается
void g(char* const a)
{
char* aa = a;
.......
aa = bbbb;
.......
}
Здравствуйте, andrey.desman, Вы писали:
AD>Здравствуйте, Kernan, Вы писали:
S>>>И if-ы лишние. delete отлично удаляет нулевой указатель. K>>не все компиляторы одинаково полезны.
AD>Компилятор здесь ни при чем. delete, равно как и free нулевого указателя — это законная операция, так что смысла в проверках нет, если только тебе не платят за килограммы кода
Ты не можешь гарантировать то, что delete не переопределён и работает корректно.
Здравствуйте, Кодт, Вы писали:
К>Наложение константности на данные, передаваемые по значению (собственно указатели) — большого смысла не несёт.
Это я ступил. Просто была давно подуманная мысль про char * const , что она так работает. Поскольку в реальности эта мысль не применялась и как следствие не продумывалась заново, она до сегодняшняего дня лежала в моске как истинная
Мафиозная диктатура это нестабильность. Если не мафиозная диктатура, то Конституция и демократия.
Здравствуйте, Kernan, Вы писали:
K>Ты не можешь гарантировать то, что delete не переопределён и работает корректно.
Ну почему же
Перегрузить можно operator delete, а поведение delete expression (о котором и идет речь) для нулевого указателея четко специфицировано стандартом, и
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Kernan, Вы писали:
К>>>auto_ptr и boost — это как раз и есть воплощение KISS, в отличие от мосвелосипедстроймонтажа. K>>Каки бусты/stl в 3-х строчках кода, а? Надеюсь, ты не из тех кто начинает использовать стратегии, где достаточно православного switch?
К>Привет участникам синтаксического оверхеда(tm)! К>
К> #include <vector>
К>void f( size_t n )
К>{
К> сhar *a = NULL; std::vector<char> a(max(n,1));
К> char *b = NULL; std::vector<char> b(max(n,1));
К> if (n <= 0) //
К> return; //
К> try//
К> { //
К> a = new char[n]; //
К> b = new char[n]; //
К> g( a, b, n ); g(&a[0], &b[0], n);
К> } //
К> catch (...) //
К> { //
К> if (a) delete [] a; //
К> if (b) delete [] b; //
К> } //
К> if (a) delete [] a; //
К> if (b) delete [] b; //
К>}
К>
К>Ну и у кого из нас три строчки?
вот только вопрос есть, тебе на вход приходит 0, а на вход функции g ты передёшь 1. Подмена понятий, однако. Хотя код неплох, есть чему поучится
Здравствуйте, Kernan, Вы писали:
K>Ты не можешь гарантировать то, что delete не переопределён и работает корректно.
А еще я не могу гарантировать, что завтра мне кирпич на голову не упадет
Здравствуйте, Kernan, Вы писали:
K>вот только вопрос есть, тебе на вход приходит 0, а на вход функции g ты передёшь 1. Подмена понятий, однако. Хотя код неплох, есть чему поучится
Если ты присмотришься внимательно, то заметишь: на вход g приходит 0.
Но в конструкторы векторов действительно приходит 1. Это нужно затем, чтобы эмулировать поведение new char[0].
Дело в том, что char* p = new char[0] — это валидный ненулевой неразыменовываемый (обрати внимание на эти три слова!) указатель за конец массива.
Вектор же не оперирует указателями. Он отдаёт итераторы, разыменовывая которые, получаем ссылки (*v.begin()); или же сразу ссылки (v[0], v.front()).
Потом ссылку можно восстановить до указателя (&v.front()) и, прибавив размер, получить указатель за конец массива (&v.front()+v.size()).
Итератор конца (v.end()) неразыменовываемый, *v.end() — UB. Так что лобовое решение &*v.end() не подходит.
Соответственно, если вектор нулевого размера, v.begin()==v.end(), получить указатель не получится.
Можно, конечно, набраться наглости и сказать v.resize(0); char*p=&v.front(); — но, повторю, это неопределённое поведение. Например, в отладочной версии STL ест шанс схлопотать assert(false).
Но даже если не будет assertion fault, можем получить p==NULL (раз размер нулевой, то внутренний массив не выделен, а внутренние указатели выставлены в некое дефолтное, т.е. нулевое значение).
То есть, указатель для g() будет валидный, неразыменовываемый (а мы и не собирались разыменовывать — n==0), но — нулевой.
Возможно, что g() способно работать и с набором параметров g(NULL,NULL,0). Этого из начальных условий задачи мы не знаем. Поэтому я и предложил трюк с max(n,1).
Тем более, что нагрузка на кучу для new char[0] и new char[1] (внутри vector) — почти одинаковая.
блин — я вообще слепой — все норм по моему — единтственно — можно труями кэтчами обложить ,
но это уже коде стайлинг
A>
A>Какие проблемы Вы видите в этих фрагментах кода и как предлагаете их решать?
A>1.
A>void g( char *, char *, size_t );
A>void f( size_t n )
A>{
A> char *a = new char[n];
A> char *b = new char[n];
A> g( a, b, n );
A> delete [] b;
A> delete [] a;
A>}
A>2.
A>int main(int argc, char* argv[])
A>{
A> if( argc > 1 )
A> printf( argv[1] );
A> return 0;
A>}
A>
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Kernan, Вы писали:
K>>вот только вопрос есть, тебе на вход приходит 0, а на вход функции g ты передёшь 1. Подмена понятий, однако. Хотя код неплох, есть чему поучится
К>Если ты присмотришься внимательно, то заметишь: на вход g приходит 0.
К>Но в конструкторы векторов действительно приходит 1. Это нужно затем, чтобы эмулировать поведение new char[0]. К>Дело в том, что char* p = new char[0] — это валидный ненулевой неразыменовываемый (обрати внимание на эти три слова!) указатель за конец массива.
К>Вектор же не оперирует указателями. Он отдаёт итераторы, разыменовывая которые, получаем ссылки (*v.begin()); или же сразу ссылки (v[0], v.front()). К>Потом ссылку можно восстановить до указателя (&v.front()) и, прибавив размер, получить указатель за конец массива (&v.front()+v.size()). К>Итератор конца (v.end()) неразыменовываемый, *v.end() — UB. Так что лобовое решение &*v.end() не подходит.
К>Соответственно, если вектор нулевого размера, v.begin()==v.end(), получить указатель не получится.
К>Можно, конечно, набраться наглости и сказать v.resize(0); char*p=&v.front(); — но, повторю, это неопределённое поведение. Например, в отладочной версии STL ест шанс схлопотать assert(false).
К>Но даже если не будет assertion fault, можем получить p==NULL (раз размер нулевой, то внутренний массив не выделен, а внутренние указатели выставлены в некое дефолтное, т.е. нулевое значение). К>То есть, указатель для g() будет валидный, неразыменовываемый (а мы и не собирались разыменовывать — n==0), но — нулевой.
К>Возможно, что g() способно работать и с набором параметров g(NULL,NULL,0). Этого из начальных условий задачи мы не знаем. Поэтому я и предложил трюк с max(n,1). К>Тем более, что нагрузка на кучу для new char[0] и new char[1] (внутри vector) — почти одинаковая.
Получается, что ты предполагаешь, что функция g проверяет n >= 0, чего может и не быть. Более того, неизвестный код может длелать какие-то действия в случае (NULL, NULL, 0).
Здравствуйте, Kernan, Вы писали:
К>>Возможно, что g() способно работать и с набором параметров g(NULL,NULL,0). Этого из начальных условий задачи мы не знаем. Поэтому я и предложил трюк с max(n,1). К>>Тем более, что нагрузка на кучу для new char[0] и new char[1] (внутри vector) — почти одинаковая. K>Получается, что ты предполагаешь, что функция g проверяет n >= 0, чего может и не быть. Более того, неизвестный код может длелать какие-то действия в случае (NULL, NULL, 0).
Я просто предложил наиболее консервативный рефакторинг.
А именно, обеспечил безопасность от исключений на ввереном мне участке, никак не затронув поведение остального кода (ни вызывающего, ни внутри g()).
Единственный упрёк в сторону векторов — то, что чуть-чуть изменяется рисунок стека и кучи. Это может сыграть в условиях стресс-тестирования.
boost::scoped_array — более предпочтительный вариант в этом плане.
Здравствуйте, carpenter, Вы писали:
C>блин — я вообще слепой — все норм по моему — единтственно — можно труями кэтчами обложить , C>но это уже коде стайлинг
Угу, слепой Среди 70 ответов выше были разные мнения и советы, и про "не всё норм", и про "можно без try-catch"...
Здравствуйте, Kernan, Вы писали:
K>Получается, что ты предполагаешь, что функция g проверяет n >= 0, чего может и не быть. Более того, неизвестный код может длелать какие-то действия в случае (NULL, NULL, 0).
Может, не может, а вдруг и не так, а сяк. Предположения летят в мусорное ведро. Акстись запускать вот так просто какой-то неизвестный код! Просто смотришь в код функции и видишь что она делает. Или доку читаешь. Или, наконец, просто один раз проверяешь.
Изначальный вопрос — дерьмо, т.к. искать сферические проблемы в сфрическом коде суть занятие бесполезное, потому что проблем можно напридумывать кучу. И спорить кто прав, кто виноват потом можно до усрачки, потому что у одного глаз косит на 30 градусов, а у другого на 17, и никак их точки зрения не совпадут...
А самый главный косяк в изначальном коде — это с особым усердием подобранные, как бы намекающие на всю глупость ситуации, имена функций и переменных. Вот где есть простор для фантазии! Кто какое имя придумает? Устроим знатный holy war!
Здравствуйте, Vamp, Вы писали:
R>>Если вылетит исключение при выделении памяти под b, то память для a не будет освобождена -> утечка память. V>Ну какая утечка? Если вылетит исключение в приведенном фрагменте, программа просто завершится и всю память почистит ОС.
Почему же ? Окружение, в котором вызывается функция f нам неизвестно. Вполне возможно что там исключение будет перехвачено, и программа нисколько не завершится. Итого: утечка.
в g(NULL,NULL,0). Этого из начальных условий задачи мы не знаем. Поэтому я и предложил трюк с max(n,1). К>>Тем более, что нагрузка на кучу для new char[0] и new char[1] (внутри vector) — почти одинаковая. K>Получается, что ты предполагаешь, что функция g проверяет n >= 0, чего может и не быть.
Не понял зачем ей это проверять. n всегда >= 0. size_t беззнаковый.
Здравствуйте, maykie, Вы писали:
M>Не понял зачем ей это проверять. n всегда >= 0. size_t беззнаковый.
Почему size_t всегда должен быть беззнаковым?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, alexander_st, Вы писали:
V>>>Вообще-то, никто не мешает вызвать delete для константных указателей. _>>Уп-с ... правда? _>>давно последний раз пробовали?
К>Да вот только что: http://codepad.org/9DrfRKDV
Из каких соображений тогда ну скажем ф-я std::string c_str() возвращает const char* а не просто char* ? может для того , что бы показать, что удалять этот буфер не надо?