Recentemente, deparei-me com este problema que não consigo entender sozinho.
O que essas três expressões realmente significam?
*ptr++
*++ptr
++*ptr
Eu tentei Ritchie. Infelizmente, porém, não foi possível acompanhar o que ele disse sobre essas três operações.
Eu sei que todos eles são executados para incrementar o ponteiro / valor apontado. Também posso supor que possa haver muitas coisas sobre precedência e ordem de avaliação. Como um incrementos o ponteiro do primeiro, em seguida, obtém o conteúdo desse ponteiro, simplesmente busca o conteúdo e, em seguida, incrementa o ponteiro etc etc. Como você pode ver, eu não tenho uma compreensão clara sobre os seus reais operações, que eu gostaria de limpar o mais rápido possível. Mas estou verdadeiramente perdido quando tenho a chance de aplicá-los em programas. Por exemplo:
int main()
{
const char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
me dá esta saída:
ello
Mas minha expectativa era que fosse impressa Hello
. Uma solicitação final - dê-me exemplos de como cada expressão funciona em um determinado snippet de código. Como na maioria das vezes, apenas um mero parágrafo da teoria passa pela minha cabeça.
(*ptr)++
(parênteses necessário para disambiguate de*ptr++
)char* p
, apontando para uma sequência terminada válida de caracteres exclusivos. Então, uma funçãofn(char ch)
que imprime tanto och
parâmetro e o caractere atual apontado pelop
. Agora, invoquefn(*p++);
Q:fn
imprime o mesmo caractere duas vezes ? Você ficaria surpreso com quantos professores erraram essa pergunta.const char* p = "Hello";
Respostas:
Aqui está uma explicação detalhada que, espero, será útil. Vamos começar com o seu programa, pois é o mais simples de explicar.
A primeira afirmação:
declara
p
como um ponteiro parachar
. Quando dizemos "ponteiro para achar
", o que isso significa? Isso significa que o valor dep
é o endereço de achar
;p
nos diz onde na memória há algum espaço reservado para armazenar achar
.A instrução também inicializa
p
para apontar para o primeiro caractere na cadeia literal"Hello"
. Para o exercício deste exercício, é importante entenderp
como apontar não para toda a cadeia, mas apenas para o primeiro caractere'H'
. Afinal,p
é um ponteiro para umchar
, não para toda a string. O valor dep
é o endereço do'H'
in"Hello"
.Então você configura um loop:
O que significa a condição do loop
*p++
? Aqui estão três coisas que tornam isso intrigante (pelo menos até que a familiaridade se manifeste):++
e indirection*
1. Precedência . Uma rápida olhada na tabela de precedência para os operadores informará que o incremento do postfix tem uma precedência mais alta (16) do que a desreferência / indireção (15). Isto significa que a expressão complexa
*p++
vai ser agrupados como:*(p++)
. Ou seja, a*
peça será aplicada ao valor dap++
peça. Então, vamosp++
participar primeiro.2. Valor da expressão Postfix . O valor de
p++
é o valor dep
antes do incremento . Se você tem:a saída será:
porque
i++
avaliai
antes do incremento. Da mesma forma,p++
vai avaliar o valor atual dep
. Como sabemos, o valor atual dep
é o endereço de'H'
.Então agora a
p++
parte de*p++
foi avaliada; é o valor atual dep
. Então a*
parte acontece.*(current value of p)
significa: acessar o valor no endereço mantido porp
. Sabemos que o valor nesse endereço é'H'
. Portanto, a expressão é*p++
avaliada como'H'
.Agora espere um minuto, você está dizendo. Se for
*p++
avaliado'H'
, por que isso não é'H'
impresso no código acima? É aí que entram os efeitos colaterais .3. Efeitos colaterais da expressão do Postfix . O postfix
++
tem o valor do operando atual, mas tem o efeito colateral de incrementar esse operando. Hã? Dê uma olhada nesseint
código novamente:Como observado anteriormente, a saída será:
Quando
i++
é avaliado no primeiroprintf()
, ele avalia como 7. Mas o padrão C garante que, em algum momento antes doprintf()
início da execução do segundo , o efeito colateral do++
operador tenha ocorrido. Ou seja, antes que o segundoprintf()
aconteça,i
será incrementado como resultado do++
operador no primeiroprintf()
. A propósito, essa é uma das poucas garantias que o padrão oferece sobre o momento dos efeitos colaterais.No seu código, então, quando a expressão
*p++
é avaliada, ela é avaliada como'H'
. Mas quando você chegar a isso:esse efeito colateral irritante ocorreu.
p
foi incrementado. Uau! Não aponta mais para'H'
, mas para o passado de um personagem'H'
: para o'e'
, em outras palavras. Isso explica sua saída do cockneyfied:Daí o coro de sugestões úteis (e precisas) nas outras respostas: para imprimir a pronúncia recebida
"Hello"
e não sua contraparte cockney, você precisa de algo comoTanto para esse. E o resto? Você pergunta sobre o significado deles:
Acabamos de falar sobre o primeiro, então vamos olhar para o segundo:
*++ptr
.Vimos em nossa explicação anterior que o incremento do postfix
p++
tem uma certa precedência , um valor e um efeito colateral . O incremento do prefixo++p
tem o mesmo efeito colateral da contraparte do postfix: incrementa seu operando em 1. No entanto, possui uma precedência diferente e um valor diferente .O incremento do prefixo tem precedência menor que o postfix; ele tem precedência 15. Em outras palavras, ele tem a mesma precedência que o operador de desreferência / indireção
*
. Em uma expressão comoo que importa não é precedência: os dois operadores são idênticos em precedência. Assim, a associatividade entra em ação. O incremento do prefixo e o operador indireto têm associatividade direita-esquerda. Por causa dessa associatividade, o operando
ptr
será agrupado com o operador mais à direita++
antes do operador mais à esquerda*
. Em outras palavras, a expressão será agrupada*(++ptr)
. Portanto, como*ptr++
por um motivo diferente, aqui também a*
peça será aplicada ao valor da++ptr
peça.Então, qual é esse valor? O valor da expressão de incremento do prefixo é o valor do operando após o incremento . Isso o torna um animal muito diferente do operador de incremento postfix. Digamos que você tenha:
A saída será:
... diferente do que vimos com o operador postfix. Da mesma forma, se você tiver:
a saída será:
Você vê o porquê?
Agora chegamos à terceira expressão que você perguntou
++*ptr
,. Esse é o mais difícil de todos, na verdade. Ambos os operadores têm a mesma precedência e associatividade direita-esquerda. Isso significa que a expressão será agrupada++(*ptr)
. A++
peça será aplicada ao valor da*ptr
peça.Então, se tivermos:
o resultado surpreendentemente egoísta será:
O que?! Ok, então a
*p
parte vai avaliar'H'
. Então o++
entra em jogo; nesse ponto, ele será aplicado ao'H'
, e não ao ponteiro! O que acontece quando você adiciona 1 a'H'
? Você recebe 1 mais o valor ASCII de'H'
72; você começa 73. declara que comochar
, e você começa achar
com o valor ASCII de 73:'I'
.Isso cuida das três expressões que você perguntou na sua pergunta. Aqui está outro, mencionado no primeiro comentário à sua pergunta:
Esse também é interessante. Se você tem:
isso lhe dará uma saída entusiasmada:
O que está acontecendo? Novamente, é uma questão de precedência , valor da expressão e efeitos colaterais . Por causa dos parênteses, a
*p
parte é tratada como uma expressão primária. Expressões primárias superam todo o resto; eles são avaliados primeiro. E*p
, como você sabe, avalia como'H'
. O restante da expressão, a++
parte, é aplicada a esse valor. Então, neste caso,(*p)++
torna-se'H'++
.Qual é o valor de
'H'++
? Se você disse'I'
que esqueceu (já!) Nossa discussão sobre valor versus efeito colateral com incremento do postfix. Lembre-se,'H'++
avalia o valor atual de'H'
. Então, o primeiroprintf()
será impresso'H'
. Então, como efeito colateral , isso'H'
será incrementado para'I'
. O segundoprintf()
imprime isso'I'
. E você tem sua alegre saudação.Tudo bem, mas nesses dois últimos casos, por que eu preciso
Por que não posso simplesmente ter algo como
Porque
"Hello"
é uma string literal. Se você tentar++*p
, você está tentando alterar'H'
a string para'I'
, tornando a string inteira"Iello"
. Em C, os literais de seqüência de caracteres são somente leitura; tentar modificá-los invoca um comportamento indefinido."Iello"
também não está definido em inglês, mas isso é apenas coincidência.Por outro lado, você não pode ter
Por que não? Porque, neste caso,
p
é uma matriz. Uma matriz não é um valor l modificável; você não pode alterar onde osp
pontos são pré ou pós-incremento ou decremento, porque o nome da matriz funciona como se fosse um ponteiro constante. (Isso não é o que realmente é; é apenas uma maneira conveniente de ver isso.)Para resumir, aqui estão as três coisas que você perguntou:
E aqui está um quarto, tão divertido quanto os outros três:
O primeiro e o segundo travarão se,
ptr
na verdade, for um identificador de matriz. O terceiro e o quarto travam septr
apontarem para uma string literal.Aí está. Espero que esteja tudo cristal agora. Você tem sido um ótimo público, e eu estarei aqui a semana toda.
fonte
Suponha
ptr
pontos para o i-ésimo elemento da matrizarr
.*ptr++
avaliaarr[i]
e defineptr
para apontar para o (i + 1) -ésimo elemento dearr
. É equivalente a*(ptr++)
.*++ptr
defineptr
para apontar para o (i + 1) -ésimo elemento dearr
e avalia comoarr[i+1]
. É equivalente a*(++ptr)
.++*ptr
aumentaarr[i]
em um e avalia seu valor aumentado; o ponteiroptr
é deixado intocado. É equivalente a++(*ptr)
.Também há mais um, mas você precisará de parênteses para escrevê-lo:
(*ptr)++
aumentaarr[i]
em um e avalia seu valor antes de ser aumentado; o ponteiroptr
é novamente deixado intocado.O resto você pode descobrir por si mesmo; Também foi respondido por @Jaguar.
fonte
*ptr++ : post increment a pointer ptr
*++ptr : Pre Increment a pointer ptr
++*ptr : preincrement the value at ptr location
Leia aqui sobre operadores de pré-incremento e pós-incremento
Isso dará
Hello
como saídafonte
Hello
A condição no seu loop é ruim:
É o mesmo que
E isso está errado, deve ser:
*ptr++
é o mesmo que*(ptr++)
, que é:*++ptr
é o mesmo que*(++ptr)
, que é:++*ptr
é o mesmo que++(*ptr)
, que é:fonte
Você está certo quanto à precedência, observe que ele
*
tem precedência sobre o incremento do prefixo, mas não sobre o incremento do postfix. Veja como essas desagregações:*ptr++
- indo da esquerda para a direita, desreferencia o ponteiro e, em seguida, aumente o valor do ponteiro (não o que ele aponta, devido à precedência do postfix sobre a desreferência)*++ptr
- incrementar o ponteiro e depois desreferenciá-lo, isso ocorre porque o prefixo e o desreferência têm a mesma precedência e, portanto, são avaliados na ordem da direita para a esquerda++*ptr
- semelhante ao anterior em termos de precedência, passando novamente da direita para a esquerda para desreferenciar o ponteiro e depois incrementar o que o ponteiro aponta. Observe que, no seu caso, esse erro levará a um comportamento indefinido, porque você está tentando modificar uma variável somente leitura (char* p = "Hello";
).fonte
Vou adicionar minha opinião, porque enquanto as outras respostas estão corretas, acho que estão faltando alguma coisa.
significa
Enquanto que
significa
É importante entender que pós incremento (e pós decremento) significa
Por que isso Importa? Bem, em C isso não é tão importante. Em C ++, porém,
ptr
pode ser um tipo complexo, como um iterador. Por exemploNesse caso, por
it
ser um tipo complexo,it++
pode ter efeitos colaterais por causa datemp
criação. Obviamente, se você tiver sorte, o compilador tentará jogar fora o código que não é necessário, mas se o construtor ou o destruidor do iterador fizer alguma coisa, eleit++
mostrará esses efeitos quando ele criartemp
.O resumo do que estou tentando dizer é Escreva o que você quer dizer . Se você quer dizer increment ptr, então
++ptr
não escrevaptr++
. Se você quer dizertemp = ptr, ptr += 1, temp
, escrevaptr++
fonte
É o mesmo que:
Portanto, o valor do objeto apontado por
ptr
é recuperado e, em seguida,ptr
incrementado.É o mesmo que:
Portanto, o ponteiro
ptr
é incrementado e o objeto apontado porptr
é lido.É o mesmo que:
Portanto, o objeto apontado por
ptr
é incrementado;ptr
em si é inalterado.fonte
O postfix e o prefixo têm maior precedência do que a desreferência, portanto
* ptr ++ aqui pós incremento ptr e, em seguida, aponta para o novo valor de ptr
* ++ ptr aqui Pre Incrementar o punho, apontando para o novo valor de ptr
++ * ptr aqui primeiro obtém o valor de ptr apontando e incrementando esse vlaue
fonte
Expressões de ponteiro: * ptr ++, * ++ ptr e ++ * ptr:
Nota : os ponteiros devem ser inicializados e devem ter um endereço válido. Como na RAM, além do nosso programa (a.out), há muito mais programas em execução simultaneamente, ou seja, se você tentar acessar alguma memória que não foi reservada para o seu sistema operacional, ocorrerá uma falha de segmentação.
Antes de explicar isso, vamos considerar um exemplo simples?
analisar a saída do código acima, espero que você tenha obtido a saída do código acima. Uma coisa é clara a partir do código acima: o nome do ponteiro ( ptr ) significa que estamos falando de endereço e * ptr significa que estamos falando de valores / dados.
CASO 1 : * ptr ++, * ++ ptr, * (ptr ++) e * (++ ptr):
acima mencionadas, todas as quatro sintaxes são semelhantes,
address gets incremented
mas todas, como o endereço é incrementado, isso é diferente.Nota : para resolver qualquer expressão, descubra quantos operadores existem na expressão e descubra as prioridades do operador. Múltiplos operadores com a mesma prioridade e verifico a ordem da evolução ou associatividade que pode da direita (R) para a esquerda (L) ou da esquerda para a direita.
* ptr ++ : Aqui existem 2 operadores, nomeadamente des-referência (*) e ++ (incremento). Ambos têm a mesma prioridade e depois checam a associatividade que é de R a L. Portanto, começa a resolver da direita para a esquerda, quaisquer que sejam os operadores que estão chegando primeiro.
* ptr ++ : first ++ surgiu durante a resolução de R para L, de modo que o endereço é incrementado, mas seu pós-incremento.
* ++ ptr : O mesmo que o primeiro endereço aqui também é incrementado, mas seu pré-incremento.
* (ptr ++) : Aqui existem 3 operadores, entre os quais o agrupamento () com prioridade mais alta, então primeiro o ptr ++ resolvido, ou seja, o endereço é incrementado, mas é postado.
* (++ ptr) : O mesmo que o caso acima aqui também é incrementado, mas o pré-incremento.
CASO 2 : ++ * ptr, ++ (* ptr), (* ptr) ++:
acima mencionado, todas as quatro sintaxes são semelhantes, em todos os valores / dados é incrementado, mas como o valor é alterado é diferente.
++ * ptr : first * veio durante a resolução de R para L, então o valor é alterado, mas seu pré-incremento.
++ (* ptr) : igual ao caso acima, o valor é modificado.
(* ptr) ++ : Aqui existem 3 operadores, dentre os quais o agrupamento () com prioridade mais alta, Inside () * ptr existe, então primeiro * ptr é resolvido, ou seja, o valor é incrementado, mas é postado.
Nota : ++ * ptr e * ptr = * ptr + 1 são iguais, em ambos os casos o valor é alterado. ++ * ptr: apenas 1 instrução (INC) é usada, o valor diretamente é alterado em um único disparo. * ptr = * ptr + 1: aqui o primeiro valor é incrementado (INC) e depois atribuído (MOV).
Para entender todas as diferentes sintaxes de incremento acima no ponteiro, vamos considerar um código simples:
No código acima, tente comentar / descomentar comentários e analisar as saídas.
Ponteiros como constantes : não existem maneiras pelas quais você pode tornar os ponteiros tão constantes, poucos que estou mencionando aqui.
1) const int * p OU int const * p : Aqui
value
é constante , o endereço não é constante, ou seja, onde p está apontando? Algum endereço? Nesse endereço, qual é o valor? Algum valor certo? Esse valor é constante, você não pode modificar esse valor, mas para onde o ponteiro está apontando? Algum endereço certo? Também pode apontar para outro endereço.Para entender isso, vamos considerar o código abaixo:
Tente analisar a saída do código acima
2) int const * p : é chamado '
**constant pointe**r
' ieaddress is constant but value is not constant
. Aqui você não tem permissão para alterar o endereço, mas pode modificar o valor.Nota : o ponteiro constante (acima do caso) deve ser inicializado enquanto se declara.
Para entender isso, verifique o código simples.
No código acima, se você observar que não há ++ * p ou * p ++, você pode pensar que este é um caso simples, porque não estamos mudando endereço ou valor, mas isso produzirá erro. Por quê ? Razão que menciono nos comentários.
Então, qual é a solução desse problema?
para mais informações sobre este caso, vamos considerar o exemplo abaixo.
3) const int * const p : Aqui, endereço e valor são constantes .
Para entender isso, verifique o código abaixo
fonte
++*p
significa que você está tentando incrementar o valor ASCII do*p
qualvocê não pode incrementar o valor porque é uma constante, para que você receba um erro
quanto ao loop while, o loop é executado até
*p++
atingir o final da string em que existe um'\0'
caractere (NULL).Agora, desde que
*p++
pule o primeiro caractere, você apenas obterá sua saída a partir do segundo caractere.O código a seguir não produzirá nada porque o loop while foi
'\0'
O código a seguir fornecerá a mesma saída que o próximo código, ou seja, ello.
...................................
fonte