Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: digimikeh en 2 Mayo 2019, 17:48 pm



Título: Representación visual de punteros y punteros a punteros..
Publicado por: digimikeh en 2 Mayo 2019, 17:48 pm
Muy buenas..

Entiendo como funcionan los punteros y los punteros a punteros (según el dibujo adjunto que he realizado, si hay algo malo, corregidme por favor)..

(https://i.imgur.com/coBDA5C.jpg)

Pero como quedaría dibujada el siguiente puntero doble?

Código
  1. int ** matriz = new int[10][10];
  2.  

Es un puntero a un puntero, pero almacena una matriz bidimensional, no logro visualizar como sería el mapa en la memoria... como seria?

Gracias.
Saludos..


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: RayR en 2 Mayo 2019, 19:42 pm
No quedaría de ninguna forma, porque tu código ni siquiera compilaría. Sería más bien:

Código:
int **matriz = new int*[10];
for(int i = 0; i < 10; i++)
    matriz[i] = new int[10];

Lo que tendrías es: el puntero matriz en la pila, apuntando al inicio de un bloque (localizado en el heap) de 10 punteros, cada uno de los cuales apuntaría al inicio de un bloque de 10 enteros, ubicados también en el heap.


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: digimikeh en 2 Mayo 2019, 20:18 pm
Gracias..

Entonces la representación visual sería así?

(https://i.imgur.com/YDluw7U.jpg)


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: Serapis en 2 Mayo 2019, 20:39 pm
No. Esto último sería una matriz de matrices, no una matriz bidimensional.

Una matriz bidimensional es lo mismo que una matriz unidimensional, solo que está particionado el espacio:

Matriz unidimensional de 16 elementos:  0-15
|00,01,02,03,04,5,06,07,08,09,10,11,12,13,14,15|

Matriz bidimensional de 4x4 elementos: =0-3x0-3
|00,01,02,03|00,01,02,03|00,01,02,03|00,01,02,03|
Así calcular el puntero absoluto de una dimensión es realtivo al puntero absoluto del primer ítem del array...

Además piensa que el puntero, apunta a una estructura y es en dicha estructura donde se apunta al primer elemento de la matriz.
Sería algo así:
array x ----> estructura safeArray ----> puntero al array.

La estructura mantiene los datos básicos del array. Como el tipo de datos que aloja y cuantos bytes por elemento utiliza así como un simple array de las dimensiones en que se subdivide el array, etc...

Código:
typedef struct tagSAFEARRAY {
  USHORT         cDims;
  USHORT         fFeatures;
  ULONG          cbElements;
  ULONG          cLocks;
  PVOID          pvData;   // puntero a los datos del array
  SAFEARRAYBOUND rgsabound[1]; // otra estructura con dos elementos, el valor del índice menor y la cantidad de elementos.
} SAFEARRAY, *LPSAFEARRAY;


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: Loretz en 2 Mayo 2019, 20:49 pm
Aprovechando que este es un tema un poco recurrente, creo que este programita puede ayudar a aclarar un poco dónde están las cosas. Puedes convertir las salidas en hexadecimal a decimal si te resulta más cómodo (calculadora de Windows o la que te guste) y ver que los punteros están realmente lejos unos de otros.

Código
  1. #include <iostream>
  2.  
  3. int main()
  4. {
  5.    const char* puntero_probablemente_a_zona_de_solo_lectura = "hola";  // segmento "data" en el ejecutable
  6.    std::cout << "apunta al string literal: \"" << puntero_probablemente_a_zona_de_solo_lectura << "\"\n";
  7.    std::cout << "su direccion es: "
  8.        << (void*)puntero_probablemente_a_zona_de_solo_lectura << "\n\n";
  9.  
  10.    const char* puntero_a_heap = new char[6] { "adios" };  // asignación "dinámica" en heap, usa new / delete
  11.    std::cout << "apunta a la C string en heap: \"" << puntero_a_heap << "\"\n";
  12.    std::cout << "su direccion es: "
  13.        << (void*)puntero_a_heap << "\n\n";
  14.    delete[] puntero_a_heap;
  15.  
  16.    char ch = 'a';
  17.    const char* puntero_a_stack = &ch;  // en el stack frame de main
  18.    std::cout << "apunta al char en stack: \'" << *puntero_a_stack << "\'\n";
  19.    std::cout << "su direccion es: "
  20.        << (void*)puntero_a_stack << "\n\n";
  21.  
  22. }



Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: RayR en 2 Mayo 2019, 20:50 pm
Sí, sería como la pusiste. Obviamente estamos hablando de punteros, y en concreto, un puntero a punteros para simular matrices de manera dinámica. En una matriz "real" (arreglo bidimensional):

Código:
int matriz[10][10];

la memoria es toda contigua y existe en la pila, pero evidentemente, y desde que estás hablado de punteros a punteros, no es a lo que te refieres. A menos que te haya entendido mal.


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: Loretz en 2 Mayo 2019, 21:09 pm
RayR; hay una falta de ortografía en tu ejemplo:

Código:
int **matriz = new int*[10];
for(int i = 0; i < 10; i++)
    matriz[i] = new int[10];

debería ser:
Código:
int** matriz = new int* [10];
    for (int i = 0; i < 10; i++)
        matriz[i] = new int; // sin el [10]

Amplío un poco el ejemplo que muestra un ciclo completo:

Código:
#include <iostream>

int main()
{
    // puntero a un array de 10 punteros a int
    int** matriz = new int* [10];
    for (int i = 0; i < 10; i++)
        matriz[i] = new int{i};  // cada uno de esos 10 punteros con valores inicializados con el indice i.

    // a ver...
    for (int i = 0; i < 10; ++i)
        std::cout << *matriz[i] << '\n';
    std::cout << '\n';

    // deletes:
    for (int i = 0; i < 10; ++i)
        delete[] matriz[i];
    delete[] matriz;
   
}


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: RayR en 2 Mayo 2019, 21:20 pm
Código:
[quote author=Loretz link=topic=495255.msg2191896#msg2191896 date=1556824168]
RayR; hay una falta de ortografía en tu ejemplo:

No. Es tal cuál lo puse. Reitero, yo me estoy guiando en lo que digimikeh pidió, que es un tamaño de 10x10.


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: Loretz en 2 Mayo 2019, 22:36 pm

No. Es tal cuál lo puse. Reitero, yo me estoy guiando en lo que digimikeh pidió, que es un tamaño de 10x10.

Ah, Ok, cierto.


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: digimikeh en 3 Mayo 2019, 15:51 pm
Perfect.. perfect, me queda todo muy claro...

Y que pasa en el caso de los punteros a funciones?.. al no usar el operador new, dudo que un puntero a función tenga que apuntar a la memoria dinámica... entonces estaría apuntando a una dirección de la pila o de algún registro o algo parecido?

Código
  1.  
  2. int sumar (int x, int y){
  3.     return x + y;
  4. }
  5.  
  6. int (* pSumar) (int)(int) = sumar;
  7.  

pSumar en este caso esta apuntando siempre a una direccion del stack?..


Gracias nuevamente..


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: RayR en 3 Mayo 2019, 18:01 pm
La cuestión con los punteros a funciones es que depende del compilador, sistema operativo, etc. a dónde apunten. La respuesta obvia sería "a la dirección de la función", pero qué es exactamente eso o en dónde existe, depende de la implementación. Lo que sí podrías dar por hecho es que las funciones no viven ni en la pila ni en el heap. Típicamente las instrucciones ejecutables se colocan en un parte de la memoria conocida como el segmento de texto (a veces llamado segmento de código), que suele ser de sólo lectura.


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: digimikeh en 3 Mayo 2019, 23:57 pm
Vale, entonces se aloja en una especie de registro...

Ya se han aclarado varias dudas
Gracias


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: Loretz en 4 Mayo 2019, 00:09 am
En la línea:
Código:
int (* pSumar) (int)(int) = sumar;
hay un error, debería ser:
Código:
int (*pSumar) (int, int) = &sumar; // el & delante no es extrictamente necesario pero conviene


Citar
Vale, entonces se aloja en una especie de registro...
Tampoco.

Como te decía RayR, la dirección de una función (no de una "función miembro", recuerdas?) suele estar en memoria de sólo lectura (que en inglés la vas a encontrar como "code segment" o "text segment"). Entonces, un puntero a función va a apuntar a esa parte de la memoria que no es stack ni heap, sino la parte donde está el código del programa cuando se carga en memoria.

En algún momento también se mencionó que los "string literals" ("hola mundo" por ejemplo) también estaban en un sector especial de memoria de solo lectura, el "data segment", que tampo es stack ni heap.

Con respecto a la Call Stak, Hay un artículo en Wikipedia que vale la pena ser leído:
https://en.wikipedia.org/wiki/Call_stack (https://en.wikipedia.org/wiki/Call_stack)

Por si no quedara del todo claro, no hay que perder de vista que cuando se habla de la "memoria" se trata de la "virtual address space". Creo que aquí encontrarás respuesta a las dudas que ni siquiera sabías que debías tener:
https://en.wikipedia.org/wiki/Virtual_address_space (https://en.wikipedia.org/wiki/Virtual_address_space)

[Nota]
También es cierto que estas representaciones corresponden a un modelo, el "C Abstract Machine Model" [http://web.stanford.edu/group/sequoia/cgi-bin/book/export/html/15 (http://web.stanford.edu/group/sequoia/cgi-bin/book/export/html/15)], que es un modelo abstracto que los compiladores se encargan de trasladar a la máquina real.
Habitualmente los microprocesadores suelen tener niveles de memoria caché y evitan acceden directamente la disco o a la memoria RAM, y en un sistema suele haber más de un microprocesador con más de un "core" cada uno, y es frecuente que compartan los mismos datos, figúrate. Apropósito de esto, el C++ define su "memory model" https://en.cppreference.com/w/cpp/language/memory_model (https://en.cppreference.com/w/cpp/language/memory_model)
[/Nota]


Título: Re: Representación visual de punteros y punteros a punteros..
Publicado por: RayR en 4 Mayo 2019, 01:54 am
Tal cual te lo explicó Loretz. Creo que yo te confundí cuando te dije que dependía. Pasa que C deja muchas cosas abiertas a que los compiladores las implementen como quieran, siempre que cumplan con ciertas condiciones. En el caso de los punteros a funciones, sé que al menos en algún compilador viejo de C, no apuntaban directamente a la dirección de la función, sino a una estructura rara y de ahí ya la localizaban. En realidad, con los compiladores/arquitecturas actuales, puedes estar bastante seguro de que apuntan a la dirección de la función en el segmento de código. Es sólo que el pedante que a veces habita en mí sale y dice: ¡no necesariamente!  :D. Eso sí, no olvides que esto se refiere a punteros a funciones; en el caso de funciones miembro, como dice Loretz, es algo muy diferente y depende totalmente de cada implementación.

Por cierto, los ejemplos que habías puesto al principio, donde los punteros estaban en la pila, son correctos siempre que los punteros sean variables locales no static. Las variables globales y las variables locales declaradas como static, (llamémoslas de forma general, variables de duración estática), se localizan en el segmento de datos. Para ser más específicos (dado que puede que leas sobre esto en otro lado y te puedas confundir), en el archivo compilado, las variables de duración estática inicializadas (ejemplo: static int n = 5;) se colocan en la sección data. Las variables de duración estática no inicializadas no se suelen almacenar en el archivo binario (dado que no tienen ningún valor específico, no tendría caso ocupar espacio) sino que se coloca su tamaño conjunto en una sección llamada bss. Cuando el programa se ejecuta, los datos inicializados se colocan en el segmento de datos (data segment). En cuanto a los no inicializados, se reserva la cantidad de espacio que bss especifica, y se inicializan a cero, también en el segmento data. Porque una vez que el programa está en ejecución ya no se suele hacer distinción entre data y bss: todo se encuentra en data.

Editado: no sé qué pasó que mi mensaje había quedado hecho un lío. En fin, en resumen, si los punteros fueran globales o locales static, se encontrarían en el segmento de datos. Los datos apuntados por ellos, naturalmente, no se verían afectados.