Quais plataformas têm algo diferente de char de 8 bits?

De vez em quando, alguém em SO aponta que char (aka ‘byte’) não é necessariamente 8 bits .

Parece que o char 8 bits é quase universal. Eu teria pensado que para plataformas tradicionais, é necessário ter um caractere de 8 bits para garantir sua viabilidade no mercado.

Agora e historicamente, que plataformas usam um char que não seja de 8 bits e por que eles diferem dos 8 bits “normais”?

Ao escrever código, e pensar em suporte multi-plataforma (por exemplo, para bibliotecas de uso geral), que tipo de consideração vale a pena dar a plataformas com chars de não 8 bits?

No passado, encontrei alguns DSPs da Analog Devices para os quais o char é de 16 bits. DSPs são um pouco de arquitetura de nicho, suponho. (Então, novamente, no momento em que o assembler codificado manualmente bateu com facilidade o que os compiladores C disponíveis podiam fazer, então eu realmente não obtive muita experiência com C nessa plataforma.)

    char é também de 16 bits nos DSPs C54x da Texas Instruments, que apareceram, por exemplo, no OMAP2. Existem outros DSPs com 16 e 32 bits. Acho que até ouvi falar de um DSP de 24 bits, mas não me lembro o quê, então talvez eu tenha imaginado.

    Outra consideração é que POSIX manda CHAR_BIT == 8 . Então, se você está usando o POSIX, você pode assumir isso. Se alguém mais tarde precisar portar seu código para uma implementação próxima do POSIX, que por acaso tiver as funções que você usa, mas um char tamanho diferente, essa é sua má sorte.

    Em geral, porém, acho que é quase sempre mais fácil trabalhar em torno do problema do que pensar sobre isso. Apenas digite CHAR_BIT . Se você quiser um tipo exato de 8 bits, use int8_t . Seu código falhará em compilar em implementações que não fornecem um, em vez de usar silenciosamente um tamanho que você não esperava. No mínimo, se eu acertasse um caso em que tinha uma boa razão para assumir isso, então eu afirmaria isso.

    Ao escrever código, e pensar em suporte multi-plataforma (por exemplo, para bibliotecas de uso geral), que tipo de consideração vale a pena dar a plataformas com chars de não 8 bits?

    Não é tanto que “vale a pena dar consideração” a algo como está jogando pelas regras. Em C ++, por exemplo, o padrão diz que todos os bytes terão “pelo menos” 8 bits. Se o seu código pressupõe que os bytes têm exatamente 8 bits, você está violando o padrão.

    Isso pode parecer bobo agora – “é claro que todos os bytes têm 8 bits!”, Ouvi dizer. Mas muitas pessoas muito inteligentes confiaram em suposições que não eram garantias, e então tudo quebrou. A história está repleta de exemplos.

    Por exemplo, a maioria dos desenvolvedores do início dos anos 90 assumiu que um atraso de tempo de CPU sem ops, tomando um número fixo de ciclos, tomaria uma quantidade fixa de tempo de relógio, porque a maioria das CPUs do consumidor era aproximadamente equivalente em energia. Infelizmente, os computadores ficaram mais rápidos muito rapidamente. Isso gerou o surgimento de checkboxs com botões “Turbo” – cuja finalidade, ironicamente, era desacelerar o computador para que os jogos usando a técnica de atraso de tempo pudessem ser jogados em uma velocidade razoável.


    Um comentarista perguntou onde no padrão diz que o char deve ter pelo menos 8 bits. Está na seção 5.2.4.2.1 . Esta seção define CHAR_BIT , o número de bits na menor entidade endereçável e possui um valor padrão de 8. Ele também diz:

    Seus valores definidos pela implementação devem ser iguais ou maiores em magnitude (valor absoluto) àqueles mostrados, com o mesmo sinal.

    Portanto, qualquer número igual a 8 ou superior é adequado para substituição por uma implementação em CHAR_BIT .

    Máquinas com arquiteturas de 36 bits possuem bytes de 9 bits. Segundo a Wikipedia, as máquinas com arquiteturas de 36 bits incluem:

    • Digital Equipment Corporation PDP-6/10
    • IBM 701/704/709/7090/7094
    • UNIVAC 1103 / 1103A / 1105/1100/2200,

    Alguns dos quais estou ciente:

    • DEC PDP-10: variável, mas na maioria das vezes caracteres de 7 bits compactados 5 por palavra de 36 bits, ou então caracteres de 9 bits, 4 por palavra
    • Controlar mainframes de dados (CDC-6400, 6500, 6600, 7600, Cyber ​​170, Cyber ​​176 etc.) caracteres de 6 bits, compactados 10 por palavra de 60 bits.
    • Mainframes da Unisys: 9 bits / byte
    • Windows CE: simplesmente não suporta o tipo `char` – requer um wchar_t de 16 bits

    Não existe código completamente portátil. 🙂

    Sim, pode haver vários tamanhos de bytes / caracteres. Sim, pode haver implementações de C / C ++ para plataformas com valores altamente incomuns de CHAR_BIT e UCHAR_MAX . Sim, às vezes é possível escrever código que não depende do tamanho do caractere.

    No entanto, quase qualquer código real não é autônomo. Por exemplo, você pode estar escrevendo um código que envia mensagens binárias para a rede (o protocolo não é importante). Você pode definir estruturas que contenham campos necessários. Do que você tem que serializar. Apenas o binário copiando uma estrutura em um buffer de saída não é portátil: geralmente você não sabe nem a ordem de bytes para a plataforma nem o alinhamento dos membros da estrutura, portanto a estrutura apenas armazena os dados, mas não descreve como os dados devem ser serializados .

    Está bem. Você pode executar transformações de ordem de bytes e mover os membros da estrutura (por exemplo, uint32_t ou similar) usando o memcpy no buffer. Por que memcpy ? Porque há muitas plataformas onde não é possível escrever 32 bits (16 bits, 64 bits – nenhuma diferença) quando o endereço de destino não está alinhado corretamente.

    Então, você já fez muito para conseguir portabilidade.

    E agora a pergunta final. Nós temos um buffer. Os dados são enviados para a rede TCP / IP. Essa rede assume bytes de 8 bits. A questão é: de que tipo deve ser o buffer? Se seus caracteres são de 9 bits? Se eles são de 16 bits? 24? Talvez cada caractere corresponda a um byte de 8 bits enviado para a rede e apenas 8 bits sejam usados? Ou talvez vários bytes de rede sejam colocados em caracteres de 24/16/9 bits? Essa é uma pergunta, e é difícil acreditar que existe uma única resposta que atenda a todos os casos. Muitas coisas dependem da implementação do soquete para a plataforma de destino.

    Então, do que estou falando. Normalmente, o código pode ser relativamente fácil de portar, em certa medida . É muito importante fazer isso se você espera usar o código em plataformas diferentes. No entanto, melhorar a portabilidade além dessa medida é uma coisa que exige muito esforço e muitas vezes dá pouco , já que o código real quase sempre depende de outro código (implementação de soquete no exemplo acima). Tenho certeza de que cerca de 90% da capacidade do código de trabalhar em plataformas com bytes diferentes de 8 bits é quase inútil, pois usa um ambiente que é limitado a 8 bits. Basta verificar o tamanho do byte e executar a declaração de tempo de compilation. Você quase certamente terá que rewrite muito para uma plataforma altamente incomum.

    Mas se o seu código é altamente “autônomo” – por que não? Você pode escrevê-lo de uma maneira que permita diferentes tamanhos de bytes.

    Parece que você ainda pode comprar um IM6100 (ou seja, um PDP-8 em um chip) fora de um depósito. Essa é uma arquitetura de 12 bits.

    Muitos chips DSP possuem caracteres de 16 ou 32 bits. TI rotineiramente faz tais chips, por exemplo .

    As linguagens de programação C e C ++, por exemplo, definem byte como “unidade de dados endereçável grande o suficiente para conter qualquer membro do conjunto de caracteres básico do ambiente de execução” (cláusula 3.6 do padrão C). Como o tipo de dados integral de caracteres C deve conter pelo menos 8 bits (cláusula 5.2.4.2.1), um byte em C é pelo menos capaz de conter 256 valores diferentes. Várias implementações de C e C ++ definem um byte como 8, 9, 16, 32 ou 36 bits

    Citado em http://en.wikipedia.org/wiki/Byte#History

    Não tenho certeza sobre outras línguas embora.

    http://en.wikipedia.org/wiki/IBM_7030_Stretch#Data_Formats

    Define um byte nessa máquina para ser variável

    A família DEC PDP-8 tinha uma palavra de 12 bits, embora você normalmente usasse ASCII de 8 bits para a saída (em um Teletipo principalmente). No entanto, também havia um código de caractere de 6 bits que permitia codificar 2 caracteres em uma única palavra de 12 bits.

    Por um lado, os caracteres Unicode são maiores que 8 bits. Como alguém mencionou anteriormente, a especificação C define tipos de dados por seus tamanhos mínimos. Use sizeof e os valores em limits.h se você quiser interrogar seus tipos de dados e descobrir exatamente o tamanho deles para sua configuração e arquitetura.

    Por esse motivo, tento me ater a tipos de dados como uint16_t quando preciso de um tipo de dados de um comprimento de bit específico.

    Edit: Desculpe, inicialmente eu fiz mal a sua pergunta.

    A especificação C diz que um object char é “grande o suficiente para armazenar qualquer membro do conjunto de caracteres de execução”. limits.h lista um tamanho mínimo de 8 bits, mas a definição deixa o tamanho máximo de um char aberto.

    Assim, o a char é pelo menos tão longo quanto o maior caractere do conjunto de execução de sua arquitetura (normalmente arredondado para o limite de 8 bits mais próximo). Se sua arquitetura tiver mais opcodes, seu tamanho de char poderá ser maior.

    Historicamente, o opcode da plataforma x86 era de um byte, então char era inicialmente um valor de 8 bits. As plataformas x86 atuais suportam opcodes com mais de um byte, mas o char é mantido com 8 bits de comprimento, pois é isso que os programadores (e os grandes volumes do código x86 existente) estão condicionados.

    Ao pensar em suporte multi-plataforma, aproveite os tipos definidos em stdint.h . Se você usar (por exemplo) um uint16_t, poderá ter certeza de que esse valor é um valor de 16 bits não assinado em qualquer arquitetura, seja esse valor de 16 bits correspondente a um char , short , int ou outra coisa. A maior parte do trabalho duro já foi feito pelas pessoas que escreveram suas bibliotecas padrão / compilador.

    Se você precisa saber o tamanho exato de um char porque você está fazendo alguma manipulação de hardware de baixo nível que requer, eu normalmente uso um tipo de dados que é grande o suficiente para manter um char em todas as plataformas suportadas (geralmente 16 bits é suficiente) e execute o valor por meio de uma rotina convert_to_machine_char quando precisar da representação exata da máquina. Dessa forma, o código específico da plataforma é confinado à function de interface e na maioria das vezes eu posso usar um uint16_t normal.

    Que tipo de consideração vale a pena dar às plataformas com caracteres não 8 bits?

    Números mágicos ocorrem, por exemplo, quando se deslocam;

    a maioria destes pode ser tratada de forma simples usando CHAR_BIT e, por exemplo, UCHAR_MAX em vez de 8 e 255 (ou similar).

    espero que sua implementação defina aqueles 🙂

    essas são as questões “comuns” …..

    outra questão indireta é dizer que você tem:

     struct xyz { uchar baz; uchar blah; uchar buzz; } 

    isso pode “apenas” tomar (melhor caso) 24 bits em uma plataforma, mas pode levar, por exemplo, 72 bits em outro lugar …..

    se cada uchar continha “flags de bit” e cada uchar tivesse apenas 2 bits “significativos” ou flags que você estava usando atualmente, e você os organizou apenas em 3 uchars para “clareza”, então pode ser relativamente “mais desperdício” uma plataforma com uchars de 24 bits …..

    nada bitfields não pode resolver, mas eles têm outras coisas para observar ….

    Nesse caso, apenas um único enum pode ser uma maneira de obter o número inteiro “menor” de que você precisa …

    talvez não seja um exemplo real, mas coisas como esta “bit” me ao portar / jogar com algum código …..

    apenas o fato de que, se um uchar é três vezes maior do que o esperado “normalmente”, 100 dessas estruturas podem desperdiçar muita memory em algumas plataformas … onde “normalmente” não é grande coisa … .

    então as coisas ainda podem ser “quebradas” ou, neste caso, “desperdiçar muita memory muito rapidamente” devido a uma suposição de que um uchar não é “muito desperdício” em uma plataforma, em relação à RAM disponível, do que em outra plataforma … ..

    o problema pode ser mais proeminente, por exemplo, para ints também, ou outros tipos, por exemplo, você tem alguma estrutura que precisa de 15 bits, então você o coloca em um int, mas em alguma outra plataforma um int tem 48 bits ou o que for …. .

    “normalmente” você pode dividi-lo em 2 uchars, mas por exemplo, com um uchar de 24 bits você só precisa de um …..

    então um enum pode ser uma solução melhor “genérica” ​​….

    depende de como você está acessando esses bits embora 🙂

    então, pode haver “falhas de design” que criam a cabeça … mesmo que o código ainda funcione / corra bem, independentemente do tamanho de um uchar ou uint …

    há coisas como estas a ter em atenção, apesar de não existirem “números mágicos” no seu código …

    espero que isso faça sentido 🙂

    ints costumava ser 16 bits (pdp11, etc.). Ir para arquiteturas de 32 bits foi difícil. As pessoas estão melhorando: dificilmente alguém assume que um ponteiro vai caber em um longo mais (você não está certo?). Ou arquivar deslocamentos, ou carimbos de data / hora ou …

    Caracteres de 8 bits já são um pouco de anacronismo. Nós já precisamos de 32 bits para armazenar todos os conjuntos de caracteres do mundo.