Текущий архив: 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 stringOemToChar(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;
Скачать: CL | DM;
Память: 0.57 MB
Время: 0.042 c