Estou estudando um pouco de C ++ e estou brigando com ponteiros. Entendo que posso ter três níveis de ponteiros declarando:
int *(*x)[5];
de modo que *x
é um ponteiro para uma matriz de 5 elementos que são ponteiros para int
. Também sei disso x[0] = *(x+0);
, x[1] = *(x+1)
e assim por diante ....
Então, dada a declaração acima, por que x[0] != x[0][0] != x[0][0][0]
?
x[0]
,x[0][0]
Ex[0][0][0]
ter tipos diferentes. Eles não podem ser comparados. Como assim!=
?int **x[5]
é uma matriz de 5 elementos. Um elemento é um ponteiro para ponteiro para int`int** x[5]
seria uma matriz de cinco ponteiros que apontam para ponteiros que apontam para int.int *(*x)[5]
é um ponteiro para uma matriz de cinco ponteiros que apontam para int.x[0] != x[0][0] != x[0][0][0]
significa? Esta não é uma comparação válida em C ++. Mesmo se você o dividirx[0] != x[0][0]
ex[0][0] != x[0][0][0]
ainda não for válido. Então, o que sua pergunta significa?Respostas:
x
é um ponteiro para uma matriz de 5 ponteiros paraint
.x[0]
é uma matriz de 5 ponteiros paraint
.x[0][0]
é um ponteiro para umint
.x[0][0][0]
é umint
.Você pode ver isso
x[0]
é uma matriz e será convertido em ponteiro para seu primeiro elemento quando usado em uma expressão (com algumas exceções). Portantox[0]
, dará o endereço do seu primeiro elementox[0][0]
que é0x500
.x[0][0]
contém o endereço de umint
que é0x100
.x[0][0][0]
contém umint
valor de10
.Então,
x[0]
é igual a&x[0][0]
e, portanto&x[0][0] != x[0][0]
,.Portanto
x[0] != x[0][0] != x[0][0][0]
,.fonte
0x100
deve aparecer imediatamente à esquerda da caixa que contém10
, da mesma maneira que0x500
aparece à esquerda da caixa. Em vez de ficar muito à esquerda e abaixo.é, de acordo com seu próprio post,
que é simplificado
Por que deveria ser igual?
O primeiro é o endereço de algum ponteiro.
O segundo é o endereço de outro ponteiro.
E o terceiro é algum
int
valor.fonte
x[0][0]
é(x[0])[0]
, ou seja*((*(x+0))+0)
, não*(x+0+0)
. A desreferência acontece antes do segundo[0]
.x[0][0] != *(x+0+0)
assim comox[2][3] != x[3][2]
.Aqui está o layout da memória do seu ponteiro:
x[0]
produz "endereço da matriz",x[0][0]
gera "ponteiro 0",x[0][0][0]
produz "algum número inteiro".Eu acredito que deveria ser óbvio agora, porque eles são todos diferentes.
O exposto acima é suficientemente próximo para o entendimento básico, e é por isso que o escrevi da maneira que o escrevi. No entanto, como hacks apontam corretamente, a primeira linha não é 100% precisa. Então, aqui estão todos os detalhes:
A partir da definição da linguagem C, o valor de
x[0]
é toda a matriz de ponteiros inteiros. No entanto, matrizes são algo com o qual você realmente não pode fazer nada em C. Você sempre manipula o endereço ou os elementos deles, nunca a matriz inteira como um todo:Você pode passar
x[0]
para osizeof
operador. Mas isso não é realmente um uso do valor, seu resultado depende apenas do tipo.Você pode tomar seu endereço que produz o valor
x
, ou seja, "endereço do conjunto" com o tipoint*(*)[5]
. Em outras palavras:&x[0] <=> &*(x + 0) <=> (x + 0) <=> x
Em todos os outros contextos , o valor de
x[0]
decairá em um ponteiro para o primeiro elemento da matriz. Ou seja, um ponteiro com o valor "endereço da matriz" e o tipoint**
. O efeito é o mesmo que se você tivesse convertidox
para um ponteiro do tipoint**
.Devido à deterioração do ponteiro de matriz no caso 3., todos os usos de
x[0]
resultam em um ponteiro que aponta o início da matriz de ponteiros; a chamadaprintf("%p", x[0])
imprimirá o conteúdo das células de memória rotuladas como "endereço da matriz".fonte
x[0]
não é o endereço da matriz.x[0]
não é o endereço da matriz, é a própria matriz. Eu adicionei uma explicação detalhada sobre isso e por que escrevi quex[0]
é o "endereço da matriz". Espero que você goste.printf("%zu\n", sizeof x[0]);
informa o tamanho da matriz, não o tamanho de um ponteiro.sizeof x[0]
...x[0]
desreferencia o ponteiro mais externo ( ponteiro para matriz do tamanho 5 do ponteiro para int) e resulta em uma matriz do tamanho 5 do ponteiro paraint
;x[0][0]
desreferencia o ponteiro mais externo e indexa a matriz, resultando em um ponteiro paraint
;x[0][0][0]
desreferencia tudo, resultando em um valor concreto.A propósito, se você se sentir confuso com o significado desse tipo de declaração, use cdecl .
fonte
Vamos considerar expressões passo a passo
x[0]
,x[0][0]
ex[0][0][0]
.Como
x
é definido da seguinte maneiraexpressão em seguida,
x[0]
é uma matriz do tipoint *[5]
. Leve em consideração que expressãox[0]
é equivalente a expressão*x
. Isso é desreferenciando um ponteiro para uma matriz, obtemos a própria matriz. Vamos denotá-lo como y, ou seja, temos uma declaraçãoExpressão
x[0][0]
é equivalente ay[0]
e tem tipoint *
. Vamos denotá-lo como z, ou seja, temos uma declaraçãoexpressão
x[0][0][0]
é equivalente à expressãoy[0][0]
que, por sua vez, é equivalente à expressãoz[0]
e tem tipoint
.Então nós temos
x[0]
tem tipoint *[5]
x[0][0]
tem tipoint *
x[0][0][0]
tem tipoint
Então eles são objetos de tipos diferentes e, a propósito, de tamanhos diferentes.
Executar por exemplo
fonte
Primeira coisa que tenho que dizer
Na figura a seguir, tudo é claro.
É apenas um exemplo, onde o valor de x [0] [0] [0] = 10
e o endereço de x [0] [0] [0] é 1001
esse endereço é armazenado em x [0] [0] = 1001
e o endereço de x [0] [0] é 2000
e esse endereço é armazenado em x [0] = 2000
Então x [0] [0] [0] ≠ x [0] [0] ≠ x [0]
.
Edições
Programa 1:
Resultado
Programa 2:
Resultado
fonte
x[0]
não contém endereço de formiga. É uma matriz. Decairá apontar para o seu primeiro elemento.Se você visualizasse as matrizes de uma perspectiva do mundo real, elas apareceriam da seguinte maneira:
x[0]
é um contêiner de carga cheio de caixas.x[0][0]
é uma única caixa, cheia de caixas de sapatos, dentro do contêiner de carga.x[0][0][0]
é uma única caixa de sapatos dentro da caixa, dentro do contêiner de carga.Mesmo que fosse a única caixa de sapatos na única caixa do contêiner de carga, ainda é uma caixa de sapatos e não um contêiner de carga
fonte
x[0][0]
haveria um único caixote cheio de pedaços de papel com as localizações das caixas de sapatos escritas?Existe um princípio em C ++ para que: uma declaração de uma variável indique exatamente a maneira de usar a variável. Considere sua declaração:
que pode ser reescrito como (para maior clareza):
Devido ao princípio, temos:
Portanto:
Então você pode descobrir a diferença.
fonte
x[0]
é uma matriz de 5 polegadas, não um ponteiro. (pode decair para um ponteiro na maioria dos contextos, mas a distinção é importante aqui).*(*x)[5]
é umint
, então(*x)[5]
é umint *
, então*x
é um(int *)[5]
, entãox
é um*((int *)[5])
. Ou seja,x
é um ponteiro para uma matriz de 5 ponteiros paraint
.Você está tentando comparar diferentes tipos por valor
Se você escolher os endereços, poderá obter mais do que espera
Lembre-se de que sua declaração faz a diferença
permitiria que as comparações que você quer, uma vez que
y
,y[0]
,y[0][0]
,y[0][0][0]
teria diferentes valores e tipos, mas o mesmo endereçonão ocupa espaço contíguo.
x
ex [0]
têm o mesmo endereço, masx[0][0]
ex[0][0][0]
são cada um em endereços diferentesfonte
int *(*x)[5]
é diferente deint **x[5]
Sendo
p
um ponteiro: você está empilhando desreferênciasp[0][0]
, o que é equivalente a*((*(p+0))+0)
.Na referência C (&) e na desreferência (*):
É equivalente a:
Veja que o & * pode ser refatorado, apenas removendo-o:
fonte
p == p
.&(&p[0])[0]
é diferente dep[0][0]
As outras respostas estão corretas, mas nenhuma delas enfatiza a idéia de que é possível que todas as três contenham o mesmo valor e, portanto, estão incompletas.
A razão pela qual isso não pode ser entendido pelas outras respostas é que todas as ilustrações, embora úteis e definitivamente razoáveis na maioria das circunstâncias, falham em cobrir a situação em que o ponteiro
x
aponta para si mesmo.Isso é muito fácil de construir, mas claramente um pouco mais difícil de entender. No programa abaixo, veremos como podemos forçar todos os três valores a serem idênticos.
NOTA: O comportamento neste programa é indefinido, mas eu o estou postando aqui apenas como uma demonstração interessante de algo que os ponteiros podem fazer, mas não deveriam .
Isso compila sem avisos em C89 e C99 e a saída é a seguinte:
Curiosamente, todos os três valores são idênticos. Mas isso não deveria ser uma surpresa! Primeiro, vamos dividir o programa.
Declaramos
x
como ponteiro para uma matriz de 5 elementos em que cada elemento é do tipo ponteiro para int. Esta declaração aloca 4 bytes na pilha de tempo de execução (ou mais, dependendo da sua implementação; nos ponteiros da minha máquina são 4 bytes), portanto,x
está se referindo a um local de memória real. Na família C de idiomas, o conteúdo dex
é apenas lixo, algo que resta do uso anterior do local, portantox
ele não aponta para lugar nenhum - certamente não para o espaço alocado.Então, naturalmente, podemos pegar o endereço da variável
x
e colocá-lo em algum lugar, então é exatamente isso que fazemos. Mas vamos em frente e colocá-lo no próprio x. Como&x
tem um tipo diferentex
, precisamos fazer um elenco para não receber avisos.O modelo de memória ficaria assim:
Portanto, o bloco de 4 bytes de memória no endereço
0xbfd9198c
contém o padrão de bits correspondente ao valor hexadecimal0xbfd9198c
. Simples o suficiente.Em seguida, imprimimos os três valores. As outras respostas explicam a que cada expressão se refere, então o relacionamento deve ficar claro agora.
Podemos ver que os valores são os mesmos, mas apenas em um nível muito baixo ... seus padrões de bits são idênticos, mas os dados de tipo associados a cada expressão significam que seus valores interpretados são diferentes. Por exemplo, se imprimíssemos
x[0][0][0]
usando a string de formato%d
, obteríamos um número negativo enorme; portanto, os "valores" são, na prática, diferentes, mas o padrão de bits é o mesmo.Isso é realmente muito simples ... nos diagramas, as setas apontam apenas para o mesmo endereço de memória e não para diferentes. No entanto, enquanto conseguimos forçar um resultado esperado a partir de um comportamento indefinido, é exatamente isso - indefinido. Este não é um código de produção, mas simplesmente uma demonstração por uma questão de integridade.
Em uma situação razoável, você usará
malloc
para criar a matriz de 5 ponteiros int e novamente para criar as entradas apontadas nessa matriz.malloc
sempre retorna um endereço exclusivo (a menos que você esteja com falta de memória; nesse caso, ele retorna NULL ou 0), para que você nunca precise se preocupar com ponteiros auto-referenciais como este.Espero que essa seja a resposta completa que você está procurando. Você não deveria esperar
x[0]
,x[0][0]
ex[0][0][0]
ser igual, mas eles poderiam ser se forçados. Se alguma coisa passou pela sua cabeça, me avise para que eu possa esclarecer!fonte
x[0]
na verdade não representam um objeto válido do tipo corretox
é um ponteiro para uma matriz, para que possamos usar o[]
operador para especificar um deslocamento desse ponteiro e desmarcá-lo. O que há de estranho aí? O resultado dex[0]
é uma matriz, e C não se queixa se você imprimi-lo usando,%p
pois é assim que é implementado de qualquer maneira.-pedantic
bandeira produz nenhum aviso, então C é muito bem com os tipos ...O tipo de
int *(*x)[5]
é, por exemplo,int* (*)[5]
um ponteiro para uma matriz de 5 ponteiros para ints.x
é o endereço da primeira matriz de 5 ponteiros para ints (um endereço com o tipoint* (*)[5]
)x[0]
o endereço da primeira matriz de 5 ponteiros para entradas (o mesmo endereço com o tipoint* [5]
) (endereço de deslocamento x por exemplo,0*sizeof(int* [5])
índice * tamanho do tipo que está sendo apontado e desreferência)x[0][0]
é o primeiro ponteiro para um int na matriz (o mesmo endereço com o tipoint*
) (endereço de deslocamento x por0*sizeof(int* [5])
e desreferencia e depois por0*sizeof(int*)
e desreferencia)x[0][0][0]
é o primeiro int sendo apontado pelo ponteiro para um int (endereço de deslocamento x por0*sizeof(int* [5])
e desreferencia e deslocamento desse endereço por0*sizeof(int*)
e desreferencia e deslocamento desse endereço por0*sizeof(int)
e desreferencia)O tipo de
int *(*y)[5][5][5]
é,int* (*)[5][5][5]
ou seja, um ponteiro para uma matriz 3d de ponteiros 5x5x5 para intsx
é o endereço da primeira matriz 3d de ponteiros 5x5x5 para ints com o tipoint*(*)[5][5][5]
x[0]
é o endereço da primeira matriz 3d de ponteiros 5x5x5 para ints (endereço de deslocamento x por0*sizeof(int* [5][5][5])
e desreferencia)x[0][0]
é o endereço da primeira matriz 2D de ponteiros 5x5 para ints (desloca o endereço x por0*sizeof(int* [5][5][5])
e a desreferencia e desloca esse endereço por0*sizeof(int* [5][5])
)x[0][0][0]
é o endereço da primeira matriz de 5 ponteiros para ints (endereço de deslocamento x por0*sizeof(int* [5][5][5])
e desreferencial e deslocamento de endereço por0*sizeof(int* [5][5])
e deslocamento de endereço por0*sizeof(int* [5])
)x[0][0][0][0]
é o primeiro ponteiro para um int na matriz (deslocar o endereço x por0*sizeof(int* [5][5][5])
e desreferenciar e deslocar esse endereço por0*sizeof(int* [5][5])
e deslocar esse endereço por0*sizeof(int* [5])
e deslocar esse endereço por0*sizeof(int*)
e desreferenciar)x[0][0][0][0][0]
é o primeiro int sendo apontado pelo ponteiro para um int (endereço de deslocamento x por0*sizeof(int* [5][5][5])
e dereferência e deslocamento desse endereço por0*sizeof(int* [5][5])
e deslocamento desse endereço por0*sizeof(int* [5])
e deslocamento desse endereço por0*sizeof(int*)
e dereferência e deslocamento desse endereço por0*sizeof(int)
e dereferência)Quanto à deterioração da matriz:
Isso equivale a passar,
int* x[][5][5]
ouint* (*x)[5][5]
seja, todos eles decaem para o último. É por isso que você não receberá um aviso do compilador para usarx[6][0][0]
na função, mas receberáx[0][6][0]
porque essas informações de tamanho são preservadasx[0]
é o endereço da primeira matriz 3d de ponteiros 5x5x5 para intsx[0][0]
é o endereço da primeira matriz 2D de ponteiros 5x5 para intsx[0][0][0]
é o endereço da primeira matriz de 5 ponteiros para intsx[0][0][0][0]
é o primeiro ponteiro para um int na matrizx[0][0][0][0][0]
é o primeiro int sendo apontado pelo ponteiro para um intNo último exemplo, é muito mais clara semanticamente para usar
*(*x)[0][0][0]
em vez dex[0][0][0][0][0]
, isto é porque a primeira e última[0]
aqui são interpretados como uma desreferenciava ponteiro em vez de um índice para uma matriz multidimensional, devido ao tipo. No entanto, eles são idênticos porque,(*x) == x[0]
independentemente da semântica. Você também pode usar*****x
, que parece que você está desreferenciando o ponteiro 5 vezes, mas na verdade é interpretado exatamente da mesma maneira: um deslocamento, uma desreferência, uma desreferência, 2 deslocamentos em uma matriz e uma desreferência, apenas por causa do tipo você está aplicando a operação.Essencialmente, quando você
[0]
ou*
um*
para um tipo não de matriz, é um deslocamento e uma desreferência devido à ordem de precedência de*(a + 0)
.Quando você
[0]
ou*
um*
de um tipo de matriz, em seguida, ele é um deslocamento em seguida, um dereference idempotent (o dereference é resolvido pelo compilador para produzir o mesmo endereço - é uma operação idempotente).Quando você
[0]
ou*
um tipo com um tipo de matriz 1d, é um deslocamento e uma desreferênciaSe você
[0]
ou**
um tipo de matriz 2D, então é apenas um deslocamento, ou seja, um deslocamento e, em seguida, uma desreferência idempotente.Se você
[0][0][0]
ou***
um tipo de matriz 3d, será uma desreferência offset + idempotente, uma desreferência offset + idempotente e uma desreferência offset + idempotente e uma desreferência. A desreferência verdadeira ocorre apenas quando o tipo de matriz é totalmente removido.Para o exemplo do
int* (*x)[1][2][3]
tipo é desembrulhado em ordem.x
tem um tipoint* (*)[1][2][3]
*x
tem um tipoint* [1][2][3]
(deslocamento 0 + desreferencia idempotente)**x
tem um tipoint* [2][3]
(deslocamento 0 + desreferencia idempotente)***x
tem um tipoint* [3]
(deslocamento 0 + desreferencia idempotente)****x
tem um tipoint*
(deslocamento 0 + desreferencia)*****x
tem tipoint
(deslocamento 0 + desreferência)fonte