use - vector of pointers in c




Com arrays, por que é o caso de um[5]== 5[a]? (12)

Acabei de descobrir que essa sintaxe feia poderia ser "útil", ou pelo menos muito divertida de se brincar quando você quer lidar com uma matriz de índices que se referem a posições na mesma matriz. 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 que tenho certeza de que não há nenhum caso de uso para isso no código real, mas achei interessante mesmo assim :)

Como Joel aponta no podcast # 34 do Stack Overflow , em C Programming Language (também conhecido como K & R), há uma menção a essa propriedade de arrays em C: a[5] == 5[a]

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


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, os dois são iguais. Portanto, a expressão é avaliada como true .


E claro

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

A principal razão para isso foi que nos anos 70, quando C foi projetado, os computadores não tinham muita memória (64 KB era muito), então o compilador C não fazia muita verificação de sintaxe. Por isso, " X[Y] " foi cegamente traduzido 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, então uma otimização de nível de montagem estava disponível. Mas o compilador não era brilhante o suficiente para reconhecê-lo, então o desenvolvedor tinha que ( A += C ). Da mesma forma, se C for 1 , uma otimização de nível de montagem diferente estará disponível e, novamente, o desenvolvedor teve que torná-lo explícito, porque o compilador não o reconheceu. (Mais recentemente compiladores fazem, então essas sintaxes são em grande parte desnecessárias nos dias de hoje)


Em arrays C , arr[3] e 3[arr] são iguais, e suas notações de ponteiro equivalentes são *(arr + 3) a *(3 + arr) . Mas, pelo contrário, [arr]3 ou [3]arr não está correto e resultará em erro de sintaxe, pois (arr + 3)* e (3 + arr)* não são expressões válidas. A razão é que o operador de desreferência deve ser colocado antes do endereço gerado pela expressão, não após o endereço.


Eu acho que algo está sendo perdido pelas outras respostas.

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

(E no array[i] , 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 neste caso.

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

Mas neste caso estamos falando especificamente sobre aritmética de ponteiro, onde um operando é um ponteiro e o outro é um inteiro. (Integer + integer é uma operação diferente e pointer + pointer não faz sentido.)

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

Para adição, ambos os operandos devem ter 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:

Para adição, ambos os operandos devem ter tipo aritmético, ou o operando à esquerda deve ser um ponteiro para um tipo de objeto completo e o operando da direita deve ter um tipo inteiro.

Nesse caso, tanto i + p como i[p] seriam ilegais.

Em termos de C ++, nós realmente temos dois conjuntos de operadores + sobrecarregados, que podem ser vagamente descritos 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?

C ++ herdou essa definição de C, que obteve de B (a comutatividade de indexação de array é explicitamente mencionada em 1972 Users 'Reference to B ), que obteve do BCPL (manual datado de 1967), que pode ter linguagens anteriores (CPL? Algol?).

Portanto, a idéia de que a indexação de array é definida em termos de adição, e essa adição, mesmo de um ponteiro e um inteiro, é comutativa, remonta a muitas décadas, às linguagens ancestrais de C.

Essas línguas eram muito menos tipificadas do que as modernas. Em particular, a distinção entre ponteiros e inteiros era freqüentemente ignorada. (Os programadores C adiantados, às vezes, usavam ponteiros como inteiros sem sinal, antes que a palavra-chave unsigned fosse adicionada ao idioma.) Assim, a ideia de tornar a adição não comutativa, porque os operandos são de tipos diferentes, provavelmente não teria ocorrido aos designers dessas linguagens. . Se um usuário quisesse adicionar duas "coisas", se essas "coisas" são inteiros, ponteiros ou qualquer outra coisa, não era permitido ao idioma impedi-lo.

E com o passar dos anos, qualquer mudança nessa regra teria quebrado o código existente (embora o padrão ANSI C de 1989 possa ter sido uma boa oportunidade).

Alterar C e / ou C ++ para requerer colocar o ponteiro à esquerda e o inteiro à direita podem quebrar algum código existente, mas não haveria perda de poder expressivo real.

Então agora temos arr[3] e 3[arr] significando exatamente a mesma coisa, embora a última forma nunca deva aparecer fora do IOCCC .


Eu sei que a pergunta está respondida, mas não pude resistir em compartilhar essa explicação.

Lembro-me de Princípios do design do compilador, Vamos supor que a é um array int e o tamanho de int é de 2 bytes, e o endereço base para a é de 1000.

Como a[5] 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 é dividido em código de 3 endereços, 5[a] 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 declaraçõ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] ele me dará

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

Ele me retornará o objeto no local 990.


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

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

Portanto, a[5] avaliará:

*(a + 5)

e 5[a] irá avaliar para:

*(5 + a)

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


Para ponteiros em C, temos

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

e também

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

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


Porque o acesso à matriz é definido em termos de ponteiros. a[i] é definido para significar *(a + i) , que é comutativo.


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

Ted Jensen explicou como:

Na verdade, isso é verdade, ou seja, onde quer que alguém escreva, a[i] pode ser substituído por *(a + i) sem nenhum problema. Na verdade, o compilador criará o mesmo código em ambos os casos. Assim, vemos que a aritmética de 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, eles não são. Estamos apenas dizendo que, para identificar um dado elemento de um array, temos a escolha de duas sintaxes, uma usando indexação de array e a outra usando aritmética de ponteiros, 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 estado C que tal 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 de i[a] ! De tudo isso vem a curiosa verdade que se:

char a[20];

escrita

a[3] = 'x';

é o mesmo que escrever

3[a] = 'x';

no compilador c

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

são maneiras diferentes de se referir a um elemento em uma matriz! (NÃO EM TODOS OS ESTRANHOS)


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 os arrays são tipo de (2) da nossa lista
Quando você define uma matriz por vez, um endereço é inicializado nesse ponteiro
Como sabemos que não podemos alterar ou modificar o valor de const no nosso programa porque ele gera um erro em tempo de compilação

A principal diferença que encontrei é ...

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

======
e de volta à sua pergunta ...
a [5] não é nada além de * (a + 5)
você pode entender facilmente por
a - contendo endereço (as pessoas o chamam como endereço base) como um (2) tipo de ponteiro em nossa lista
[] - esse operador pode ser substituído pelo ponteiro *.

então finalmente ...

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




pointer-arithmetic