Реверсия строки

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Реверсия строки

Сообщение Azazaz » 14.03.2016 21:37:26

Писал я процедуру для реверсии строки и столкнулся с совершенно непонятным протестом компилятора, который выражается в вылете без каких либо объяснений при исполнении. (Код под консоль).
Код: Выделить всё
program rev;

{$R *.res}

{процедура реверсии строки}
procedure  Reverse(var S: String);
var
   l, i: word;
   {указатели используються для обмена двух символов из левой и правой части строки}
   pleft, pright: PChar;
   ch: Char;
begin
l:= Length(S);
pleft:= @S[1]; pright:= @S[l];
for i:= 0 to Pred(l div 2)
     do begin
      ch:= pleft^; {сохряняем символ}
      pleft^:= pright^; // краш на этой строке
      pright^:= ch;{востанавливаем}
      {двигаем к центру строки}
      inc(pleft); dec(pright);
     end;
end;

var
  S: String;
begin
  S:= 'lalala';
  Writeln(S); Reverse(S); Writeln(S);
  Readln;
end.

Версия компилятора 3.0.0, Вин10. Я уже писал подобный код и он на более старых версиях выполнялся корректно, может в новой версии так нельзя делать?
Azazaz
новенький
 
Сообщения: 41
Зарегистрирован: 21.04.2015 20:00:03

Re: Реверсия строки

Сообщение Дож » 14.03.2016 21:51:56

Безотносительно данной конкретной ошибки, код некорректен. Если написать
Код: Выделить всё
S[1] := ch;

то компилятор посмотрит число ссылок на строку 'lalala', и если их больше одной, то скопирует строку, сделает так, чтобы S ссылалось на эту новую строку и уже в скопированной строке запишет в первый символ ch. Если взят указатель на строку, то компилятор этого понять не сможет:
Код: Выделить всё
var
  S1, S2: AnsiString;
  P: PChar;
begin
  S1 := 'abcd';
  S2 := S1;
  P := @S2[1];
  P^ := 'X'; // ошибочный код в этой строке
  Writeln(S1);
end.


Варианты решения проблемы:
1) Использовать ShortString вместо String, либо другой тип строк, не управляемый автоматически компилятором
2) Обращаться по индексам S[i+1] и S[Length(S) - i]
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Реверсия строки

Сообщение Azazaz » 14.03.2016 22:12:48

Вот рабочая версия с ShortString:
Код: Выделить всё
program rev;

{$R *.res}

{процедура реверсии строки}
procedure  Reverse(var S: ShortString);
var
   l, i: word;
   {указатели используються для обмена двух символов из левой и правой части строки}
   pleft, pright: PChar;
   ch: Char;
begin
l:= Length(S);
pleft:= @S[1]; pright:= @S[l];
for i:= 0 to Pred(l div 2)
     do begin
      ch:= pleft^; {сохряняем символ}
      pleft^:= pright^; // краш на этой строке
      pright^:= ch;{востанавливаем}
      {двигаем к центру строки}
      inc(pleft); dec(pright);
     end;
end;

var
  S: ShortString;
begin
  S:= 'lalala';
  Writeln(S); Reverse(S); Writeln(S);
  Readln;
end.


Вот что написано на вики по этому поводу:
String

The type String may refer to ShortString or AnsiString, depending from the {$H} switch. If the switch is off ({$H-}) then any string declaration will define a ShortString. It size will be 255 chars, if not otherwise specified. If it is on ({$H+}) string without length specifier will define an AnsiString, otherwise a ShortString with specified length. In mode delphiunicode' String is UnicodeString.


Добавлено спустя 9 минут 59 секунд:
Можно обойти дублирование в случае с AnsiString что бы иметь дело с одной единственной оригинальной строкой?
Azazaz
новенький
 
Сообщения: 41
Зарегистрирован: 21.04.2015 20:00:03

Re: Реверсия строки

Сообщение Дож » 14.03.2016 22:39:57

А, ну понятно, откуда ошибка, — константа 'lalala' хранится в неизменяемой области памяти, к ней нет доступа на чтение.

Можно обойти дублирование в случае с AnsiString что бы иметь дело с одной единственной оригинальной строкой?

Легально — нельзя, нелегально — можно, конечно.

Добавлено спустя 13 часов 40 минут 34 секунды:
Для окончания расследования, вот работающий вариант замены символа по указателю:
Код: Выделить всё
var
  S1, S2: AnsiString;
  P: PChar;
begin
  Readln(S1);
  S2 := S1;
  P := @S2[1];
  P^ := 'X';
  Writeln(S1);
end.

Код: Выделить всё
C:\data\temp>fpc -gl -Sd str2.pas && echo abcd | str2.exe
Free Pascal Compiler version 3.0.0rc1 [2015/08/10] for i386
Copyright (c) 1993-2015 by Florian Klaempfl and others
Target OS: Win32 for i386
Compiling str2.pas
Linking str2.exe
10 lines compiled, 0.2 sec, 35056 bytes code, 1316 bytes data
Xbcd
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Реверсия строки

Сообщение bormant » 16.03.2016 20:30:51

Azazaz
Зачем такие сложности? http://www.freepascal.org/docs-html/rtl ... tring.html
Если уж нужно самому
Код: Выделить всё
procedure Reverse(var s: String);
var p, q: PChar; t: Char;
begin
  p:=@s[1]; q:=@s[Length(s)];
  while p<q do begin
    t:=p^; p^:=q^; q^:=t; Inc(p); Dec(q);
  end;
end;
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: Реверсия строки

Сообщение Дож » 16.03.2016 21:01:26

bormant, ваш код не решает проблему краша:
Код: Выделить всё
procedure Reverse(var s: String);
var p, q: PChar; t: Char;
begin
  p:=@s[1]; q:=@s[Length(s)];
  while p<q do begin
    t:=p^; p^:=q^; q^:=t; Inc(p); Dec(q);
  end;
end;

var
  S: String;
begin
  S := 'lalala';
  Reverse(S);
  Writeln(S);
end.

Код: Выделить всё
C:\data\temp>fpc -gl -Mdelphi bormant.pas && bormant.exe
Free Pascal Compiler version 3.0.0rc1 [2015/08/10] for i386
Copyright (c) 1993-2015 by Florian Klaempfl and others
Target OS: Win32 for i386
Compiling bormant.pas
Linking bormant.exe
16 lines compiled, 0.2 sec, 34128 bytes code, 1300 bytes data
Runtime error 216 at $0040145C
  $0040145C  REVERSE,  line 6 of bormant.pas
  $004014A4  main,  line 14 of bormant.pas
  $00407E41
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Реверсия строки

Сообщение bormant » 17.03.2016 23:27:45

На причину краша вам же уже указали, строка в сегменте, недоступном на запись, ну нельзя туда писать, что ж поделать:
Код: Выделить всё
S := Copy('lalala',1,255); Reverse(S); WriteLn(S);


Добавлено спустя 16 минут 8 секунд:
Другой вариант -- возвращать копию самому:
Код: Выделить всё
function Reverse(const s: String): String;
var i: Integer; p, q: PChar;
begin
  p:=@s[1]; SetLength(Result,Length(s)); q:=@Result[Length(s)];
  for i:=1 to Length(s) do begin
    q^:=p^; Inc(p); Dec(q);
  end;
end;
begin
  WriteLn(Reverse('lalala'));
end.
или
Код: Выделить всё
function Reverse(const s: String): String;
var i, j: Integer;
begin
  j:=Length(s)+1; SetLength(Result,j-1);
  for i:=1 to j-1 do Result[i]:=s[j-i];
end;
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: Реверсия строки

Сообщение Sergei I. Gorelkin » 18.03.2016 05:22:12

UniqueString - делает счетчик ссылок строк белым и пушистым.
http://www.freepascal.org/docs-html/rtl ... tring.html
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1395
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Реверсия строки

Сообщение sign » 18.03.2016 07:32:34

bormant писал(а):
Код: Выделить всё
function Reverse(const s: String): String;
var i: Integer; p, q: PChar;
begin
  p:=@s[1]; SetLength(Result,Length(s)); q:=@Result[Length(s)];
  for i:=1 to Length(s) do begin
    q^:=p^; Inc(p); Dec(q);
  end;
end;
begin
  WriteLn(Reverse('lalala'));
end.

А теперь подайте на вход - Reverse('lalala Превед русским буковам!')
sign
энтузиаст
 
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53

Re: Реверсия строки

Сообщение Дож » 18.03.2016 09:40:40

bormant, с кем вы разговариваете и на чей вопрос отвечаете? :)
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 899
Зарегистрирован: 12.10.2008 16:14:47

Re: Реверсия строки

Сообщение bormant » 18.03.2016 09:59:52

sign,
см. про однобайтовые кодировки, WideChar, помогут оба два. UTF-8 -- это транспортная кодировка, использовать ее для чего-то большего, чем передача в/получение из, а тем более для посимвольной обработки, плохая идея. Тем не менее, посимвольная итерация по UTF-8 строке пишется не намного сложнее.
Аватара пользователя
bormant
постоялец
 
Сообщения: 407
Зарегистрирован: 21.03.2012 11:26:01

Re: Реверсия строки

Сообщение sign » 18.03.2016 11:13:37

bormant писал(а):Тем не менее, посимвольная итерация по UTF-8 строке пишется не намного сложнее.

Вот это и нужно учитывать сразу.
А не когда кракозябры полезут.
sign
энтузиаст
 
Сообщения: 1131
Зарегистрирован: 30.08.2009 09:20:53


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 11

Рейтинг@Mail.ru
cron