Eu me deparei com uma experiência estranha em programação C. Considere este código:
int main(){
int array1[6] = {0, 1, 2, 3, 4, 5};
int array2[6] = {6, 7, 8, 9, 10, 11};
printf("%d\n", array1[-1]);
return 0;
}
Ao compilar e executar isso, não recebo erros ou avisos. Como meu palestrante disse, o índice do array -1
acessa outra variável. Ainda estou confuso, por que diabos uma linguagem de programação tem esse recurso? Quero dizer, por que permitir índices de matriz negativos?
programming-languages
arrays
c
Mohammed Fawzan
fonte
fonte
-1
de uma sub-matriz é uma maneira perfeitamente válida de se referir ao elemento antes dessa matriz na matriz maior. A outra é que, se o índice for inválido, o programa é inválido, mas na maioria das implementações você terá um mau comportamento silencioso, não um erro fora do intervalo.Respostas:
A operação de indexação de matriz
a[i]
ganha seu significado com os seguintes recursos de CA sintaxe
a[i]
é equivalente a*(a + i)
. Portanto, é válido dizer5[a]
para chegar ao 5º elemento dea
.Aritmética do ponteiro diz que, dado um ponteiro
p
e um número inteiroi
,p + i
o ponteirop
avançou pori * sizeof(*p)
bytesO nome de uma matriz
a
é rapidamente convertido em ponteiro para o 0º elemento dea
Com efeito, a indexação de matriz é um caso especial de indexação de ponteiro. Como um ponteiro pode apontar para qualquer lugar dentro de uma matriz, qualquer expressão arbitrária que pareça não
p[-1]
está errada pelo exame e, portanto, os compiladores não (não podem) consideram todas essas expressões como erros.Seu exemplo
a[-1]
ondea
é realmente o nome de uma matriz é realmente inválido. IIRC, é indefinido se houver um valor significativo de ponteiro como resultado da expressãoa - 1
ondea
é conhecido como um ponteiro para o 0º elemento de uma matriz. Portanto, um compilador inteligente pode detectar isso e sinalizá-lo como um erro. Outros compiladores ainda podem ser compatíveis, permitindo que você atire no próprio pé, fornecendo um ponteiro para um slot de pilha aleatório.A resposta da ciência da computação é:
Em C, o
[]
operador é definido em ponteiros, não em matrizes. Em particular, é definido em termos de aritmética de ponteiro e desreferência de ponteiro.Em C, um ponteiro é abstratamente uma tupla
(start, length, offset)
com a condição que0 <= offset <= length
. A aritmética do ponteiro é essencialmente aritmética elevada no deslocamento, com a ressalva de que, se o resultado da operação violar a condição do ponteiro, é um valor indefinido. Remover a referência de um ponteiro adiciona uma restrição adicional a issooffset < length
.C tem uma noção de
undefined behaviour
que permite que um compilador represente concretamente essa tupla como um número único e não precisa detectar nenhuma violação da condição do ponteiro. Qualquer programa que satisfaça a semântica abstrata estará seguro com a semântica concreta (com perdas). Qualquer coisa que viole a semântica abstrata pode ser, sem comentários, aceito pelo compilador e pode fazer o que quiser com ele.fonte
As matrizes são simplesmente dispostas como blocos de memória contíguos. Um acesso de matriz como um [i] é convertido em um acesso ao endereço de localização da memóriaOf (a) + i. Este é o código
a[-1]
é perfeitamente compreensível, simplesmente se refere ao endereço um antes do início da matriz.Isso pode parecer loucura, mas há muitas razões pelas quais isso é permitido:
a[-1]
é válido. Por exemplo, se eu sei quea
não é realmente o início da matriz, mas um ponteiro no meio da matriz,a[-1]
simplesmente obtém o elemento da matriz que fica à esquerda do ponteiro.fonte
a[-1]
faz todo o sentido para alguns casos dea
, neste caso particular, é claro ilegal (mas não pego pelo compilador)Como as outras respostas explicam, esse é um comportamento indefinido em C. Considere que C foi definido (e é usado principalmente) como um "assembler de alto nível". Os usuários de C o valorizam por sua velocidade intransigente, e verificar as coisas em tempo de execução está (principalmente) fora de questão por uma questão de puro desempenho. Algumas construções C que parecem absurdas para pessoas vindas de outras línguas fazem todo sentido em C, assim
a[-1]
. Sim, nem sempre faz sentido (fonte
Pode-se usar esse recurso para escrever métodos de alocação de memória que acessam diretamente a memória. Um desses usos é verificar o bloco de memória anterior usando um índice de matriz negativo para determinar se os dois blocos podem ser mesclados. Usei esse recurso ao desenvolver um gerenciador de memória não volátil.
fonte
C não é fortemente digitado. Um compilador C padrão não verifica os limites da matriz. A outra coisa é que uma matriz em C nada mais é do que um bloco contíguo de memória e a indexação inicia em 0; portanto, um índice de -1 é o local de qualquer padrão de bits anterior
a[0]
.Outros idiomas exploram índices negativos de uma maneira agradável. No Python,
a[-1]
retornará o último elemento,a[-2]
retornará o penúltimo elemento e assim por diante.fonte
int
,a[-5]
e, geralmente,int i; ... a[i] = ...;
são digitados corretamente. Erros de índice são detectados apenas em tempo de execução. Obviamente, um compilador inteligente pode detectar algumas violações.Em palavras simples:
Todas as variáveis (incluindo matrizes) em C são armazenadas na memória. Digamos que você tenha 14 bytes de "memória" e inicialize o seguinte:
Além disso, considere o tamanho de um int como 2 bytes. Então, hipoteticamente, nos 2 primeiros bytes de memória, o número inteiro a será salvo. Nos próximos 2 bytes, o número inteiro da primeira posição da matriz será salvo (isso significa matriz [0]).
Então, quando você diz que o array [-1] é como se referir ao número inteiro salvo na memória que está logo antes do array [0], que em nosso é, hipoteticamente, o número inteiro a. Na realidade, não é exatamente assim que as variáveis são armazenadas na memória.
fonte
fonte