Em C ++, size_t
(ou, mais corretamente, T::size_type
que é "geralmente" size_t
; isto é, um unsigned
tipo) é usado como o valor de retorno para size()
, o argumento para operator[]
etc. (consulte std::vector
et al.)
Por outro lado, as linguagens .NET usam int
(e, opcionalmente long
) para a mesma finalidade; de fato, os idiomas compatíveis com CLS não são necessários para suportar tipos não assinados .
Dado que o .NET é mais recente que o C ++, algo me diz que pode haver problemas usando unsigned int
até mesmo para coisas que "não podem" ser negativas como um índice ou comprimento de matriz. A abordagem do C ++ é "artefato histórico" para compatibilidade com versões anteriores? Ou existem trocas reais e significativas de design entre as duas abordagens?
Por que isso importa? Bem ... o que devo usar para uma nova classe multidimensional em C ++; size_t
ou int
?
struct Foo final // e.g., image, matrix, etc.
{
typedef int32_t /* or int64_t*/ dimension_type; // *OR* always "size_t" ?
typedef size_t size_type; // c.f., std::vector<>
dimension_type bar_; // maybe rows, or x
dimension_type baz_; // e.g., columns, or y
size_type size() const { ... } // STL-like interface
};
-1
é retornado de funções que retornam um índice, para indicar "não encontrado" ou "fora do intervalo". Também é retornado deCompare()
funções (implementandoIComparable
). Um int de 32 bits é considerado o tipo digitar para um número geral, pelo que espero que sejam razões óbvias.Respostas:
Sim. Para certos tipos de aplicativos, como processamento de imagem ou matriz, geralmente é necessário acessar elementos relativos à posição atual:
Nesses tipos de aplicativos, você não pode executar a verificação de intervalo com números inteiros sem sinal sem pensar com cuidado:
Em vez disso, você deve reorganizar sua expressão de verificação de intervalo. Essa é a principal diferença. Os programadores também devem se lembrar das regras de conversão de números inteiros. Em caso de dúvida, leia novamente http://en.cppreference.com/w/cpp/language/operator_arithmetic#Conversions
Muitos aplicativos não precisam usar índices de matriz muito grandes, mas precisam executar verificações de intervalo. Além disso, muitos programadores não são treinados para fazer essa expressão reorganizar a ginástica. Uma única oportunidade perdida abre a porta para uma exploração.
O C # foi projetado para os aplicativos que não precisarão de mais de 2 ^ 31 elementos por matriz. Por exemplo, um aplicativo de planilha não precisa lidar com tantas linhas, colunas ou células. O C # lida com o limite superior por ter aritmética marcada opcional que pode ser ativada para um bloco de código com uma palavra-chave sem mexer nas opções do compilador. Por esse motivo, o C # favorece o uso de número inteiro assinado. Quando essas decisões são consideradas completamente, faz sentido.
C ++ é simplesmente diferente e é mais difícil obter o código correto.
Com relação à importância prática de permitir que a aritmética assinada remova uma possível violação do "princípio do mínimo espanto", um exemplo é o OpenCV, que usa um inteiro assinado de 32 bits para o índice de elementos da matriz, tamanho da matriz, contagem de canais de pixels, etc. processing é um exemplo de domínio de programação que usa fortemente o índice de matriz relativa. O fluxo insuficiente de números inteiros não assinados (resultado negativo contornado) complicará severamente a implementação do algoritmo.
fonte
Essa resposta realmente depende de quem usará seu código e de quais padrões eles desejam ver.
size_t
é um tamanho inteiro com uma finalidade:Portanto, sempre que desejar trabalhar com o tamanho de objetos em bytes, você deve usar
size_t
. Agora, em muitos casos, você não está usando essas dimensões / índices para contar bytes, mas a maioria dos desenvolvedores prefere usá-los para finssize_t
de consistência.Observe que você sempre deve usar
size_t
se sua classe tem a aparência de uma classe STL. Todas as classes STL na especificação são usadassize_t
. É válido para o compilador para typedefsize_t
para serunsigned int
, e também é válido para que possa ser typedefed paraunsigned long
. Se você usarint
oulong
diretamente, você encontrará os compiladores onde uma pessoa que acha que sua classe seguiu o estilo da STL fica presa porque você não seguiu o padrão.Quanto ao uso de tipos assinados, há algumas vantagens:
int
, mas é muito mais difícil confundir o códigounsigned int
.int32_t
euint32_t
). Isso pode simplificar a interoperabilidade da APIA grande desvantagem dos tipos assinados é a óbvia: você perde metade do seu domínio. Um número assinado não pode contar tão alto quanto um número não assinado. Quando o C / C ++ apareceu, isso foi muito importante. Era necessário ser capaz de abordar todos os recursos do processador e, para isso, era necessário usar números não assinados.
Para os tipos de aplicativos direcionados ao .NET, não havia uma necessidade tão forte de um índice não assinado de domínio completo. Muitos dos propósitos para esses números são simplesmente inválidos em um idioma gerenciado (o pool de memória vem à mente). Além disso, quando o .NET foi lançado, os computadores de 64 bits eram claramente o futuro. Estamos muito longe de precisar de todo o intervalo de um número inteiro de 64 bits, portanto, sacrificar um bit não é tão doloroso quanto antes. Se você realmente precisa de 4 bilhões de índices, basta mudar para o uso de números inteiros de 64 bits. Na pior das hipóteses, você o executa em uma máquina de 32 bits e é um pouco lento.
Eu vejo o comércio como uma conveniência. Se você possui poder computacional suficiente para não se importar em desperdiçar um pouco do seu tipo de índice que nunca usará, é conveniente apenas digitar
int
ou selong
afastar dele. Se você realmente quisesse esse último pedaço, provavelmente deveria ter prestado atenção à assinatura dos seus números.fonte
size()
wasreturn bar_ * baz_;
; isso agora não cria um problema em potencial com excesso de número inteiro (wrap-around) que eu não teria se não usassesize_t
?bar_ * baz_
possa estourar um número inteiro assinado, mas não um número inteiro não assinado. Limitando-nos a C ++, vale a pena notar que o estouro não assinado é definido na especificação, mas o estouro assinado é um comportamento indefinido; portanto, se a aritmética do módulo de números inteiros não assinados é desejável, use-os definitivamente, porque é realmente definido!size()
transbordou a multiplicação assinada , você está no idioma UB land. (e, nofwrapv
modo, veja a seguir :) Quando então , com apenas um pouquinho mais, ela transbordou a multiplicação não assinada , você em terra de bug de código de usuário - retornaria um tamanho falso. Portanto, acho que não assinado compra muito aqui.Penso que a resposta de rwong acima já destaca excelentemente as questões.
Vou adicionar meu 002:
size_t
, ou seja, um tamanho que ...... é necessário apenas para índices de intervalo quando
sizeof(type)==1
, ou seja, se você estiver lidando comchar
tipos de byte ( ). (Mas, observamos, ele pode ser menor que um tipo ptr :xxx::size_type
poderia ser usado em 99,9% dos casos, mesmo que fosse um tipo de tamanho assinado. (compararssize_t
)std::vector
amigos escolheremsize_t
, um tipo não assinado , para o tamanho e a indexação é considerado por alguns como uma falha de design. Eu concordo. (Sério, tire 5 minutos e assista à palestra relâmpago CppCon 2016: Jon Kalb "sem assinatura: Uma Diretriz para um Código Melhor" .)size_t
para ser consistente com a Biblioteca Padrão, ou use (um assinado )intptr_t
oussize_t
para cálculos de indexação fáceis e menos propensos a erros.intptr_t
se você deseja assinar e deseja o tamanho da palavra da máquina ou usessize_t
.Para responder diretamente à pergunta, não é inteiramente um "artefato histórico", pois a questão teórica de precisar abordar mais da metade do espaço de endereço ("indexação" ou) deve ser, aehm, abordada de alguma forma em uma linguagem de baixo nível como C ++.
Em retrospecto, eu pessoalmente acho que é uma falha de design que a Biblioteca Padrão usa sem assinatura em
size_t
todo o lugar, mesmo onde não representa um tamanho de memória bruto, mas uma capacidade de dados digitados, como nas coleções:Vou repetir o conselho de Jon aqui:
(* 1) isto é, não assinado == máscara de bits, nunca faça contas nele (aqui ocorre a primeira exceção - você pode precisar de um contador que o envolva - este deve ser um tipo não assinado).
(* 2) quantidades que significam algo em que você conta e / ou faz contas.
fonte
ssize_t
, definido como o pendente assinado emsize_t
vez deintptr_t
, que pode armazenar qualquer ponteiro (não membro) e, portanto, pode ser maior?size_t
definição um pouco confusa. Consulte size_t vs. intptr e en.cppreference.com/w/cpp/types/size_t Aprendeu algo novo hoje. :-) Acho que o restante dos argumentos permanece, vou ver se consigo consertar os tipos usados.Vou acrescentar que, por razões de desempenho, normalmente uso size_t, para garantir que erros de cálculo causem um subfluxo, o que significa que as duas verificações de intervalo (abaixo de zero e acima de size ()) podem ser reduzidas a uma:
usando int assinado:
usando int não assinado:
fonte