Russian Qt Forum

Программирование => С/C++ => Тема начата: a_n_y_a от Август 19, 2009, 08:36



Название: Почему не срабатывает блок try?
Отправлено: a_n_y_a от Август 19, 2009, 08:36
Есть такой код:

#include "iostream"

class A
{
public:
   int b;
   void Pokaz(){printf("Pokaz\n");};
};


int main()
{
   try
   {
      A *a=0;
      a->b=5;
      a->Pokaz();
   }
   catch(...)
   {
      printf("try\n");
   };
};

При выполнении выдает следующее:
[root@localhost ~]# /usr/NULL/Null/Debug/Null
Ошибка сегментирования
[root@localhost ~]#

Почему не срабатывает try?



Название: Re: Почему не срабатывает блок try?
Отправлено: Пантер от Август 19, 2009, 09:10
С какими параметрами собрана Qt?


Название: Re: Почему не срабатывает блок try?
Отправлено: pastor от Август 19, 2009, 09:27
А причем здесь Qt?


Название: Re: Почему не срабатывает блок try?
Отправлено: a_n_y_a от Август 19, 2009, 09:30
Qt здесь не использовалась, но код с использование Qt в такой же ситуации тоже не работает правильно. В этом примере ошибка выделена в простом коде.


Название: Re: Почему не срабатывает блок try?
Отправлено: BRE от Август 19, 2009, 09:43
А кто сказал, что плюсовый try должен отрабатывать такие исключения?  ;)


Название: Re: Почему не срабатывает блок try?
Отправлено: Авварон от Август 19, 2009, 09:45
а я чего-то не понимаю, или исключение некому бросать? сегфолт - это системное событие а не результат системного вызова (как при new). Я бы сказал, что это не исключение...


Название: Re: Почему не срабатывает блок try?
Отправлено: Winstrol от Август 19, 2009, 10:09
Почему не срабатывает try?
Под windows разыменование нулевого указателя это так называемое SEH-исключение, а не с++-исключение. Для того, чтобы его ловить в блоке catch, нужно установить функцию транслирующую SEH в с++-исключения с помощью вызова системной  _set_se_translator. Примнры кода можно найти поиском по _set_se_translator.


Название: Re: Почему не срабатывает блок try?
Отправлено: Пантер от Август 19, 2009, 11:29
А причем здесь Qt?
:) Что-то вспомнилось про ключик -exeption. Ошибся малость.


Название: Re: Почему не срабатывает блок try?
Отправлено: a_n_y_a от Август 20, 2009, 09:53
Посмотрел я в интернете, прошелся поиском по своему линуксу, и что то ничего нет. В интернете есть примеры только для виндов. Что же, под линуксом ничего сделать невозможно?


Название: Re: Почему не срабатывает блок try?
Отправлено: denka от Август 20, 2009, 10:11
Возможно - перед обращением к указателю проверять валидный ли он


Название: Re: Почему не срабатывает блок try?
Отправлено: BRE от Август 20, 2009, 10:13
Посмотрел я в интернете, прошелся поиском по своему линуксу, и что то ничего нет. В интернете есть примеры только для виндов. Что же, под линуксом ничего сделать невозможно?
Почитай про системные сигналы в linux. Для начала man signal.



Название: Re: Почему не срабатывает блок try?
Отправлено: a_n_y_a от Август 20, 2009, 10:20
Возможно - перед обращением к указателю проверять валидный ли он

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


Название: Re: Почему не срабатывает блок try?
Отправлено: ритт от Август 20, 2009, 10:44
Код:
if (ptr) ptr->doSomething();
:)


Название: Re: Почему не срабатывает блок try?
Отправлено: a_n_y_a от Август 20, 2009, 12:45
Код:
if (ptr) ptr->doSomething();
:)
Делаю:

if (ptr) printf("валидный указатель");
else printf("Не валидный указатель");
delete ptr;
printf(Удалили);

При ptr=NULL получаю:
Не валидный указатель
Удалили

При ptr указывающий на самоудалившийся процесс, получаю:
валидный указатель 

И все, сообщение "Удалили" я не получаю, программа вылетает на delete ptr.
Как мне проверить, объект удален из памяти или нет? В программе с Qt ptr указывает на процесс.


Название: Re: Почему не срабатывает блок try?
Отправлено: Rcus от Август 20, 2009, 12:53
Классический случай dangling pointer.
Что значит указывает на процесс, всмысле на объект класса QProcess? Тогда QPointer же.


Название: Re: Почему не срабатывает блок try?
Отправлено: a_n_y_a от Август 20, 2009, 13:45
Это продолжение темы
http://www.prog.org.ru/index.php?topic=10158.msg61489#msg61489
Только в родительском классе создается объект PotokThread и запускается на выполенение, его адрес при завершении программы проверяется на валидность. Вот там у меня грабли. Как мне проверить валидность указателя на объект?


Название: Re: Почему не срабатывает блок try?
Отправлено: pastor от Август 20, 2009, 13:48
А зачем проверять валидность указателя на объект PotokThread при завершении работы приложения? Расскажи лучше, что нужно получить в итоге? Для чего эти проверки?


Название: Re: Почему не срабатывает блок try?
Отправлено: Igors от Август 20, 2009, 14:28
Код:
if (ptr) ptr->doSomething();
:)
Делаю:

if (ptr) printf("валидный указатель");
else printf("Не валидный указатель");
delete ptr;
printf(Удалили);

При ptr=NULL получаю:
Не валидный указатель
Удалили

При ptr указывающий на самоудалившийся процесс, получаю:
валидный указатель 

И все, сообщение "Удалили" я не получаю, программа вылетает на delete ptr.
Как мне проверить, объект удален из памяти или нет? В программе с Qt ptr указывает на процесс.

Если Вы имеете указатель, то Вы же и ответственны за его корректность, язык в этом никак помочь не может. Стандартный подход: немедленно обнулить указатель после delete 

delete(ptr);
ptr = 0;

Чтобы полностью исключить его дальнейшее использование. Любые действия с уже удаленным указателем ведут к очень тяжелым последствиям (просто вылет = самое лучшее из них).  Ваш текст будет работать как Вы описали если, например, если вы вызвали delete второй раз для того же указателя. Использование exception в данном случае ничего не даст а только запутает: ptr может быть "корректным адресом" но данные по этому адресу уже разрушены, хуже того - этот адрес может уже используется для совершенно других данных.

Чтобы найти ошибку надо смотреть все что делается с указателем начиная с его создания (обычно new). Это может быть очень непросто но других методов нет.


Название: Re: Почему не срабатывает блок try?
Отправлено: kuzulis от Август 20, 2009, 14:59
Цитировать
if (ptr) printf("валидный указатель");
else printf("Не валидный указатель");
delete ptr;
printf(Удалили);

При ptr=NULL получаю:
Не валидный указатель
Удалили

При ptr указывающий на самоудалившийся процесс, получаю:
валидный указатель

И все, сообщение "Удалили" я не получаю, программа вылетает на delete ptr.
Как мне проверить, объект удален из памяти или нет? В программе с Qt ptr указывает на процесс.
1. Нужно было при самоудалении процесса обнулять указатель на него. По ходу два раза delete вызывается (как говорили выше)
   т.е. объект удалили, а указатель забыли обнулить, при этом в переменной ptr остался адрес удаленного объекта (но объекта уже не существует)! И может быть этот адрес уже используется другим процессом и т.п. Поэтому и вылетает! :) (если я правильно понял ситуевину)
2. Не нужно при указателе = NULL  удалять его :)

т.е. типо так нужно:
Код:
if (ptr) printf("валидный указатель");
else printf("Не валидный указатель");
if (ptr) {
    delete ptr;
    ptr=0;
    printf(Удалили);
}


Название: Re: Почему не срабатывает блок try?
Отправлено: kuzulis от Август 20, 2009, 15:08
т.е наверное у вас что то типо этого:

Код:
class A 
{
public:
 ...
}

A *ptr1, *ptr2;

ptr1 = new A;
ptr2 = ptr1;

delete ptr1;
ptr1=0;

if (ptr2) printf("валидный указатель");
else printf("Не валидный указатель");
delete ptr2;
printf(Удалили);

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



Название: Re: Почему не срабатывает блок try?
Отправлено: pastor от Август 20, 2009, 16:29
2. Не нужно при указателе = NULL  удалять его :)

Абсолютно ничего не произойдет при:

Код
C++ (Qt)
ptr = 0;
...
delete ptr;
 


0-й указатель "удалять" можно.


Название: Re: Почему не срабатывает блок try?
Отправлено: hamlo от Август 20, 2009, 20:51
Код:
class A{
private:
int do_something(){ return 0; };
};

class B {

public:
B();

private:
A* a;
}

B::B(){
a=0

if(!a){
a = new A;
}
a->do_something;
}


Название: Re: Почему не срабатывает блок try?
Отправлено: a_n_y_a от Август 21, 2009, 09:17
Вот код:

void Potok::ZapuskThread()
{
   Pt=new PotokThread((QObject*)this);
   connect(Pt , SIGNAL(finished()), Pt , SLOT(deleteLater()));
   QObject::connect(Pt, SIGNAL(valueChanged(int)), ui.Zapolnenie, SLOT(setValue(int)), Qt::QueuedConnection);
    Pt->start();
};

Potok::~Potok()
{
   delete Pt;
};

Если происходит прерывание программы пока PotokThread не завершился, происходит утечка
памяти, т.е. надо специально удалять объект. (Я так понимаю). Для этого надо проверить его
валидность.


Название: Re: Почему не срабатывает блок try?
Отправлено: BRE от Август 21, 2009, 09:25
Если происходит прерывание программы пока PotokThread не завершился, происходит утечка
памяти, т.е. надо специально удалять объект. (Я так понимаю). Для этого надо проверить его
валидность.
Это если в PotokThread запущен цикл обработки событий:
Код
C++ (Qt)
Potok::~Potok()
{
Pt->quit();
Pt->wait()
};
 

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


Название: Re: Почему не срабатывает блок try?
Отправлено: spectre71 от Август 21, 2009, 09:26
Вот код:

void Potok::ZapuskThread()
{
   Pt=new PotokThread((QObject*)this);
   connect(Pt , SIGNAL(finished()), Pt , SLOT(deleteLater()));
   QObject::connect(Pt, SIGNAL(valueChanged(int)), ui.Zapolnenie, SLOT(setValue(int)), Qt::QueuedConnection);
    Pt->start();
};

Potok::~Potok()
{
   delete Pt;
};

Если происходит прерывание программы пока PotokThread не завершился, происходит утечка
памяти, т.е. надо специально удалять объект. (Я так понимаю). Для этого надо проверить его
валидность.
После
connect(Pt , SIGNAL(finished()), Pt , SLOT(deleteLater()));
Pt->start();
НЕЛЯЗЯ ДЕЛАТЬ!!!
delete Pt;
Может получить повторное удаление Pt! Это баг!

Если происходит прерывание программы система освобождает ресурсы связанные с ней и никакой утечки нет.


Название: Re: Почему не срабатывает блок try?
Отправлено: pastor от Август 21, 2009, 11:02
Если происходит прерывание программы пока PotokThread не завершился

Я уже предлагал вариант завершения потока при завершении работы приложения. Не подходит такой вариант?



Название: Re: Почему не срабатывает блок try?
Отправлено: a_n_y_a от Август 21, 2009, 12:16
Нет, конечно, твой вариант завершения я попробовал, все работает без нареканий. Но вопрос более общий, как перехватить ситуацию в коде вообще, когда происходит прерывание программы по какой то ошибке, например деление на ноль.  Мне предложили _set_se_translator, но что то я не могу найти его применение в линуксе. Есть много в инете по виндовс. На своем диске с линуксом я прошелся поиском, тоже ничего нет. Только wine и bost. Существует ли общее решение проблемы?


Название: Re: Почему не срабатывает блок try?
Отправлено: Igors от Август 21, 2009, 12:20
Вот код:

void Potok::ZapuskThread()
{
   Pt=new PotokThread((QObject*)this);
   connect(Pt , SIGNAL(finished()), Pt , SLOT(deleteLater()));
   QObject::connect(Pt, SIGNAL(valueChanged(int)), ui.Zapolnenie, SLOT(setValue(int)), Qt::QueuedConnection);
    Pt->start();
};

Potok::~Potok()
{
   delete Pt;
};

Если происходит прерывание программы пока PotokThread не завершился, происходит утечка
памяти, т.е. надо специально удалять объект. (Я так понимаю). Для этого надо проверить его
валидность.

В С/C++ не существует способа проверить валидность указателя, надо полагаться на то что все операции с ним были корректны. В данном случае Вы перестарались и удаляете дважды  :)  Одно из удалений надо убрать

Вариант 1
        PotokThread * Pt=new PotokThread((QObject*)this);
   connect(Pt , SIGNAL(finished()), Pt , SLOT(deleteLater()));

Не делайте Pt членом класса, если deleteLater его грохнет, Вы остаетесь с уже удаленным указателем

Вариант 2 (по-моему лучше)
       // connect(Pt , SIGNAL(finished()), Pt , SLOT(deleteLater()));  

Т.е. просто уберите/отключите эту строку. Остальное как у Вас, только в деструктор неплохо бы добавить

Potok::~Potok()
{
        if (Pt->IsRunning())
           Pt->exit();

   delete Pt;
}


Название: Re: Почему не срабатывает блок try?
Отправлено: BRE от Август 21, 2009, 12:21
Нет, конечно, твой вариант завершения я попробовал, все работает без нареканий. Но вопрос более общий, как перехватить ситуацию в коде вообще, когда происходит прерывание программы по какой то ошибке, например деление на ноль.  Мне предложили _set_se_translator, но что то я не могу найти его применение в линуксе. Есть много в инете по виндовс. На своем диске с линуксом я прошелся поиском, тоже ничего нет. Только wine и bost. Существует ли общее решение проблемы?
Я же тебе писал по поводу сигналов linux.  ???
man signal


Название: Re: Почему не срабатывает блок try?
Отправлено: Igors от Август 21, 2009, 12:42
Нет, конечно, твой вариант завершения я попробовал, все работает без нареканий. Но вопрос более общий, как перехватить ситуацию в коде вообще, когда происходит прерывание программы по какой то ошибке, например деление на ноль.  Мне предложили _set_se_translator, но что то я не могу найти его применение в линуксе. Есть много в инете по виндовс. На своем диске с линуксом я прошелся поиском, тоже ничего нет. Только wine и bost. Существует ли общее решение проблемы?
Немного толку от перехвата общего "в программе произошла ошибка". Ну перехватили и что будете делать? Данные уже наверняка искалечены, остается показать окно с извинениями и выйти (да и то неизвестно живы ли еще окна).  Имеет смысл перехватывать на каком-то участке, для этого есть try catch на любой платформе. Хотя перехват обращения по неверному адресу и возможен, это ошибка в программе которую надо исправлять, а не перехватывать


Название: Re: Почему не срабатывает блок try?
Отправлено: Пантер от Август 21, 2009, 13:02
ИМХО, нужно изначально писать так, чтобы отпала надобность в try.


Название: Re: Почему не срабатывает блок try?
Отправлено: BRE от Август 21, 2009, 13:19
Немного толку от перехвата общего "в программе произошла ошибка". Ну перехватили и что будете делать? Данные уже наверняка искалечены, остается показать окно с извинениями и выйти (да и то неизвестно живы ли еще окна).  Имеет смысл перехватывать на каком-то участке, для этого есть try catch на любой платформе. Хотя перехват обращения по неверному адресу и возможен, это ошибка в программе которую надо исправлять, а не перехватывать
Такое поведение подходит для офисных приложений.  ;)
А если процесс управляет чем-то серьезным? Извиниться и выйти...

Почему все данные должны быть искалечены? Часть - может быть, но не все!
Например, перехватив подобные сигналы, можно в обработчике, сохранить важные параметры и спокойно умереть, а процесс-контроллер получит сигнал, что один из дочерних процессов убит и перезапустит его. Дальше считываем сохраненные параметры и продолжаем работу.


Название: Re: Почему не срабатывает блок try?
Отправлено: Rcus от Август 21, 2009, 13:31
А как вы узнаете что область памяти в которой хранятся ваши важные данные не испорчена уже? :) Ошибки доступа к мертвым указателям такие, ага.


Название: Re: Почему не срабатывает блок try?
Отправлено: BRE от Август 21, 2009, 13:40
А как вы узнаете что область памяти в которой хранятся ваши важные данные не испорчена уже? :) Ошибки доступа к мертвым указателям такие, ага.
При таком исходе, это хотя бы дает надежду на возможность сохранения данных, если разрушены, то разрушены.

А проверить валидность данных в блоке можно, например, по присутствию меток и сигнатур или подсчетом контрольных сумм....


Название: Re: Почему не срабатывает блок try?
Отправлено: a_n_y_a от Август 21, 2009, 13:44
Вопрос решился таким образомм:

void term_handler(int i)
{
  printf ("Terminating\n");
  exit(EXIT_SUCCESS);
};

int main()
{
      struct sigaction sa;
      sigset_t newset;
      sigemptyset(&newset);
      sa.sa_handler = term_handler;
      sigaction(SIGSEGV, &sa, 0);

      A *a=0;
      a->b=5;
      a->Pokaz();
...   


Название: Re: Почему не срабатывает блок try?
Отправлено: Igors от Август 21, 2009, 14:04
Вопрос решился таким образомм:

void term_handler(int i)
{
  printf ("Terminating\n");
  exit(EXIT_SUCCESS);
};

int main()
{
      struct sigaction sa;
      sigset_t newset;
      sigemptyset(&newset);
      sa.sa_handler = term_handler;
      sigaction(SIGSEGV, &sa, 0);

      A *a=0;
      a->b=5;
      a->Pokaz();
...   

Это будет работать и под Mac OSX. Но не на Вындоуз, так что говорить о кросс-платформенности не приходится


Название: Re: Почему не срабатывает блок try?
Отправлено: navrocky от Ноябрь 18, 2009, 17:15
Тем кому интересна эта тема, почитайте вот это - http://www.visualdata.ru/blog/109-segv-signal.html (http://www.visualdata.ru/blog/109-segv-signal.html).

Возможность нормально транслировать SIGSEGV и SIGFPE в языковые исключения и потом их обрабатывать есть.