terça-feira, 2 de março de 2010

Char, AnsiChar e Widechar

Um Char é um Byte, certo? Errado. Embora isso fosse fato antigamente, e inclusive fornecia grandes facilidades para quem trabalhava com C e C++, hoje a cena mudou.
Com a globalização e a internacionalização da TI e do desenvolvimento de software foram incorporados no nosso dia-a-dia a possibilidade ou necessidade de usar caracteres de idiomas diferentes,  e o Unicode.
Sendo assim, hoje temos mais de um tipo de char, e seus tamanhos são diferentes.

Um Char tem, no Lazarus 9.26 para Win32 i386 incontestáveis 8 bits, ou seja, um byte.

Um AnsiChar tem com certeza apenas um byte também.

Um WideChar tem dois bytes, podendo conter uma gama muito maior de caracteres.

Qual é a diferença entre um Char e um AnsiChar então? A resposta é que o AnsiChar é explicitamente um caracter de apenas um byte, enquanto que o Char pode mudar de acordo com a versão do Free Pascal ou a plataforma.

Por exemplo, no Delphi o Char já é, automaticamente, um caracter Unicode, de dois bytes.

Durante muito tempo softwares foram desenvolvidos assumindo-se que um Char = um Byte. Isso gerou a seguinte situação: Executáveis e bibliotecas compilados com "versões" diferentes do Char podem não ser mais compatíveis entre si. O PChar, que era um "vetor" aberto de caracteres terminados em #0, usando caracteres de dois bytes deve ser fechado com #0#0, e esse é apenas um dos exemplos que podem causar buffer overflows, e abrir brechas no seu software.

O contrário também é verdadeiro. Se o caracter "A" em um char de 8 bits corresponde ao #65, o caracter "A" usando um WideChar seria #65#0. Passando uma WideString (string formada por WideChars) para uma função em uma biblioteca que espere por uma AnsiString pode resultar em apenas o primeiro caracter ser reconhecido. Isso porque a string "Anzol", por exemplo, em widechar seria  formada por 10 bytes, a saber: #65#0#110#0#122#0#111#0#108#0  Repare que logo após cada byte conhecido nosso da tabela ASCII há um byte #0. Isso porque esses caracteres ASCII "comuns" também ocupam dois bytes, mas seus valores são baixos, por isso ocupam apenas o byte menos significativo (onde o menos significativo é o da direita).

Isso fará com que quando a função da DLL espera "Anzol" como AnsiString e recebe "Anzol" como widestring, a função reconheça o  #65 como "A" e o #0 como final da string.

Por isso, sempre que for usar caracteres, vetores de caracteres, ponteiros para caracteres e qualquer record ou estrutura que os contenha em sua formação use o SizeOf para saber o tamanho dos dados, em bytes. Nunca assuma que um Char = um Byte.

O exemplo em Lazarus pode ser compilado em Delphi e consiste apenas em uma form com um memo e botão. Serve para exemplificar o  uso de SizeOf.


unit Unit1; 

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls;

type

  { TForm1 }

  TForm1 = class(TForm)
    btVerifica: TButton;
    Memo1: TMemo;
    procedure btVerificaClick(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end; 

var
  Form1: TForm1; 

implementation

{ TForm1 }

procedure TForm1.btVerificaClick(Sender: TObject);
var
  a: Char;
  b: AnsiChar;
  c: WideChar;
begin
  Memo1.Lines.Add('Tamanho de um Char: ' + IntToStr(SizeOf(a)));
  Memo1.Lines.Add('Tamanho de um AnsiChar: ' + IntToStr(SizeOf(b)));
  Memo1.Lines.Add('Tamanho de um WideChar: ' + IntToStr(SizeOf(c)));
end;


initialization
  {$I Unit1.lrs}

end.


3 comentários:

  1. Existe como transformar uma Widechar em char simplesmente?

    ResponderExcluir
    Respostas
    1. Se você quer converter um único widechar para ansichar (o char antigo normal) Acredito que um simples Ansichar(valor) funcionaria. Você pode também apontar para ele um pbyte e pegar o byte menos significativo (mas não vejo porque fazer algo assim)

      Se você quer converter uma widestring para ansistring acredito que funcione ansistring(valor). Mas essa conversão não funcionará direito se houver nessa string caracteres de dois bytes, caracteres com surrogate pairs ou caracteres fora da tabela ascii.

      Leia esses links, eles tem tudo o que você precisa saer sobre strings e unicode no Delphi

      http://www.joelonsoftware.com/articles/Unicode.html
      http://edn.embarcadero.com/article/images/38980/Delphi_and_Unicode.pdf
      http://www.embarcadero.com/images/dm/technical-papers/delphi-unicode-migration.pdf
      http://flovato.blogspot.com.br/2010/05/delphi-strings-o-que-quando-e-como.html
      http://www.andreanolanusse.com/pt/delphi-unicode-entendo-os-avisos-warning-do-compilar-sua-aplicacao/


      Quando você usar a API do windows dê preferência para usar as funções que terminam em W em vez das que terminam em A (todas as funções da API são duplicadas para uso com ansichae A e com widechar W).

      Quando tiver que usar dll's de terceiros dê preferência por declarar variáveis e usar ansistrings, ansichar e pansichar no lugar dos tipos padrão.

      Excluir

Postagens populares

Marcadores

delphi (60) C# (31) poo (21) Lazarus (19) Site aos Pedaços (15) sql (13) Reflexões (10) .Net (9) Humor (9) javascript (9) ASp.Net (8) api (8) Básico (6) Programação (6) ms sql server (5) Web (4) banco de dados (4) HTML (3) PHP (3) Python (3) design patterns (3) jQuery (3) livros (3) metaprogramação (3) Ajax (2) Debug (2) Dicas Básicas Windows (2) Pascal (2) games (2) linguagem (2) música (2) singleton (2) tecnologia (2) Anime (1) Api do Windows (1) Assembly (1) Eventos (1) Experts (1) GNU (1) Inglês (1) JSON (1) SO (1) datas (1) developers (1) dicas (1) easter egg (1) firebird (1) interfaces (1) introspecção (1) memo (1) oracle (1) reflexão (1)