Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Потрепаться";
Текущий архив: 2005.10.30;
Скачать: [xml.tar.bz2];

Вниз

Access Violation при использовании StrUpper   Найти похожие ветки 

 
raiks   (2005-10-09 00:36) [0]

У меня обнаружилась довольно странная проблема: при попытке использовать библиотечные функции для работы со строками PChar (типа StrUpper, StrLower) у меня постоянно вываливается табличка об Access Violation. После исследования под отладчиком выяснилось, что сбой происходит в момент записи измененного символа обратно в строку, то есть инструкция

MOV [ESI-1],AL

приводит к ошибке. Под 5-м Билдером ничего подобного не наблюдается? В чем же дело?


 
Джо ©   (2005-10-09 00:38) [1]

Код засекречен?


 
raiks   (2005-10-09 00:59) [2]

В смысле? :)


 
Германн ©   (2005-10-09 01:47) [3]

В смысле "никакая ассемблерная инструкция, сама по себе, не может вызвать ошибку!" Может вызвать ошибку её применение с неверными параметрами. А вот уж насчет параметров, будь любезен приведи исходник! На Асме, Билдере, Дельфи - не важно. Важно знать, на что указывает [ESI-1]. И как сей регистр получил это значение.


 
raiks   (2005-10-09 02:48) [4]

Вот исходник библиотечной функции StrUpper. Ошибка вылезает при любом параметре:

function StrUpper(Str: PChar): PChar; assembler;
asm
       PUSH    ESI
       MOV     ESI,Str
       MOV     EDX,Str
@@1:    LODSB
       OR      AL,AL
       JE      @@2
       CMP     AL,"a"
       JB      @@1
       CMP     AL,"z"
       JA      @@1
       SUB     AL,20H
       MOV     [ESI-1],AL         // !!! В этом месте происходит Access Violation
       JMP     @@1
@@2:    XCHG    EAX,EDX
       POP     ESI
end;

Самое смешное, что я ради интереса установил Делфи 3, и в нем происходит та же ошибка! Ума не приложу, что за фигня твориться...


 
Джо ©   (2005-10-09 02:53) [5]


>  [4] raiks   (09.10.05 02:48)

Вот интересен ход твоих мыслей. Вместо того, чтобы привести свой код, из-за которого получаешь Access Violation, ты зачем-то приводишь код библиотечной функции, который любой желающий может посмотреть и сам, если ему захочется...


 
Джо ©   (2005-10-09 03:00) [6]

Приношу свои извинения автору вопроса. Действительно, AV получаем даже при выполнении кода, приведенного в качестве примера к этой функции в Справке. Хм.


 
Германн ©   (2005-10-09 03:31) [7]

Не торопись! Д6 уже работает достаточно долго.


 
GanibalLector ©   (2005-10-09 03:45) [8]

Она не воспринимает маленькие англ.буквы! С русскими\цифрами\БОЛЬШИМИ  английскими все в порядке.


 
GanibalLector ©   (2005-10-09 03:47) [9]

2 GanibalLector ©   (09.10.05 03:45) [8]
С другой стороны,я не вижу тогда смысла!
З.Ы. А пример и вправду не работает!


 
Джо ©   (2005-10-09 03:55) [10]


>  [7] Германн ©   (09.10.05 03:31)
> Не торопись! Д6 уже работает достаточно долго.

Точно такой же код StrUpper и в Delphi 2005. Хотя лично я не вижу нужды в этой функции вообще, но - странно, все-таки.


 
Юрий Зотов ©   (2005-10-09 03:57) [11]

procedure TForm1.FormCreate(Sender: TObject);
begin
 Caption := String(StrUpper(PChar(Caption)))
end;

И все прекрасно работает (D7).

Как правило, Access Violation при работе с PChar возникает по тривиальной причине - не выделена память. Программистом, естественно.


 
Джо ©   (2005-10-09 04:02) [12]


>  [11] Юрий Зотов ©   (09.10.05 03:57)

Пример в Справке вводит в заблуждение.


 
Джо ©   (2005-10-09 04:06) [13]


> [11] Юрий Зотов ©   (09.10.05 03:57)

Кроме того, вот попробуйте это:

const
 S: PChar = "string";
begin
 StrUpper(S);
end;

Как вы думаете - такой результат функции (AV) можно предугадать, изучив текст Справки?


 
begin...end ©   (2005-10-09 10:00) [14]

В справке, конечно, ошибка. Но AV можно было предсказать и без справки.

И память здесь выделена. Только писать в неё нельзя.


 
jack128 ©   (2005-10-09 14:32) [15]

Юрий Зотов ©   (09.10.05 3:57) [11]
Гм..  Работает то оно конечно работает. Но так лудше не писать, а то войдет в привычку, а потом глюков необерешся ;)

type
 TTest= class
 private
   FCaption: string;
   procedure SetCaption(const Value: string);
 protected
   procedure Changed; virtual;
 public
   property Caption: string read FCaption write SetCaption;
 end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 with TTest.Create do
 try
   Caption := "Test"; // Первый ShowMessage пошел.
   Caption := String(StrUpper(PChar(Caption))); // А вот второй - нет. Правда весело? ;)
 finally
   Free;
 end;
end;

{ TTest }

procedure TTest.Changed;
begin
 ShowMessage("Changed");
end;

procedure TTest.SetCaption(const Value: string);
begin
 if FCaption <> Value then
 begin
   FCaption := Value;
   Changed;
 end;
end;


 
raiks   (2005-10-09 15:30) [16]

2 All:

Спасибо всем за ответы.

Собственно говоря, я решил написать об этом не из-за того, что мне так уж потребна функция StrUpper, а потому, что ее ошибка - всего лишь один из прецедентов. Не работают также функции StrLower и еще некоторые, связанные с обработкой строк ASCIIZ. Более того! Не работает даже CharUpper, импортируемая из [b]ntdll.dll[/b]. Честно говоря, я не понимаю, в чем тут дело, ибо [i]в C++ Builder 5 те же самые функции (в том числе и внешняя CharUpper) работают нормально[/i]. Почему же происходит сбой в StrUpper при обработке строк со строчными буквами "a"..."z"? Если посмотреть в исходник (см. выше), то можно увидеть, что остальные символы попросту пропускаются. Корень зла кроется в строке [i]MOV [ESI-1],AL[/i], то есть AV происходит при попытке изменения символа в строке (записи в память). Конечно, ничего нового я не открыл, но все же непонятно, виновата ли тут система (XP), или еще что нибудь. Интересно было бы проверить под 98-й Виндой, но нет под рукой дистрибутива.

На все это я наткнулся по чистой случайности, когда писал на встроенном ассемблере.


 
GrayFace ©   (2005-10-09 16:08) [17]

raiks   (09.10.05 15:30) [16]
Это особенность компиллятора.
Например, приведенный выше код:
const
S: PChar = "string";
begin
StrUpper(S);
end;

Здесь S - это указатель на константную строку "string", а константы изменять низзя.


 
Anatoly Podgoretsky ©   (2005-10-09 16:09) [18]

Так и не дождались реального кода от автора. Типичный партизан.


 
Джо ©   (2005-10-09 16:10) [19]


>  [18] Anatoly Podgoretsky ©   (09.10.05 16:09)
> Так и не дождались реального кода от автора. Типичный партизан.

Код можно взять из Справки (раздел Examples к сабжевой функции). В ветке выше уже упоминался недобрым словом :)


 
Anatoly Podgoretsky ©   (2005-10-09 16:14) [20]

Я код могу взять где угодно, но не про мой же код, а про код автора, в котором у него возникает ошибка, он бы еще привер асм код из kernel


 
begin...end ©   (2005-10-09 16:15) [21]

AV будет и в случае такого кода:

var
 P: PChar;
begin
 P := "string";
 StrUpper(P)
end.

И это тоже совершенно нормально и понятно.


 
Anatoly Podgoretsky ©   (2005-10-09 16:18) [22]

AV будет, нет прав на запись в данную область.
Другая типичная ошибка, S string
OemToChar(PChar(S), PChar(S));


 
Джо ©   (2005-10-09 16:22) [23]


>  [21] begin...end ©   (09.10.05 16:15)
> AV будет и в случае такого кода:
>
> var
>  P: PChar;
> begin
>  P := "string";
>  StrUpper(P)
> end.
>
> И это тоже совершенно нормально и понятно.

Не стану спорить насчет понятности, но в чем же нормальность?


 
Джо ©   (2005-10-09 16:33) [24]


>  [23] Джо ©   (09.10.05 16:22)

Пардон, следует читать так: "Не стану спорить насчет нормальности, но в чем же понятность?"
Я вот что имею в виду. Если функция берет на себя возвращать указатель на массив символов, то вполне правомочно ожидать, что память для буфера она выделит сама.


 
begin...end ©   (2005-10-09 16:34) [25]

> Джо ©   (09.10.05 16:22) [23]

> Не стану спорить насчет понятности, но в чем же нормальность?

А в чём ненормальность?

Строковый литерал находится в read-only области памяти (что и неудивительно -- зачем же константную строку размещать в памяти с правом записи?). Указатель на этот литерал присваивается переменной P. Затем производится попытка изменить содержимое read-only памяти -- и получаем вполне закономерное AV. Так что же здесь ненормального?


 
Anatoly Podgoretsky ©   (2005-10-09 16:36) [26]

А стоит поспорить, как раз понятно, но не нормально. Пытаться изменять константу - это не нормально, но желание понятно.


 
begin...end ©   (2005-10-09 16:37) [27]

> Джо ©   (09.10.05 16:33) [24]

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

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


 
Джо ©   (2005-10-09 16:41) [28]

[27] begin...end ©   (09.10.05 16:37)
> Если функция принимает указатель на область памяти, которую
> она собирается изменять,

А из чего следует, что она собирается его изменять? Это было бы естественным в случае с процедурой. А раз функция - то она должна нечто возвращать, оставив параметр неизменным. Иначе зачем тогда возвращать еще раз тот же указатель, который ей и был передан? Мое мнение таково, что эту функцию нельзя использовать, не ознакомившись предварительно с ее исходным кодом. Потому что сигнатура вводит в заблуждение.


 
begin...end ©   (2005-10-09 16:46) [29]

> Джо ©   (09.10.05 16:41) [28]

> А из чего следует, что она собирается его изменять?

Из справки.

function StrUpper(Str: PChar): PChar;

Description: StrUpper converts Str to uppercase and returns Str.


Из этого следует, что она возвращает тот же самый указатель, что и принимает. А значит, она изменяет область памяти, на которую этот указатель указывает. Иначе она возвращала бы уже другой указатель.


 
Anatoly Podgoretsky ©   (2005-10-09 16:54) [30]

В справке четко указано
StrUpper converts Str to uppercase and returns Str.
Почти все Str функции так работают, а для большинства программистов работа с PChar это терраинкогнито, ну не понимает их народ.
Борланд говорит что это только было включено в Д1 для совместимости с WinApi, с тех пор появились длинные строки, которые полностью совместимы с PChar


 
raiks   (2005-10-09 16:58) [31]

Давайте разъясним кое-какие вещи. Во-первых, StrUpper - процедура. А во-вторых, вот пример моего "партизанского" кода, который вызывает сбой:

//Delphi:

procedure DoSomething(p: PChar);
asm
 mov esi, p          // Адрес первого символа - в ESI (= mov esi, eax)
 sub [esi], $20h   // Перевести символ в верхний регистр
end;

var p: PChar;
begin
 p:="suxx";                          // Память для строки уже выделена!
 DoSomething(p);                // ACCESS VIOLATION
end;

// То же самое для C++:

void DoSomething(char * c)
{
  asm
    {
      mov esi, c
      sub [esi], 0x20
    }
}

char * c = "suxx";
DoSomething(c);                // РАБОТАЕТ

Ни о каких константах речи не идет - происходит тривиальная операция, в результате которой вылезает AV. Идем дальше:

function CharUpper; external user32 name "CharUpperA";

var p: PChar;
begin
 p:="suxx";
 CharUpper(p);               // "Access Violation in ntdll.dll бла-бла-бла"
end;

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
 char * c = "suxx";
 CharUpper(c);              // OK
}


 
begin...end ©   (2005-10-09 17:04) [32]

> raiks   (09.10.05 16:58) [31]

> p:="suxx"; // Память для строки уже выделена!

Да, она выделена. Иначе где же размещается строка "suxx"?

Но у областей памяти есть разные атрибуты доступа. И строка "suxx" размещена в памяти с атрибутом ReadOnly. Вот в этом-то и весь фокус.


 
Anatoly Podgoretsky ©   (2005-10-09 17:17) [33]

Давай все таки поставим точки над И
function StrUpper(Str: PChar): PChar; это из справки
Если это твоя собственная процедура, то где ее код?


 
raiks   (2005-10-09 17:17) [34]

2 begin..end:

А разве нормально, что стандартные подпрограммы (это я все о StrUpper/ StrLower etc.) вызывают сбой при элементарных рутинных операциях, когда не работает пример даже из официального хэлпа? Неужели наивные Борландовские программеры не знали о такой вещи, как атрибут доступа, и ошиблись и в 3, и в 6 версиях Делфи? :) Опять-таки, почему в С++Builder АБСОЛЮТНО тот же код работает? Вот чего я никак не могу понять... Может, дело в настройках компилятора? В NT"шном ядре? В чем-то еще?


 
begin...end ©   (2005-10-09 17:25) [35]

> raiks   (09.10.05 17:17) [34]

> А разве нормально, что стандартные подпрограммы (это я все
> о StrUpper/ StrLower etc.) вызывают сбой при элементарных
> рутинных операциях, когда не работает пример даже из официального
> хэлпа?

То, что возникает AV в данном случае -- это нормально. То, что в справке приведён некорректный пример -- это ненормально.


 
sniknik ©   (2005-10-09 17:25) [36]

> АБСОЛЮТНО тот же код работает?
это АБСОЛЮТНО разный код

var p: PChar;
begin
p:="suxx";  
DoSomething(p);
end;

<>

char * c = "suxx";
DoSomething(c);

в первом память не выделена.


 
raiks   (2005-10-09 17:28) [37]

Anatoly Podgoretsky:

Sorry, перепутал. Но разве это что-то меняет? Пример кода я уже написал выше. Никакой terra incognito PChar из себя не представляет - обычный указатель на строку ASCIIZ. Хорошо, возьмем не PChar, а record. Почему же тогда следующая процедура работает, а строка PChar вызывает сбой?

type
 TMyRec = packed record
   i: Integer;
   n: array [0..4] of Char;
 end;

procedure CopyRec(r1, r2: TMyRec);
asm
 mov esi, r1
 mov edi, r2
 mov ecx, 9
 rep movsb
end;

var r1, r2: TMyRec;
begin
 r1.i:=5;
 r1.n:="suxxx";
 CopyRec(r1, r2);       // OK
end;


 
raiks   (2005-10-09 17:34) [38]

sniknik:

И чем же он "абсолютно разный"? По вашему мнению, память не выделяется при присвоении строке PChar значения? Разница лишь в том, что в С++ строке присвоено значение при объявлении, а синтаксис Delphi этого не позволяет делать с локальными переменными. В остальном оба примера идентичны. Посмотрите под отладчиком код, и вы увидите, что обе строки уже размещены в памяти перед вызовом процедуры. Не поленитесь.


 
begin...end ©   (2005-10-09 17:41) [39]

> raiks   (09.10.05 17:34) [38]

> Посмотрите под отладчиком код, и вы увидите, что обе строки
> уже размещены в памяти перед вызовом процедуры. Не поленитесь.

Не поленитесь и Вы посмотреть под отладчиком (только не под встроенным), где в памяти расположена строка "suxx" в том случае, когда возникает AV, и какой атрибут доступа эта память имеет.


 
Anatoly Podgoretsky ©   (2005-10-09 17:47) [40]

raiks   (09.10.05 17:28) [37]
Вот это и доказывает, что представляет. Ознакомься с принципами работы Виндоус по работе с памятью, в части аттрибутов доступа.


 
sniknik ©   (2005-10-09 17:48) [41]

raiks   (09.10.05 17:34) [38]
PChar это не стринг.

> Разница лишь в том, что в С++ строке присвоено значение при объявлении
ну если нет разници то делай аналогично.

char * c;

c = "suxx";
DoSomething(c);

это тоже самое, в с/с++?. (боюсь ошибиться, т.к. с не знаю, но по моему нет)


 
sniknik ©   (2005-10-09 17:53) [42]

raiks   (09.10.05 17:34) [38]
адекватная запись в дельфи будет (более менее ;)

const с: array[0..length("suxx")] of char = "suxx";
begin
 DoSomething(c);
end;


 
raiks   (2005-10-09 17:56) [43]

Все! Всем спасибо. Методом научного тыка разобрался :) Надо было писать так:

var
 p: PChar;
begin
 p:="suxx";
 p:=StrNew(p);
 StrUpper(p);       // No Access Violation :)
end;

Странно, что борландовцы этого в хэлпе не написали.
Уфф...


 
sniknik ©   (2005-10-09 18:04) [44]

> Методом научного тыка разобрался :)
нифига ж себе "научный тык"
это после прямых указаний (только за свое. есть и реньше)

sniknik ©   (09.10.05 17:25) [36]
...
> в первом память не выделена.

> Странно, что борландовцы этого в хэлпе не написали.
не там смотрел. это основы работы с динамическими переменными (PChar в частности)


 
GuAV ©   (2005-10-09 18:53) [45]

raiks   (09.10.05 17:56) [43]

Нужно освобождать через StrDispose.


 
Anatoly Podgoretsky ©   (2005-10-09 19:06) [46]

raiks   (09.10.05 17:56) [43]
Смотрим в справку backward compatibility routines
И смотрим справку по StrDispose StrDispose is provided for backward compatibility only


 
Джо ©   (2005-10-09 20:19) [47]


> [29] begin...end ©   (09.10.05 16:46)
>Description: StrUpper converts Str to uppercase and returns Str.
> [30] Anatoly Podgoretsky ©   (09.10.05 16:54)

Да, это я "зеванул".
Но в чем загадочная цель возвращать тот же самый указатель, который и был передан. Я, собственно, к тому и вел, что именно такая декларация функции не делает очевидным (без докумментации) ее назначение. Т.е, просто нарушается принцип сокрытия подробностей реализации.


 
Anatoly Podgoretsky ©   (2005-10-09 20:26) [48]

Джо ©   (09.10.05 20:19) [47]
Для того что бы можно было использовать как функцию, а значит включить как параметр. Почти все Str функции так построены. То есть только для удобства. На самом деле это как бы "var" параметр


 
y-soft ©   (2005-10-09 20:28) [49]

>Джо ©   (09.10.05 20:19) [47]

 Но в чем загадочная цель возвращать тот же самый указатель, который и был передан

Ничего загадочного. Чтобы потом не забывали освобождать выделенную под результат память как раз по причине принципа сокрытия реализации :)

Хотя в такого рода процедурах для большей ясности принято возвращать результат через var параметр

Что-то типа

procedure StrUpper(var S: PChar);


 
y-soft ©   (2005-10-09 20:29) [50]

>Anatoly Podgoretsky ©   (09.10.05 20:26) [48]

Опередил :)


 
begin...end ©   (2005-10-09 20:34) [51]

> Джо ©   (09.10.05 20:19) [47]

> Но в чем загадочная цель возвращать тот же самый указатель,
> который и был передан.

Представьте, что StrUpper -- это процедура, и ей указатель передаётся как var-параметр. Теперь попробуйте переписать хотя бы тот неправильный пример из справки. Сравните читабельность кода в этих двух вариантах.

P.S. А Вы на C никогда не писали? strcpy, strcat, memcpy, itoa... Вроде, пользуются люди, и ничего...


 
Anatoly Podgoretsky ©   (2005-10-09 20:45) [52]

begin...end ©   (09.10.05 20:34) [51]
Это не var параметр, передается по значению, здесь var параметр и не требуется, поскольку значение не меняется.

function StrUpper(Str: PChar): PChar;


 
begin...end ©   (2005-10-09 21:02) [53]

> Anatoly Podgoretsky ©   (09.10.05 20:45) [52]

Я понимаю, что не меняется. Я написал так из-за [24].

Но даже если и без var-параметра -- всё равно читаемость ухудшится.


 
raiks   (2005-10-09 22:29) [54]

Anatoly Podgoretsky:

Несмотря на то, что функция StrUpper возвращает указатель, сам аргумент тоже изменяется - PChar всегда передается по ссылке вне зависимости от того, объявлен он как var или нет (это касается всех указателей, а PChar - всего лишь указатель на строку с нулевым окончанием).


 
Anatoly Podgoretsky ©   (2005-10-09 22:40) [55]

Ничего здесь по ссылке не передается, а передается значения указателя, вот если ты объявишь var P: PChar вот тогда будет по ссылке.
А что за тип PChar объяснять не стоит. Это все таки не указатель на строку с нулевым символом, это не более чем соглашение, которое не всегда и использвется, иногда передается на "строку" и без завершающего нуля, дополнительно передается и длина и эти функции как правило не должны анилизировать этот ноль, а работать с переданной длиной.
Я уже говорил, ты не совсем разобрался что такое PChar отсюда и твои проблемы.


 
begin...end ©   (2005-10-10 13:35) [56]

> raiks   (09.10.05 22:29) [54]

Гм... Давайте всё-таки вместе разберёмся и поставим все точки над "i", чтобы никаких вопросов не осталось.

Выясним, почему дельфишный код из [31] не работает, а дельфишный же код [37] работает. Поскольку Вы пишете подпрограммы на ассемблере, думаю, что разобраться труда не составит.

1. Код, аналогичный [31]:

procedure P;
var
 P: PChar;
begin
 P := "suxx"; // (1)
 P^ := "a"    // (2)
end


Этот код от [31] принципиально не отличается -- вначале переменной типа PChar присваивается константная строка, затем производится попытка изменить эту строку (в данном случае -- поменять первый символ).

Ставим контрольную точку на выделенной строке, вызываем процедуру, жмём Ctrl-Alt-C и смотрим в окно ЦП. Вот весь ассемблерный код этой процедуры (строки ассемблерного кода пронумерованы соответственно строкам дельфишного):

(1) mov eax,$0044d950
(2) mov byte ptr [eax],$61
(3) ret


Первое, что можно заметить -- оптимизатор не стал размещать локальную переменную P в стеке, а использует для этого регистр eax. Т.е. содержимое eax -- это содержимое переменной P. Начинаем пошагово выполнять код (по F8).

В строке (1) этой переменной присваивается значение $0044d950. У Вас этот адрес может быть другим. Что это за значение? В окне CPU переходим в окошко с содержимым памяти, в контекстном меню выбираем "Goto Address..." и вводим либо "$0044d950" (разумеется, если у Вас адрес отличается от этого, нужно ввести именно Ваш), либо (если строка (1) уже выполнилась) просто "eax". Видим, что в памяти по этому адресу как раз и находится строка "suxx", причём завершающаяся нулём. Вот в этом-то всё присваивание константной строки PChar-переменной и заключается, понимаете? Память для строки уже выделена (она была выделена ещё при запуске Вашей программы), строка уже находится в памяти, а всё присваивание сводится к тому, что в переменную P просто помещается адрес этой строки. Больше ничего не происходит.

Идём дальше. В строке (2) пытаемся поменять первый символ строки, т.е. в байт по адресу, содержащемуся в eax, пытаемся записать символ "a" (с кодом $61). Нетрудно убедиться, что именно здесь и происходит AV. Почему? Тут уже не обойтись без стороннего отладчика, т.к., насколько я знаю, встроенный отладчик не позволяет посмотреть на атрибуты доступа памяти. Подойдёт любой другой, позволяющий это увидеть (например, я пользуюсь OllyDbg -- его можно свободно скачать в Сети). Всё, что нужно -- это найти ту самую строку "suxx" (а если в коде ничего не изменялось, то она так и будет расположена по адресу $0044d950) и посмотреть, в какой области памяти она находится. Можно увидеть, что у диапазона адресов $004D0000..$00556000 (а именно в этот диапазон входит адрес строки) атрибуты -- RE, что означает read + execute. А W (write) -- нету. Т.е. в этот диапазон адресов писать нельзя. Отсюда и AV. И то, что константная строка размещена в памяти без права записи -- по-моему, вполне логично.

(3) -- выход из подпрограммы.

2. Код, аналогичный [37]:

procedure P;
var
 N: array [0..4] of Char;
begin
 N := "suxx";
 N[0] := "a"
end

(1) add esp,-$08
(2) mov eax,[$0044d964]
(3) mov [esp],eax
(4) mov al,[$0044d968]
(5) mov [esp+$04],al
(6) mov byte ptr [esp],$61
(7) pop ecx
(8) pop edx
(9) ret


Тут уже посложнее.

(1) -- выделение памяти под локальную переменную N (массив). Достаточно 5 байт, но компилятор выделяет 8 -- видимо, ему так удобнее.

(2) -- в eax помещается содержимое памяти по адресу $0044d964. У Вас этот адрес опять может быть другим. Это опять адрес константной строки "suxx" (можно проверить). Т.е. в eax копируются (читать-то ведь по адресу $0044d964 можно!) первые 4 символа строки (а она состоит из пяти -- не забываем про завершающий ноль).

(3) -- эти 4 символа строки копируются в начало массива N (esp -- это и есть адрес массива).

(4), (5) -- то же, что и (2) и (3), только для последнего символа.

(6) -- запись в первый символ массива. Заметьте -- не в ту память, где изначально находилась константная строка, а в копию этой строки. В том, что в память по адресу, содержащемуся в ESP, писать можно, опять легко убедиться с помощью отладчика (не встроенного). У меня атрибуты такие: RW (read + write), и ещё guarded (типично для стека). Поэтому никакого AV тут и нету.

(7), (8), (9) -- балансировка стека и выход из процедуры.

-------------------------------------------------------------------------------

Теперь скажите -- всё понятно?


 
Игорь Шевченко ©   (2005-10-10 14:16) [57]

"You can"t modify a constant, float upstream, win an argument with the IRS, or satisfy this compiler"
(c) MPW C Compiler


 
GrayFace ©   (2005-10-11 16:46) [58]

Anatoly Podgoretsky ©   (09.10.05 16:18) [22]
AV будет, нет прав на запись в данную область.
Другая типичная ошибка, S string
OemToChar(PChar(S), PChar(S));

А почему так нельзя?


 
Джо ©   (2005-10-11 16:50) [59]


>  [57] Игорь Шевченко ©   (10.10.05 14:16)
> "You can"t modify a constant, float upstream, win an argument
> with the IRS, or satisfy this compiler"
> (c) MPW C Compiler

Класс :)


 
Anatoly Podgoretsky ©   (2005-10-11 17:32) [60]

GrayFace ©   (11.10.05 16:46) [58]
Особенности работы функции PChar, если аргумент пустой, то будет возвращена строка-константа из кодового сегмента, которая естественно ReadOnly



Страницы: 1 2 вся ветка

Форум: "Потрепаться";
Текущий архив: 2005.10.30;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.65 MB
Время: 0.069 c
2-1128495508
Dush
2005-10-05 10:58
2005.10.30
Grid


1-1129009022
kolos_rus
2005-10-11 09:37
2005.10.30
Как узнать активную форму?


14-1128922219
syte_ser78
2005-10-10 09:30
2005.10.30
посоветуйте программу для составления блок-схем, алгоритмов


2-1128513848
Vladimir88
2005-10-05 16:04
2005.10.30
Вещественные числа


4-1124647881
DR0N
2005-08-21 22:11
2005.10.30
Как перехватить сообщения поступающие панели?





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский