Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Непонятная лшибка при выполнении программы

33K
13 июля 2009 года
Posix86749
54 / / 13.07.2009
Привет, всем.
Есть код:
Код:
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 int srchandl;
 int desthandl;
 int srclength;
 int destlength;
 char *buf;
 int qw;

//-=-------------------------
 if(OpenDialog1->Execute())
 {
      srchandl=FileOpen(OpenDialog1->FileName,fmOpenRead);
      srclength=FileSeek(srchandl,0,2);
      FileSeek(srchandl,0,0);
      buf=new char [srclength+3];
      buf="";
      qw=FileRead(srchandl,buf,srclength);
      FileClose(srchandl);
      int q=1;
      int k=0;
      for(int i=0; i<srclength-1;i++)
       {
         if(((int)buf)!=10)
          {
           StringGrid1->Cells[k][q]=buf;
           StringGrid1->Cells[k][q+1]=(int)buf;
           k++;
          }
         else
          {
           q+=2;
           k=0;
          }
       }
   
 }
 else
 {
 }
}

Смысл такой: при нажатии на эту кнопку открывается окно выбора файла, выбираем файлик (*.txt)и обрабатываем то что в нем лежит. Ошибка возникает когда в файле больше 101 символа, если меньше, то все функционирует нормально, если больше то выскакивает сообщение:

Project raised exception class EAccessViolation with message 'Access Violation at address 46303644. Read Of Address 46303644'.
Кто может подсказать почему?
288
13 июля 2009 года
nikitozz
1.2K / / 09.03.2007
 
Код:
buf="";


Вот этой строкой, вы не обнуляете строку, как того хотите, а заставляете buf указывать совсем на другую область памяти.
33K
13 июля 2009 года
Posix86749
54 / / 13.07.2009
Если не трудно, объясните как обнулить строку такого типа. а то я что-то не понимаю как это сделать:confused:
92
13 июля 2009 года
Тень Пса
2.2K / / 19.10.2006
нафига srclength+3 ? по идее должно быть +1 и в конце добавить \0 после чтения, если FileRead этого не делает, но и того не надо, потому что всё равно дальше srclength ты не проходишь по циклу.
зачем buf=""?
сделал new, читай спокойно строчку из файла.
1
13 июля 2009 года
kot_
7.3K / / 20.01.2000
не забыв в последствии вызвать delete.
И еще замечание - обычно лучше использовать такую конструкцию:
 
Код:
if(!OpenDialog1->Execute())
 {
//Делаем чтото если пользователь не выбрал файл
ShowMessage("Отменено пользователем");
return
}
//делаем все остальное
1
13 июля 2009 года
kot_
7.3K / / 20.01.2000
Кстати - любопытная задача для ньюбов. :)
Данную функцию можно переписать следующим образом:
Код:
if(!OpenDialog1->Execute()){ return;}

      int srchandl=FileOpen(OpenDialog1->FileName,fmOpenRead);
      int srclength=FileSeek(srchandl,0,2);
      FileSeek(srchandl,0,0);
      char* buf=new char[srclength+1];
      if(!FileRead(srchandl,buf,srclength)){
       ShowMessage("Error");
       return;
      }
      FileClose(srchandl);
      int q=1;
      int k=0;
      while(int tmp= (int)*buf++)
       if(tmp!=10){
        StringGrid1->Cells[k][q]=(char)tmp;
        StringGrid1->Cells[k][q+1]=tmp;
        k++;
       }
       else{
         q+=2;
         k=0;
       }
     delete[] buf;

Код содержит ошибку. Какую и как ее избежать? :)
1
13 июля 2009 года
kot_
7.3K / / 20.01.2000
Я кстати, предлагаю обычно этот пример в контексте классического старуструповского примера - но тут тоже самое в принципе. :)
240
13 июля 2009 года
aks
2.5K / / 14.07.2006
Цитата: kot_

Код содержит ошибку. Какую и как ее избежать? :)


Это ты кого щас спрашиваешь? :D

1
13 июля 2009 года
kot_
7.3K / / 20.01.2000
:) да это в принцыпе я вспомнил задачу которую предлагал на собеседовании и модифицировал ее под этот пример
33K
14 июля 2009 года
Posix86749
54 / / 13.07.2009
Цитата: kot_

Код содержит ошибку. Какую и как ее избежать? :)



я в этом деле дилетант, но мне кажется что здесь косяк в том что
1. При попадании нескольких символов с кодом "10", в гриде будте пропускаться несколько строк подряд (для моей проги - это ошибка :) )
2.

 
Код:
while(int tmp= (int)*buf++)
здесь кажись какой-то косяк, только не могу понять в чем:) наверно потому что в с++ не шарю, и просто не пойму что делает эта строчка :D
240
14 июля 2009 года
aks
2.5K / / 14.07.2006
Да меморилик там, если файл не читается. =)
92
14 июля 2009 года
Тень Пса
2.2K / / 19.10.2006
а теперь все дружно сказали: "дааа, точно, а я думал так, но Акс раньше ответил" :D
288
14 июля 2009 года
nikitozz
1.2K / / 09.03.2007
Цитата: Тень Пса
а теперь все дружно сказали: "дааа, точно, а я думал так, но Акс раньше ответил" :D



Эх, опередил :D

1
14 июля 2009 года
kot_
7.3K / / 20.01.2000
На самом деле в коде допущено 3 ошибки. Первая (и самая серьезная) ошибка в том, что происходит удаление невалидного указателя. Мы же в цикле выполняем инкремент, до тех пор пока он валиден, и соотвественно последующая попытка очистить память приведет к ошибке доступа.
Нужно перед циклом сохранять указатель на начало памяти, а затем его востанавливать.
Вторая ошибка - коде производится проверка на количество прочитаных байт, но не проводится проверка на то что файл собственно открыт. Но то о чем сказал aks не верно. Почему? Потому что функции File* не боросают исключений, а оператор new исключения не сгенерирует. Если бы мы предположили что в массиве есть хотябы один элемент и обратились к нему напрямую - то возможно получили бы меморилик (но такое само по себе является ошибкой).
Третья ошибка - проверка количества прочитаных байт. Функции File* возвращают при ошибке -1. Наша проверка этого не учитывает.
Но на самом деле последние две ошибки не существенны и в принципе (по крайней мере в контексте заявленного уровня вопроса для начинающих) их можно не учитывать, тем более что это не языковые ошибки, а особенности конкретной среды разработки.
Кроме того, в коде не проверяется успешность выделения памяти, но это опять же - далеко не уровень начинающего и зависит от требований к коду.
Таким образом исправленый код:
Код:
if(!OpenDialog1->Execute()){ return;}

      int srchandl=FileOpen(OpenDialog1->FileName,fmOpenRead);
      if(srchandl == -1){
       ShowMessage("Error open file");
       return;
      }
      int srclength=FileSeek(srchandl,0,2);
      if(srclength == -1){
       ShowMessage("Error seek file");
       return;
      }
      FileSeek(srchandl,0,0);
      char* buf=new char[srclength+1];
      char* first = buf;
      //Используем единообразный подход
      int srccount = FileRead(srchandl,buf,srclength);
      if(srccount == -1){
       ShowMessage("Error read file");
       return;
      }
      FileClose(srchandl);
      int q=1;
      int k=0;
      while(int tmp= (int)*buf++)
       if(tmp!=10){
        StringGrid1->Cells[k][q]=(char)tmp;
        StringGrid1->Cells[k][q+1]=tmp;
        k++;
       }
       else{
         q+=2;
         k=0;
       }
     buf = first;
     delete[] buf;
     first = NULL;
1
14 июля 2009 года
kot_
7.3K / / 20.01.2000
[holywar]
Это кстати к тому, что к борландовским примерам из справки надо обязательно прикладывать свою голову - и учитывать, что примеры зачастую делались калькой с делфей - ну а делфи это ацтой, что подтверждают множество холиваров.
[/holywar]
:):):)
1
14 июля 2009 года
kot_
7.3K / / 20.01.2000
И еще - если оставить в покое арифметику указателей, а вернуться именно к задаче чтения данных - в приведенном коде (я имею ввиду код из борландовской справки, который выложил топикстартер) имеется пример создания потенциальной уязвимости. В качестве управляющего значения в цикле используется не количество реально прочтенных байт, а размер массива. Потенциально создается ситуация - читаю то, не знаю что.
288
14 июля 2009 года
nikitozz
1.2K / / 09.03.2007
Но ведь потенциальный меморилик (хоть и маловероятный) все же остался. :)
 
Код:
int srccount = FileRead(srchandl,buf,srclength);
   if(srccount == -1){
       ShowMessage("Error read file");
       return;
   }


Если чтение файла вернет ошибку, мы выйдем из функции, не удалив выделенную память.
288
14 июля 2009 года
nikitozz
1.2K / / 09.03.2007
И тогда уже кстати по поводу уязвимостей.
Не лучшая на мой вгляд идея определять размер выдяемой памяти из внешнего источника (как размер файла). Хотя это скорее не мой взгляд, цитата из Саттера :)
И еще файлик размером больше numeric_limits<int>::max() байт мы обработать не сможем. Хотя здесь скорее надо обратиться к первому пункту. :)

Хотя конечно в этой конкретной задаче это имеет мало значения.
1
14 июля 2009 года
kot_
7.3K / / 20.01.2000
Цитата: nikitozz
Но ведь потенциальный меморилик (хоть и маловероятный) все же остался. :)
 
Код:
int srccount = FileRead(srchandl,buf,srclength);
   if(srccount == -1){
       ShowMessage("Error read file");
       return;
   }


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


Да. Зарапортовался. Спасибо.
По поводу выделения памяти на основании внешнего источника - саттеровская рекомендация верна, так же как верна рекомендация "никогда не прыгайте с высоты, что бы не поломать ногу". Что делать в этом случае парашютистам? :) Правильно, для парашютиста опасность поломать ногу - это часть того, чем он занимается.
Точно так же и здесь - если вы читаете нечто извне - некоторые параметры будут определяться не вами. Либо - второй подход - использовать фиксированные размеры буфера (например читать блоками по 4К). Либо... Все зависит от уровня паранойи в вашей задаче.
Тут кстати надо понимать, что есть определенная грань - ведь по сути можно сказать - "нафига все эти навороты, если можно использовать например auto_ptr?" Но, если продолжить аналогию с парашютным спортом, более безопасно прыгать тендемом - все проблемы ложаться на инструктора. Но тогда зачем вообще прыгать с парашютом? :) Т.е. зачем использовать С++, когда есть более безопасные языки? Правильный ответ - должно определяться задачей.

240
14 июля 2009 года
aks
2.5K / / 14.07.2006
[QUOTE=kot_;291354]Но то о чем сказал aks не верно. Почему?[/
QUOTE]
Не верно то что там потенциальный меморилик? Да есть он там. Уж не знаю что там функция вернет, которая в условии стоит, но внутри условного оператора есть. Остальной код не смотрел - ибо не хочется разбираться в отрыве от контекста и не используемых мной либ. =)
14
14 июля 2009 года
Phodopus
3.3K / / 19.06.2008
Там еще и хендллик в том же месте где и мемори. И не только в том же :)
33K
15 июля 2009 года
Posix86749
54 / / 13.07.2009
Народ, вот вы тут часто упоминаете такое слово "меморилик" - это вообще чего такое?:confused:
33K
15 июля 2009 года
Posix86749
54 / / 13.07.2009
И я что-то не пойму, что делает эта строчка
 
Код:
while(int tmp= (int)*buf++)


Насколько моих познаний хватает... этот цикл будет выполнятся, до тех пор пока...в в переменной buf что-то есть? так? т.е. еслt на очередной итерации эелемент buf окажется пустым цикл закончится?
240
15 июля 2009 года
aks
2.5K / / 14.07.2006
Цитата: Posix86749
Народ, вот вы тут часто упоминаете такое слово "меморилик" - это вообще чего такое?:confused:


Утечка памяти. Тоесть выделили, а удалить забыли. Может быть еще и утечка ресурсов - когда системный ресурс у ОС запросили, а не освободили.

Цитата: Posix86749
И я что-то не пойму, что делает эта строчка
 
Код:
while(int tmp= (int)*buf++)


Насколько моих познаний хватает... этот цикл будет выполнятся, до тех пор пока...в в переменной buf что-то есть? так? т.е. еслt на очередной итерации эелемент buf окажется пустым цикл закончится?


Цикл будет выполняться, пока не встретит элемент массива равный нулую. =)

33K
15 июля 2009 года
Posix86749
54 / / 13.07.2009
Цитата: aks
Цикл будет выполняться, пока не встретит элемент массива равный нулую. =)


подождите, но ведь в условии нету сравнения с нулем. есть только присваивание переменной tmp очередного значения buf. Блин, ваще понять не могу. Понимаю что все проще паренной репы, а все равно не пойму:mad:

92
15 июля 2009 года
Тень Пса
2.2K / / 19.10.2006
ну, а это условие что вообще делает??? это проверка tmp на значение.

по сути while (tmp != 0) (только ессна тут tmp меняется)
1
15 июля 2009 года
kot_
7.3K / / 20.01.2000
Цитата: aks
Утечка памяти. Тоесть выделили, а удалить забыли. Может быть еще и утечка ресурсов - когда системный ресурс у ОС запросили, а не освободили.


Цикл будет выполняться, пока не встретит элемент массива равный нулую. =)


классика

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог