funciones - punteros c++
Expresiones de puntero:*ptr++,*++ ptr y++*ptr (10)
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 p
como un puntero a char
. Cuando decimos "puntero a un char
", ¿qué significa eso? Significa que el valor de p
es la dirección de un char
; p
nos dice que en la memoria hay espacio reservado para contener un char
.
La instrucción también inicializa p
para apuntar al primer carácter en el literal de cadena "Hello"
. Por el bien de este ejercicio, es importante entender que p
no apunta a toda la cuerda, sino solo al primer caracter, ''H''
. Después de todo, p
es un puntero a un char
, no a toda la cadena. El valor de p
es la dirección de ''H''
en "Hello"
.
Luego configura un bucle:
while (*p++)
¿Qué significa la condición de bucle *p++
? Hay tres cosas en juego que hacen que esto desconcierte (al menos hasta que la familiaridad se establece):
- La precedencia de los dos operadores, postfix
++
e indirección*
- El valor de una expresión de incremento de postfijo
- El efecto secundario de una expresión de incremento de postfix
1. Precedencia . Un rápido vistazo a la tabla de precedencia para los operadores le dirá que el incremento de postfijo tiene una precedencia más alta (16) que la desreferencia / indirección (15). Esto significa que la expresión compleja *p++
se va a agrupar como: *(p++)
. Es decir, la parte *
se aplicará al valor de la parte p++
. Así que tomemos la parte p++
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 i
antes del incremento. Del mismo modo, p++
va a evaluar el valor actual de p
. Como sabemos, el valor actual de p
es la dirección de ''H''
.
Entonces ahora se ha evaluado la parte p++
de *p++
; es el valor actual de p
. Entonces la *
parte sucede. *(current value of p)
significa: acceder al valor en la dirección mantenida por p
. Sabemos que el valor en esa dirección es ''H''
. Entonces, la expresión *p++
evalúa como ''H''
.
Ahora espera un minuto, estás diciendo. Si *p++
evalúa a ''H''
, ¿por qué esa ''H''
imprime en el código anterior? Ahí es donde entran los efectos secundarios .
3. Efectos secundarios de la expresión de Postfix . El postfix ++
tiene el valor del operando actual, pero tiene el efecto secundario de incrementar ese operando. ¿Huh? Eche un vistazo a ese código int
nuevamente:
int i = 7;
printf ("%d/n", i++);
printf ("%d/n", i);
Como se señaló anteriormente, el resultado será:
7
8
Cuando se evalúa i++
en el primer printf()
, se evalúa como 7. Pero el estándar C garantiza que en algún momento antes de que el segundo printf()
comience a ejecutarse, se producirá el efecto secundario del operador ++
. Es decir, antes de que ocurra el segundo printf()
, i
habrá incrementado como resultado del operador ++
en el primer printf()
. Por cierto, esta es una de las pocas garantías que ofrece la norma sobre el momento de los efectos secundarios.
En su código, cuando se evalúa la expresión *p++
, se evalúa como ''H''
. Pero cuando llegas a esto:
printf ("%c", *p)
ese molesto efecto secundario ha ocurrido. p
se ha incrementado. Whoa! Ya no apunta a ''H''
, sino a un caracter pasado ''H''
: a la ''e''
, en otras palabras. Eso explica tu 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++);
Demasiado para eso. ¿Qué pasa con el resto? Usted pregunta acerca de los significados de estos:
*ptr++
*++ptr
++*ptr
Acabamos de hablar sobre el primero, así que veamos el segundo: *++ptr
.
Vimos en nuestra explicación anterior que el incremento posfix p++
tiene cierta precedencia , un valor y un efecto secundario . El incremento de prefijo ++p
tiene el mismo efecto secundario que su contraparte de postfijo: 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 sufijo; tiene prioridad 15. En otras palabras, tiene la misma precedencia que el operador de desreferencia / indirección *
. En una expresión como
*++ptr
lo que importa no es la precedencia: los dos operadores son idénticos en precedencia. Así que la asociatividad entra en juego. El incremento de prefijo y el operador de indirección tienen asociatividad de derecha a izquierda. Debido a esa asociatividad, el operando ptr
se va a 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 agrupará *(++ptr)
. Entonces, como con *ptr++
pero por una razón diferente, aquí también la parte *
se va a aplicar al valor de la parte ++ptr
.
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 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 sobre la que preguntaste, ++*ptr
. Ese es el más complicado del lote, en realidad. Ambos operadores tienen la misma precedencia y asociatividad de derecha a izquierda. Esto significa que la expresión se agrupará ++(*ptr)
. La parte ++
se aplicará al valor de la parte *ptr
.
Entonces si tenemos:
char q[] = "Hello";
char* p = q;
printf ("%c", ++*p);
la producción sorprendentemente egoísta va a ser:
I
¡¿Qué?! De acuerdo, entonces la parte *p
va a evaluar a ''H''
. Entonces el ++
entra en juego, en ese punto, se aplicará a la ''H''
, ¡no al puntero en absoluto! ¿Qué sucede cuando agrega 1 a ''H''
? Obtiene 1 más el valor ASCII de ''H''
, 72; obtienes 73. Representa eso como un char
, y obtienes el char
con el valor ASCII de 73: ''I''
.
Eso se ocupa 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á este resultado entusiasta:
HI
¿Que esta pasando? De nuevo, es una cuestión de precedencia , valor de expresión y efectos secundarios . Debido a los paréntesis, la parte *p
se trata como una expresión primaria. Las expresiones primarias prevalecen sobre todo lo demás; ellos son evaluados primero. Y *p
, como saben, evalúa a ''H''
. El resto de la expresión, la parte ++
, se aplica a ese valor. Entonces, en este caso, (*p)++
convierte en ''H''++
.
¿Cuál es el valor de ''H''++
? Si dijo ''I''
, ya olvidó (¡ya!) Nuestra discusión sobre el valor frente al efecto secundario con el incremento de postfijo. Recuerde, ''H''++
evalúa el valor actual de ''H''
. Entonces esa primera printf()
va a imprimir ''H''
. Luego, como efecto secundario , esa ''H''
se incrementará a ''I''
. El segundo printf()
imprime ese ''I''
. Y tienes tu alegre saludo.
Está bien, pero en esos dos últimos casos, ¿por qué necesito
char q[] = "Hello";
char* p = q;
¿Por qué no puedo tener algo así como
/*const*/ char* p = "Hello";
printf ("%c", ++*p); // attempting to change string literal!
Porque "Hello"
es un literal de cadena. Si intenta ++*p
, está tratando de cambiar la ''H''
en la cadena a ''I''
, haciendo que toda la cadena sea "Iello"
. En C, los literales de cadena son de solo lectura; intentar modificarlos invoca un comportamiento indefinido. "Iello"
está definido en inglés, 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, p
es una matriz. Una matriz no es un valor l modificable; no puede cambiar dónde p
puntos 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, es solo una manera conveniente de verlo).
Para resumir, aquí están las tres cosas sobre las 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 bloquean si ptr
es realmente un identificador de matriz. El tercero y cuarto se bloqueará si ptr
apunta a un literal de cadena.
Ahí tienes. Espero que todo sea cristal ahora. Has sido una gran audiencia, y estaré aquí toda la semana.
Recientemente me he encontrado con este problema que no puedo entender por mí mismo.
¿Qué significan REALMENTE estas tres Expresiones?
*ptr++
*++ptr
++*ptr
Lo he intentado con Ritchie. Pero desafortunadamente no pudo seguir lo que dijo sobre estas 3 operaciones.
Sé que todos se realizan para incrementar el puntero / el valor apuntado. También puedo adivinar que puede haber muchas cosas sobre la precedencia y el orden de evaluación. Al igual que uno incrementa el puntero primero y luego obtiene el contenido de ese puntero, uno simplemente busca el contenido y luego incrementa el puntero, etc. Como puede ver, no entiendo claramente sus operaciones reales , lo que me gustaría despejar tan pronto como sea posible. Pero estoy realmente perdido cuando tengo la oportunidad de aplicarlos en los programas. Por ejemplo:
int main()
{
const char *p = "Hello";
while(*p++)
printf("%c",*p);
return 0;
}
me da esta salida:
ello
Pero mi expectativa era que imprimiera Hello
. Una solicitud final: proporcione ejemplos de cómo funciona cada expresión en un fragmento de código dado. Como la mayoría de las veces, solo un simple párrafo de teoría se me pasa por la cabeza.
La condición en tu ciclo es mala:
while(*p++)
printf("%c",*p);
Es lo mismo que
while(*p)
{
p++;
printf("%c",*p);
}
Y eso está mal, esto debería ser:
while(*p)
{
printf("%c",*p);
p++;
}
*ptr++
es lo mismo que *(ptr++)
, que es:
const char *ptr = "example";
char value;
value = *ptr;
++ptr;
printf("%c", value); // will print ''e''
*++ptr
es lo mismo que *(++ptr)
, que es:
const char *ptr = "example";
char value;
++ptr;
value = *ptr;
printf("%c", value); // will print ''x''
++*ptr
es lo mismo que ++(*ptr)
, que es:
const char *ptr = "example";
char value;
value = *ptr;
++value;
printf("%c", value); // will print ''f'' (''e'' + 1)
Supongamos que ptr
apunta al elemento i-ésimo de la matriz arr
.
*ptr++
evalúaarr[i]
y estableceptr
para apuntar al elemento (i + 1) -th dearr
. Es equivalente a*(ptr++)
.*++ptr
estableceptr
para apuntar al elemento (i + 1) -th dearr
y evalúaarr[i+1]
. Es equivalente a*(++ptr)
.++*ptr
aumentaarr[i]
en uno y evalúa su valor aumentado; el punteroptr
septr
. Es equivalente a++(*ptr)
.
También hay uno más, pero necesitaría paréntesis para escribirlo:
-
(*ptr)++
aumentaarr[i]
en uno y evalúa su valor antes de aumentar; el punteroptr
nuevamente se deja intacto.
El resto lo puedes descubrir tú mismo; también fue respondida por @Jaguar.
Tiene razón sobre la precedencia, tenga en cuenta que *
tiene precedencia sobre el incremento de prefijo, pero no sobre el incremento de postfijo. Así es como estas rupturas:
*ptr++
- yendo de izquierda a derecha, desreferenciando el puntero y luego incrementando el valor del puntero (no lo que apunta, debido a la precedencia de postfix sobre la desreferencia)
*++ptr
- incrementa el puntero y luego lo desreferencia, esto es porque el prefijo y la desreferencia tienen la misma precedencia y por lo tanto se evalúan en orden de derecha a izquierda
++*ptr
- similar a la anterior en términos de precedencia, yendo de nuevo de derecha a izquierda para desreferenciar el puntero y luego incrementar a lo que apunta el puntero. Tenga en cuenta que en su caso esto dará lugar a un comportamiento indefinido porque está tratando de modificar una variable de solo lectura ( char* p = "Hello";
).
Voy a agregar mi opinión porque, aunque las otras respuestas son correctas, creo que se están perdiendo algo.
v = *ptr++
medio
temp = ptr;
ptr = ptr + 1
v = *temp;
Donde como
*++ptr
medio
ptr = ptr + 1
v = *ptr
Es importante entender que el incremento posterior (y posterior decremento) significa
temp = ptr // Temp created here!!!
ptr = ptr + 1 // or - 1 if decrement)
v = *temp // Temp destroyed here!!!
¿Por qué eso importa? Bueno en C eso no es tan importante. En C ++, aunque ptr
podría ser un tipo complejo como un iterador. Por ejemplo
for (std::set<int>::iterator it = someSet.begin(); it != someSet.end(); it++)
En este caso, it
es un tipo complejo it++
puede tener efectos secundarios debido a la creación de la temp
. Por supuesto, si tienes suerte, el compilador intentará descartar el código que no es necesario, pero si el constructor o el destructor del iterador hacen algo it++
mostrará los efectos cuando crea la temp
.
El corto de lo que estoy tratando de decir es Escribe lo que quieres decir . Si quiere decir incrementar ptr , escriba ++ptr
no ptr++
. Si te refieres a temp = ptr, ptr += 1, temp
entonces escribe ptr++
postfix y el prefijo tiene mayor precedencia que la referencia
* ptr ++ aquí incrementa ptr y luego apunta al nuevo valor de ptr
* ++ ptr aquí Pre Incrementa el puño y luego apunta al nuevo valor de ptr
++ * ptr aquí primero obtén el valor de ptr apuntando e incrementando ese valor
*ptr++ : post increment a pointer ptr
*++ptr : Pre Increment a pointer ptr
++*ptr : preincrement the value at ptr location
Lea here sobre los operadores de incremento previo y post incremento
Esto dará hello
como salida
int main()
{
const char *p = "Hello";
while(*p)
printf("%c",*p++);//Increment the pointer here
return 0;
}
Expresiones de puntero: * ptr ++, * ++ ptr y ++ * ptr:
Nota : los punteros deben inicializarse y deben tener una dirección válida. Debido a que en la RAM, aparte de nuestro programa (a.out), hay mucho más programa ejecutándose simultáneamente, es decir, si intenta acceder a alguna memoria que no estaba reservada para su sistema operativo, lo hará a través de la falla de segmentación.
Antes de explicar esto, consideremos un ejemplo simple.
#include<stdio.h>
int main()
{
int num = 300;
int *ptr;//uninitialized pointer.. must be initialized
ptr = #
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
*ptr = *ptr + 1;//*ptr means value/data on the address.. so here value gets incremented
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
/** observe here that "num" got changed but manually we didn''t change, it got modified by pointer **/
ptr = ptr + 1;//ptr means address.. so here address got incremented
/** char pointer gets incremented by 1 bytes
Integer pointer gets incremented by 4 bytes
**/
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
}
analizar el resultado del código anterior, espero que haya obtenido el resultado del código anterior. Una cosa es clara desde el código anterior es que el nombre del puntero ( ptr ) significa que estamos hablando de la dirección y * ptr significa que estamos hablando de valor / datos.
CASO 1 : * ptr ++, * ++ ptr, * (ptr ++) y * (++ ptr):
Las cuatro sintaxis mencionadas anteriormente son similares, en todas las address gets incremented
pero la forma en que se incrementa la dirección es diferente.
Nota : para resolver cualquier expresión, averigüe cuántos operadores hay en expresión y luego descubra las prioridades del operador. Tengo varios operadores que tienen la misma prioridad y luego verifico el orden de evolución o asociatividad que puede ser de derecha (R) a izquierda (L) o de izquierda a derecha.
* ptr ++ : Aquí 2 operadores están a saber de-referencia (*) y ++ (incremento). Ambos tienen la misma prioridad y luego verifican la asociatividad, que es de R a L. Así que comienza a resolver de derecha a izquierda, sin importar qué operadores vengan primero.
* ptr ++ : primero ++ se produjo mientras se resolvía de R a L, por lo que la dirección se incrementa pero su incremento en el puesto.
* ++ ptr : Igual que el primero aquí también la dirección se incrementa pero es un incremento previo.
* (ptr ++) : Aquí hay 3 operadores, entre ellos la agrupación () que tiene la más alta prioridad, así que primero ptr ++ resuelto, es decir, la dirección se incrementa pero se publica.
* (++ ptr) : al igual que en el caso anterior, la dirección también se incrementa pero se incrementa de manera previa.
CASO 2 : ++ * ptr, ++ (* ptr), (* ptr) ++:
arriba mencionado, las 4 sintaxis son similares, en todos los valores / datos se incrementa, pero cómo cambia el valor es diferente.
++ * ptr : primero * llegó mientras resolvía de R a L, por lo que el valor se cambia pero es un incremento previo.
++ (* ptr) : al igual que en el caso anterior, el valor se modifica.
(* ptr) ++ : Aquí hay 3 operadores, entre ellos la agrupación () que tiene la prioridad más alta, Inside () * ptr está ahí, entonces primero * ptr se resuelve, es decir, el valor se incrementa pero se publica.
Nota : ++ * ptr y * ptr = * ptr + 1 son iguales, en ambos casos se cambia el valor. ++ * ptr: solo se usa 1 instrucción (INC), directamente el valor se cambia en una sola toma. * ptr = * ptr + 1: aquí el primer valor se incrementa (INC) y luego se asigna (MOV).
Para entender todo lo anterior, la sintaxis de incremento diferente en el puntero permite considerar un código simple:
#include<stdio.h>
int main()
{
int num = 300;
int *ptr;
ptr = #
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
*ptr++;//address changed(post increment), value remains un-changed
// *++ptr;//address changed(post increment), value remains un-changed
// *(ptr)++;//address changed(post increment), value remains un-changed
// *(++ptr);//address changed(post increment), value remains un-changed
// ++*ptr;//value changed(pre increment), address remains un-changed
// (*ptr)++;//value changed(pre increment), address remains un-changed
// ++(*ptr);//value changed(post increment), address remains un-changed
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
}
En el código anterior, intente comentar / des-comentar comentarios y analizar resultados.
Punteros como constante : no hay formas en que pueda hacer que los punteros sean constantes, pocos de los que menciono aquí.
1) const int * p OR int const * p : Aquí el value
es constante , la dirección no es constante , es decir, ¿dónde p está apuntando? ¿Alguna dirección? En esa dirección, ¿cuál es el valor? Algunos valoran ¿no? Ese valor es constante, no puede modificar ese valor, pero ¿hacia dónde apunta el puntero? Algunas direcciones ¿verdad? Puede señalar a otra dirección también.
Para entender esto, consideremos el siguiente código:
#include<stdio.h>
int main()
{
int num = 300;
const int *ptr;//constant value, address is modifible
ptr = #
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
*ptr++;//
// *++ptr;//possible bcz you are trying to change address which is possible
// *(ptr)++;//possible
// *(++ptr);//possible
// ++*ptr;//not possible bcz you trying to change value which is not allowed
// (*ptr)++;//not possible
// ++(*ptr);//not possible
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
}
Intenta analizar el resultado del código anterior
2) int const * p : se llama '' **constant pointe**r
'', es decir, la address is constant but value is not constant
. Aquí no tiene permitido cambiar la dirección pero puede modificar el valor.
Nota : el puntero constante (arriba del caso) debe inicializarse mientras se declara.
Para entender esto, verifique el código simple.
#include<stdio.h>
int main()
{
int x = 300;
int* const p;
p = &x;
printf("x = %d p =%p and *p = %d/n",num,p,*p);
}
En el código anterior, si observa que no hay ++ * p o * p ++ Entonces, puede pensar que este es un caso simple porque no estamos cambiando la dirección o el valor, pero producirá un error. Por qué ? Motivo que menciono en los comentarios.
#include<stdio.h>
int main()
{
int x = 300;
/** constant pointer must initialize while decaring itself **/
int* const p;//constant pointer i.e its pointing to some address(here its pointing to garbage), it should point to same address(i.e garbage ad
dress only
p = &x;// but here what we are doing ? we are changing address. we are making p to point to address of x instead of garbage address.
printf("x = %d p =%p and *p = %d/n",num,p,*p);
}
Entonces, ¿cuál es la solución de este problema?
int* const p = &x;
Para obtener más información sobre este caso, consideremos el siguiente ejemplo.
#include<stdio.h>
int main()
{
int num = 300;
int *const ptr = #//constant value, address is modifible
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
*ptr++;//not possible
// *++ptr;//not possible bcz you are trying to change address which is not possible
// *(ptr)++;//not possible
// *(++ptr);//not possible
// ++*ptr;// possible bcz you trying to change value which is allowed
// (*ptr)++;// possible
// ++(*ptr);// possible
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
}
3) const int * const p : Aquí tanto la dirección como el valor son constantes .
Para entender esto, verifique el código a continuación
#include<stdio.h>
int main()
{
int num = 300;
const int* const ptr = #//constant value,constant address
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
*ptr++;//not possible
++*ptr;//not possible
printf(" num = %d ptr = %p and data on ptr : %d /n",num,ptr,*ptr);
}
*ptr++ // 1
Esto es lo mismo que:
tmp = *ptr;
ptr++;
Entonces se recupera el valor del objeto apuntado por ptr
, luego se incrementa ptr
.
*++ptr // 2
Esto es lo mismo que:
++ptr;
tmp = *ptr;
De modo que el puntero ptr
se incrementa, luego se lee el objeto apuntado por ptr
.
++*ptr // 3
Esto es lo mismo que:
++(*ptr);
Entonces el objeto apuntado por ptr
se incrementa; ptr
sí mismo no se modifica.
const char *p = "Hello";
*p means "Hello"
^
|
p
*p++ means "Hello"
^
|
p
*++p means "Hello"
^
| (WHILE THE STATEMENT IS EXECUTED)
p
*++p means "Hello"
^
| (AFTER THE STATEMENT IS EXECUTED)
p
++*p
significa que está tratando de incrementar el valor ASCII de *p
que
is "Hello"
^
|
p
no puede incrementar el valor porque es una constante por lo que obtendría un error
en cuanto a su ciclo while, el ciclo se ejecuta hasta que *p++
llegue al final de la cadena donde hay un carácter ''/0''
(NULO).
Ahora bien, dado que *p++
omite el primer carácter, solo obtendrá su salida a partir del segundo carácter.
El siguiente código no generará nada porque while loop tiene ''/0''
const char *p = "Hello";
while(''/0'')
printf("%c",*p);
El siguiente código le dará el mismo resultado que el próximo código, es decir, ello.
const char *p = "Hello";
while(*++p)
printf("%c",*p);
...................................
const char *p = "Hello";
while(*p++)
printf("%c",*p);