С Новым годом! Форум программистов, компьютерный форум, киберфорум
С++ для начинающих
Войти
Регистрация
Восстановить пароль
Карта форума Темы раздела Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.71/14: Рейтинг темы: голосов - 14, средняя оценка - 4.71
3 / 3 / 0
Регистрация: 03.06.2019
Сообщений: 64
1

Почему данный код валидный?

02.01.2020, 22:47. Показов 2895. Ответов 27
Метки нет (Все метки)

Author24 — интернет-сервис помощи студентам
Привет всем. Написал вот этот код на компиляторе clang новейшей версии:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>
using namespace std;
 
class arr{
    char* data;
    public:
    arr(char* arr){cout<<"Ctor"<<endl;}
    ~arr(){
        cout<<"Dtor"<<endl;
        delete data;
    }
};
 
int main(){
    arr A(nullptr);
    A.~arr();
}
Освобождение памяти происходит 2 раза, это говорит и вывод в консоль:
Ctor
Dtor
Dtor
[Program finished]

Но никаких ошибок. В чем дело, ребята?
1
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
02.01.2020, 22:47
Ответы с готовыми решениями:

Почему работает данный код?
В разделе &quot;Комбинаторика&quot; ( https://www.cyberforum.ru/combinatorics/thread572992.html ) задали...

Как работает данный код? И почему не компилируется?
Обьясните пожалуйста как работает данный код, и скажите почему он не компилируется? И есть ли...

Как работает данный код и почему такой вывод?
Написал какой-то код, выводит в дебаге 5 в релизе 0 #include &lt;iostream&gt; using namespace std;...

Почему данный код игнорирует 1-ый символ и приходится первую букву дублировать?
Здравствуйте, подскажите пожалуйста почему данный код игнорирует 1-ый символ и приходится первую...

27
Эксперт С++
8972 / 4318 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
02.01.2020, 22:55 2
Лучший ответ Сообщение было отмечено NuMeRiC_ как решение

Решение

Цитата Сообщение от NuMeRiC_ Посмотреть сообщение
В чем дело, ребята?
если явно не инициализировать указатель чем то осмысленным,
тогда он будет содержать мусор.

однако, на некоторых системах
(например на линуксах)
при старте процесса вся его стековая память зануляется.
это делается из соображений безопасности.

поэтому, даже неинициализированный указатель содержит нуль.
в этом случае твой код может отработать без ошибок.

однако, это не делает сам код валидным.
он по прежнему некорректен.

если комплировать visual studio - приложение упадёт с ошибкой.
0
199 / 155 / 45
Регистрация: 11.11.2019
Сообщений: 348
02.01.2020, 23:02 3
Не упадет, т.к. "The C++ language guarantees that delete p will do nothing if p is equal to NULL"
1
Эксперт С++
8972 / 4318 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
02.01.2020, 23:06 4
Цитата Сообщение от fao Посмотреть сообщение
Не упадет, т.к. "The C++ language guarantees that delete p will do nothing if p is equal to NULL"
с чего ты взял, что неинициализированный указатель будет содержать ноль?

на самом деле ещё как падает.

gcc
https://rextester.com/EUF58920

cl
https://rextester.com/OKWN18097
1
3 / 3 / 0
Регистрация: 03.06.2019
Сообщений: 64
02.01.2020, 23:07  [ТС] 5
То есть при первом освобождении поинтер указывает на NULL, а значит второе его освобождение не приведет ни к чему, так?
0
85 / 34 / 20
Регистрация: 15.12.2019
Сообщений: 88
02.01.2020, 23:15 6
Цитата Сообщение от hoggy Посмотреть сообщение
с чего ты взял, что неинициализированный указатель будет содержать ноль?
на самом деле ещё как падает.
gcc
https://rextester.com/EUF58920
cl
https://rextester.com/OKWN18097
Нет ошибок в обоих вариантах, если в описании класса инициализировать указатель как
C++
1
2
3
char* data=nullptr;
или
char* data{};
Даже IDE на это намекает
Почему данный код валидный?


А вариант ТСА у меня не крашится и отрабатывает в VS19. Мне тоже интересно было бы узнать, почему, инициализрованный нулем указатель можно удалять неограниченное число раз без ошибок, учитывая то, что после первой отработки деструктора, он уже может заполнятся мусором и следовательно критовать. Ведь мы же освобождаем память, занимаемую самим указателем, а никак не память, на которую ссылается указатель.
0
199 / 155 / 45
Регистрация: 11.11.2019
Сообщений: 348
02.01.2020, 23:21 7
Цитата Сообщение от NuMeRiC_ Посмотреть сообщение
То есть при первом освобождении поинтер указывает на NULL, а значит второе его освобождение не приведет ни к чему, так?
У Вас конструктор ничего не делает с указателем data. Но в VS (возможно не всегда) неинициализированный указатель становится равным NULL. Поэтому прога в VS не падает. Но в любом случае это плохая практика.

поэтому hoggy здесь абсолютно прав
0
85 / 34 / 20
Регистрация: 15.12.2019
Сообщений: 88
02.01.2020, 23:30 8
А стоп я сам дурак. Мы же удаляем объект по заданному в указателе адресу, а не сам указатель. Надо спать идти
0
199 / 155 / 45
Регистрация: 11.11.2019
Сообщений: 348
02.01.2020, 23:35 9
Цитата Сообщение от nyaka_wai Посмотреть сообщение
А стоп я сам дурак. Мы же удаляем объект по заданному в указателе адресу, а не сам указатель. Надо спать идти
NULL можно удалять сколько угодно потому что с++ гарантирует, что ничего страшного не будет
0
Эксперт С++
8972 / 4318 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
02.01.2020, 23:52 10
Цитата Сообщение от nyaka_wai Посмотреть сообщение
Мне тоже интересно было бы узнать, почему, инициализрованный нулем указатель можно удалять неограниченное число раз без ошибок, учитывая то, что после первой отработки деструктора, он уже может заполнятся мусором и следовательно критовать.
не может он заполняться мусором.

деструктор - это на самом деле самая обычная функция,
в которой обычно прописывают процедуру освобождения ресурсов.
но не более того.

деструктор не уничтожает объект.
после того, как деструктор уже отработал,
объект никуда не девается.
память, которую занимает объект, так же никуда не девается.

и если твой объект хранит внутри себя нулевой указатель,
то этот указатель так же никуда не денется,
и по прежнему будет хранить ноль.

можешь хоть 10 раз вызывать деструктор - никакого мусора там по волшебству не образуется.

волшебство начинается когда освобождается память, которую когда-то занимал объект.
только после этого память может быть перезаписана чем то иным.
не раньше.

для автоматических объектов (как в случае ТС) такое волшебство начинается,
когда звершается область видимости имени объекта.
1
85 / 34 / 20
Регистрация: 15.12.2019
Сообщений: 88
03.01.2020, 00:12 11
Цитата Сообщение от hoggy Посмотреть сообщение
не может он заполняться мусором.
деструктор - это на самом деле самая обычная функция,
в которой обычно прописывают процедуру освобождения ресурсов.
но не более того.
деструктор не уничтожает объект.
после того, как деструктор уже отработал,
объект никуда не девается.
память, которую занимает объект, так же никуда не девается.
и если твой объект хранит внутри себя нулевой указатель,
то этот указатель так же никуда не денется,
и по прежнему будет хранить ноль.
можешь хоть 10 раз вызывать деструктор - никакого мусора там по волшебству не образуется.
волшебство начинается когда освобождается память, которую когда-то занимал объект.
только после этого память может быть перезаписана чем то иным.
не раньше.
для автоматических объектов (как в случае ТС) такое волшебство начинается,
когда звершается область видимости имени объекта.
Еще раз спасибо, теперь я знаю больше ^__^
0
Вездепух
Эксперт CЭксперт С++
12792 / 6669 / 1795
Регистрация: 18.10.2014
Сообщений: 16,882
03.01.2020, 01:17 12
Цитата Сообщение от NuMeRiC_ Посмотреть сообщение
Но никаких ошибок. В чем дело, ребята?
Этот код порождает неопределенное поведение по целому ряду причин. Никто вам не обещал, что неопределенное поведение будет проявляться в виде каких-то "ошибок". Неопределенное поведение может проявлять себя как угодно или не проявлять вообще. Вопросов типа "в чем дело" про неопределенное поведение нет и быть не может.

Вот и все объяснение.

С практической же точки зрения вам в данном случае просто случайно повезло. Мусор в вашем указателе случайно оказался нулем. Добавьте в функцию main парочку посторонних переменных и все накроется медным тазом.

Цитата Сообщение от hoggy Посмотреть сообщение
однако, на некоторых системах
(например на линуксах)
при старте процесса вся его стековая память зануляется.
это делается из соображений безопасности.
Ничего подобного ни на каких "линуксах" нет и никогда не было.
1
Эксперт С++
8972 / 4318 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
03.01.2020, 01:24 13
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Ничего подобного ни на каких "линуксах" нет и никогда не было.
https://rextester.com/WWRH29370

тоже самое, только под виндой:
https://rextester.com/EWR23187
0
Вездепух
Эксперт CЭксперт С++
12792 / 6669 / 1795
Регистрация: 18.10.2014
Сообщений: 16,882
03.01.2020, 01:35 14
Цитата Сообщение от hoggy Посмотреть сообщение
https://rextester.com/WWRH29370
И? К чему здесь эти абсолютно ничего не значащие примеры?

http://coliru.stacked-crooked.... 3468843e46

Я просто ума не приложу, как можно из очевидно бессмысленного эксперимента сделать такой далеко идущий вывод про "при старте процесса вся его стековая память зануляется". Просто классическая диссертация Василия Ивановича наблюдается ("Вывод: у таракана уши располагаются на ногах").
0
Эксперт С++
8972 / 4318 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
03.01.2020, 01:39 15
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
И? К чему здесь эти абсолютно ничего не значащие примеры?
эти примеры наглядно иллюстрируют,
что в некоторых случаях память содержит нули.
а в некоторых - не содержит.
0
Вездепух
Эксперт CЭксперт С++
12792 / 6669 / 1795
Регистрация: 18.10.2014
Сообщений: 16,882
03.01.2020, 01:42 16
Цитата Сообщение от hoggy Посмотреть сообщение
эти примеры наглядно иллюстрируют,
что в некоторых случаях память содержит нули.
а в некоторых - не содержит.
Ну то есть, вы обнаружили, что мусор в памяти - это мусор в памяти, то есть содержать он может что угодно, в том числе и нули.

Однако как из этого родилась чепуха про некие "зануления стековой памяти" - по-прежнему не ясно.
1
Комп_Оратор)
Эксперт по математике/физике
9005 / 4704 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
03.01.2020, 01:45 17
Лучший ответ Сообщение было отмечено NuMeRiC_ как решение

Решение

NuMeRiC_, в вашем вопросе два независимых подвопроса. Я убрал освобождение по непредсказуемому адресу и прэкспериментировал с пустым деструктором. Его вызов сам по себе безобиден. Потом я вспомнил, что при реинициализации при помощи placement new деструктор вызывают принудительно и циклы destructor/new(placementPtr) могут в одной области работать сколько угодно.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
 
class arr{
    public:
    int data;
 
    arr(int data_=0):data(data_){cout<<"Ctor"<<endl;}
    ~arr(){
       if(data){
            cout<<"Dtor "<<data<<endl;
           --data;
           this->~arr();
        }
 
    }
};
int main()
{
arr A(1000);
A.~arr();
return 0;
}
Однако если выделить память по указателю а в деструкторе затребовать его освобождения то вызвать деструктор дважды подряд не получится. Может есть операционки которым это всё равно, но я не встречал.
0
Эксперт С++
8972 / 4318 / 960
Регистрация: 15.11.2014
Сообщений: 9,760
03.01.2020, 01:46 18
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Я просто ума не приложу, как можно из очевидно бессмысленного эксперимента
мне очевидно, что в случае с gcc память на стеке содержит нули.

ты наверное думаешь, что это случайность?

Добавлено через 1 минуту
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
Ну то есть, вы только что открыли, что мусор в памяти - это мусор в памяти, то есть содержать он может что угодно, в том числе и нули. На то он и мусор.
Однако как из этого родилась чепуха про некие "зануления стековой памяти" - по-прежнему не ясно.
в отличие от обычного мусора, в случае с gcc, нули - не случайность,
Бро.
0
Вездепух
Эксперт CЭксперт С++
12792 / 6669 / 1795
Регистрация: 18.10.2014
Сообщений: 16,882
03.01.2020, 01:46 19
Цитата Сообщение от hoggy Посмотреть сообщение
мне очевидно, что в случае с gcc память на стеке содержит нули.
ты наверное думаешь, что это случайность?
Я уже полностью опроверг ваше "очевидно" своим контрпримером. Нет, в случае с gcc память на стеке НЕ содержит нули.
0
Комп_Оратор)
Эксперт по математике/физике
9005 / 4704 / 630
Регистрация: 04.12.2011
Сообщений: 14,003
Записей в блоге: 16
03.01.2020, 01:50 20
Цитата Сообщение от TheCalligrapher Посмотреть сообщение
зануления стековой памяти
Были бы дороговаты. Сообщество прокляло бы Линуса Торвальдса. Ещё раз прокляло бы.
0
03.01.2020, 01:50
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
03.01.2020, 01:50
Помогаю со студенческими работами здесь

Что делает данный код?
#include &lt;iostream&gt; #include &lt;queue&gt; using namespace std; int main() { queue &lt;int&gt; x1;...

Помогите упростить данный код
Не знаю как упростить((( #include &lt;iostream.h&gt; #include &lt;math.h&gt; #include &lt;conio.h&gt;...

Что означает данный код?
Здравствуйте, что делает/означает данная строка кода? В гугле не мог найти, так как не знаю...

Объясните, пожалуйста, данный код
Здравствуйте, объясните пожалуйста построчно данный код. Задача: В одномерном массиве исключить...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2024, CyberForum.ru