Помощь - Поиск - Пользователи - Календарь
Полная версия: Как скопировать строку PChar ?
Форум «Всё о Паскале» > Pascal, Object Pascal > Теоретические вопросы
Ldnb
Думал, что, судя по описанию, функция StrCopy делает полноценное копирование, то есть автоматически выделяет память под копию данных. Как оказалось, нет, копируется только указатель, то есть, если после копирования из буферной переменной её значение меняется, то меняется и "скопированная" строчка.
Так как же всё-таки работать с этим странным PChar?
TarasBer
Перед StrCopy надо заранее выделить память (длина строки + 1 байт на нулевой символ)

> Так как же всё-таки работать с этим странным PChar?

Как? С геморроем...
В современных реализациях Паскаля память автоматом выделяется под строки сколь угодно большой длины, и не надо никакого PChar.
Ldnb
К сожалению, надо на Турбо Паскале... ;(

Можно маленький примерчик? Должно быть как-то так?

var p: PChar;
buffer: {буфер длины len}
...
new(p, len+1);
StrCopy(p, buffer);




Кстати, а со StrCat как обстоит дело? Тоже как-то извращаться?
TarasBer
> К сожалению, надо на Турбо Паскале... ;(

Задание такое, что ли? Ну главное из этого задания, я так понял, вы уже усвоили - что строки в стиле ЦЭ есть великое зло.

> Можно маленький примерчик? Должно быть как-то так?

Примерно так, да.
volvo
Цитата
Так как же всё-таки работать с этим странным PChar?
Правильно копировать строку, предварительно прочитав имеющуюся документацию по модулю Strings

var P, P1: PChar;
...
P1 := StrNew(P); { <--- Будет создана копия строки P }
...
StrDispose(P1); { <--- Не забываем за собой чистить }


Цитата
Как? С геморроем...
dry.gif "Плохому танцору... " (С) Народная мудрость...
Ldnb
Спасибо!
А правильно добавить строку p2 к строке p1, стало быть, можно так:
 StrCat(p1, StrNew(P2)); 
?
volvo
А как потом освобождать память в таком случае?
Ldnb
А как правильно? sad.gif Подскажите, пожалуйста.
В общем, какими функциями пользоваться, если требуется работать со строками переменной (большой) длины, дописывая к одним из них копии других, а также строковые константы... Инициализировать их, считывая из файла с помощью буфера путём P1 := StrNew(buf); уже получается, а дальше всё равно лезут глюки...
volvo
Хм... Вот так:

uses strings;
var
s: array[0 .. 79] of char;
p, pp, p1, p2: Pchar;
begin
writeln(memavail); { <--- потом сравним, сколько памяти было и сколько осталось }
readln(s);
P := strnew(s);
pp := strnew(s);

p2 := strcat(pp, strnew(p));

p[3] := '8'; { Проверка, действительно ли создается копия строки P }

writeln('p = ', p);
writeln('p2 = ', p2);

strdispose(p2); { <--- Удалять ТОЛЬКО P2, строку PP удалять не надо, она "внутри" P2 }
strdispose(p);
writeln(memavail); { <--- Сравниваем с ранее напечатанным числом }
end.

утечек памяти нет. Если у тебя все еще продолжаются глюки - ты бы привел программу (с заданием). Глядишь, и поправили бы общими усилиями...

Да и вообще, тему по-моему пора переносить в раздел "Задачи", тут уже речь не о теории а именно о реализации.
Ldnb
Так...значит, всё-таки операция strcat(pp, strnew(p)); корректна, отлично
А как быть в случае дописывания к строке PChar строковой константы? Похоже, здесь и начинались глюки. Попробовать, что ли, так?
 p2 := strcat(pp, strnew(PChar('константа'));
Проверю-ка...

TarasBer
Цитата(volvo @ 11.05.2010 14:04) *

dry.gif "Плохому танцору... " (С) Народная мудрость...


Это к чему было? Вы хотите сказать, что только у меня проблемы с этим типом данных? Что утечки памяти и уязвимости, связанные с этим типом, придумал я?

Цитата

p2 := strcat(pp, strnew(PChar('константа'));


На самом деле, прекрасно прокатывает такой код:
p2 := strcat(pp, strnew('const'));
Однако, если забыть про strnew, то появляется утечка.
volvo
Цитата
Попробовать, что ли, так?
Так не пойдет, будет утечка памяти.

uses strings;
var
s: array[0 .. 79] of char;
p, pp, p2: Pchar;

begin
writeln(memavail);
readln(s);
pp := strnew(s);

p := nil;
p2 := strcat(pp, strnew(strcat('', strpcopy(p, ' just a test'))));
writeln('p2 = ', p2);

strdispose(p2);
writeln(memavail);
end.

Так - не будет...
TarasBer
Цитата(volvo @ 12.05.2010 12:58) *

Так не пойдет, будет утечка памяти.


Что я делаю не так?
Нажмите для просмотра прикрепленного файла
Просто приведение к PChar лишний раз не нужно, и всё.
Ldnb
blink.gif blink.gif blink.gif blink.gif blink.gif blink.gif
Бился, бился с отладкой программы, переделывал и так и этак, пока не локализовал причину... Если коротко, то причина вот в чём:
{$X+}
uses strings;

var testArr:array [0..1] of PChar;
begin
testArr[0]:= StrNew('!');
testArr[1]:= StrNew('abcdefgh');
StrCat(testArr[0], StrNew(testArr[1]));
write(testArr[1]); readln;
end.


Почему это происходит???? Нет, то есть вроде как приблизительно понятно, почему, но ЭТО ВООБЩЕ НОРМАЛЬНО??? И как это обойти?

{вместо строчки StrCat(testArr[0], StrNew(testArr[1])); можно вставить StrCat(testArr[0], StrNew(PChar('abcdefgh')));
результат не изменится}
volvo
Ты читать умеешь?
Цитата
Declaration:
function StrNew(Str: PChar): PChar;
Все, дальше можешь ничего не делать, уже будет неправильно. Ты НЕ МОЖЕШЬ вместо PChar передать String в функцию. Точка. Разговор с точки зрения компилятора - закончен. В результате функция возвращает все, что угодно, но только не правильный результат. Для конвертации Pascal-евской строки в PChar недостаточно просто написать PChar('string'), это тебе не Дельфи, в Турбо-Паскале для подобных вещей существует
Цитата
StrPCopy (function) (Strings unit)
Copies a Pascal-style string to a null-terminated string.
Declaration:
function StrPCopy(Dest: PChar; Source: String): PChar;

Remarks:
StrPCopy does not perform any length checking.
The destination buffer must have room for at least Length(Source)+1 characters.

(особенно внимательно читай ремарку!!!)

Я бы написал свою функцию, которая будет выделять память под null-terminated строку и копировать туда символы из строки Паскалевской. Тогда твой код перепишется очень просто:

uses strings;

function Convert(s: string): PChar;
var p: Pointer;
begin
GetMem(p, Length(s) + 1);
StrPCopy(PChar(p), s);
Convert := p;
end;

var
testArr: array[0 .. 1] of PChar;
begin
testArr[0] := Convert('!');
testArr[1] := Convert('abcdefgh');

StrLCat(testArr[0], StrNew(testArr[1]), StrLen(testArr[1]) + 1);
writeln(testArr[0]); { Будет выведено '!abcdefgh', я так понимаю, именно это и должно было получиться?}
ReadLn;
end.

Почему не StrCat, а StrLCat? Очень просто: StrCat не осуществляет контроль границ, то есть, если под первый параметр выделено меньше места, чем надо для хранения результата - то последствия будут неприятными. StrLCat же увеличивает размер буфера так, чтобы результат в него поместился (собственно, для этого там и предусмотрен третий параметр)...

Читайте документацию, внимательно читайте, это помогает разобраться...
Ldnb
За поправление спасибо, учтём, но меня интересует сейчас не writeln(testArr[0]); а то,что выводится в writeln(testArr[1]); проверил, в твоём примере, к сожалению, результат тот же самый...
Ldnb
Эх... видимо нормально работать с PChar всё-таки нельзя, действительно глючный тип, если при попытке что-либо дописать к строке хоть что используй, хоть StrCat, хоть StrLCat, данные могут просто залезть на уже выделенную и используемую в другой переменной память, и затереть её. И потом мучайся отлаживай.


З.Ы. writeln(testArr[1]) выводит 'h'
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.