Com matrizes, por que é que a [5] == 5 [a]?

1622

Como Joel aponta no podcast Stack Overflow # 34 , na linguagem de programação C (também conhecida como: K & R), há menção dessa propriedade de matrizes em C:a[5] == 5[a]

Joel diz que é por causa da aritmética dos ponteiros, mas eu ainda não entendo. Por que faza[5] == 5[a] ?

Dinah
fonte
48
algo como um [+] também funcionaria como * (a ++) OU * (++ a)?
Egon
45
@Egon: Isso é muito criativo, mas infelizmente não é assim que os compiladores funcionam. O compilador interpreta a[1]como uma série de tokens, não seqüências de caracteres: * ({local inteiro de} a {operador} + {inteiro} 1) é o mesmo que * ({inteiro} 1 {operador} + {local inteiro de} a) mas não é o mesmo que * ({local inteiro de} a {operator} + {operator} +)
Dinah
11
Uma variação composta interessante sobre isso é ilustrada no acesso à matriz ilógica , onde você tem char bar[]; int foo[];e foo[i][bar]é usado como expressão.
22612 Jonathan
5
@EldritchConundrum, por que você acha que 'o compilador não pode verificar se a parte esquerda é um ponteiro'? Sim pode. É verdade que a[b]= *(a + b)para qualquer dado ae b, mas era a escolha livre dos designers de linguagem +para serem definidos como comutativos para todos os tipos. Nada poderia impedi-los de proibir i + penquanto permitiam p + i.
ach
13
@Andrey One geralmente espera +ser comutativo, então talvez o verdadeiro problema seja optar por fazer as operações dos ponteiros parecerem aritméticas, em vez de projetar um operador de deslocamento separado.
Eldritch Conundrum

Respostas:

1924

O padrão C define o []operador da seguinte maneira:

a[b] == *(a + b)

Portanto a[5], avaliará para:

*(a + 5)

e 5[a]avaliará para:

*(5 + a)

aé um ponteiro para o primeiro elemento da matriz. a[5]é o valor que está a 5 elementos mais adiante a, que é o mesmo que *(a + 5), e da matemática do ensino fundamental sabemos que esses são iguais (a adição é comutativa ).

Mehrdad Afshari
fonte
325
Gostaria de saber se não é mais como * ((5 * sizeof (a)) + a). Ótima explicação.
John MacIntyre
92
@ Dinah: Do ponto de vista do compilador C, você está certo. Não é necessário sizeof e as expressões que mencionei são O MESMO. No entanto, o compilador levará em consideração o tamanho de quando produzir código de máquina. Se a é uma matriz int, a[5]será compilado para algo como em mov eax, [ebx+20]vez de[ebx+5]
Mehrdad Afshari
12
@ Dinah: A é um endereço, digamos 0x1230. Se a estava na matriz int de 32 bits, então um [0] está em 0x1230, um [1] está em 0x1234, um [2] em 0x1238 ... a [5] em x1244 etc. Se adicionarmos 5 a 0x1230, obtemos 0x1235, o que está errado.
James Curran
36
@ sr105: Esse é um caso especial para o operador +, em que um dos operandos é um ponteiro e o outro um número inteiro. O padrão diz que o resultado será do tipo do ponteiro. O compilador / tem que ser / inteligente o suficiente.
aib 23/12/08
48
"da matemática do ensino fundamental, sabemos que são iguais" - entendo que você está simplificando, mas estou com aqueles que acham que isso acabou simplificando demais . Não é fundamental isso *(10 + (int *)13) != *((int *)10 + 13). Em outras palavras, há mais coisas acontecendo aqui do que a aritmética da escola primária. A comutatividade depende criticamente do compilador, reconhecendo qual operando é um ponteiro (e para qual tamanho do objeto). Em outras palavras (1 apple + 2 oranges) = (2 oranges + 1 apple), mas (1 apple + 2 oranges) != (1 orange + 2 apples).
LarsH
288

Como o acesso ao array é definido em termos de ponteiros. a[i]é definido para significar *(a + i), que é comutativo.

David Thornley
fonte
42
Matrizes não são definidas em termos de ponteiros, mas o acesso a elas é.
Lightness Races in Orbit
5
Eu acrescentaria "então é igual a *(i + a), que pode ser escrito como i[a]".
Jim Balter
4
Sugiro que você inclua a citação do padrão, que é a seguinte: 6.5.2.1: 2 Uma expressão postfix seguida por uma expressão entre colchetes [] é uma designação subscrita de um elemento de um objeto de matriz. A definição do operador subscrito [] é que E1 [E2] é idêntico a (* ((E1) + (E2))). Devido às regras de conversão que se aplicam ao operador binário +, se E1 for um objeto de matriz (equivalentemente, um ponteiro para o elemento inicial de um objeto de matriz) e E2 for um número inteiro, E1 [E2] designará o E2-ésimo elemento de E1 (contando a partir de zero).
Vality 17/02/2015
Para ser mais correto: as matrizes se decompõem em ponteiros quando você as acessa.
12431234123412341234123
Nitpick: Não faz sentido dizer que " *(a + i)é comutativo". No entanto, *(a + i) = *(i + a) = i[a]porque a adição é comutativa.
Andreas Rejbrand 13/10/19
231

Eu acho que algo está faltando nas outras respostas.

Sim, p[i]por definição é equivalente a *(p+i), o qual (porque a adição é comutativa) é equivalente a *(i+p), ao qual (novamente, pela definição do []operador) é equivalente a i[p].

(E array[i], em , o nome da matriz é implicitamente convertido em um ponteiro para o primeiro elemento da matriz.)

Mas a comutatividade da adição não é tão óbvia nesse caso.

Quando ambos os operandos são do mesmo tipo, ou mesmo de diferentes tipos numéricos que são promovidos a um tipo comum, comutatividade faz todo o sentido: x + y == y + x.

Mas, neste caso, estamos falando especificamente sobre aritmética de ponteiros, em que um operando é um ponteiro e o outro é um número inteiro. (Inteiro + inteiro é uma operação diferente, e ponteiro + ponteiro é um disparate.)

A descrição do +operador da norma C ( N1570 6.5.6) diz:

Além disso, ambos os operandos devem ter um tipo aritmético ou um operando deve ser um ponteiro para um tipo de objeto completo e o outro deve ter um tipo inteiro.

Poderia facilmente ter dito:

Além disso, ambos os operandos devem ter um tipo aritmético ou o operando esquerdo deve ser um ponteiro para um tipo de objeto completo e o operando direito deve ter um tipo inteiro.

nesse caso, ambos i + pe i[p]seria ilegal.

Em termos de C ++, realmente temos dois conjuntos de +operadores sobrecarregados , que podem ser descritos livremente como:

pointer operator+(pointer p, integer i);

e

pointer operator+(integer i, pointer p);

dos quais apenas o primeiro é realmente necessário.

Então, por que é assim?

O C ++ herdou essa definição de C, que a obteve de B (a comutatividade da indexação de matriz é explicitamente mencionada na Referência do Usuário de 1972 para B ), que a obteve de BCPL (manual de 1967), que pode muito bem ter sido obtida até idiomas anteriores (CPL? Algol?).

Portanto, a ideia de que a indexação de array é definida em termos de adição, e essa adição, mesmo de ponteiro e número inteiro, é comutativa, remonta há muitas décadas aos idiomas ancestrais de C.

Essas línguas eram muito menos tipificadas do que o C moderno. Em particular, a distinção entre ponteiros e números inteiros era frequentemente ignorada. (Os programadores C iniciais às vezes usavam ponteiros como números inteiros não assinados, antes que a unsignedpalavra - chave fosse adicionada ao idioma.) Portanto, a idéia de tornar a adição não comutativa porque os operandos são de tipos diferentes provavelmente não teria ocorrido aos projetistas dessas linguagens. Se um usuário quiser adicionar duas "coisas", sejam elas números inteiros, ponteiros ou qualquer outra coisa, não cabe ao idioma impedi-lo.

E, ao longo dos anos, qualquer alteração a essa regra quebraria o código existente (embora o padrão ANSI C de 1989 possa ter sido uma boa oportunidade).

Alterar C e / ou C ++ para exigir colocar o ponteiro à esquerda e o número inteiro à direita pode quebrar algum código existente, mas não haveria perda de potência expressiva real.

Portanto, agora temos arr[3]e 3[arr]significamos exatamente a mesma coisa, embora a última forma nunca deva aparecer fora da IOCCC .

Keith Thompson
fonte
12
Descrição fantástica desta propriedade. De uma visão de alto nível, acho que 3[arr]é um artefato interessante, mas que raramente deve ser usado. A resposta aceita para esta pergunta (< stackoverflow.com/q/1390365/356> ), que perguntei há algum tempo, mudou a maneira como pensava sobre a sintaxe. Embora geralmente não exista uma maneira certa e errada de fazer essas coisas, esses tipos de recursos fazem você pensar de uma maneira separada dos detalhes da implementação. Há um benefício nessa maneira diferente de pensar, que em parte se perde quando você se fixa nos detalhes da implementação.
Dinah
3
A adição é comutativa. Para o padrão C defini-lo de outra forma, seria estranho. É por isso que não poderia ser tão fácil dizer "Além disso, ambos os operandos devem ter tipo aritmético ou o operando esquerdo deve ser um ponteiro para um tipo de objeto completo e o operando direito deve ter um tipo inteiro". - Isso não faria sentido para a maioria das pessoas que adicionam coisas.
Iheanyi
9
@iheanyi: A adição geralmente é comutativa - e geralmente leva dois operandos do mesmo tipo. A adição de ponteiro permite adicionar um ponteiro e um número inteiro, mas não dois ponteiros. O IMHO já é um caso especial suficientemente estranho, que exigir que o ponteiro seja o operando esquerdo não seria um fardo significativo. (Alguns idiomas utilizam "+" para concatenação de string; isso certamente não é comutativa.)
Keith Thompson
3
@ Supercat, isso é ainda pior. Isso significaria que às vezes x + 1! = 1 + x. Isso violaria completamente a propriedade associativa da adição.
Iheanyi 21/10
3
@iheanyi: Eu acho que você quis dizer propriedade comutativa; a adição já não é associativa, pois na maioria das implementações (1LL + 1U) -2! = 1LL + (1U-2). De fato, a mudança tornaria associativas algumas situações que atualmente não são, por exemplo, 3U + (UINT_MAX-2L) seriam iguais (3U + UINT_MAX) -2. O que seria melhor, no entanto, é que o idioma adicione novos tipos distintos para números inteiros que podem ser promovidos e "embrulhe" anéis algébricos, de modo que a adição de 2 a um ring16_tque contenha 65535 produziria a ring16_tcom valor 1, independente do tamanho deint .
Supercat 21/10
196

E claro

 ("ABCD"[2] == 2["ABCD"]) && (2["ABCD"] == 'C') && ("ABCD"[2] == 'C')

A principal razão para isso foi que, nos anos 70, quando o C foi projetado, os computadores não tinham muita memória (64 KB eram muito); portanto, o compilador C não fazia muita verificação de sintaxe. Portanto, " X[Y]" foi traduzido cegamente para " *(X+Y)"

Isso também explica as sintaxes " +=" e " ++". Tudo na forma " A = B + C" tinha a mesma forma compilada. Mas, se B fosse o mesmo objeto que A, uma otimização no nível de montagem estava disponível. Mas o compilador não era brilhante o suficiente para reconhecê-lo, então o desenvolvedor teve que ( A += C). Da mesma forma, se Cfoi 1, uma otimização de nível de montagem diferente estava disponível, e novamente o desenvolvedor teve que torná-lo explícito, porque o compilador não reconhecê-lo. (Mais recentemente, compiladores fazem, portanto, essas sintaxes são praticamente desnecessárias atualmente)

James Curran
fonte
127
Na verdade, isso é falso; o primeiro termo "ABCD" [2] == 2 ["ABCD"] é avaliado como verdadeiro, ou 1 e 1! = 'C': D
Jonathan Leffler
8
@ Jonathan: a mesma ambiguidade levou à edição do título original deste post. Somos a equivalência matemática de marcas iguais, sintaxe de código ou pseudo-código. Eu argumento a equivalência matemática, mas como estamos falando de código, não podemos escapar de estar vendo tudo em termos de sintaxe de código.
Dinah
19
Isso não é um mito? Quero dizer que os operadores + = e ++ foram criados para simplificar o compilador? Algum código fica mais claro com eles, e é uma sintaxe útil, independentemente do que o compilador faça com ele.
Thomas Padron-McCarthy
6
+ = e ++ tem outro benefício significativo. se o lado esquerdo alterar alguma variável enquanto estiver avaliado, a alteração será feita apenas uma vez. a = a + ...; fará isso duas vezes.
Johannes Schaub - litb
8
Não - "ABCD" [2] == * ("ABCD" + 2) = * ("CD") = 'C'. Dereferencing uma corda dá-lhe um char, não uma sub
MSalters
55

Parece que ninguém mencionou o problema de Dinah com sizeof:

Você só pode adicionar um número inteiro a um ponteiro, não pode adicionar dois ponteiros juntos. Dessa forma, ao adicionar um ponteiro a um número inteiro, ou um número inteiro a um ponteiro, o compilador sempre sabe qual bit tem um tamanho que precisa ser levado em consideração.

user30364
fonte
1
Há uma conversa bastante exaustiva sobre isso nos comentários da resposta aceita. Referenciei a conversa na edição à pergunta original, mas não resolvi diretamente sua preocupação muito válida com o tamanho da questão. Não sei como fazer isso melhor no SO. Devo fazer outra edição na origem. questão?
Dinah
50

Para responder a pergunta literalmente. Nem sempre é verdade quex == x

double zero = 0.0;
double a[] = { 0,0,0,0,0, zero/zero}; // NaN
cout << (a[5] == 5[a] ? "true" : "false") << endl;

impressões

false
Peter Lawrey
fonte
27
Na verdade, um "nan" não é igual a si mesmo: cout << (a[5] == a[5] ? "true" : "false") << endl;é false.
TrueY
8
@ Verdadeiro: Ele afirmou isso especificamente para o caso NaN (e especificamente isso x == xnem sempre é verdade). Eu acho que essa era a intenção dele. Então ele é tecnicamente correto (e possivelmente, como se costuma dizer, o melhor tipo de correto!).
Tim Čas 13/02/2015
3
A pergunta é sobre C, seu código não é código C. Há também um NANin <math.h>, que é melhor que 0.0/0.0, porque 0.0/0.0é UB quando __STDC_IEC_559__não está definido (a maioria das implementações não define __STDC_IEC_559__, mas na maioria das implementações 0.0/0.0ainda funcionará)
12431234123412341234123
26

Acabei de descobrir que essa sintaxe feia pode ser "útil" ou pelo menos muito divertida de se brincar quando você deseja lidar com uma matriz de índices que se referem a posições na mesma matriz. Ele pode substituir colchetes aninhados e tornar o código mais legível!

int a[] = { 2 , 3 , 3 , 2 , 4 };
int s = sizeof a / sizeof *a;  //  s == 5

for(int i = 0 ; i < s ; ++i) {  

           cout << a[a[a[i]]] << endl;
           // ... is equivalent to ... 
           cout << i[a][a][a] << endl;  // but I prefer this one, it's easier to increase the level of indirection (without loop)

}

Claro, tenho certeza de que não há nenhum caso de uso para isso no código real, mas achei interessante de qualquer maneira :)

Frédéric Terrazzoni
fonte
Quando você vê i[a][a][a]que pensa que eu sou um ponteiro para uma matriz ou uma matriz de um ponteiro para uma matriz ou uma matriz ... e aé um índice. Quando você vê a[a[a[i]]], você acha que a é um ponteiro para uma matriz ou matriz e ié um índice.
12431234123412341234123
1
Uau! É um uso muito legal desse recurso "estúpido". Pode ser útil em concursos algorítmicos em alguns problemas))
Serge Breusov
26

Boa pergunta / respostas.

Só quero salientar que ponteiros e matrizes C não são os mesmos , embora neste caso a diferença não seja essencial.

Considere as seguintes declarações:

int a[10];
int* p = a;

Em a.out, o símbolo aestá em um endereço que é o início da matriz e o símbolo pem um endereço em que um ponteiro é armazenado, e o valor do ponteiro nesse local de memória é o início da matriz.

PolyThinker
fonte
2
Não, tecnicamente eles não são os mesmos. Se você definir algum b como int * const e apontá-lo para uma matriz, ele ainda será um ponteiro, o que significa que, na tabela de símbolos, b se refere a um local de memória que armazena um endereço, que por sua vez aponta para onde a matriz está .
PolyThinker
4
Muito bom ponto. Lembro-me de ter um bug muito desagradável quando defini um símbolo global como char s [100] em um módulo, declaro-o como extern char * s; em outro módulo. Depois de vincular tudo, o programa se comportou de maneira estranha. Porque o módulo que usa a declaração externa estava usando os bytes iniciais da matriz como um ponteiro para char.
Giorgio
1
Originalmente, no BCPL dos avós de C, uma matriz era um ponteiro. Ou seja, o que você conseguiu quando escreveu (transliterei para C) int a[10]foi um ponteiro chamado 'a', que apontava para armazenamento suficiente para 10 números inteiros, em outro lugar. Assim, a + i e j + i tinham a mesma forma: adicione o conteúdo de alguns locais de memória. Na verdade, eu acho que o BCPL não tinha tipo, então eles eram idênticos. E o tamanho do tipo de escala não se aplicava, já que o BCPL era puramente orientado a palavras (também em máquinas endereçadas por palavras).
Dave
Eu acho que a melhor maneira de entender a diferença é comparar int*p = a;com int b = 5; No último, "b" e "5" são números inteiros, mas "b" é uma variável, enquanto "5" é um valor fixo. Da mesma forma, "p" e "a" são os endereços de um caractere, mas "a" é um valor fixo.
James Curran
20

Para ponteiros em C, temos

a[5] == *(a + 5)

e também

5[a] == *(5 + a)

Por isso, é verdade que a[5] == 5[a].

user1055604
fonte
15

Não é uma resposta, mas apenas um pouco de reflexão. Se a classe estiver sobrecarregando o operador de índice / índice, a expressão 0[x]não funcionará:

class Sub
{
public:
    int operator [](size_t nIndex)
    {
        return 0;
    }   
};

int main()
{
    Sub s;
    s[0];
    0[s]; // ERROR 
}

Como não temos acesso à classe int , isso não pode ser feito:

class int
{
   int operator[](const Sub&);
};
Ajay
fonte
2
class Sub { public: int operator[](size_t nIndex) const { return 0; } friend int operator[](size_t nIndex, const Sub& This) { return 0; } };
precisa
1
Você já tentou compilá-lo? Há um conjunto de operadores que não podem ser implementados fora da classe (ou seja, como funções não estáticas)!
quer
3
Opa, você está certo. " operator[]deve ser uma função membro não estática com exatamente um parâmetro." Eu estava familiarizado com essa restrição operator=, não achei que fosse aplicável [].
Ben Voigt
1
Claro, se você alterar a definição de []operador, nunca mais seria equivalente ... se a[b]for igual a *(a + b)e você alterar isso, terá que sobrecarregar também int::operator[](const Sub&);e intnão é uma classe ...
Luis Colorado,
7
Isso ... não é ... C.
MD XF
11

Tem uma explicação muito boa em A TUTORIAL ON POINTERS AND ARRAYS IN C de Ted Jensen.

Ted Jensen explicou como:

De fato, isso é verdade, ou seja, onde quer que alguém escreva, a[i]ele pode ser substituído *(a + i) sem problemas. De fato, o compilador criará o mesmo código em ambos os casos. Assim, vemos que a aritmética do ponteiro é a mesma coisa que a indexação de array. Qualquer sintaxe produz o mesmo resultado.

Isso NÃO está dizendo que ponteiros e matrizes são a mesma coisa, não são. Estamos dizendo apenas que, para identificar um determinado elemento de uma matriz, temos a opção de duas sintaxes, uma usando indexação de matriz e outra usando aritmética de ponteiro, que produzem resultados idênticos.

Agora, olhando para esta última expressão, parte dela .. (a + i), é uma adição simples usando o operador + e as regras de C declaram que essa expressão é comutativa. Isto é (a + i) é idêntico a (i + a). Assim, poderíamos escrever *(i + a)tão facilmente quanto *(a + i). Mas *(i + a)poderia ter vindo i[a]! De tudo isso vem a curiosa verdade de que se:

char a[20];

escrita

a[3] = 'x';

é o mesmo que escrever

3[a] = 'x';
Como Bhullar
fonte
4
a + i NÃO é uma adição simples, porque é uma aritmética de ponteiro. se o tamanho do elemento de a for 1 (char), sim, é como o número inteiro +. Mas se for (por exemplo) um número inteiro, poderá ser equivalente a um + 4 * i.
Alex Brown
@AlexBrown Sim, é aritmética de ponteiro, e é exatamente por isso que sua última frase está errada, a menos que você primeiro converta 'a' para ser um (caractere *) (supondo que um int seja 4 caracteres). Realmente não entendo por que tantas pessoas estão se desligando do resultado real da aritmética dos ponteiros. O objetivo inteiro da aritmética do ponteiro é abstrair os valores subjacentes do ponteiro e deixar o programador pensar nos objetos que estão sendo manipulados, em vez de endereçar valores.
precisa
8

Sei que a pergunta foi respondida, mas não pude resistir a compartilhar esta explicação.

Lembro-me de Principles of Compiler design, vamos assumir que aé uma intmatriz e o tamanho inté de 2 bytes, e o endereço base para aé 1000.

Como a[5]vai funcionar ->

Base Address of your Array a + (5*size of(data type for array a))
i.e. 1000 + (5*2) = 1010

Assim,

Da mesma forma, quando o código c for dividido em código de três endereços, 5[a]ele se tornará ->

Base Address of your Array a + (size of(data type for array a)*5)
i.e. 1000 + (2*5) = 1010 

Então, basicamente, ambas as instruções estão apontando para o mesmo local na memória e, portanto a[5] = 5[a],.

Essa explicação também é a razão pela qual índices negativos em matrizes funcionam em C.

ou seja, se eu acessar, a[-5]isso me dará

Base Address of your Array a + (-5 * size of(data type for array a))
i.e. 1000 + (-5*2) = 990

Ele retornará o objeto no local 990.

Ajinkya Patil
fonte
6

Em matrizes C , arr[3]e 3[arr]são as mesmas, e os seus equivalentes são notações ponteiro *(arr + 3)para *(3 + arr). Mas, ao contrário [arr]3ou [3]arrnão é correto e irá resultar em erro de sintaxe, como (arr + 3)*e (3 + arr)*não são expressões válidas. O motivo é que o operador de desreferência deve ser colocado antes do endereço gerado pela expressão, não depois do endereço.

Krishan
fonte
6

no compilador c

a[i]
i[a]
*(a+i)

Existem diferentes maneiras de se referir a um elemento em uma matriz! (NÃO É ESTRANHO)

AVIK DUTTA
fonte
5

Um pouco de história agora. Entre outras línguas, o BCPL teve uma influência bastante importante no desenvolvimento inicial de C. Se você declarou uma matriz no BCPL com algo como:

let V = vec 10

que realmente alocou 11 palavras de memória, não 10. Normalmente, V foi o primeiro e continha o endereço da palavra imediatamente seguinte. Assim, diferentemente de C, nomear V foi para esse local e pegou o endereço do elemento zeroeth da matriz. Portanto, a indireção da matriz no BCPL, expressa como

let J = V!5

realmente precisava fazer J = !(V + 5)(usando a sintaxe BCPL), pois era necessário buscar V para obter o endereço base da matriz. Assim V!5e 5!Veram sinônimos. Como uma observação anedótica, o WAFL (Warwick Functional Language) foi escrito em BCPL, e o melhor de minha memória tendeu a usar a última sintaxe, e não a primeira, para acessar os nós usados ​​como armazenamento de dados. Concedido que isso é algo entre 35 e 40 anos atrás, então minha memória está um pouco enferrujada. :)

A inovação de dispensar a palavra extra de armazenamento e fazer com que o compilador insira o endereço base da matriz quando foi nomeado veio mais tarde. De acordo com o artigo de história da C, isso aconteceu aproximadamente na época em que as estruturas foram adicionadas a C.

Observe que !no BCPL havia um operador de prefixo unário e um operador de infixo binário, nos dois casos fazendo indiretamente. apenas que a forma binária incluía uma adição dos dois operandos antes de fazer a indireção. Dada a natureza orientada para a palavra da BCPL (e B), isso realmente fez muito sentido. A restrição de "ponteiro e número inteiro" foi necessária em C quando ganhou tipos de dados e sizeofse tornou uma coisa.

dgnuff
fonte
1

Bem, esse é um recurso que só é possível por causa do suporte ao idioma.

O compilador interpreta a[i]como *(a+i)e a expressão é 5[a]avaliada como *(5+a). Como a adição é comutativa, verifica-se que ambos são iguais. Portanto, a expressão é avaliada como true.

Harsha JK
fonte
Embora redundante, isso é claro, conciso e curto.
Bill K
0

Em C

 int a[]={10,20,30,40,50};
 int *p=a;
 printf("%d\n",*p++);//output will be 10
 printf("%d\n",*a++);//will give an error

Ponteiro é uma "variável"

nome da matriz é um "mnemônico" ou "sinônimo"

p++;é válido, mas a++é inválido

a[2] é igual a 2 [a] porque a operação interna de ambos é

"Aritmética do ponteiro" calculada internamente como

*(a+3) é igual a *(3+a)

Jayghosh Wankar
fonte
-4

tipos de ponteiro

1) ponteiro para dados

int *ptr;

2) ponteiro const para dados

int const *ptr;

3) ponteiro const para dados const

int const *const ptr;

e as matrizes são do tipo (2) da nossa lista
Quando você define uma matriz de cada vez, um endereço é inicializado nesse ponteiro
Como sabemos que não podemos alterar ou modificar o valor const no nosso programa, pois gera um erro ao compilar Tempo

A principal diferença que encontrei é ...

Podemos reinicializar o ponteiro por um endereço, mas não o mesmo caso com uma matriz.

======
e voltando à sua pergunta ...
a[5]não é nada, mas *(a + 5)
você pode entender facilmente
a - contendo o endereço (as pessoas chamam de endereço base) como um tipo de ponteiro (2) em nossa lista
[]- que o operador pode ser substituível com ponteiro *.

então finalmente ...

a[5] == *(a +5) == *(5 + a) == 5[a] 
Jeet Parikh
fonte