¿Qué son punteros cercanos, lejanos y enormes?


Puede alguien explicarme estos indicadores con un ejemplo adecuado ... ¿y cuando se usan estos punteros?

Author: bolov, 2010-08-26

6 answers

En los viejos tiempos, de acuerdo con el manual de Turbo C, un puntero cercano era meramente de 16 bits cuando todo el código y los datos cabían en el segmento. Un far pointer estaba compuesto por un segmento y un offset, pero no se realizó normalización. Y un puntero enorme se normalizó automáticamente. Dos punteros lejanos podrían posiblemente apuntar a la misma ubicación en la memoria, pero ser diferentes, mientras que los punteros enormes normalizados que apuntan a la misma ubicación de memoria siempre serían iguales.

 12
Author: PP.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-26 13:40:51

El ejemplo principal es la arquitectura Intel X86.

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

La solución de Intel fue crear "registros de segmento" de 16 bits cuyo contenido se desplazaría a la izquierda 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 se creó el concepto de segmento de 64 KiB. Por lo tanto, un puntero "cercano" solo 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" era de 32 bits (12345678h, DS seguido de DX) y siempre funcionaría (pero era más lento ya que tenía que cargar dos registros y luego restaurar el registro DS cuando haya terminado).

(Como señala supercat a continuación, un desplazamiento a DX que se desbordó se "volcaría" antes de que se agregue a DS a consigue la dirección final. Esto permitía desplazamientos de 16 bits para acceder a cualquier dirección en el segmento de 64 KiB, no solo la parte que estaba ± 32 KiB desde donde apuntaba DX, como se hace en otras arquitecturas con desplazamiento relativo de 16 bits en algunas instrucciones.)

Sin embargo, tenga en cuenta que podría tener dos punteros "lejanos" que son valores diferentes pero apuntan a la misma dirección. Por ejemplo, el puntero lejano 100079B8h apunta al mismo lugar que 12345678h. una operación no válida: los punteros pueden diferir, pero aún así apuntan al mismo lugar.

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

Motorola no tuvo este problema con su serie 6800 de procesadores, ya que estaban limitados a 64 KiB, Cuando crearon la arquitectura 68000, fueron directamente a registros de 32 bits, y por lo tanto nunca tuvieron necesidad de punteros cercanos, lejanos o enormes. (En cambio, su problema era que solo los 24 bits inferiores de la dirección realmente importaban, por lo que algunos programadores (notoriamente Apple) usaban los 8 bits altos como "indicadores", causando problemas cuando los buses de direcciones se expandían a 32 bits (4 GiB).)

Linus Torvalds solo resistió hasta el 80386, que ofrecía un "modo protegido" donde las direcciones eran 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 cosas de segmentos extraños, y es por eso que no tiene soporte de puntero cercano y lejano en Linux (y por qué ninguna empresa que diseñe una nueva arquitectura volverá a ellos si quieren soporte de Linux). Y se comieron los juglares de Robin, y hubo mucho regocijo. (Yay...)

 28
Author: Mike DeSimone,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-12-06 04:29:46

Diferencia entre punteros lejanos y enormes:

Como sabemos por defecto, los punteros son near por ejemplo: int *p es a near puntero. 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 al puntero al que se refiere. Una dirección que consiste solo en un desplazamiento tiene un rango de 0 - 64K bytes.

Far y huge punteros:

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

Limitación del puntero far:

No podemos cambiar o modificar la dirección del segmento de una dirección lejana dada aplicando cualquier operación aritmética sobre ella. Es decir, mediante el uso de operador aritmético no podemos saltar de un segmento a otro segmento.

Si incrementa la dirección lejana más allá del valor máximo de su dirección de desplazamiento en lugar de incrementar la dirección de segmento, repetirá su dirección de desplazamiento en orden cíclico. Esto también se llama envoltura, es decir, si el desplazamiento es 0xffff y agregamos 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 punteros enormes y lejanos:

1.Cuando un puntero lejano es incrementado o decrementado SOLO el desplazamiento del puntero es realmente incrementado o decrementado, pero en caso de un puntero enorme, tanto el segmento como el valor de desplazamiento cambiarán.

Considere el siguiente Ejemplo, tomado de AQUÍ :

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

Entonces la salida es:

0000:0000

No hay cambio en el valor del segmento.

Y en caso de Punteros enormes :

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

La salida es:

0001:0000

Esto se debe a la operación de incremento no solo el valor de desplazamiento, pero el valor del segmento también cambian.Eso significa que el segmento no cambiará en el caso de los 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 los desplazamientos son compared.In en otras palabras, los operadores relacionales solo funcionarán en punteros lejanos si los valores de segmento de los punteros que se comparan son los mismos. Y en caso de enorme esto no sucederá, en realidad la comparación de direcciones absolutas toma lugar.Vamos a 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
{[48] {} En[18]} 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 para ambos p y p1 es 12341 (1234*10+1 o 1230*10+41) pero no se consideran iguales en el 1er caso porque en el caso de far los punteros solo se comparan las compensaciones, es decir, verificará si 0001==0041. Lo cual es falso.

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

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

    Supongamos que si tenemos 0x1234:1234 entonces la forma normalizada de la misma es 0x1357:0004 (la dirección absoluta es 13574). Un puntero enorme se normaliza solo cuando se realiza alguna operación aritmética en y no normalizado 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;
     }
    

    Salida:

    h=1234:1234
    
    h1=1357:0005
    

    Explicación:huge el puntero no se normaliza en caso de asignación.Pero si se realiza una operación aritmética en él, será normalized.So, h es 1234:1234 y h1 es 1357:0005 que se normaliza.

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

    Tomemos 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 el 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 incrementamos far pointer en 1, será 0000:0010.Y como incrementamos puntero enorme por 1 entonces será 0001:0000 porque es desplazamiento no puede ser mayor que 15 en otras palabras se normalizará.

 19
Author: Vikas Verma,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-06 14:33:22

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

Near: un puntero de 16 bits que puede direccionar cualquier byte en un segmento de 64k

Far: un puntero de 32 bits que contiene un segmento y un desplazamiento. Tenga en cuenta que debido a que los segmentos pueden superponerse, dos punteros lejanos diferentes pueden apuntar a la misma dirección.

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

Tee: una bebida con mermelada y pan.

Eso nos llevará de vuelta a doh oh oh oh

¿Y cuando se usan estos punteros?

En la década de 1980 y 90' hasta que las ventanas de 32 bits se hicieron omnipresentes,

 3
Author: JeremyP,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-26 14:04:24

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

En sistemas de 16 bits, los datos se particionaban 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 solo podía almacenar hasta 64 Kb de datos.

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

Los programas de 16 bits que tenían más de 64 kb de datos como requisito podían acceder a asignadores que devolverían un puntero LEJANO, que era un id de segmento de datos en los 16 bits superiores, y un puntero en ese segmento de datos, en los 16 bits inferiores.

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 almacenamiento de 32 bits-pero el asignador se ha encargado de organizar un rango de segmentos de datos, con ID consecutivos, de modo que simplemente incrementando el selector de segmentos de datos el siguiente fragmento de 64 Kb de datos puede ser alcanzado.

Los estándares subyacentes del 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++ tienen el mismo tamaño. Así que los atributos CERCANOS, LEJANOS y ENORMES eran extensiones proporcionadas por los diversos proveedores de compiladores.

 2
Author: Chris Becke,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-26 13:52:38

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 puede apuntar a un subconjunto útil de cosas. Muchas personas han dado respuestas relacionadas con la arquitectura x86 de 16 bits. Varios tipos de punteros eran comunes en sistemas de 16 bits, aunque las distinciones de cerca/miedo podrían reaparecer en sistemas de 64 bits, dependiendo de cómo se implementen (no me sorprendería si muchos sistemas de desarrollo van a punteros de 64 bits para todo, a pesar del hecho de que en muchos casos será muy derrochador).

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

Algunos programadores se quejan de tener que distinguir entre memoria "cercana" y "lejana", pero en muchos casos hacer tales distinciones puede permitir a los compiladores producir mucho mejor código.

(nota: Incluso en muchos sistemas de 32 bits, se puede acceder a ciertas áreas de memoria directamente sin instrucciones adicionales, mientras que otras áreas no pueden. Si, por ejemplo, en un 68000 o un ARM, se mantiene un registro apuntando a la variable global almacenamiento, será posible cargar directamente cualquier variable dentro de los primeros 32K (68000) o 2K (ARM) de ese registro. Obtener una variable almacenada en otro lugar requerirá una instrucción adicional para calcular la dirección. Colocar variables de uso más frecuente en las regiones preferidas y avisar al compilador permitiría una generación de código más eficiente.

 2
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-08-26 16:18:56