C / C ++: aritmética de ponteiro

Eu estava lendo um pouco em Aritmética de Ponteiro, e me deparei com duas coisas que eu não conseguia entender nem sei que é uso

address_expression - address_expression 

e também

 address_expression > address_expression 

Alguém pode por favor explicá-los para mim, como eles funcionam e quando são usados.

Editar:

O que eu quis dizer é o que eles produzem se eu pegar dois endereços e subtraí-los

E se eu pegar dois endereços e compará-los, qual é o resultado ou comparando com base

Edit: Agora eu entendo o resultado da subtração de endereços, mas comparando endereços eu ainda não entendi.

Eu entendo que 1 <2, mas como é um endereço maior do que outro e com o que eles são comparados

A subtração de ponteiro produz o número de elementos de matriz entre dois pointers do mesmo tipo.

Por exemplo,

 int buf[10] = /* initializer here */; &buf[10] - &buf[0]; // yields 10, the difference is 10 elements 

Comparação de pointers. Por exemplo, para o operador relacional: a operação produz 1 se o elemento de matriz pontual ou membro de estrutura no lado esquerdo for após o elemento de matriz pontual ou membro de estrutura no lado direito e ele gerar 0 caso contrário. Lembre-se de matrizes e estruturas são seqüências ordenadas.

  &buf[10] > &buf[0]; // 1, &buf[10] element is after &buf[0] element 

Várias respostas aqui afirmaram que os pointers são números. Esta não é uma descrição precisa dos pointers, conforme especificado pelo padrão C.

Em grande parte, você pode pensar em pointers como números e como endereços na memory, desde que (a) você entenda que a subtração do ponteiro converte a diferença de bytes em elementos (do tipo de pointers sendo subtraídos), e (b) você entenda os limites onde este modelo quebra.

O seguinte usa o padrão C 1999 (ISO / IEC 9899, ​​segunda edição, 1999-12-01). Espero que o seguinte seja mais detalhado do que o solicitante solicitado, mas, dadas algumas das distorções aqui, julgo que informações precisas e precisas devem ser fornecidas.

Por 6.5.6 parágrafo 9, você pode subtrair dois pointers que apontam para elementos da mesma matriz ou para um passado o último elemento da matriz. Então, se você tem int a[8], b[4]; , você pode subtrair um ponteiro para um [5] de um ponteiro para um [2], porque um [5] e um [2] são elementos no mesmo array. Você também pode subtrair um ponteiro para um [5] de um ponteiro para um [8], porque um [8] é um passado do último elemento do array. (a [8] não está na matriz; a [7] é o último elemento.) Você não pode subtrair um ponteiro para um [5] de um ponteiro para b [2], porque um [5] não está no mesma matriz que b [2]. Ou, mais precisamente, se você fizer tal subtração, o comportamento é indefinido. Note que não é meramente o resultado que não é especificado; você não pode esperar que você obtenha algum número possivelmente sem sentido como resultado: O comportamento é indefinido. De acordo com o padrão C, isso significa que o padrão C não diz nada sobre o que ocorre como conseqüência. Seu programa poderia lhe dar uma resposta razoável, ou poderia anular ou excluir arquivos, e todas essas consequências estariam de acordo com o padrão C.

Se você fizer uma subtração permitida, o resultado será o número de elementos do segundo elemento apontado para o primeiro elemento apontado. Assim, a[5]-a[2] é 3 e a[2]-a[5] é -3. Isso é verdade, independentemente do tipo a . A implementação de C é necessária para converter a distância de bytes (ou qualquer unidade utilizada) em elementos do tipo apropriado. Se a é uma matriz de double de oito bytes cada, então a[5]-a[2] é 3, para 3 elementos. Se a é uma matriz de char de um byte cada, então a[5]-a[2] é 3, para 3 elementos.

Por que os pointers nunca seriam apenas números? Em alguns computadores, especialmente em computadores mais antigos, o endereçamento de memory era mais complicado. Os primeiros computadores tinham pequenos espaços de endereço. Quando os fabricantes queriam criar espaços de endereços maiores, também queriam manter alguma compatibilidade com o software antigo. Eles também tiveram que implementar vários esquemas para endereçar memory, devido a limitações de hardware, e esses esquemas podem ter envolvido a movimentação de dados entre memory e disco ou a alteração de registros especiais no processador que controlava como os endereços eram convertidos em locais de memory física. Para que os pointers trabalhem em máquinas como essa, eles precisam conter mais informações do que apenas um endereço simples. Por causa disso, o padrão C não define apenas pointers como endereços e permite que você faça aritmética com eles. Apenas uma quantidade razoável de aritmética é definida, e a implementação de C é necessária para fornecer as operações necessárias para fazer esse trabalho aritmético, mas não mais.

Mesmo em máquinas modernas, pode haver complicações. Nos processadores Alpha da Digital, um ponteiro para uma function não contém o endereço da function. É o endereço de um descritor da function. Esse descritor contém o endereço da function e contém algumas informações adicionais necessárias para chamar a function corretamente.

Com relação aos operadores relacionais, como > , a norma C diz no 6.5.8, parágrafo 5, que você pode comparar os mesmos pointers que você pode subtrair, como descrito acima, e você também pode comparar pointers para membros de um object agregado ( um struct ou união). Os pointers para membros de uma matriz (ou seu endereço final) se comparam da maneira esperada: Os pointers para elementos com índices mais altos são maiores que os pointers para elementos com índices mais baixos. Ponteiros para dois membros da mesma união comparam iguais. Para pointers para dois membros de uma estrutura, o ponteiro para o membro declarado posteriormente é maior que o ponteiro para o membro declarado anteriormente.

Contanto que você permaneça dentro das restrições acima, então você pode pensar em pointers como números que são endereços de memory.

Normalmente, é fácil para uma implementação C fornecer o comportamento requerido pelo padrão C. Mesmo se um computador tiver um esquema de ponteiro composto, como um endereço de base e deslocamento, geralmente todos os elementos de uma matriz usarão o mesmo endereço de base um do outro, e todos os elementos de uma estrutura usarão o mesmo endereço de base um do outro. Assim, o compilador pode simplesmente subtrair ou comparar as partes de deslocamento do ponteiro para obter a diferença ou comparação desejada.

No entanto, se você subtrair pointers para matrizes diferentes em um computador desse tipo, poderá obter resultados estranhos. É possível que o padrão de bits formado por um endereço de base e deslocamento pareça maior (quando interpretado como um único inteiro) do que outro ponteiro, mesmo que aponte para um endereço mais baixo na memory. Esse é um dos motivos pelos quais você deve permanecer dentro das regras definidas pelo padrão C.

Subtrair dois endereços de ponteiro retorna o número de elementos desse tipo .

Portanto, se você tiver uma matriz de inteiros e dois pointers, subtrair esses pointers retornará o número de valores int entre, não o número de bytes. O mesmo com os tipos de char. Portanto, você precisa ter cuidado com isso, especialmente se estiver trabalhando com um buffer de byte ou caracteres largos, que sua expressão está calculando o valor correto. Se você precisar de compensações de buffer baseadas em byte para algo que não usa um único byte para armazenamento (int, curto, etc) você precisa converter seus pointers para char * primeiro.

Os pointers podem ser considerados apenas como números que representam o endereço de memory, como 0x0A31FCF20 (ou 2736770848 em decimal) ou 0xCAFEDEAD (às vezes, os sistemas usam isso para indicar um erro, não me lembro dos detalhes).

A comparação de pointers é freqüentemente usada na sorting de matrizes de pointers. Matrizes ordenadas de pointers são úteis quando você precisa verificar se um ponteiro está em uma lista de pointers; se a lista estiver classificada, você não precisa procurar em todos os elementos da lista para descobrir se o ponteiro está nessa lista. Você precisa usar comparações para classificar uma lista.

A aritmética de ponteiro costuma ser usada quando você tem um ponteiro para um bloco de dados e precisa acessar algo que não está no início do bloco de dados. Por exemplo:

 const char *string = "hello world!" const char *substring = string+6; std::cout < < string << "\n"; std::cout << substring << std::endl; 

Isso produziria:

 hello world! world! 

Aqui temos a string depois dos 6 primeiros caracteres de "hello world!", Ou "world!" . Tenha em mente que você deve usar std::string onde estiver disponível, se possível. Um conceito muito semelhante à aritmética de pointers é o iterador de access random.

A subtração de pointers pode ajudá-lo a encontrar a distância entre esses dois pointers. Se você tiver um ponteiro para o primeiro elemento de uma matriz e um ponteiro para um elemento após o último elemento da matriz, subtrair esses dois pointers ajudará a localizar o tamanho da matriz.

Outro caso em que você pode tratar pointers como números inteiros está em uma versão otimizada de uma linked list, chamada de linked list XOR. Você pode encontrar mais detalhes sobre isso aqui . Eu posso expandir isso se você quiser; Deixe-me saber nos comentários.

A primeira expressão subtrai um ponteiro do outro. Como um exemplo simples de por que isso pode ser útil, considere uma string C. A string está na memory contígua, então se você tivesse o endereço do primeiro caractere da string, e o endereço do último caractere, você poderia encontrar o tamanho da string fazendo:

 int strLength = (last_char_address - first_char_address) + 1; 

Essa aritmética de ponteiro é sensível ao tipo , o que significa que o resultado da aritmética representa o número de elementos – do tipo específico – entre dois pointers. No exemplo acima, usando char , a diferença é o número de caracteres. Isso funciona de forma semelhante para, por exemplo, pointers para duas structs .

Da mesma forma, sua segunda expressão é simplesmente comparar pointers e o resultado será 1 ou 0. Como um exemplo muito simples, o endereço do elemento 5 de uma matriz é sempre > o endereço do elemento 4 : &string[4] > &string[5] é verdade.

Você pode tratar um endereço como um int de várias maneiras. A única diferença é que int representa o número de tamanhos nesse endereço. Por exemplo, se int * p tiver o valor de, por exemplo, 234 (de alguma instrução segura de, por exemplo, p = new int[12]; ), representa o endereço 234. Se fizermos p += 1; , é apenas adicionar, em termos de tamanho int. Agora p é (assumindo int de 4 bytes para este exemplo) 238, também conhecido como p[1] . De fato, p[x] é equivalente a *(p+x) . Você pode comparar e tal como um int. Em alguns contextos isso é útil, por exemplo, no exemplo dado, p[0] agora se refere ao que foi p[1] . Isso evita ter que fazer algo como p = &p[1] que desreferencia desnecessariamente.