Tratando de entender el comportamiento de los punteros en C, yo estaba un poco sorprendido por la siguiente (ejemplo de código siguiente):
#include <stdio.h>
void add_one_v1(int *our_var_ptr)
{
*our_var_ptr = *our_var_ptr +1;
}
void add_one_v2(int *our_var_ptr)
{
*our_var_ptr++;
}
int main()
{
int testvar;
testvar = 63;
add_one_v1(&(testvar)); /* Try first version of the function */
printf("%d\n", testvar); /* Prints out 64 */
printf("@ %p\n\n", &(testvar));
testvar = 63;
add_one_v2(&(testvar)); /* Try first version of the function */
printf("%d\n", testvar); /* Prints 63 ? */
printf("@ %p\n", &(testvar)); /* Address remains identical */
}
De salida:
64
@ 0xbf84c6b0
63
@ 0xbf84c6b0
¿Qué hace exactamente el *our_var_ptr++
declaración en la segunda función (add_one_v2
) hacer ya que claramente no es el mismo que *our_var_ptr = *our_var_ptr +1
?
- Posibles duplicados de Puntero expresiones: *ptr++, *ptr++y ++*ptr
Debido a la preferencia de los operadores de las normas y el hecho de que
++
es un operador de sufijo,add_one_v2()
no eliminar el puntero, pero el++
en realidad está siendo aplicado el propio puntero. Sin embargo, recuerde que C utiliza siempre de paso por valor:add_one_v2()
es el incremento de su copia local del puntero, que no tendrá efecto alguno en el valor almacenado en esa dirección.Como una prueba, reemplace
add_one_v2()
con estos trozos de código y ver cómo el resultado se verá afectado:Este es uno de esos pequeños lo tengo que hacer en C y C++, muy divertido. Si desea doblar tu cerebro, figura la siguiente:
Es una copia de cadena. Los punteros seguir recibiendo incrementa hasta un personaje con un valor de cero es copiado. Una vez que usted sepa por qué este truco funciona, nunca voy a olvidar cómo ++ funciona en punteros de nuevo.
P. S. siempre Se pueden reemplazar el operador con el fin de paréntesis. El siguiente incrementará el valor señalado en, más bien que el propio puntero:
OK,
esto funciona así:
our_var_ptr
(que contiene 63).our_var_ptr
luego se incrementa DESPUÉS de la evaluación. Todo cambia cuando el puntero que apunta, no lo que se señala en.Efectivamente es lo mismo que hacer esto:
Sentido? Marca de Rescate de la respuesta tiene un buen ejemplo de esto, salvo que en realidad se utiliza el resultado.
*our_var_ptr++
. Se podría lanzar una advertencia, pero si hizo que fuera, tendría que romper MUUUY cosas. Con*(our_var_ptr)++
el interior de los paréntesis se evalúan primero aour_var_ptr
; lo que equivale a*our_var_ptr++
.our_var_ptr = our_var_ptr + 1;
es la razón por la que estewhile (*dst++ = *src++) ;
funciona como una copia de cadena en C.Mucha confusión aquí, así que aquí es una modificación del programa de la prueba a hacer lo que pasa claro (o al menos claroer):
con el resultado:
Cuatro cosas a tener en cuenta:
add_one_v2
es no incrementa, y ni es el siguiente valor, pero el puntero seadd_one_v2
sucede después de la eliminación de referencias¿Por qué?
++
se une más fuertemente que*
(como eliminar o multiplicación) por lo que el incremento enadd_one_v2
se aplica para el puntero, y no lo señala.Como los otros han señalado, la precedencia de operadores causa de la expresión en la v2 función de ser visto como
*(our_var_ptr++)
.Sin embargo, dado que este es un post-operador de incremento, no es del todo cierto decir que se incrementa el puntero y, a continuación, elimina referencias a ella. Si esto fuera cierto no creo que usted estaría consiguiendo 63 como su salida, ya que se trataría de devolver el valor en la siguiente posición de memoria. En realidad, creo que la secuencia lógica de las operaciones es:
Como htw explicó, que usted no está viendo el cambio en el valor del puntero porque es de ser aprobada por el valor de la función.
our_var_ptr es un puntero a la memoria. es decir, en la celda de memoria donde se almacenan los datos. (n este caso 4 bytes en el formato binario de un int).
*our_var_ptr es la eliminan las referencias puntero que vaya a la ubicación del puntero ‘puntos’ en.
++ incrementa un valor.
así.
*our_var_ptr = *our_var_ptr+1
elimina referencias al puntero y le añade uno el valor en esa ubicación.Ahora agregar en precedencia de operadores a leer como
(*our_var_ptr) = (*our_var_ptr)+1
y se ve que la eliminación de referencias que ocurra primero, así que tome el valor y incremement ella.En el otro ejemplo, el operador ++ tiene menor prioridad que el *, por lo que toma el puntero que pasa, añade uno (por lo que apunta a la basura ahora), y luego regresa. (recuerde que los valores se pasan siempre por valor de C, de modo que cuando la función devuelve el original testvar puntero sigue siendo el mismo, sólo cambia el puntero dentro de la función).
Mi consejo, cuando el uso de eliminación de referencias (o cualquier cosa) utilizar los soportes para tomar su decisión explícita. No trate de recordar las reglas de prioridad como acabarás usando otro lenguaje de un día que tiene ligeramente diferentes y te vas a confundir. O el viejo y terminan olvidándose de que tiene mayor prioridad (como yo lo hago con * y ->).
Si no utiliza el paréntesis para especificar el orden de las operaciones, de prefijo y postfix incrementos tienen prioridad sobre los de referencia y de eliminar. Sin embargo, el prefijo de incremento y postfix incremento son diferentes operaciones. En ++x, el operador toma una referencia a la variable, agregue una y retorno por valor. En x++, el operador de incremento de la variable, pero se devuelve su valor anterior. Se comportan como el tipo de esta (imagino que se declaren como de métodos dentro de la clase):
(Tenga en cuenta que no es una copia involucrados en el postfix de incremento, lo que es menos eficiente. Esa es la razón por la que usted debe prefieres ++i en lugar de la i++ en los bucles, aunque la mayoría de los compiladores de hacerlo de forma automática para usted en estos días.)
Como se puede ver, postfix incremento se procesa en primer lugar, pero, debido a la forma en que se comporta, usted será eliminar la referencia al valor previo del puntero.
Aquí está un ejemplo:
En la segunda línea, el puntero x se incrementa antes de la eliminación de referencias, pero la eliminación de referencias que va a suceder sobre el antiguo valor de x (que es un dirección devuelto por el postfix de incremento). De modo que y se inicializa con ‘a’, y y z con ‘c’. Pero si se hace así:
Aquí, x se eliminan las referencias y la valor señalado por él (‘a’) se incrementa (‘b’). Desde el postfix incremento devuelve el valor antiguo, y todavía será inicializado con ‘un’. Y puesto que el puntero no cambia, z se inicializa con el nuevo valor ‘b’.
Ahora vamos a comprobar el prefijo casos:
Aquí, la eliminación de referencias que va a suceder en el que se incrementa el valor de x (que es devuelto de inmediato por el prefijo de operador de incremento), por lo tanto, y y z se inicializa con ‘c’. Para obtener un comportamiento diferente, puede cambiar el orden de los operadores:
Aquí usted asegurarse de que se incrementa el contenido de x en primer lugar y el valor del puntero que nunca cambian, de modo que y y z será asignado con ‘b’. En el strcpy función (como se menciona en otra respuesta), el incremento es también hecho primero:
En cada iteración, src++ se procesa en primer lugar y, siendo un postfix incremento, devuelve el valor antiguo de la src. Entonces, el valor antiguo de src (que es un puntero) se eliminan las referencias a asignar a lo que está en el lado izquierdo del operador de asignación. El horario de verano es entonces incrementado y su valor antiguo se eliminan las referencias a convertirse en un lvalue y recibir el antiguo valor src. Esta es la razón por la dst[0] = src[0], dst[1] = src[1], etc, hasta *dst se le asigna 0, romper el bucle.
Anexo:
Todo el código de esta respuesta fue probado con lenguaje C. En C++ usted probablemente no será capaz de enumerar-inicializar el puntero. Por lo tanto, si usted desea probar los ejemplos en C++, se debe inicializar una matriz primero y, a continuación, se degrada a un puntero:
char *x = {'a', 'c'}; char y = *++x; //or *(++x); char z = *x;
y y z se a o c?El ‘++’ operador tiene mayor precedencia sobre el ‘*’ operador, lo que significa que la dirección de puntero se incrementa antes de que se eliminan las referencias.
El ‘+’ de operador, sin embargo, tiene menor prioridad que ‘*’.
Voy a intentar responder a esta de un poco de un ángulo diferente…
Paso 1
Echemos un vistazo a los operadores y los operandos:
En este caso es el operando, y tiene dos operadores, en este caso * para eliminar y ++ para incrementar.
Paso 2
el que tiene la prioridad más alta
++ tiene mayor precedencia sobre *
Paso 3
Dónde está ++, que está a la derecha, lo que significa que POST Incremento
En este caso, el compilador de tomar una «nota mental» para realizar el incremento DESPUÉS de se hace con todos los otros operadores…
nota si era *++p, a continuación, lo hará ANTES de
así que en este caso, es equivalente a la toma de los dos de el procesador de registro, uno tendrá el valor de la eliminan las referencias *p y el otro tendrá el valor de las incrementa p++, la razón por la que en este caso no son dos, es el POST de la actividad…
Aquí es donde en este caso es complicado, y se ve como una contradicción.
Uno esperaría que el ++ para tomar precedencia sobre el *, que lo hace, solo que el POST significa que se va a aplicar sólo después de que TODOS los demás operandos, ANTES de la próxima ‘;’ token…
Porque la prueba es un puntero, prueba++ (esto es, sin eliminar la referencia) se incrementa el puntero (se incrementa el valor de la prueba, que pasa a ser el (destino) dirección de lo que está señalado). Porque el destino es de tipo uint32_t, prueba++ se incrementará en 4 bytes, y si el destino fue, por ejemplo, una matriz de este tipo, entonces la prueba que ahora se apunta al siguiente elemento.
Cuando se hace este tipo de manipulaciones, a veces usted tiene que convertir el puntero de la primera para obtener la deseada desplazamiento de memoria.
Esto incrementará la dirección 1 byte solo 😉
De K&R, página 105: «El valor de *t++ es el personaje que t señalado antes de t se incrementa».
Las imágenes son de un valor de aproximadamente mil palabras (más o menos un millón o así)…y los símbolos pueden ser fotos (y viceversa).
Así que para aquellos de nosotros que busca
tl;dr
del (optimizado el consumo de datos) y aún así quiero «(en su mayoría) sin pérdida» de la codificación, el vector de imágenes/fotografías/ilustraciones/demos son de suma importancia.En otras palabras, ignorar mis últimas 2 declaraciones y ver a continuación.
Porque es el puntero que se pasa por valor de sólo la copia local se incrementa. Si usted realmente desea incrementar el puntero tiene que pasar por referencia como esta: