Aquí hay una explicación detallada que espero sea útil. Comencemos con su programa, ya que es el más simple de explicar.
int main()
{
const char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
La primera declaración:
const char* p = "Hello";
declara pcomo puntero a char. Cuando decimos "puntero a un char", ¿qué significa eso? Significa que el valor de pes la dirección de a char; pnos dice dónde en la memoria hay un espacio reservado para guardar a char.
La instrucción también se inicializa ppara señalar el primer carácter en el literal de cadena "Hello". Por el bien de este ejercicio, es importante entender pque no apunta a toda la cadena, sino solo al primer carácter 'H',. Después de todo, pes un puntero a uno char, no a toda la cadena. El valor de pes la dirección de la 'H'en "Hello".
Luego configuras un bucle:
while (*p++)
¿Qué significa la condición de bucle *p++? Aquí hay tres cosas que hacen que esto sea desconcertante (al menos hasta que se establezca la familiaridad):
- La precedencia de los dos operadores, postfix
++e indirection*
- El valor de una expresión de incremento de postfix
- El efecto secundario de una expresión de incremento de postfix
1. Precedencia . Un rápido vistazo a la tabla de precedencia para operadores le dirá que el incremento de postfix tiene una precedencia más alta (16) que la desreferencia / indirección (15). Esto significa que el complejo de expresión *p++va a ser agrupados como: *(p++). Es decir, la *parte se aplicará al valor de la p++parte. Así que tomemos la p++parte primero.
2. Valor de expresión de postfix . El valor de p++es el valor de p antes del incremento . Si usted tiene:
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
la salida será:
7
8
porque i++evalúa iantes del incremento. Del mismo modo p++se va a evaluar el valor actual de p. Como sabemos, el valor actual de pes la dirección de 'H'.
Entonces ahora la p++parte de *p++ha sido evaluada; que es el valor actual de p. Entonces *sucede la parte. *(current value of p)significa: acceder al valor en la dirección que posee p. Sabemos que el valor en esa dirección es 'H'. Entonces la expresión se *p++evalúa como 'H'.
Ahora espera un minuto, estás diciendo. Si se *p++evalúa como 'H', ¿por qué eso no se 'H'imprime en el código anterior? Ahí es donde entran los efectos secundarios .
3. Postfix expresión efectos secundarios . El postfix ++tiene el valor del operando actual, pero tiene el efecto secundario de incrementar ese operando. ¿Eh? Eche un vistazo a ese intcódigo nuevamente:
int i = 7;
printf ("%d\n", i++);
printf ("%d\n", i);
Como se señaló anteriormente, el resultado será:
7
8
Cuando i++se evalúa en el primero printf(), se evalúa a 7. Pero el estándar C garantiza que en algún momento antes de que el segundo printf()comience a ejecutarse, el efecto secundario del ++operador habrá tenido lugar. Es decir, antes de que printf()ocurra lo segundo , ise habrá incrementado como resultado del ++operador en lo primero printf(). Esto, por cierto, es una de las pocas garantías que ofrece el estándar sobre el momento de los efectos secundarios.
En su código, entonces, cuando *p++se evalúa la expresión , se evalúa como 'H'. Pero para cuando llegues a esto:
printf ("%c", *p)
ese molesto efecto secundario ha ocurrido. pha sido incrementado Whoa! Ya no apunta a 'H', sino a un pasado del personaje 'H': al 'e', en otras palabras. Eso explica su salida cockneyfied:
ello
De ahí el coro de sugerencias útiles (y precisas) en las otras respuestas: para imprimir la pronunciación recibida "Hello"y no su contraparte cockney, necesita algo como
while (*p)
printf ("%c", *p++);
Mucho para eso. ¿Qué pasa con el resto? Usted pregunta sobre el significado de estos:
*ptr++
*++ptr
++*ptr
Acabamos de hablar sobre la primera, por lo que vamos a ver el segundo: *++ptr.
Vimos en nuestra explicación anterior que el incremento de postfix p++tiene cierta precedencia , un valor y un efecto secundario . El incremento de prefijo ++ptiene el mismo efecto secundario que su contraparte postfix: incrementa su operando en 1. Sin embargo, tiene una precedencia diferente y un valor diferente .
El incremento de prefijo tiene una precedencia menor que el postfix; tiene prioridad 15. En otras palabras, tiene la misma prioridad que el operador de desreferencia / indirección *. En una expresión como
*++ptr
lo importante no es la precedencia: los dos operadores son idénticos en precedencia. Entonces la asociatividad entra en acción. El incremento de prefijo y el operador de indirección tienen asociatividad derecha-izquierda. Debido a esa asociatividad, el operando ptrse agrupará con el operador más a la derecha ++antes que el operador más a la izquierda *,. En otras palabras, la expresión se va a agrupar *(++ptr). Entonces, como con *ptr++pero por una razón diferente, aquí también la *parte se aplicará al valor de la ++ptrparte.
Entonces, ¿cuál es ese valor? El valor de la expresión de incremento de prefijo es el valor del operando después del incremento . Esto lo convierte en una bestia muy diferente del operador de incremento de postfix. Digamos que tienes:
int i = 7;
printf ("%d\n", ++i);
printf ("%d\n", i);
El resultado será:
8
8
... diferente de lo que vimos con el operador postfix. Del mismo modo, si tiene:
const char* p = "Hello";
printf ("%c ", *p); // note space in format string
printf ("%c ", *++p); // value of ++p is p after the increment
printf ("%c ", *p++); // value of p++ is p before the increment
printf ("%c ", *p); // value of p has been incremented as a side effect of p++
la salida será:
H e e l // good dog
¿Ves por qué?
Ahora llegamos a la tercera expresión que preguntaste, ++*ptr. Ese es el más complicado de todos, en realidad. Ambos operadores tienen la misma precedencia y asociatividad derecha-izquierda. Esto significa que la expresión se agrupará ++(*ptr). La ++parte se aplicará al valor de la *ptrparte.
Entonces si tenemos:
char q[] = "Hello";
char* p = q;
printf ("%c", ++*p);
El resultado sorprendentemente egoísta será:
I
¡¿Qué?! Bien, entonces la *pparte se va a evaluar 'H'. Luego ++entra en juego, en ese momento, se aplicará al 'H'puntero, ¡no al puntero en absoluto! ¿Qué sucede cuando agregas 1 a 'H'? Obtiene 1 más el valor ASCII de 'H'72; se obtiene 73. Representar que como char, y se obtiene el charcon el valor ASCII de 73: 'I'.
Eso se encarga de las tres expresiones que preguntaste en tu pregunta. Aquí hay otro, mencionado en el primer comentario a su pregunta:
(*ptr)++
Ese también es interesante. Si usted tiene:
char q[] = "Hello";
char* p = q;
printf ("%c", (*p)++);
printf ("%c\n", *p);
te dará esta salida entusiasta:
HI
¿Que esta pasando? Nuevamente, es una cuestión de precedencia , valor de expresión y efectos secundarios . Debido a los paréntesis, la *pparte se trata como una expresión primaria. Las expresiones primarias triunfan sobre todo lo demás; son evaluados primero. Y *p, como sabes, evalúa a 'H'. El resto de la expresión, la ++parte, se aplica a ese valor. Entonces, en este caso, se (*p)++convierte 'H'++.
¿Cuál es el valor de 'H'++? Si dijiste 'I', has olvidado (¡ya!) Nuestra discusión sobre el valor frente a los efectos secundarios con el incremento de postfix. Recuerde, 'H'++evalúa el valor actual de 'H' . Entonces eso primero printf()se va a imprimir 'H'. Luego, como efecto secundario , 'H'se incrementará a 'I'. El segundo printf()imprime eso 'I'. Y tienes tu alegre saludo.
De acuerdo, pero en esos dos últimos casos, ¿por qué necesito
char q[] = "Hello";
char* p = q;
¿Por qué no puedo tener algo como
/*const*/ char* p = "Hello";
printf ("%c", ++*p); // attempting to change string literal!
Porque "Hello"es una cadena literal. Si lo intentas ++*p, estás tratando de cambiar 'H'la cadena a 'I', haciendo que toda la cadena "Iello". En C, los literales de cadena son de solo lectura; intentar modificarlos invoca un comportamiento indefinido. "Iello"no está definido en inglés también, pero eso es solo una coincidencia.
Por el contrario, no puedes tener
char p[] = "Hello";
printf ("%c", *++p); // attempting to modify value of array identifier!
Por qué no? Porque en este caso, pes una matriz. Una matriz no es un valor l modificable; no puede cambiar los ppuntos por incremento o decremento previo o posterior, porque el nombre de la matriz funciona como si fuera un puntero constante. (Eso no es lo que realmente es; esa es solo una forma conveniente de verlo).
En resumen, estas son las tres cosas que preguntaste:
*ptr++ // effectively dereferences the pointer, then increments the pointer
*++ptr // effectively increments the pointer, then dereferences the pointer
++*ptr // effectively dereferences the pointer, then increments dereferenced value
Y aquí hay un cuarto, tan divertido como los otros tres:
(*ptr)++ // effectively forces a dereference, then increments dereferenced value
El primero y el segundo se bloquearán si en ptrrealidad es un identificador de matriz. El tercero y el cuarto se bloquearán si ptrapunta a una cadena literal.
Ahí tienes. Espero que todo sea cristal ahora. Has sido una gran audiencia, y estaré aquí toda la semana.
(*ptr)++(paréntesis necesarios para desambiguar*ptr++)