N.B. No hay reclamos sobre los estándares C aquí.
Como una adición rápida a la respuesta de @Brian Hooper, "[t] la suma de dos punteros significa ... er ... nada", sin embargo, la suma de un puntero y un entero permite compensar desde el puntero inicial .
Restar un puntero de mayor valor desde un puntero de valor inferior le proporciona el desplazamiento entre los dos. Tenga en cuenta que no estoy contabilizando la búsqueda de memoria aquí; Supongo que los valores de memoria están dentro del alcance accesible del proceso.
Así que si tiene un puntero a una serie de ubicaciones de memoria consecutivas en el montón, o una matriz de ubicaciones de memoria en la pila (cuyo nombre de variable decae a un puntero), estos punteros (el puntero real y el que se desintegra a un puntero) apuntará a la pregunta de la ubicación de la memoria del puño (es decir, el elemento [0]
). Agregar un valor entero al puntero equivale a incrementar el índice entre paréntesis por el mismo número.
#include <stdio.h>
#include <stdlib.h>
int main()
{
// This first declaration does several things (this is conceptual and not an exact list of steps the computer takes):
// 1) allots space on the stack for a variable of type pointer
// 2) allocates number of bytes on the heap necessary to fit number of chars in initialisation string
// plus the NULL termination '\0' (i.e. sizeof(char) * <characters in string> + 1 for '\0')
// 3) changes the value of the variable from step 1 to the memory address of the beginning of the memory
// allocated in step 2
// The variable iPointToAMemoryLocationOnTheHeap points to the first address location of the memory that was allocated.
char *iPointToAMemoryLocationOnTheHeap = "ABCDE";
// This second declaration does the following:
// 1) allots space on the stack for a variable that is not a pointer but is said to decay to a pointer allowing
// us to so the following iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap;
// 2) allots number of bytes on the stack necessary to fit number of chars in initialisation string
// plus the NULL termination '\0' (i.e. sizeof(char) * <characters in string> + 1 for '\0')
// The variable iPointToACharOnTheHeap just points to first address location.
// It just so happens that others follow which is why null termination is important in a series of chars you treat
char iAmASeriesOfConsecutiveCharsOnTheStack[] = "ABCDE";
// In both these cases it just so happens that other chars follow which is why null termination is important in a series
// of chars you treat as though they are a string (which they are not).
char *iJustPointToMemoryLocationsYouTellMeToPointTo = NULL;
iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap;
// If you increment iPointToAMemoryLocationOnTheHeap, you'll lose track of where you started
for(; *(++iJustPointToMemoryLocationsYouTellMeToPointTo) != '\0' ;) {
printf("Offset of: %ld\n", iJustPointToMemoryLocationsYouTellMeToPointTo - iPointToAMemoryLocationOnTheHeap);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo);
}
printf("\n");
iJustPointToMemoryLocationsYouTellMeToPointTo = iPointToAMemoryLocationOnTheHeap;
for(int i = 0 ; *(iJustPointToMemoryLocationsYouTellMeToPointTo + i) != '\0' ; i++) {
printf("Offset of: %ld\n", (iJustPointToMemoryLocationsYouTellMeToPointTo + i) - iPointToAMemoryLocationOnTheHeap);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo + i);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo + i);
}
printf("\n");
iJustPointToMemoryLocationsYouTellMeToPointTo = iAmASeriesOfConsecutiveCharsOnTheStack;
// If you increment iAmASeriesOfConsecutiveCharsOnTheStack, you'll lose track of where you started
for(; *(++iJustPointToMemoryLocationsYouTellMeToPointTo) != '\0' ;) {
printf("Offset of: %ld\n", iJustPointToMemoryLocationsYouTellMeToPointTo - iAmASeriesOfConsecutiveCharsOnTheStack);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo);
}
printf("\n");
iJustPointToMemoryLocationsYouTellMeToPointTo = iAmASeriesOfConsecutiveCharsOnTheStack;
for(int i = 0 ; *(iJustPointToMemoryLocationsYouTellMeToPointTo + i) != '\0' ; i++) {
printf("Offset of: %ld\n", (iJustPointToMemoryLocationsYouTellMeToPointTo + i) - iAmASeriesOfConsecutiveCharsOnTheStack);
printf("%s\n", iJustPointToMemoryLocationsYouTellMeToPointTo + i);
printf("%c\n", *iJustPointToMemoryLocationsYouTellMeToPointTo + i);
}
return 1;
}
Lo primero destacable que hacemos en este programa es copiar el valor del puntero iPointToAMemoryLocationOnTheHeap
-iJustPointToMemoryLocationsYouTellMeToPointTo
. Así que ahora ambos apuntan a la misma ubicación de memoria en el montón. Hacemos esto para no perder la pista del comienzo.
En el primer bucle for
, incrementamos el valor que acabamos de copiar en iJustPointToMemoryLocationsYouTellMeToPointTo
(incrementándolo en 1 significa que apunta a una ubicación de memoria más alejada de iPointToAMemoryLocationOnTheHeap
).
El segundo ciclo es similar pero quería mostrar más claramente cómo el aumento del valor se relaciona con el desplazamiento y cómo funciona la aritmética.
El tercer y cuarto bucle repiten el proceso pero trabajan en una matriz en la pila en lugar de la memoria asignada en el montón.
Tenga en cuenta el asterisco *
al imprimir el char
individual. Esto le dice a printf que muestre lo que apunta la variable, y no el contenido de la variable en sí. Esto está en contraste con la línea de arriba donde se imprime el resto de la cadena y no hay asterisco antes de la variable porque printf() está mirando la serie de ubicaciones de memoria en su totalidad hasta que se alcanza NULL.
Aquí está la salida en ubuntu 15.10 ejecutándose en un i7 (el primer y tercer bucle de salida comienzan en un desplazamiento de 1 porque mi elección de bucle de for
se incrementa al principio del bucle en vez de do{}while()
; solo quería para mantenerlo simple):
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Offset of: 0
ABCDE
A
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Offset of: 0
ABCDE
A
Offset of: 1
BCDE
B
Offset of: 2
CDE
C
Offset of: 3
DE
D
Offset of: 4
E
E
Nota: la adición del puntero es compatible en el sentido de que 'T * p =/** /; p = p + 1' es válido (y apunta un elemento más).Sin embargo, difiere un poco de tu pregunta :) –
muchas respuestas indican que la diferencia significaría el número de objetos de un tipo que se puede almacenar entre dos punteros p1 y p2. ¿Qué sucede si el puntero es del tipo 'void *'? – Chubsdad
@chubsdad: entonces no puedes restarlos. – Potatoswatter