Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Подводные камни
  
О разделе

Список по категориям
Общий список

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  08:31[Войти] | [Зарегистрироваться]

Цикл for.Выполняется итерация для пустого списка

Алексей Михайличенко
дата публикации 10-07-2009 07:04

КАТЕГОРИЯПРОГРАММИСТ .Цикл for.Выполняется итерация для пустого списка
ПРОДУКТDelphi
ПЛАТФОРМАWindows


При работе со списками вроде TList, и вообще везде, где есть свойство Count, обычным является перебор элементов в цикле for:

 list := TList.Create;

 for i := 0 to list.Count-1 do
    обращение к list.Items[i]...

Если переменная i имеет тип Integer, то все работает замечательно.

Но если переменная i имеет тип Word (напомним, этот тип допускает диапазон 0..65535, что, казалось бы, достаточно для большинства списков), то проект компилируется без проблем, но в работе происходит ошибка:

На пустом списке происходит вход в тело цикла, обращение к Items[0], и, соответственно, ошибка List index out of bounds (0).

Отключение оптимизации на ситуацию не влияет.

Понятно, что для пустого списка (Count = 0) цикл выполняться вообще не должен: for i := 0 to -1 — нет проходов.

Причина ошибки в том, что при вхождении в цикл выполняется сравнение переменной цикла типа Word (которая, как мы уже сказали, понимает только 0..65535) с числом со знаком (-1), и происходит ошибка переполнения типа Word.

Кстати, если попытаться задать границы цикла константами, то код вообще не откомпилируется, и будет выдана вполне внятная ошибка:

var i: Word;
for i := 0 to -1 do
[Error] Unit1.pas(34): Constant expression violates subrange bounds

Психологическая подоплека ошибки — в том, что при представлении заполненного списка программист совершенно справедливо подразумевает неотрицательное множество индексов — 0,1,2 и далее, и ему так и просится беззнаковая переменная цикла.

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

   procedure TForm1.Button1Click(Sender: TObject);
   var
     list: TList;
     i: word;
   begin
     list := TList.Create;

34    for i := 0 to list.Count-1 do
35      if assigned(list.Items[i]) then // произвольное обращение к элементу
36        ShowMessage('assigned');
37
38    list.Free;
   end;
Unit1.pas.34: for i := 0 to list.Count-1 do

00452985 668B5F08         mov bx,[edi+$08]
00452989 4B               dec ebx
0045298A 6685DB           test bx,bx           <<<<<<<<<<<<<<<<<<<
ошибка сравнения здесь
0045298D 7221             jb +$21              <<<<<<<<<<<<<<<<<<<
перехода на конец цикла не происходит
0045298F 43               inc ebx
00452990 33F6             xor esi,esi

Unit1.pas.35: if assigned(list.Items[i]) then

00452992 0FB7D6           movzx edx,si
00452995 8BC7             mov eax,edi
00452997 E8DC15FCFF       call TList.Get
0045299C 85C0             test eax,eax
0045299E 740A             jz +$0a

Unit1.pas.36: ShowMessage('assigned');

004529A0 B8C4294500       mov eax,$004529c4
004529A5 E87E50FDFF       call ShowMessage
004529AA 46               inc esi

Unit1.pas.34: for i := 0 to list.Count-1 do

004529AB 66FFCB           dec bx
004529AE 75E2             jnz -$1e

Unit1.pas.38: list.Free;

А вот работающий код, для переменной типа Integer:

Unit1.pas.34: for i := 0 to list.Count-1 do

00452985 8B5F08           mov ebx,[edi+$08]
00452988 4B               dec ebx
00452989 85DB             test ebx,ebx
0045298B 7C1E             jl +$1e             <<<<<<<<<<<<<<<<<<<<<<<
переход происходит
0045298D 43               inc ebx
0045298E 33F6             xor esi,esi


004529A8 4B               dec ebx
004529A9 75E5             jnz -$1b



Типовые решения

Следует использовать для переборов списков только Integer—переменные цикла. А уж если важна оптимизация нескольких байт — то более короткие, но обязательно знаковые типы: Shortint -128..127, Smallint -32768..32767. Либо можно использовать границы цикла for i := 1 to Count, а обращаться к элементу Item[i-1], хотя это будет отличаться от общепринятого подхода, и может вызвать трудности у коллег (все привыкли, что текущий элемент в цикле - это i).

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


Комментарий


Это естественное поведение компилятора, и оно характерно для всех версий Паскаля. Программист не предусмотрел возможного переполнения и получил неожиданный эффект. Обойти ошибку, оставаясь с беззнаковой переменной цикла, можно, если заранее проверять список на пустоту. Но это некрасивое решение. Наиболее грамотным решением будет использовать знаковые целые типы для переменной цикла. И не имеет смысла экономить на размере переменной цикла, даже если для итераций хватит двух или одного байта. Базовым размером для процессора является 4 байта, и с ним он работает наиболее эффективно, а компилятор генерит наиболее эффективный код.

Короче говоря, универсальным правилом является: для переменной цикла for используйте тип Integer, и будет вам счастье.


Смотрите также материалы по темам:


[Операторы, синтаксис языка.] [Целые числа]

 Обсуждение материала [ 19-08-2011 00:53 ] 22 сообщения
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования