Текущий архив: 2005.10.30;
Скачать: CL | DM;
ВнизAccess Violation при использовании StrUpper Найти похожие ветки
← →
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.58 MB
Время: 0.041 c