Long long em c99
No padrão C99 eles introduziram long long
. Qual é o propósito disso? Em minha (limitada) experiência de programação C, eu vi apenas um int de 4 bytes e um comprimento de 8 bytes. Por exemplo, no Compiler Explorer:
Se long
já é 8
, por que é necessário adicionar outro long long
tipo? O que isso faz com o compilador / arquitetura?
Respostas
Se longo já é 8, por que é necessário adicionar outro tipo longo longo? O que isso faz com o compilador / arquitetura?
"Se tempo já é 8" nem sempre é verdade que existe muito código que se baseia em 32-bit long
e int
, como 32 ou 16 bits.
Exigir long
como 64 bits quebraria as bases do código. Esta é uma grande preocupação.
No entanto, exigir long
que permaneça em 32 bits (e não long long
) não tornaria o acesso aos inteiros de 64 bits padrão, portanto, uma justificativa para long long
.
Permitir long
como 32 bits ou 64 bits (ou outros) permite a transição.
Várias funções passam / retornam long
como fseek(), ftell()
. Eles se beneficiam por long
serem mais de 32 bits para suporte a arquivos grandes.
A prática recomendada incentiva uma abordagem mais ampla long
: "Os tipos usados size_t
e ptrdiff_t
não devem ter uma classificação de conversão de inteiro maior que a de signed long int
, a menos que a implementação suporte objetos grandes o suficiente para tornar isso necessário." Isso está relacionado a tamanhos de memória que excedem 32 bits.
Talvez no futuro uma implementação possa ser usada int/long/long long/intmax_t
como 32/64/128/256 bits.
IAC, vejo tipos de largura fixa intN_t
aumentando em popularidade em relação a long
e long long
. Eu costumo usar tipos de largura fixa ou bool
, ( unsigned
) char
, int
/ unsigned
,, size_t
( u
) intmax_t
e leave signed char
, ( unsigned
) short
, ( unsigned
) long
, ( unsigned
) long long
para casos especiais.
O padrão C apenas garante que um int
pode ter (em termos gerais) 2 bytes, um long
pode ter 4 bytes e um long long
pode ter 8 bytes.
Na verdade, o MSVC ainda usa 4 bytes long
, embora tenha 4 bytes int
.
O único requisito relevante para int
e long
, então e agora, é que int
deve ter pelo menos 16 bits e long
deve ter pelo menos 32 bits. Os sistemas de 16 e 32 bits tendem a ter 32 bits long
, e as máquinas de 64 bits eram muito menos comuns no final da década de 1990. Portanto, antes do C99, os programadores não podiam contar com a disponibilidade de um tipo inteiro de 64 bits. Esse problema foi resolvido com a introdução de long long
, que deve ter pelo menos 64 bits. (Eu acredito que já foi fornecido pelo GCC e talvez outros compiladores como uma extensão).
Hoje em dia, muitos (mas não todos) sistemas de 64 bits usam um de 64 bits long
e não se preocupam em torná-lo long long
maior, então ele também é de 64 bits e, em certo sentido, redundante. Esses são provavelmente os sistemas com os quais você está familiarizado, mas eles não representam tudo que existe.
Acho que você não percebeu que está fazendo uma grande suposição errada sobre como os requisitos de largura de tipo C funcionam: ISO C apenas define um intervalo de valor mínimo como a menor magnitude permitida LONG_MAX
e LONG_MIN
(-2147483647, não 8 porque ISO C permite um complemento e inteiros com sinal / magnitude com sinal, não apenas o complemento de 2.) As implementações reais podem ter tipos mais amplos, geralmente para corresponder a uma largura de registro ou tamanho de operando que a máquina alvo pode fazer de forma eficiente.
Muito já foi escrito sobre isso no Stack Overflow e em outros lugares, o que não vou tentar repetir aqui. Veja tambémhttps://en.cppreference.com/w/c/language/arithmetic_types
Isso o levou ao erro de olhar para as opções de largura de tipo no x86-64 System V ABI e presumir que outras implementações C são as mesmas, eu acho. x86-64 é um ISA de 64 bits que pode trabalhar de forma eficiente com inteiros de 64 bits, portanto, 64 bits long
foi uma escolha bastante sensata.
Nenhuma ABI sã para uma máquina de 32 bits como a i386 usaria 64 bits long
porque isso não é necessário, apenas 32 bits. Usar 64 bits significaria que ele não caberia em um único registro. Compile com -m32
ou compile para ARM de 32 bits. Godbolt também tem GCC para AVR e MSP430. Nessas máquinas de 8 e 16 bits, o GCC escolhe as menores larguras permitidas pelo ISO C (2 bytes int
, etc.)
Em 1999, o x86-64 nem existia. (Alguns outros ISAs de 64 bits fizeram, como Alpha). Portanto, olhar para um dos 2 ABIs principais para entender as opções do C99 não o levará muito longe.
É claro que C precisa de um tipo com garantia de pelo menos 64 bits, para permitir que as pessoas escrevam programas que façam matemática inteira de 64 bits com eficiência.
E, por falar nisso, o x86-64 pode fazer coisas inteiras de 32 bits com a eficiência de 64 bits, às vezes com mais eficiência. Portanto, fazer long
um tipo de 64 bits não é, sem dúvida, muito bom. Alguns códigos usam long
porque desejam um tipo que precisa ser de 32 bits, mas não se beneficia de tê-lo mais amplo. Para tal código, 64 bits long
apenas desperdiça espaço de cobertura do cache / largura de banda da memória e tamanho do código (prefixos REX). No C99, a escolha ideal seria int_least32_t
, mas é irritantemente longa para digitar e raramente usada.
Mas, long
às vezes, espera-se que o OTOH seja "o tipo mais eficiente (1-registro)", embora não haja tal garantia e ABIs LLP64 como o Windows x64 com 32 bits long
não sejam assim.
Outra lata de worms é int_fast32_t
a má escolha IMO do C99 e x86-64 System V para torná-lo um tipo de 64 bits. (Eu tenho uma resposta escrita pela metade para Cpp uint32_fast_t resolve para uint64_t, mas é mais lento para quase todas as operações do que uint32_t (x86_64). Por que ele resolve para uint64_t? Que devo terminar ... int_fast32_t
levanta a questão de "rápido para quê propósito ", e em muitas implementações não é o que você esperava para muitos casos.
Veja também
- C ++ - o tipo inteiro mais rápido?
- Como os tipos [u] int_fastN_t devem ser definidos para x86_64, com ou sem o x32 ABI?
- Por que uint32_t seria preferido em vez de uint_fast32_t?
- Por que uint_least16_t é mais rápido do que uint_fast16_t para multiplicação em x86_64?
- Otimizações de compilador permitidas via "int", "mínimo" e "rápido" tipos de largura não fixa C / C ++
Existem alguns limites, mas o autor do compilador é livre para escolher os comprimentos para os tipos de variáveis C padrão (char, short, int, long, long long). Naturalmente, char será um byte para essa arquitetura (a maioria dos compiladores C tem 8 bits). E, naturalmente, você não pode ter algo menor ser maior do que algo maior, long não pode ser menor que um int. Mas certamente em 1999 vimos a transição de 16 para 32 bits do x86 e, por exemplo, int mudou de 16 para 32 com várias ferramentas, mas ficou por muito tempo 32. Mais tarde, a transição do x86 de 32 para 64 bits aconteceu e, dependendo da ferramenta, havia tipos disponíveis ajudar.
O problema já existia muito antes disso e a solução não era fixar os comprimentos dos tipos, eles são, dentro das regras, até os autores do compilador quanto ao tamanho. Mas os autores do compilador precisam criar um arquivo stdint.h que corresponda à ferramenta e ao destino (stdint.h é específico para uma ferramenta e destino no mínimo e pode ser uma versão da ferramenta e opções de compilação para essa ferramenta, etc). Assim, por exemplo, uint32_t é sempre 32 bits. Alguns autores irão converter isso para int, outros para long, etc. em seu stdint.h. Os tipos de variáveis da linguagem C permanecem limitados a char, short, int, etc. de acordo com a linguagem (uint32_t não é um tipo de variável, ele é convertido em um tipo de variável por meio de stdint.h). Essa solução / contorno era uma maneira de evitar que todos enlouquecessem e manter a linguagem viva.
Os autores freqüentemente escolherão, por exemplo, se os GPRs são de 16 bits para que sejam 16 bits, e se 32 bits forem de 32 bits e assim por diante, mas eles têm alguma liberdade.
Sim, isso significa especificamente que não há razão para supor que quaisquer duas ferramentas para um alvo específico (o computador em que você está lendo isso, por exemplo) usem as mesmas definições para int e long em particular, e se você quiser escrever código para esta plataforma que pode transportar entre essas ferramentas (que suportam esta plataforma), então use os tipos stdint.h e não int, long, etc ... Certamente se você estiver cruzando plataformas um msp430 mcu, um arm mcu, uma máquina arm linux , uma máquina baseada em x86, em que os tipos, mesmo para o mesmo "conjunto de ferramentas" (gnu gcc e binutils por exemplo), não têm as mesmas definições para int e long, etc. char e short tendem a ser 8 e 16 bits, int e long tendem a variar mais, às vezes do mesmo tamanho, às vezes diferentes, mas a questão é não supor.
É trivial detectar os tamanhos, para opções de versão / destino / linha de comando do compilador, ou apenas seguir a rota padrão para minimizar problemas posteriormente.