Что происходит, когда AnsiString приводится к PAnsiString? - PullRequest
5 голосов
/ 14 февраля 2009

У меня есть метод (Delphi 2009):

procedure TAnsiStringType.SetData(const Value: TBuffer; IsNull: boolean = False);
begin
  if not IsNull then
    FValue:= PAnsiString(Value)^;
  inherited;
end;

Это абстрактный метод базового класса, где «Value: Pointer» ожидает указатель соответствующих данных, как:

String = PString
AnsiString = PAnsiString
Integer = PInteger
Boolean = PBoolean

Поэтому я пытаюсь передать значение следующим образом:

var
  S: AnsiString;
begin
  S:= 'New AnsiString Buffer';
  SetBuffer(PAnsiString(S));
end;

Но приведение из AnsiString к PAnsiString НЕ работает, я понимаю, почему, но я хочу знать, каков результат преобразования. Поэтому я написал простой тест:

var
  Buffer: AnsiString;
  P1: Pointer;
  P2: Pointer;
  P3: Pointer;
  P4: Pointer;
begin
  P1:= PAnsiString(Buffer);
  P2:= Addr(Buffer);
  P3:= @Buffer;
  P4:= Pointer(Buffer);
  P5:= PChar(Buffer[1]);

  WriteLn('P1: ' + IntToStr(Integer(P1)));
  WriteLn('P2: ' + IntToStr(Integer(P2)));
  WriteLn('P3: ' + IntToStr(Integer(P3)));
  WriteLn('P4: ' + IntToStr(Integer(P4)));
  WriteLn('P5: ' + IntToStr(Integer(P5)));
end;

Результат:

P1: 5006500
P2: 1242488
P3: 1242488
P4: 5006500
P5: 67

Где:

- P2 and P3, is the address of Buffer: AnsiString 
- P5 is the Char Ord value of Buffer[1] char, in this case "67 = C"
- How about P1 and P4?

Что означает P1 и P4?

Ответы [ 2 ]

15 голосов
/ 14 февраля 2009

AnsiString реализован как указатель. Переменная AnsiString содержит только адрес. Адрес соответствует первому символу в строке или nil, если строка пуста.

A PAnsiString - указатель на переменную AnsiString . Это указатель на указатель на первый символ строки. Когда вы говорите PAnsiString(Buffer), вы говорите компилятору обрабатывать указатель в Buffer, как если бы он был указателем на AnsiString вместо указателя на символьные данные. Адрес 5006500 является местоположением первого символа строки, C.

В вашей памяти есть запись, представляющая строку:

                +-----------+
                | $ffffffff | -1 reference count (4 bytes)
                +-----------+
Buffer:         | $00000001 | length (4 bytes)
+---------+     +-----------+
| 5006500 | --> |       'C' | first character (1 byte)
+---------+     +-----------+
                |        #0 | null terminator (1 byte)
                +-----------+

Buffer содержит адрес байта с C в нем. Вы вводите тип, который будет иметь тип PAnsiString вместо AnsiString. Вы сказали компилятору, что у вас есть этот макет:

                                  +-----------+
                                  |       ... |
                                  +-----------+
Buffer:                           |       ... |
+---------+     +-----------+     +-----------+
| 5006500 | --> | $00000043 | --> |   garbage | first character
+---------+     +-----------+     +-----------+
                                  |       ... |
                                  +-----------+

Когда я рассуждаю об указателях, я рисую диаграммы вот так. Если у вас на столе нет бумаги, значит, вы оказываете медвежью услугу.

7 голосов
/ 14 февраля 2009

Хорошая головоломка, но у меня есть решение:

  • P2 и P3 - адрес указателя на буфер
  • P1 и P4 - адрес буфера
  • P5 - первый элемент в буфере

Я добавил комментарий в коде:

var
  Buffer: AnsiString;
  P1: Pointer;
  P2: Pointer;
  P3: Pointer;
  P4: Pointer;
  P5: Pointer;
begin
  P1:= PAnsiString(Buffer); 
  (* A cast from AnsiString to PAnsiString has no real meaning 
     because both are a pointer to a block of characters ()
  P2:= Addr(Buffer);
  P3:= @Buffer;
  (* Both Addr and @ give the address of a variable. The variable Buffer is 
     a pointer so we get the address of the pointer, not the value of the 
     pointer. *)
  P4:= Pointer(Buffer);
  (* See the remark on P1. Due to the cast both give the same result. *)
  P5:= PChar(Buffer[1]);
  (* This looks like a pointer to the first element. But the cast changes 
     it into the character. *)
  WriteLn('P1: ' + IntToStr(Integer(P1)));
  WriteLn('P2: ' + IntToStr(Integer(P2)));
  WriteLn('P3: ' + IntToStr(Integer(P3)));
  WriteLn('P4: ' + IntToStr(Integer(P4)));
  WriteLn('P5: ' + IntToStr(Integer(P5)));
end;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...