2010-08-26 20 views

Respuesta

11

En los viejos tiempos, de acuerdo con el manual de Turbo C, un puntero cercano era simplemente 16 bits cuando todo su código y datos encajan en un segmento. Un puntero lejano estaba compuesto de un segmento así como un desplazamiento, pero no se realizó ninguna normalización. Y un gran puntero se normalizó automáticamente. Es posible que dos punteros lejanos indiquen la misma ubicación en la memoria pero que sean diferentes, mientras que los punteros enormes normalizados que apuntan a la misma ubicación de memoria siempre serán iguales.

+0

gracias ... bt pueden decirme ejemplo ... para que también se puedan usar en el presente. –

+7

@Vishwanath: No, no son realmente utilizables para el nuevo código. Solo eran para plataformas Intel de 16 bits, que fueron obsoletas hace mucho tiempo (creo que el Intel 386 fue el primer chip en admitir efectivamente el modelo de memoria plana de 32 bits). Si está escribiendo un código que tiene que preocuparse por esto, está escribiendo código heredado. –

+0

gracias PP y Billy ... lo tengo nw. –

2

Esta terminología se usó en arquitecturas de 16 bits.

En sistemas de 16 bits, los datos se dividieron en segmentos de 64 Kb. Cada módulo cargable (archivo de programa, biblioteca cargada dinámicamente, etc.) tenía un segmento de datos asociado, que podía almacenar hasta 64 Kb de datos solamente.

Un puntero NEAR era un puntero con almacenamiento de 16 bits, y se refería a los datos (solo) en el segmento de datos de los módulos actuales.

programas de 16 bits que tenían más de 64 KB de datos como requisito podría acceder asignadores especiales que devuelven un puntero FAR - que era un identificador de segmento de datos en los 16 bits superiores, y un puntero a ese segmento de datos, en la parte baja 16 bits

Sin embargo, los programas más grandes querrían tratar con más de 64Kb de datos contiguos. Un puntero ENORME se ve exactamente como un puntero lejano, tiene 32 bits de almacenamiento, pero el asignador se ha ocupado de organizar una gama de segmentos de datos con identificadores consecutivos, de modo que simplemente incrementando el selector de segmentos de datos el siguiente fragmento de datos de 64 Kb puede ser alcanzado.

Los estándares subyacentes de lenguaje C y C++ nunca reconocieron oficialmente estos conceptos en sus modelos de memoria: se supone que todos los punteros en un programa C o C++ son del mismo tamaño. Por lo tanto, los atributos NEAR, FAR y HUGE fueron extensiones proporcionadas por los diversos proveedores de compiladores.

3

Todas las cosas en esta respuesta son relevantes solo para el antiguo modelo de memoria segmentada 8086 y 80286.

cerca de: un puntero de 16 bits que puede hacer frente a cualquier byte en un segmento 64k

el momento: un puntero de 32 bits que contiene un segmento y un desplazamiento. Tenga en cuenta que, dado que los segmentos se pueden superponer, dos punteros lejanos pueden señalar la misma dirección.

huge: un puntero de 32 bits en el que el segmento está "normalizado" para que dos punteros lejanos apunten a la misma dirección a menos que tengan el mismo valor.

tee: una bebida con mermelada y pan.

que nos va a traer de vuelta a DOH oh oh oh

y cuando se utilizan estos indicadores?

en la década de 1980 y el 90' hasta 32 bits de Windows se convirtió en omnipresente,

+0

^^ por fin tengo algo en breve! –

28

El ejemplo principal es la arquitectura Intel X86.

Intel 8086 era, internamente, un procesador de 16 bits: todos sus registros tenían 16 bits de ancho. Sin embargo, el bus de direcciones tenía 20 bits de ancho (1 MiB).Esto significaba que no podía mantener una dirección completa en un registro, lo que lo limita a los primeros 64 kB.

La solución de Intel consistía en crear "registros de segmento" de 16 bits cuyo contenido se desplazaría cuatro bits y se agregaría a la dirección. Por ejemplo:

DS ("Data Segment") register: 1234 h 
DX ("D eXtended") register: + 5678h 
           ------ 
Actual address read:   179B8h 

Esto creó el concepto de segmento de 64 Kib. Por lo tanto, un puntero "cercano" sería el contenido del registro DX (5678h), y no sería válido a menos que el registro DS ya estuviera configurado correctamente, mientras que un puntero "lejano" tuviera 32 bits (12345678h, DS seguido de DX) y siempre funcionaría (pero fue más lento ya que tenía que cargar dos registros y luego restaurar el registro DS cuando terminó).

(Como supercat observa a continuación, un desplazamiento a DX que desbordó sería "roll over" antes de ser añadido a DS para obtener la dirección final. Esto permitió desplazamientos de 16 bits para acceder a cualquier dirección en el segmento de 64 Kib, no solo la parte que era ± 32 kB desde donde apuntaba DX, como se hace en otras arquitecturas con direccionamiento de desplazamiento relativo de 16 bits en algunas instrucciones).

Sin embargo, tenga en cuenta que podría tener dos punteros "lejanos" que diferentes valores pero apuntan a la misma dirección. Por ejemplo, el puntero lejano 100079B8h apunta al mismo lugar que 12345678h. Por lo tanto, la comparación de puntero en punteros lejanos era una operación no válida: los punteros podían diferir, pero aún señalar el mismo lugar.

Aquí fue donde decidí que los Mac (con los procesadores Motorola 68000 en ese momento) no eran tan malos después de todo, así que me perdí grandes punteros. IIRC, solo eran punteros que garantizaban que todos los bits superpuestos en los registros de segmentos eran 0, como en el segundo ejemplo.

Motorola no tuvo este problema con su serie 6800 de procesadores, ya que estaban limitados a 64 kb. Cuando crearon la arquitectura 68000, pasaron directamente a registros de 32 bits y nunca tuvieron necesidad de cerca, lejos , o grandes punteros. (En cambio, su problema era que solo los últimos 24 bits de la dirección realmente importaban, por lo que algunos programadores (notoriamente Apple) usarían los 8 bits altos como "indicadores de puntero", causando problemas cuando los buses de direcciones se expandieron a 32 bits (4 GiB) .)

Linus Torvalds simplemente se mantuvo hasta el 80386, que ofrecía un "modo protegido" donde las direcciones eran de 32 bits, y los registros de segmento eran la mitad alta de la dirección, y no se necesitaba ninguna adición, y escribió Linux desde el principio para usar solo el modo protegido, sin elementos de segmento extraño, y es por eso que no tienes soporte de puntero cercano y lejano en Linux (y por qué ninguna compañía que diseña una nueva arquitectura volverá a ellos si quieren soporte de Linux) . Y comieron los juglares de Robin, y hubo mucho regocijo. (Yay ...)

+0

Curiosamente, muchos programas basados ​​en 68000 terminaron con limitaciones de 32 K en lugares donde el software 8088 tendría limitaciones de 64 KB. Dado que la memoria era a menudo escasa, y que el 8088 podía funcionar con cantidades de 16 bits en lugares donde el 68000 requeriría usar cantidades de 32 bits o aceptar límites de 32 K, el 8088 era en realidad un diseño notablemente práctico. – supercat

+0

El "límite de 32k" para el 68k fue cuando usó un desplazamiento relativo de 16 bits para ramas y saltos. Si no intenta ordenar las funciones en el segmento del código para tratar de mantener las cosas juntas, simplemente se daría por vencido y abofeteará un límite de 32k en todo cuando podría haber tenido un límite de 64k. Todavía es menos un desastre que el sistema Intel donde los punteros de diferentes valores podrían ser iguales. De nuevo, ahora todo es historia, e incluso los sistemas integrados no tienen problemas para hacer el direccionamiento de 32 bits en estos días. –

+0

En el Macintosh clásico, muchas estructuras de datos estaban limitadas a 32 K porque el sistema operativo optó por utilizar tipos de 16 bits para muchos propósitos en lugar de tipos de 32 bits; Esperaría que lo mismo fuera cierto con una gran cantidad de software de memoria limitada para la plataforma 68000 (especialmente las variantes de bus de 16 bits). Las instrucciones addr + disp16 del 68000 apuntan y extienden el desplazamiento para permitir tanto el desplazamiento positivo como el negativo. El 8088, sin embargo, puede permitir que un desplazamiento de 16 bits alcance +/- 65535 bytes dentro de un único objeto, en lugar de +/- 32767 bytes, si el desplazamiento se ajusta dentro de un segmento. – supercat

2

En algunas arquitecturas, un puntero que puede apuntar a cada objeto en el sistema será más grande y más lento para trabajar que uno que pueda apuntar a un subconjunto útil de cosas. Muchas personas dieron respuestas relacionadas con la arquitectura x86 de 16 bits. Varios tipos de punteros eran comunes en los sistemas de 16 bits, aunque las distinciones de casi/miedo podrían reaparecer en sistemas de 64 bits, dependiendo de cómo se implementen (no me sorprendería que muchos sistemas de desarrollo vayan a punteros de 64 bits para todo, a pesar de que en muchos casos será un gran desperdicio).

En muchos programas, es bastante fácil subdividir el uso de la memoria en dos categorías: pequeñas cosas que juntas suman una cantidad bastante pequeña (64K o 4GB) pero se accederá a menudo, y cosas más grandes que pueden sumar a una cantidad mucho mayor, pero a la que no se necesita acceder tan a menudo. Cuando una aplicación necesita trabajar con parte de un objeto en el área de "cosas grandes", copia esa parte en el área de "cosas pequeñas", trabaja con ella y, si es necesario, la vuelve a escribir.

Algunos programadores se quejan por tener que distinguir entre la memoria "cerca" y "lejos", pero en muchos casos hacen tales distinciones pueden permitir que los compiladores para producir mucho mejor código.

(nota: Incluso en muchos sistemas de 32 bits, ciertas áreas de la memoria se puede acceder directamente sin instrucciones adicionales, mientras que otras áreas no puede Si, por ejemplo, en una o un brazo 68000, se tiene un registro que apunta a. almacenamiento de variable global, será posible cargar directamente cualquier variable dentro de los primeros 32K (68000) o 2K (ARM) de ese registro. La recuperación de una variable almacenada en otro lugar requerirá una instrucción adicional para calcular la dirección. Colocación de variables más frecuentemente utilizadas en las regiones preferidas y dejar que el compilador de conocimientos permitiría la generación de código más eficiente

+0

¿Qué significa que el puntero grande se normalizó automáticamente mientras que el puntero distante no? ¿Qué significa la palabra ** "normalizar" ** aquí? – Destructor

+0

@Destructor: cada puntero en el 8086 tiene dos partes de 16 bits, un segmento que es difícil de manipular y un desplazamiento que se puede manipular mucho más cómodamente. Las direcciones de hardware se toman multiplicando el segmento por 16 y agregando el desplazamiento. Los objetos de hasta 65536 bytes que están alineados en límites de 16 bytes pueden manipularse fácilmente configurando el segmento para que identifique el inicio del objeto y luego usar el desplazamiento para acceder a las ubicaciones dentro de él, pero el hecho de que cada ubicación se puede identificar 4096 diferentes maneras a veces pueden ser problemáticas. – supercat

+0

@Destructor: La normalización de un puntero significa reemplazarlo con un puntero que identifica la misma ubicación física pero tiene un desplazamiento en el rango 0-15. Para algunos patrones de uso, los operadores relacionales y la aritmética de punteros que ignoran el segmento en conjunto cumplirán con el Estándar C y funcionarán, pero implicaría que uno puede tener dos punteros diferentes, cuya diferencia es cero, y ninguno de los cuales es mayor que el otro, pero que son, sin embargo, desiguales y que acceden a cosas diferentes. Tener operadores relacionales tratar los punteros como valores de 32 bits (con segmento como la palabra superior) ... – supercat

18

Diferencia entre punteros y enormes:.

Como sabemos de manera predeterminada, los punteros son near por ejemplo: int *p es un puntero near. El tamaño del puntero near es de 2 bytes en el caso del compilador de 16 bits. Y ya sabemos muy bien que el tamaño varía de compilador a compilador; solo almacenan el desplazamiento de la dirección del puntero al que hace referencia. Una dirección que consta de solo un desplazamiento tiene un rango de 0 a 64 k bytes.

Farhuge y punteros:

Farhuge y punteros tienen un tamaño de 4 bytes. Almacenan el segmento y el desplazamiento de la dirección a la que hace referencia el puntero. Entonces, ¿cuál es la diferencia entre ellos?

Limitación de más puntero:

No podemos cambiar o modificar la dirección del segmento de dirección de momento determinado mediante la aplicación de cualquier operación aritmética en él. Es decir, mediante el uso de operador aritmético no podemos saltar de un segmento a otro segmento.

Si va a incrementar la dirección mucho más allá del valor máximo de su dirección de desplazamiento en lugar de incrementar la dirección del segmento se repetirá su dirección de desplazamiento en orden cíclico. Esto también se llama envoltura, es decir, si offset es 0xffff y añadir 1 entonces es 0x0000 y de manera similar si disminuimos 0x0000 por 1, entonces es 0xffff y recuerde que no hay cambio en el segmento.

Ahora voy a comparar grandes y lejanos punteros:

1. Cuando un puntero lejos se incrementa o decrementa SOLO el desplazamiento del puntero es en realidad aumentada o disminuida, pero en caso de puntero grande tanto el valor de segmento como el de desplazamiento cambiarán.

Considérese el siguiente ejemplo, tomado de HERE:

int main() 
    { 
    char far* f=(char far*)0x0000ffff; 
    printf("%Fp",f+0x1); 
    return 0; 
    } 

entonces la salida es:

0000:0000 

No hay ningún cambio en el valor de segmento.

Y en caso de punteros grandes:

int main() 
{ 
char huge* h=(char huge*)0x0000000f; 
printf("%Fp",h+0x1); 
return 0; 
} 

la salida es:

0001:0000 

Esto es debido a la operación de incremento no sólo valor de desplazamiento, pero el valor segmento también significa change.That segmento no lo hará cambio en caso de punteros far pero en el caso del puntero huge, puede moverse de un segmento a otro.

2. Cuando los operadores relacionales se utilizan en punteros lejanos, solo se comparan los desplazamientos. En otras palabras, los operadores relacionales solo trabajarán en punteros lejanos si los valores de segmento de los punteros que se comparan son los mismos. Y en caso de gran esto no sucederá, en realidad comparación de direcciones absolutas nos lleva place.Let entender con la ayuda de un ejemplo de far puntero:

int main() 
{ 
char far * p=(char far*)0x12340001; 
char far* p1=(char far*)0x12300041; 
if(p==p1) 
printf("same"); 
else 
printf("different"); 
return 0; 
} 

Salida:

different 

En huge puntero :

int main() 
{ 
char huge * p=(char huge*)0x12340001; 
char huge* p1=(char huge*)0x12300041; 
if(p==p1) 
printf("same"); 
else 
printf("different"); 
return 0; 
} 

salida:

same 

Explicación: Como vemos la dirección absoluta tanto para p y p1 es 12341 (1234*10+1 o 1230*10+41) pero no se consideran iguales en el primero caso, ya que en caso de far punteros sólo se comparan las compensaciones es decir, se comprobará si 0001==0041. Que es falso

Y en el caso de los punteros grandes, la operación de comparación se realiza en direcciones absolutas que son iguales.

  1. Un puntero ahora nunca está normalizado, pero un puntero huge se normaliza. Un puntero normalizado es uno que tiene tanto de la dirección como sea posible en el segmento, lo que significa que el desplazamiento nunca es mayor que 15.

    suponer si tenemos 0x1234:1234 entonces la forma normalizada de la misma es 0x1357:0004 (dirección absoluta es 13574) Un puntero grande se normaliza solo cuando se realiza una operación aritmética en él, y no se normaliza durante la asignación.

    int main() 
    { 
        char huge* h=(char huge*)0x12341234; 
        char huge* h1=(char huge*)0x12341234; 
        printf("h=%Fp\nh1=%Fp",h,h1+0x1); 
        return 0; 
    } 
    

    de salida:

    h=1234:1234 
    
    h1=1357:0005 
    

    Explicación: huge puntero no se normaliza en caso de assignment.But si una operación aritmética se realiza sobre el mismo, será normalized.So, h es 1234:1234 y h1 es 1357:0005 que está normalizado.

    4.El desplazamiento del puntero grande es menor que 16 debido a la normalización y no es así en el caso de los punteros lejanos.

    permite echar un ejemplo para entender lo que quiero decir:

    int main() 
        { 
        char far* f=(char far*)0x0000000f; 
        printf("%Fp",f+0x1); 
        return 0; 
        } 
    

Salida:

0000:0010 

En caso de huge puntero:

 int main() 
     { 
     char huge* h=(char huge*)0x0000000f; 
     printf("%Fp",h+0x1); 
     return 0; 
     } 

     Output: 
     0001:0000 

Explicación: a medida que aumentamos nt far puntero por 1 será 0000:0010. Y a medida que incrementemos el puntero enorme en 1, entonces será 0001:0000 porque su desplazamiento no puede ser mayor que 15, en otras palabras, se normalizará.

+1

+1 por contenido. Asegúrese de formatear su respuesta correctamente la próxima vez. –

+0

Finalmente entiendo las diferencias. Gracias. – hkBattousai