Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2005.10.30;
Скачать: CL | DM;

Вниз

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;
Скачать: CL | DM;

Наверх




Память: 0.66 MB
Время: 0.037 c
1-1128585166
DUDAS
2005-10-06 11:52
2005.10.30
Использование ActionManager и ActionToolBar


2-1128601635
koks
2005-10-06 16:27
2005.10.30
Как запретить одновременный запуск нескольких копий пронграммы


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


14-1128700447
ArtemESC
2005-10-07 19:54
2005.10.30
Алгоритм возведения числа в произвольную степень


3-1127150201
kilonet
2005-09-19 21:16
2005.10.30
Структура БД. Нужна помощь