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?

InformationsquelleAutor ChristopheD | 2009-05-13

13 Comentarios

  1. 40

    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:

    void add_one_v2(int *our_var_ptr)
    {
        (*our_var_ptr)++;  //Now stores 64
    }
    
    void add_one_v2(int *our_var_ptr)
    {
        *(our_var_ptr++);  //Increments the pointer, but this is a local
                           //copy of the pointer, so it doesn't do anything.
    }
    • Que no es cierto acerca de la orden de operación. En add_one_v2, el ++ se aplica para el puntero, no el de eliminar. Sin embargo, ya que es un post de incremento, la eliminación de referencias está sucediendo ANTE el incremento.
    • Estás hablando de la original add_one_v2, o uno de mis ejemplos con paréntesis?
    • Estoy hablando de la original. Sólo estoy tratando de señalar que la declaración de «se incrementa el puntero y, a continuación, eliminar…» es incorrecta.
    • Gracias por la rápida respuesta (y para el informativo comentario Torlack!). ++*our_var_ptr parece que el «trabajo» de la misma como add_one_v1. Una cosa que no entiendo, sin embargo: si las funciones están trabajando sólo con copias locales de los punteros –> ¿cómo lograr influir en el valor impreso en la principal? O ¿debo interpretar que la declaración?
    • El puntero es una copia local, pero el valor que se apunta a que no.
    • Gotcha, gracias por el recordatorio. Fijo. @ChristopheD: Si desreferenciar un puntero, puede cambiar el valor almacenado en esa ubicación en la memoria. Así que, básicamente, usted tiene una copia de la dirección de memoria de los que no puede influir en lo que la dirección de memoria en que el puntero se mantenga en el principal, pero usted puede cambiar lo que el valor de esa dirección de memoria señala.
    • Muchas gracias (htw, torlack): que tiene sentido!

  2. 61

    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:

    while (*dst++ = *src++) ;

    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:

    (*our_var_ptr)++;
    • Gracias por este muy buen ejemplo!
    • Este ejemplo, junto con unos cuantos «Mira ma, sin manos!» fragmentos de código, aparece en ese bastión de C conocimiento, El Lenguaje de Programación C.
  3. 33

    OK,

    *our_var_ptr++;

    esto funciona así:

    1. La eliminación de referencias que ocurra primero, dándole la posición de memoria indicada por our_var_ptr (que contiene 63).
    2. A continuación, se evalúa la expresión, el resultado de 63 es todavía 63.
    3. El resultado es echado fuera (que no están haciendo nada con ella).
    4. 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:

    *our_var_ptr;
    our_var_ptr = our_var_ptr + 1; 

    Sentido? Marca de Rescate de la respuesta tiene un buen ejemplo de esto, salvo que en realidad se utiliza el resultado.

    • Gracias, muy clara la explicación!
    • GCC no acepta: *our_var_ptr++; Que acepta como: *(our_var_ptr)++
    • También la OMI debería ser *our_var_ptr; *our_var_ptr = *our_var_ptr + 1;
    • Te garantizo GCC aceptará *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 a our_var_ptr; lo que equivale a *our_var_ptr++.
    • 1; es el efecto que el cartel original quería; no es lo que yo estaba tratando de explicar. our_var_ptr = our_var_ptr + 1; es la razón por la que este while (*dst++ = *src++) ; funciona como una copia de cadena en C.
  4. 7

    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):

    #include <stdio.h>
    
    void add_one_v1(int *p){
      printf("v1: pre:   p = %p\n",p);
      printf("v1: pre:  *p = %d\n",*p);
        *p = *p + 1;
      printf("v1: post:  p = %p\n",p);
      printf("v1: post: *p = %d\n",*p);
    }
    
    void add_one_v2(int *p)
    {
      printf("v2: pre:   p = %p\n",p);
      printf("v2: pre:  *p = %d\n",*p);
        int q = *p++;
      printf("v2: post:   p = %p\n",p);
      printf("v2: post:  *p = %d\n",*p);
      printf("v2: post:   q = %d\n",q);
    }
    
    int main()
    {
      int ary[2] = {63, -63};
      int *ptr = ary;
    
        add_one_v1(ptr);         
        printf("@ %p\n", ptr);
        printf("%d\n", *(ptr));  
        printf("%d\n\n", *(ptr+1)); 
    
        add_one_v2(ptr);
        printf("@ %p\n", ptr);
        printf("%d\n", *ptr);
        printf("%d\n", *(ptr+1)); 
    }

    con el resultado:

    v1: pre:   p = 0xbfffecb4
    v1: pre:  *p = 63
    v1: post:  p = 0xbfffecb4
    v1: post: *p = 64
    @ 0xbfffecb4
    64
    -63
    
    v2: pre:   p = 0xbfffecb4
    v2: pre:  *p = 64
    v2: post:  p = 0xbfffecb8
    v2: post: *p = -63
    v2: post:  q = 64
    
    @ 0xbfffecb4
    64
    -63

    Cuatro cosas a tener en cuenta:

    1. cambios en la copia local de el puntero se no se refleja en el llamado puntero.
    2. los cambios en el destino de los locales puntero afectan el destino de la llamada puntero (al menos hasta que el destino puntero se actualiza)
    3. el valor señalado en add_one_v2 es no incrementa, y ni es el siguiente valor, pero el puntero se
    4. el incremento del puntero en add_one_v2 sucede después de la eliminación de referencias

    ¿Por qué?

    • Porque ++ se une más fuertemente que * (como eliminar o multiplicación) por lo que el incremento en add_one_v2 se aplica para el puntero, y no lo señala.
    • post incrementos de suceder después de la evaluación de la frase, por lo que la eliminación de referencias obtiene el primer valor de la matriz (elemento 0).
    • Gracias por tomarse el tiempo para escribir esta respuesta, es este tipo de cosas que hace StackOverflow tan gran ambiente! He tenido más interesantes respuestas aquí en una hora que me he encontrado en el mismo tiempo en mi pésimo C tutoriales 😉
    • Gracias! Creo que este es el punto más importante – «Porque ++ se une más fuertemente que *»
    • También, la gente debe tomar nota de esto : int p = *p++; NO ES EQUIVALENTE A *p++; *p++ todavía tiene «primer valor» solamente. mientras que en q es «segundo valor».( Creo que mencionó que en el segundo punto)
  5. 6

    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:

    1. Guarda el valor actual del puntero
    2. Incrementar el puntero
    3. Eliminar el puntero de valor guardado en el paso 1

    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.

    • Gracias por esta buena explicación!
  6. 3

    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 ->).

    • Gracias por responder, creo que el consejo acerca de lo que es más explícita al uso de los corchetes es sólido!
  7. 3

    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):

    //prefix increment (++x)
    auto operator++()
    {
        (*this) = (*this) + 1;
        return (*this);
    }
    
    //postfix increment (x++)
    auto operator++(int) //unfortunatelly, the "int" is how they differentiate
    {
        auto temp = (*this);
        (*this) = (*this) + 1; //same as ++(*this);
        return temp;
    }

    (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:

    char * x = {'a', 'c'};
    char   y = *x++; //same as *(x++);
    char   z = *x;

    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í:

    char * x = {'a', 'c'};
    char   y = (*x)++;
    char   z = *x;

    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:

    char * x = {'a', 'c'};
    char   y = *++x; //same as *(++x)
    char   z = *x;

    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:

    char * x = {'a', 'c'};
    char   y = ++*x; //same as ++(*x)
    char   z = *x;

    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:

    char * strcpy(char * dst, char * src)
    {
        char * aux = dst;
        while(*dst++ = *src++);
        return aux;
    }

    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 w[] = {'a', 'c'};
    char * x = w;
    char   y = *x++; //or the other cases
    char   z = *x;
    • char *x = {'a', 'c'}; char y = *++x; //or *(++x); char z = *x; y y z se a o c?
    • con ‘c’. Gracias por señalar que! Me fijo en la respuesta.
  8. 1

    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 ‘*’.

    • Ah! No. El ++ se une con más fuerza, por lo que es el puntero que se incrementa. Pero es el post-incremento de la versión, lo que pasa después la eliminación de referencias. Así que: eliminar el puntero y devolver un valor, a continuación, incrementar el puntero (no el valor señalado).
  9. 1

    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…

  10. 1
        uint32_t* test;
        test = &__STACK_TOP;
    
    
        for (i = 0; i < 10; i++) {
            *test++ = 0x5A5A5A5A;
        }
    
        //same as above
    
        for (i = 0; i < 10; i++) {
            *test = 0x5A5A5A5A;
            test++;
        }

    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.

            ((unsigned char*) test)++;

    Esto incrementará la dirección 1 byte solo 😉

  11. 0

    De K&R, página 105: «El valor de *t++ es el personaje que t señalado antes de t se incrementa».

  12. 0

    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;drdel (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.

    Valid forms:


    *a++ *(a++)
    (a++)[0] a++[0]
    0[a++] //Don't you dare use this (“educational purposes only”)
    //These produce equivalent (side) effects;
    val=*a,++a,val
    ptr=a,++a,*ptr
    *(ptr=a,++a,ptr)

    *++a *(++a)
    *(a+=1) *(a=a+1)
    (++a)[0] (a+=1)[0] (a=a+1)[0] //()'s are necessary
    0[++a] //0[a+=1], etc... “educational purposes only”
    //These produce equivalent (side) effects:
    ++a,*a
    a+=1,*a
    a=a+1,*a

    ++*a ++(*a)
    *a+=1
    *a=*a+1
    ++a[0] ++(a[0])
    ++0[a] //STAY AWAY

    (*a)++ //Note that this does NOT return a pointer;
    //Location `a` points to does not change
    //(i.e. the 'value' of `a` is unchanged)
    val=*a,++*a,val

    Notas

    /* Direccionamiento indirecto/deferencia operador debe preceder el destino identificador: */
    una++*;
    una*++;
    ++una*;
  13. -1

    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:

    void inc_value_and_ptr(int **ptr)
    {
       (**ptr)++;
       (*ptr)++;
    }
    • En realidad no responder a la pregunta. Él estaba preguntando qué *ptr++ hace. Respuesta correcta consiste en describir de precedencia.

Dejar respuesta

Please enter your comment!
Please enter your name here