Главная страница
    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]
Вот это и доказывает, что представляет. Ознакомься с принципами работы Виндоус по работе с памятью, в части аттрибутов доступа.



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

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

Наверх





Память: 0.57 MB
Время: 0.039 c
2-1128722761
Serg!1!!
2005-10-08 02:06
2005.10.30
Grafika


8-1117665158
TechnoDreamer
2005-06-02 02:32
2005.10.30
Autolevels, autocolor и autocontrast


3-1126793407
erika
2005-09-15 18:10
2005.10.30
генератор через IBquery


14-1129112039
Mike Kouzmine
2005-10-12 14:13
2005.10.30
Подробности происхождения современного человека


2-1128563154
Viktop
2005-10-06 05:45
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
Английский Французский Немецкий Итальянский Португальский Русский Испанский