Aritmética del puntero

¿Alguien tiene buenos artículos o explicaciones (blogs, ejemplos) para la aritmética del puntero? Figura la audiencia es un grupo de progtwigdores de Java aprendiendo C y C ++.

Primero, el video binky puede ayudar. Es un buen video sobre punteros. Para la aritmética, aquí hay un ejemplo:

int * pa = NULL; int * pb = NULL; pa += 1; // pa++. behind the scenes, add sizeof(int) bytes assert((pa - pb) == 1); print_out(pa); // possibly outputs 0x4 print_out(pb); // possibly outputs 0x0 (if NULL is actually bit-wise 0x0) 

(Tenga en cuenta que el incremento de un puntero que contiene un valor nulo de puntero es estrictamente un comportamiento indefinido. Usamos NULL porque solo nos interesaba el valor del puntero. Normalmente, solo usemos el incremento / decremento cuando apuntamos a los elementos de una matriz).

A continuación se muestran dos conceptos importantes

  • la sum / resta de un entero a un puntero significa mover el puntero hacia adelante / hacia atrás por N elementos. Entonces, si un int tiene 4 bytes grandes, pa podría contener 0x4 en nuestra plataforma después de haberse incrementado en 1.
  • la resta de un puntero por otro puntero significa obtener su distancia, medida por elementos. Entonces restar pb de pa rendirá 1, ya que tienen una distancia de elemento.

En un ejemplo práctico. Supongamos que escribe una función y las personas le proporcionan un puntero de inicio y final (algo muy común en C ++):

 void mutate_them(int *begin, int *end) { // get the amount of elements ptrdiff_t n = end - begin; // allocate space for n elements to do something... // then iterate. increment begin until it hits end while(begin != end) { // do something begin++; } } 

ptrdiff_t es de qué tipo es (end – begin). Puede ser un sinónimo de “int” para algún comstackdor, pero puede ser de otro tipo para otro. Uno no puede saber, por lo que uno elige el typedef genérico ptrdiff_t .

Aquí es donde aprendí consejos: http://www.cplusplus.com/doc/tutorial/pointers.html

Una vez que comprenda los punteros, la aritmética del puntero es fácil. La única diferencia entre esta y la aritmética normal es que el número que está agregando al puntero se multiplicará por el tamaño del tipo al que apunta el puntero. Por ejemplo, si tiene un puntero a un int y un tamaño de int es de 4 bytes, (pointer_to_int + 4) evaluará a una dirección de memoria de 16 bytes (4 ints) adelante.

Entonces cuando escribes

 (a_pointer + a_number) 

en la aritmética del puntero, lo que realmente está sucediendo es

 (a_pointer + (a_number * sizeof(*a_pointer))) 

en aritmética regular.

aplicando NLP, llámalo dirección aritmética. Los “indicadores” son temidos e incomprendidos sobre todo porque son enseñados por las personas equivocadas y / o en el escenario equivocado con ejemplos incorrectos de la manera incorrecta. No es de extrañar que nadie lo “consiga”.

al enseñar punteros, la facultad continúa sobre “p es un puntero a a, el valor de p es la dirección de un” y así sucesivamente. simplemente no funcionará. aquí está la materia prima para que usted construya. practica con eso y tus estudiantes lo obtendrán.

‘int a’, a es un entero, almacena valores de tipo entero. ‘int * p’, p es una ‘estrella’, almacena valores de tipo ‘estrella int’.

‘a’ es cómo obtienes el entero ‘qué’ almacenado en un (intenta no usar ‘valor de a’) ‘y a’ es cómo obtienes el ‘dónde’ está almacenado uno mismo (intenta decir ‘dirección’)

‘b = a’ para que esto funcione, ambos lados deben ser del mismo tipo. si a es int, b debe ser capaz de almacenar un int. (Entonces ______ b, el espacio en blanco está lleno de ‘int’)

‘p = & a’ para que esto funcione, ambos lados deben ser del mismo tipo. si a es un número entero, y a es una dirección, p debe ser capaz de almacenar direcciones de enteros. (Entonces ______ p, el espacio en blanco está lleno de ‘int *’)

ahora escriba int * p de manera diferente para resaltar la información del tipo:

int * | pag

que es ‘p’? ans: es ‘int *’. entonces ‘p’ es una dirección de un entero.

int | *pag

¿Qué es ‘* p’? ans: es un ‘int’. entonces ‘* p’ es un número entero.

ahora a la aritmética de dirección:

int a; a = 1; a = a + 1;

¿Qué estamos haciendo en ‘a = a + 1’? pensar en ello como ‘siguiente’. Como a es un número, esto es como decir ‘próximo número’. Como a tiene 1, decir ‘siguiente’ lo convertirá en 2.

// ejemplo falaz ¡¡¡usted ha sido advertido!!! int * p int a; p = & a; p = p + 1;

¿Qué estamos haciendo en ‘p = p + 1’? todavía dice ‘siguiente’. Esta vez, p no es un número sino una dirección. Entonces, lo que estamos diciendo es ‘siguiente dirección’. La siguiente dirección depende del tipo de datos, más específicamente del tamaño del tipo de datos.

printf (“% d% d% d”, sizeof (char), sizeof (int), sizeof (float));

entonces ‘siguiente’ para una dirección avanzará sizeof (tipo de datos).

esto ha funcionado para mí y para todas las personas que solía enseñar.

Considero un buen ejemplo de aritmética de puntero la siguiente función de longitud de cadena:

 int length(char *s) { char *str = s; while(*str++); return str - s; } 

Entonces, la clave para recordar es que un puntero es solo una variable del tamaño de una palabra que se escribe para la desreferenciación. Eso significa que ya sea un vacío *, int *, long long **, sigue siendo solo una variable de tamaño de palabra. La diferencia entre estos tipos es lo que el comstackdor considera el tipo desreferenciado. Solo para aclarar, el tamaño de palabra significa el ancho de una dirección virtual. Si no sabe lo que esto significa, simplemente recuerde en una máquina de 64 bits, los punteros son de 8 bytes, y en una máquina de 32 bits, los punteros son 4 bytes. El concepto de una dirección es SÚPER importante para entender los indicadores. Una dirección es un número capaz de identificar de forma única una determinada ubicación en la memoria. Todo en la memoria tiene una dirección. Para nuestros propósitos, podemos decir que cada variable tiene una dirección. Esto no necesariamente es siempre cierto, pero el comstackdor nos permite suponer esto. La dirección en sí misma es byte granular, lo que significa que 0x0000000 especifica el comienzo de la memoria y 0x00000001 es un byte en la memoria. Esto significa que al agregar uno a un puntero, avanzamos un byte hacia adelante en la memoria. Ahora, vamos a tomar matrices. Si crea una matriz de tipo quux que tenga 32 elementos grandes, abarcará desde el principio de su asignación, hasta el comienzo de su asignación más 32 * sizeof (quux), ya que cada celda de la matriz es sizeof (quux) grande. Entonces, realmente cuando especificamos un elemento de una matriz con matriz [n], eso es solo azúcar sintáctica (taquigrafía) para * (matriz + tamaño de (quux) * n). La aritmética de punteros realmente solo está cambiando la dirección a la que se refiere, por lo que podemos implementar strlen con

 while(*n++ != '\0'){ len++; } 

ya que estamos escaneando, byte a byte hasta que lleguemos a cero. ¡Espero que ayude!

Hay varias formas de abordarlo.

El enfoque intuitivo, que es lo que piensan la mayoría de los progtwigdores de C / C ++, es que los punteros son direcciones de memoria. El ejemplo de Litb toma este enfoque. Si tiene un puntero nulo (que en la mayoría de las máquinas corresponde a la dirección 0) y agrega el tamaño de un int, obtendrá la dirección 4. Esto implica que los punteros son básicamente enteros de fantasía.

Desafortunadamente, hay algunos problemas con esto. Para empezar, puede que no funcione. No se garantiza que un puntero nulo realmente use la dirección 0. (Aunque la asignación de la constante 0 a un puntero genera el puntero nulo).

Además, no tiene permitido incrementar el puntero nulo, o de manera más general, un puntero siempre debe apuntar a la memoria asignada (o un elemento pasado), o la constante 0 del puntero nulo especial.

Entonces, una forma más correcta de pensarlo es que los punteros son simplemente iteradores que le permiten iterar sobre la memoria asignada. Esta es realmente una de las ideas clave detrás de los iteradores STL. Están modelados para comportarse tanto como punteros, y para proporcionar especializaciones que reparan punteros crudos para que funcionen como iteradores adecuados.

Una explicación más elaborada de esto se da aquí , por ejemplo.

Pero esta última vista significa que realmente debe explicar los iteradores STL, y luego simplemente decir que los punteros son un caso especial de estos. Puede incrementar un puntero para apuntar al siguiente elemento en el búfer, al igual que un std::vector::iterator . Puede señalar un elemento más allá del final de una matriz, al igual que el iterador final en cualquier otro contenedor. Puede restar dos punteros que apuntan al mismo búfer para obtener la cantidad de elementos entre ellos, al igual que con los iteradores, y al igual que con los iteradores, si los punteros apuntan a búferes separados, no puede compararlos significativamente. (Para un ejemplo práctico de por qué no, considere lo que sucede en un espacio de memoria segmentada. ¿Cuál es la distancia entre dos punteros que apuntan a segmentos separados?)

Por supuesto, en la práctica, existe una estrecha correlación entre las direcciones de la CPU y los punteros C / C ++. Pero no son exactamente lo mismo. Los punteros tienen algunas limitaciones que pueden no ser estrictamente necesarias en su CPU.

Por supuesto, la mayoría de los progtwigdores de C ++ se desorientan en la primera comprensión, a pesar de que es técnicamente incorrecta. Por lo general, es lo suficientemente parecido a cómo se comporta su código y la gente cree que lo consigue y sigue adelante.

Pero para alguien que viene de Java, y acaba de aprender acerca de los indicadores desde cero, la última explicación puede ser fácilmente comprendida, y les traerá menos sorpresas más adelante.

Este es uno muy bueno en el enlace aquí sobre Pointer Arithmetic

Por ejemplo:

Puntero y matriz

Fórmula para calcular la dirección de ptr + i donde ptr tiene el tipo T *. entonces la fórmula para la dirección es:

addr (ptr + i) = addr (ptr) + [sizeof (T) * i]

Para el tipo de int en la plataforma de 32 bits, addr (ptr + i) = addr (ptr) + 4 * i;

Sustracción

También podemos calcular ptr – i. Por ejemplo, supongamos que tenemos una matriz int llamada arr. int arr [10]; int * p1, * p2;

 p1 = arr + 3 ; // p1 == & arr[ 3 ] p2 = p1 - 2 ; // p1 == & arr[ 1 ]