@66demon666
Сетевой админ, АТС-админ

Почему в данном случае не работает обращение к элементам массива по индексу?

Всем привет! Столкнулся с интересной ситуацией. Так как я не очень часто трогаю Desktop языки, то раньше почти не работал с указателями и памятью кроме динамических массивов. Сейчас пишу утилиту для себя, получаю список сетевых интерефейсов и ARP таблицу.

function MyGetIpNetTable(): PMIB_IPNETTABLE;
        var
            ipTable       : PMIB_IPNETTABLE;
            size          : Dword;
            Buffer, ptSeek: PByte;
        begin
            GetIpNetTable(nil, size, True);   //После первого вызова в size попадает необходимый размер буфера
            GetMem(Buffer, size);
            ShowMessage('IpNetTable size: ' + IntToStr(size));
            ipTable := PMIB_IPNETTABLE(Buffer);
            case GetIpNetTable(ipTable, size, True) of
                NO_ERROR:
                    begin
                        ShowMessage('GetIpNetTable no error');
                        Result := ipTable;
                    end;
                else
                    begin
                        Result := nil;
                    end;

            end;
        end;


Первым вызовом GetIpNetTable определяю размер буфера, выделяю память, вторым вызовом получаю данные. Код копейка в копейку с официальной доки microsoft. Но когда в дальнейшем я пробую перебирать таблицу ipTable.table обычным циклом for, то сразу после первого элемента получаю out of range, как будто он не может вычислить адрес следующего элемента.
ipTable   := MyGetIpNetTable();
            ptSeek    := @ipTable.table;
            for var i := 0 to ipTable.dwNumEntries - 1 do
                begin
                     ShowMessage(IntToStr(ipTable.table[i].dwIndex));
                end;


Однако, если я сделаю то же самое вручную, увеличивая указатель на фиксированный размер строки в таблице (_MIB_IPNETROW), то всё прекрасно работает.

ifTable   := MyGetIfTable();
            ptSeek    := @ifTable.table;
            for var i := 0 to ifTable^.dwNumEntries - 1 do
                begin
                    ifRow := PMIB_IFROW(ptSeek);
                    try
                        ListBox1.Items.Add(TEncoding.UTF8.GetString(ifRow.bDescr));
                        ifIndexes[i] := ifRow.dwIndex;
                    Except
                        on Exception do
                            ListBox1.Items.Add('?????')

                    end;
                    ptSeek := ptSeek + SizeOf(_MIB_IFROW);
                end;


Всю кукушку сломал, и на листочке рисовал - не понимаю, почему по индексу обращаться не получается, а прибавляя размер структуры вручную - получается?

Я понял, что это как-то связано с padding, но как именно?

(тут представлены куски 2-х функций, но с ними одинаковая история абсолютно)
  • Вопрос задан
  • 85 просмотров
Решения вопроса 1
wataru
@wataru
Разработчик на С++, экс-олимпиадник.
Могли бы вы привести тип PMIB_IPNETTABLE? Поищите ее в исходниках, все должно стать понятно.

Не специалист по паскалю, но если это структура от winAPI, то она сишная, и там идет работа с указателями и никакой длины массива нет (как часть table). А массивы в паскале по другому реализованы - там длина должна рядом хранится. В этой структуре длина тоже есть, но как часть структуры, а не часть массива. Поэтому с точки зрения паскаля у вас там массив длины 1.
Через указатели все работает, потому что проверок никаких не делается на выход за границу массива.
Массив бы тоже сработал, но там проверка длины встроенная.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
Vapaamies
@Vapaamies
Психанул и снес свои ответы козлам, не отмечающим…
сразу после первого элемента получаю out of range

На самом деле, конкретно здесь, скорее всего, поведение отладочной сборки, в которой наверняка {$R+}. Гуглить, читать. Нужно или обернуть данный участок в {$R-}, или пользовать адресную арифметику в виде PMIB_IPNETROW(@ifTable.table)[i].

Но в си, повторюсь, разницы с массивами нет, поэтому оно там вполне работает и через table[i]

В Си нет понятия {$R+}, поэтому там сплошь уязвимости, ибо людям свойственно ошибаться.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы