Memoria asignada sin asignación utilizando malloc, ¿cómo?

A continuación se muestra un simple fragmento de código:

int main() { int *p; p=(int*)malloc(sizeof(int));//allocate m/y 4 1 int printf("P=%p\tQ=%p",p,p+2); } 

En una ejecución de muestra, me dio la salida de la siguiente manera:

 P=0x8210008 Q=0x8210010 

La dirección de inicio de P es-P = 0x8210008, el siguiente byte es 0x8210009, el siguiente byte es 0x821000A, el siguiente byte es 0x821000B.Así que los 4 bytes para int terminan allí. No hemos asignado más memoria utilizando malloc. Entonces, ¿cómo p + 2 nos lleva a 0x8210010, que es de 8 bytes después de P (0x8210008).

Primero, el hecho de que haya impreso una dirección no implica que la memoria esté asignada en esa dirección. Simplemente has agregado números y producido otros números.

Segundo, la razón por la que obtuviste el número al agregar dos fue ocho mayor que la dirección base en lugar de dos mayor que la dirección base porque, al agregar números enteros a los punteros en C, la aritmética se realiza en términos de elementos apuntados a , no en términos de bytes en la memoria (a menos que los elementos apuntados sean bytes). Supongamos que tiene una matriz de int, digamos int x[8] , y tiene un puntero a x[3] . Agregar dos a ese puntero produce un puntero a x[5] , no un puntero a dos bytes más allá del inicio de x[3] . Es importante recordar que C es una abstracción, y el estándar C especifica qué sucede dentro de esa abstracción. Dentro de la abstracción de C, la aritmética de punteros funciona en números de elementos, no en direcciones de memoria sin formato. Se requiere la implementación de C (el comstackdor y las herramientas que convierten el código C en ejecución del progtwig) para realizar las operaciones en las direcciones de memoria sin formato que se requieren para implementar la abstracción especificada por el estándar C. Normalmente, esto significa que el comstackdor multiplica un número entero por el tamaño de un elemento al agregarlo a un puntero. Así que dos se multiplican por cuatro (en una máquina donde un int es de cuatro bytes), y los ocho que se obtienen se agregan a la dirección base.

Tercero, no puedes confiar en este comportamiento. El estándar C define la aritmética de punteros solo para los punteros que apuntan a objetos dentro de matrices, incluido un objeto ficticio al final de la matriz. Además, los punteros a objetos individuales actúan como matrices de un elemento. Entonces, si tiene un puntero p que apunta a un int, se le permite calcular p+0 o p+1 , porque apuntan al único objeto en la matriz ( p+0 ) y al objeto ficticio uno más allá del último Elemento en la matriz ( p+1 ). No está permitido calcular p-1 o p+2 , porque están fuera de la matriz. Tenga en cuenta que esto no es una cuestión de desreferenciar el puntero (intentar leer o escribir memoria en la dirección calculada): incluso el mero cálculo de los resultados de la dirección en un comportamiento que no está definido por el estándar C: su progtwig podría fallar, podría darle Resultados “correctos”, o podría eliminar todos los archivos en su cuenta, y todos esos comportamientos estarían de acuerdo con el estándar C.

Es poco probable que el mero cálculo de una dirección fuera de los límites genere un comportamiento tan extraño. Sin embargo, el estándar lo permite porque algunos procesadores de computadora tienen esquemas de dirección inusuales que requieren más trabajo que la simple aritmética. Quizás el segundo esquema de direcciones más común después del espacio de direcciones planas es una dirección base y un esquema de desplazamiento. En tal esquema, los 16 bits altos de un puntero de cuatro bytes podrían contener una dirección base, y los 16 bits bajos podrían contener un desplazamiento. Para una dirección base b y un desplazamiento o determinados, la dirección virtual correspondiente podría ser 4096 * b + o. (Tal esquema es capaz de direccionar solo 2 20 bytes, y muchos valores diferentes de base y desplazamiento pueden referirse a la misma dirección. Por ejemplo, base 0 y desplazamiento 4096 se refieren a la misma dirección que base 1 y desplazamiento 0.) Con un esquema de base y desplazamiento, el comstackdor podría implementar aritmética de punteros agregando solo el desplazamiento e ignorando la base. (Dicha implementación de C puede admitir arreglos de hasta solo 65536 bytes, la extensión es direccionable solo por el desplazamiento.) En dicha implementación, si tiene puntero a int p con una encoding de 0x0000fffc (base 0, desplazamiento 65532), e int es de cuatro bytes, entonces p+2 tendrá el valor 0x00000004, no el valor que es ocho mayor (0x00010004).

Este es un ejemplo en el que la aritmética de punteros produce valores que no esperaría de una máquina de direcciones planas. Es más difícil imaginar una implementación donde la aritmética de punteros que no es válida de acuerdo con el estándar C produciría un locking. Sin embargo, considere una implementación en la que la memoria deba ser intercambiada manualmente por un proceso, porque el procesador no tiene el hardware para admitir la memoria virtual. En tal implementación, los punteros pueden contener direcciones de estructuras en la memoria que describen las ubicaciones de los discos y otra información utilizada para administrar el intercambio de memoria. En tal implementación, hacer aritmética de punteros puede requerir leer las estructuras en la memoria, y así hacer aritmética de punteros no válida puede leer direcciones no válidas.

Porque se trata como un desplazamiento de elemento entero desde el puntero. Has asignado una matriz para un solo entero. Cuando pides p+2 es lo mismo que &p[2] . Si quieres dos bytes desde el principio, primero debes convertirlos a char* :

 char *highWordAddr = (char*)p + 2; 

C está feliz de permitirte hacer cualquier aritmética de punteros que te guste. Solo porque p+2 parece a cualquier otra dirección no significa que sea válida. De hecho, en este caso, no lo es.

Tenga mucho cuidado cada vez que vea la aritmética de punteros que no va a salir de los límites asignados.