Eu estive revisando a programação C e há apenas algumas coisas me incomodando.
Vamos pegar este código, por exemplo:
int myArray[5] = {1, 2, 2147483648, 4, 5};
int* ptr = myArray;
int i;
for(i=0; i<5; i++, ptr++)
printf("\n Element %d holds %d at address %p", i, myArray[i], ptr);
Eu sei que um int pode conter um valor máximo de 2.147.483.647 positivo. Então, analisando isso, ele "transborda" para o próximo endereço de memória que faz com que o elemento 2 apareça como "-2147483648" nesse endereço? Mas isso realmente não faz sentido, porque, na saída, ele ainda diz que o próximo endereço contém o valor 4, depois 5. Se o número tivesse transbordado para o próximo endereço, isso não mudaria o valor armazenado nesse endereço? ?
Lembro-me vagamente da programação no MIPS Assembly e de observar os endereços mudarem valores durante o programa passo a passo que os valores atribuídos a esses endereços mudariam.
A menos que eu esteja lembrando incorretamente, eis outra pergunta: se o número atribuído a um endereço específico for maior que o tipo (como em myArray [2]), isso não afetará os valores armazenados no endereço subseqüente?
Exemplo: temos int myNum = 4 bilhões no endereço 0x10010000. É claro que o myNum não pode armazenar 4 bilhões, por isso aparece como um número negativo nesse endereço. Apesar de não poder armazenar esse número grande, ele não afeta o valor armazenado no endereço subsequente de 0x10010004. Corrigir?
Os endereços de memória têm apenas espaço suficiente para armazenar determinados tamanhos de números / caracteres e, se o tamanho ultrapassar o limite, ele será representado de maneira diferente (como tentar armazenar 4 bilhões no int, mas aparecerá como um número negativo) e portanto, não afeta os números / caracteres armazenados no próximo endereço.
Desculpe se eu fui ao mar. Eu tenho tido um grande peido cerebral o dia todo com isso.
fonte
int c = INT.MAXINT; c+=1;
que aconteceu com c.Respostas:
Não, não tem. Em C, as variáveis têm um conjunto fixo de endereços de memória para trabalhar. Se você estiver trabalhando em um sistema com 4 bytes
ints
e definir umaint
variável como2,147,483,647
e depois adicionar1
, a variável geralmente conterá-2147483648
. (Na maioria dos sistemas. O comportamento é realmente indefinido.) Nenhum outro local de memória será modificado.Em essência, o compilador não permitirá que você atribua um valor muito grande para o tipo. Isso irá gerar um erro do compilador. Se você forçar com um caso, o valor será truncado.
Observado de maneira bit a bit, se o tipo puder armazenar apenas 8 bits e você tentar forçar o valor
1010101010101
com um caso, você terminará com os 8 bits inferiores, ou01010101
.No seu exemplo, independentemente do que você faça
myArray[2]
,myArray[3]
conterá '4'. Não há "transbordamento". Você está tentando colocar algo com mais de 4 bytes, apenas cortando tudo na parte alta, deixando os 4 bytes inferiores. Na maioria dos sistemas, isso resultará em-2147483648
.Do ponto de vista prático, você deseja apenas garantir que isso nunca aconteça. Esses tipos de transbordamentos geralmente resultam em defeitos difíceis de resolver. Em outras palavras, se você acha que existe alguma chance de todos os seus valores estarem em bilhões, não use
int
.fonte
Estouro de número inteiro assinado é um comportamento indefinido. Se isso acontecer, seu programa é inválido. O compilador não precisa verificar isso para você; portanto, ele pode gerar um executável que parece fazer algo razoável, mas não há garantia de que isso acontecerá.
No entanto, o estouro de número inteiro não assinado está bem definido. Ele envolverá o módulo UINT_MAX + 1. A memória não ocupada por sua variável não será afetada.
Consulte também https://stackoverflow.com/q/18195715/951890
fonte
int
. Suponho que eles poderiam usar o código Gray ou BCD ou EBCDIC . não sei por que alguém projetaria hardware para fazer aritmética com código Gray ou EBCDIC, mas, novamente, não sei por que alguém fariaunsigned
com binário e assinariaint
com qualquer coisa que não fosse o complemento do 2.Então, existem duas coisas aqui:
No nível do idioma:
Em C:
Para aqueles que gostariam de um exemplo "que qualquer coisa", eu já vi:
transformar-se em:
e sim, é uma transformação legítima.
Isso significa que há realmente riscos potenciais de sobrescrever memória em excesso devido a alguma transformação estranha do compilador.
Nota: em Clang ou gcc, use
-fsanitize=undefined
em Debug para ativar o Undefined Behavior Sanitizer, que interromperá o estouro / excesso de números inteiros assinados.Ou significa que você pode substituir a memória usando o resultado da operação para indexar (desmarcada) em uma matriz. Infelizmente, isso é muito mais provável na ausência de detecção de estouro / estouro.
Nota: em Clang ou gcc, use
-fsanitize=address
em Debug para ativar o Address Sanitizer, que interromperá o acesso fora dos limites.No nível da máquina :
Realmente depende das instruções de montagem e da CPU que você usa:
Add
:Observe que, se as coisas acontecem nos registradores ou na memória, em nenhum dos casos a CPU substitui a memória durante o estouro.
fonte
Para responder melhor à @ StevenBurnap, a razão pela qual isso acontece é a maneira como os computadores funcionam no nível da máquina.
Sua matriz é armazenada na memória (por exemplo, na RAM). Quando uma operação aritmética é executada, o valor na memória é copiado nos registros de entrada do circuito que executa a aritmética (a ALU: Unidade Lógica Aritmética ), a operação é executada nos dados nos registros de entrada, produzindo um resultado no registro de saída. Esse resultado é então copiado de volta para a memória no endereço correto na memória, deixando outras áreas da memória intocadas.
fonte
Primeiro (assumindo o padrão C99), convém incluir
<stdint.h>
o cabeçalho padrão e usar alguns dos tipos aqui definidos, principalmente oint32_t
que é exatamente um número inteiro assinado de 32 bits ou ouint64_t
que é exatamente um número inteiro não assinado de 64 bits, e assim por diante. Você pode usar tipos comoint_fast16_t
por motivos de desempenho.Leia as respostas de outras pessoas explicando que a aritmética não assinada nunca é derramada (ou transborda) para locais de memória adjacentes. Cuidado com o comportamento indefinido no estouro assinado .
Então, se você precisar calcular exatamente números inteiros enormes (por exemplo, você deseja calcular fatorial de 1000 com todos os seus 2568 dígitos em decimal), você deseja bigints, também conhecidos como números de precisão arbitrários (ou números). Os algoritmos para uma aritmética bigint eficiente são muito inteligentes e geralmente requerem o uso de instruções especializadas da máquina (por exemplo, alguns adicionam palavras com carry, se o seu processador tiver isso). Por isso, recomendo fortemente, nesse caso, usar alguma biblioteca bigint existente como o GMPlib
fonte