¿Qué problemas de programación se resuelven mejor con punteros? [cerrado]


58

Bueno, básicamente entiendo cómo usar punteros, pero no la mejor manera de usarlos para hacer una mejor programación.

¿Cuáles son los buenos proyectos o problemas para resolver que involucran el uso de punteros para poder entenderlos mejor?


Pregunte qué problemas de programación se resuelven mejor con punteros, no qué proyectos / programas. Puede escribir un programa usando punteros o no, ellos (proyectos) son simplemente una construcción demasiado grande para una respuesta significativa.
Finalizado el

37
Los problemas de programación se crean utilizando punteros, no resueltos. :)
xpda

44
En realidad, me pregunto qué problemas se pueden resolver sin puntero. Muy pocos a ciencia cierta.
deadalnix

44
@deadalnix, depende si quiere decir punteros explícitos o punteros implícitos. Si te refieres a punteros implícitos (es decir, hay punteros usados ​​en alguna parte), entonces sería imposible escribir cualquier programa que use la pila o el montón. Si te refieres a punteros explícitos, entonces no tienes restricciones, puedes hacer cualquier tamaño de asignación creando nuevos marcos de pila (básicamente usando una llamada de función) y desasignación usando llamadas de cola, suponiendo que el compilador los optimice.
dan_waterworth

3
uf, algunas de estas respuestas son horribles.

Respuestas:


68

La manipulación de grandes cantidades de datos en la memoria es donde realmente brillan los punteros.

Pasar un objeto grande por referencia es equivalente a pasar un número antiguo simple. Puede manipular las partes necesarias directamente en lugar de copiar un objeto, alterarlo y luego devolver la copia para colocarla en lugar del original.


12
Esta es una gran respuesta y agregaría que no necesita objetos grandes para manipular grandes cantidades de datos; ocasionalmente manipular objetos grandes lo hará, pero un problema aún más común es manipular un montón de objetos pequeños con mucha frecuencia. Lo cual, si lo piensa, es casi todos los programas de computadora.
jhocking

44

El concepto de puntero le permite referirse a los datos por dirección sin duplicar el almacenamiento de datos. Este enfoque permite escribir algoritmos eficientes como:

  1. Clasificación
    Al mover datos en un algoritmo de clasificación, puede mover el puntero en lugar de los datos en sí mismos; piense en ordenar millones de filas en una cadena de 100 caracteres; ahorras muchos movimientos de datos innecesarios.

  2. Listas vinculadas
    Puede almacenar la ubicación del elemento siguiente y / o anterior y no todos los datos asociados con el registro.

  3. Paso de parámetros
    En este caso, pasa la dirección de los datos en lugar de los datos en sí. Nuevamente, piense en un algoritmo de compresión de nombres que se ejecute en millones de filas.

El concepto puede extenderse a estructuras de datos como bases de datos relacionales donde un puntero es similar a una clave foránea . Algunos lenguajes no fomentan el uso de punteros como C # y COBOL.

Se pueden encontrar ejemplos en muchos lugares como:

La siguiente publicación puede ser relevante de alguna manera:


14
No diría que C # no fomenta el uso de punteros; en cambio, no fomenta el uso de punteros no administrados : hay muchas cosas en C # que se pasan por referencia en lugar de valor.
Blakomen

Buena, buena explicación
Sabby

55
Tuve que votar a favor del comentario de C #. Las "referencias" de C # son equivalentes a los punteros, es solo que C # lo abstrae de saber que realmente solo está tratando con un puntero a un objeto en el montón administrado.
MattDavey

1
@MattDavey: las referencias de C # no son punteros. No puede agregar desde ellos, usarlos para indexar una matriz, usar indirección múltiple, etc. No puede tener referencias a referencias, por ejemplo.
Billy ONeal

55
@Billy ONeal la idea de que "es solo un puntero si puedes hacer aritmética" es francamente ridícula, pero no tengo la paciencia para discutir contigo.
MattDavey

29

Me sorprende que ninguna otra respuesta haya mencionado esto: los punteros le permiten crear estructuras de datos no contiguas y no lineales, en las que un elemento puede estar relacionado con muchos otros de formas complejas.

Las listas vinculadas (individual, doble y circularmente vinculadas), árboles (rojo-negro, AVL, trie, binario, partición espacial ...) y gráficos son ejemplos de estructuras que se pueden construir de forma más natural en términos de referencias en lugar de solo valores .


Estás olvidando la lista vinculada XOR [=
dan_waterworth

@dan_waterworth: No. Sé que la magia negra es demasiado buena.
Jon Purdy

8

Una forma simple es en el polimorfismo. El polimorfismo solo funciona con punteros.

Además, utiliza punteros cada vez que necesita una asignación de memoria dinámica. En C, esto generalmente sucede cuando necesita almacenar datos en una matriz, pero no conoce el tamaño en el momento de la compilación. Luego llamaría a malloc para asignar la memoria y un puntero para acceder a ella. Además, lo sepa o no, cuando usa una matriz, usa punteros.

for(int i = 0; i < size; i++)
   std::cout << array[i];

es el equivalente de

for(int i = 0; i < size; i++)
   std::cout << *(array + i);

Este conocimiento le permite hacer cosas realmente geniales como copiar una matriz completa en una línea:

while( (*array1++ = *array2++) != '\0')

En c ++, usa new para asignar memoria para un objeto y almacenarlo en un puntero. Hace esto cada vez que necesita crear un objeto durante el tiempo de ejecución en lugar de durante el tiempo de compilación (es decir, un método crea un nuevo objeto y lo almacena en una lista).

Para entender mejor los punteros:

  1. Encuentre algunos proyectos que jueguen con las cadenas en C tradicionales y las matrices.
  2. Encuentra algunos proyectos que usan herencia.

  3. Aquí está el proyecto en el que me corté los dientes:

Lea en dos matrices nxn de un archivo y realice las operaciones básicas de espacio vectorial en ellas e imprima su resultado en la pantalla.

Para hacer esto, debe usar matrices dinámicas y consultar sus matrices por punteros, ya que tendrá dos matrices de matrices (matrices dinámicas multidimensionales). Después de terminar ese proyecto, tendrá una muy buena idea de cómo usar punteros.


2
"El polimorfismo solo funciona con punteros". No es preciso: el polimorfismo también funciona con referencias. Que son, en última instancia, punteros, pero creo que la distinción es importante, especialmente para los novatos.
Laurent Pireyn

su ejemplo para copiar una matriz en una línea es realmente para copiar una cadena terminada en cero en una línea, no una matriz general.
tcrosley

8

Para comprender realmente por qué los punteros son importantes, debe comprender la diferencia entre la asignación de montón y la asignación de pila.

El siguiente es un ejemplo de una asignación de pila:

struct Foo {
  int bar, baz
};

void foo(void) {
  struct Foo f;
}

Los objetos asignados en la pila solo existen durante la ejecución de la función actual. Cuando la llamada a foofuera de alcance también lo hace la variable f.

Un caso en el que esto se convierte en un problema es cuando necesita devolver algo distinto de un tipo integral de una función (por ejemplo, la estructura Foo del ejemplo anterior).

Por ejemplo, la siguiente función daría como resultado el llamado "comportamiento indefinido".

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  struct Foo f;
  return &f;
}

Si desea devolver algo como struct Foo *de una función, lo que realmente necesita es una asignación de montón:

struct Foo {
  int bar, baz
};

struct Foo *foo(void) {
  return malloc(sizeof(struct Foo));
}

La función mallocasigna un objeto en el montón y devuelve un puntero a ese objeto. Tenga en cuenta que el término "objeto" se usa libremente aquí, que significa "algo" en lugar de objeto en el sentido de la programación orientada a objetos.

El programador controla la vida útil de los objetos asignados en el montón. La memoria para este objeto estará reservada hasta que el programador la libere, es decir, llamando free()o hasta que el programa salga.

Editar : no noté que esta pregunta está etiquetada como una pregunta de C ++. Los operadores de C ++ newy new[]realizan la misma función que malloc. Los operadores deletey delete[]son análogos a free. Si bien newy deletedebe usarse exclusivamente para asignar y liberar objetos C ++, el uso de mallocy freees perfectamente legal en el código C ++.


1
(+1) también conocido como "variables asignadas estáticas" y "variables asignadas dinámicas" ;-)
umlcat

4

Escriba cualquier proyecto no trivial en C y tendrá que averiguar cómo / cuándo usar punteros. En C ++, utilizará principalmente objetos habilitados para RAII que administran punteros internamente, pero en C los punteros sin formato tienen un papel mucho más frecuente. En cuanto a qué tipo de proyecto debe hacer, puede ser algo no trivial:

  • Un servidor web pequeño y simple
  • Herramientas de línea de comandos de Unix (less, cat, sort, etc.)
  • Algún proyecto real que quieras hacer por sí mismo y no solo para aprender

Recomiendo el último


Entonces, ¿voy por un proyecto que quiero hacer (como un juego 2D) y se supone que necesitaré y aprenderé punteros?
dysoco

3
En mi experiencia, hacer proyectos reales que están ligeramente fuera de su alcance en cuanto a habilidades es la mejor manera de aprender. Los conceptos se pueden aprender leyendo y escribiendo pequeños programas de "juguetes". Sin embargo, para aprender realmente cómo puede usar esos conceptos para escribir mejores programas, simplemente tiene que escribir programas que no sean de juguete.
Viktor Dahl

3

Casi todos los problemas de programación que se pueden resolver con punteros se pueden resolver con otros tipos de referencias más seguros (no se refieren a referencias de C ++ , pero el concepto general de CS de tener una variable se refiere al valor de los datos almacenados en otro lugar).

Los punteros al ser una implementación específica de bajo nivel de referencias, donde puede manipular directamente las direcciones de memoria son muy potentes, pero pueden ser un poco peligrosos de usar (por ejemplo, señalar ubicaciones de memoria fuera del programa).

El beneficio de usar los punteros directamente es que serán un poco más rápidos al no tener que hacer ningún control de seguridad. Los lenguajes como Java que no implementan directamente punteros de estilo C sufrirán un ligero impacto en el rendimiento, pero reducirán muchos tipos de situaciones difíciles de depurar.

En cuanto a por qué necesita indirección, la lista es bastante larga, pero esencialmente las dos ideas clave son:

  1. La copia de valores de objetos grandes es lenta y hará que el objeto se almacene en RAM dos veces (potencialmente muy costoso), pero la copia por referencia es casi instantánea utilizando solo unos pocos bytes de RAM (para la dirección). Por ejemplo, supongamos que tiene ~ 1000 objetos grandes (cada uno de los cuales equivale aproximadamente a 1 MB de RAM) en la memoria, y su usuario debe poder seleccionar el objeto actual (sobre el cual actuará el usuario). Tener una variable selected_objectque sea una referencia a uno de los objetos es mucho más eficiente que copiar el valor del objeto actual en una nueva variable.
  2. Tener estructuras de datos complicadas que se refieren a otros objetos como listas enlazadas o árboles, donde cada elemento en la estructura de datos se refiere a otros elementos en la estructura de datos. El beneficio de referirse a otros elementos en la estructura de datos significa que no tiene que mover todos los elementos de la lista en la memoria solo porque insertó un nuevo elemento en el medio de la lista (puede tener inserciones de tiempo constante).

2

La manipulación de imágenes a nivel de píxel es casi siempre más fácil y rápida con punteros. A veces solo es posible utilizando punteros.


1
No creo que la afirmación "a veces solo sea posible utilizando punteros" sea cierta. Hay lenguajes que no exponen punteros al desarrollador, pero pueden usarse para resolver problemas en el mismo espacio.
Thomas Owens

Tal vez ... No tengo conocimiento de todos los idiomas disponibles, pero ciertamente no me gustaría escribir una rutina para aplicar un filtro de convolución a una imagen usando un lenguaje que no tenga punteros.
Dave Nay

@Thomas, si bien tienes razón, la pregunta es sobre c ++, que expone los punteros al desarrollador.
Jonathan Henson el

@ Jonathan Aún así, si puede resolver un problema en un idioma que no expone punteros, entonces también es posible resolver ese problema en un lenguaje que sí exponga punteros sin usar punteros. Eso hace que la primera oración sea verdadera, pero la segunda oración es falsa: hasta donde sé, no hay problemas que no puedan resolverse sin el uso de punteros.
Thomas Owens

1
Por el contrario, el procesamiento de imágenes a menudo implica "aritmética de coordenadas", por ejemplo, tomar el seno y el coseno de un ángulo y multiplicarlo con las coordenadas x e y. En otros casos, se necesita una replicación de píxeles envolvente o fuera del borde. En este sentido, la aritmética del puntero es bastante inadecuada.
rwong

1

Los punteros se utilizan en muchos lenguajes de programación debajo de la superficie sin molestar al usuario al respecto. C / C ++ solo te da acceso a ellos.

Cuándo usarlos: tan a menudo como sea posible, porque copiar datos es ineficiente. Cuándo no usarlos: cuando desee dos copias que se puedan cambiar individualmente. (Lo que básicamente terminará copiando el contenido del objeto_1 en otro lugar en la memoria y devolviendo un puntero, esta vez apuntando al objeto_2)


1

Los punteros son una parte esencial para la implementación de cualquier estructura de datos en C y las estructuras de datos son una parte esencial de cualquier programa no trivial.

Si desea saber por qué los punteros son tan vitales, le sugiero que aprenda qué es una lista vinculada e intente escribir una sin usar punteros. No le he planteado un desafío imposible (SUGERENCIA: los punteros se utilizan para hacer referencia a ubicaciones en la memoria, ¿cómo hace referencia a las cosas en las matrices?).


+1 por mencionar estructuras de datos, el uso en algoritmos es una consecuencia natural.
Matthieu M.

0

Como un ejemplo de la vida real, construir un libro de órdenes de límite.

El feed ITCH 4.1, por ejemplo, tiene un concepto de órdenes de "reemplazo", donde los precios (y por lo tanto, la prioridad) pueden cambiar. Desea poder tomar órdenes y moverlas a otro lugar. La implementación de una cola de doble extremo con punteros hace que la operación sea muy fácil.


0

Al leer los diversos sitios de StackExchange, me doy cuenta de que está de moda hacer una pregunta como esta. A riesgo de críticas y votos negativos, seré honesto. Esto no está destinado a troll o flamear, solo quiero ayudar, al dar una evaluación honesta de la pregunta.

Y esa evaluación es la siguiente: esta es una pregunta muy extraña para hacerle a un programador en C. Casi todo lo que dice es "No sé C." Si analizo un poco más y más cínicamente, hay un trasfondo de seguimiento a esta "pregunta": "¿Hay algún atajo que pueda tomar para adquirir de forma rápida y repentina el conocimiento de un programador de C experimentado, sin dedicar tiempo? para estudio independiente? Cualquier "respuesta" que alguien pueda dar no es un sustituto para ir y hacer el trabajo preliminar de comprender el concepto subyacente y su uso.

Siento que es más constructivo ir a aprender C bien, de primera mano, que ir a la web y preguntarle a la gente esto. Cuando conozca bien C, no se molestará en hacer preguntas como esta, será como preguntar "¿qué problemas se resuelven mejor con un cepillo de dientes?"


0

Aunque los punteros realmente brillan mientras se trabaja con objetos de memoria grandes, todavía hay una manera de hacer lo mismo sin ellos.

El puntero es absolutamente esencial para la llamada programación dinámica , es cuando no sabes cuánta memoria necesitarás antes de que se ejecute tu programa. En la programación dinámica, puede solicitar fragmentos de memoria durante el tiempo de ejecución y colocar sus propios datos en ellos, por lo que necesita puntero o referencias (la diferencia no es importante aquí) para poder trabajar con esos fragmentos de datos.

Siempre que pueda reclamar cierta memoria durante el tiempo de ejecución y colocar sus datos en la memoria recién adquirida, puede hacer lo siguiente:

  1. Puede tener estructuras de datos autoextendibles. Esas son estructuras que pueden extenderse reclamando memoria adicional siempre que su capacidad se agote. La propiedad clave de cada estructura autoextensible es que consiste en pequeños bloques de memoria (denominados nodos, elementos de lista, etc., dependiendo de la estructura) y cada bloque contiene referencias a otros bloques. Estas estructuras "vinculadas" crean la mayoría de los tipos de datos modernos: gráficos, árboles, listas, etc.

  2. Puede programar usando el paradigma OOP (Programación Orientada a Objetos). Todo el OOP se basa en el uso de variables no directas, sino de referencias a instancias de clase (denominadas objetos) y su manipulación. No puede existir una instancia única sin punteros (aunque es posible usar clases solo estáticas incluso sin punteros, eso es más bien una excepción).


0

Es curioso, acabo de responder una pregunta sobre C ++ y hablé sobre punteros.

La versión corta es que NUNCA necesitas punteros a menos que 1) la biblioteca que estás usando te fuerce 2) Necesitas una referencia anulable.

Si necesita una matriz, una lista, una cadena, etc., simplemente téngala en la pila y use un objeto stl. Los objetos stl que regresan o pasan son rápidos (hecho no verificado) porque tienen un código interno que copia un puntero en lugar de un objeto y solo copiará los datos si le escribe. Este es C ++ normal, ni siquiera el nuevo C ++ 11 que lo hará más fácil para los escritores de bibliotecas.

Su pregunta puede ser respondida en esta parte

Si utiliza un puntero, asegúrese de que esté en una de estas dos condiciones. 1) Está pasando una entrada que puede ser anulable. Un ejemplo es un nombre de archivo opcional. 2) Si quieres regalar la propiedad. Como si pasa o devuelve el puntero, no le quedan NINGUNAS copias ni utiliza el puntero que regala.

ptr=blah; func(ptr); //never use ptr again for here on out.

Pero no he usado punteros o punteros inteligentes durante mucho tiempo y he perfilado mi aplicación. Corre muy rápido.

NOTA ADICIONAL: Noto que escribo mis propias estructuras y las paso. Entonces, ¿cómo hago esto sin usar punteros? No es un contenedor STL, por lo que pasar por ref es lento. Siempre cargo mi lista de datos / deques / mapas y demás. No recuerdo haber devuelto ningún objeto a menos que fuera algún tipo de lista / mapa. Ni siquiera una cuerda. Miré el código para objetos individuales y noté que hago algo como esto, { MyStruct v; func(v, someinput); ... } void func(MyStruct&v, const D&someinput) { fillV; }así que devuelvo objetos (múltiples) o preasignar / pasar una referencia para llenar (solo).

Ahora, si estaba escribiendo su propio deque, mapa, etc., deberá usar punteros. Pero no necesitas hacerlo. Deje que STL y posiblemente aumente la preocupación por eso. Solo necesita escribir datos y las soluciones. No contenedores para contenerlos;)

Espero que ahora nunca uses punteros: D. Buena suerte con libs que te obligan a


0

Los punteros son muy útiles para trabajar con dispositivos mapeados en memoria. Puede definir una estructura que refleje (digamos) un registro de control, luego asignarlo a la dirección del registro de control real en la memoria y manipularlo directamente. También puede apuntar directamente a un búfer de transferencia en una tarjeta o chip si la MMU lo ha asignado al espacio de memoria del sistema.


0

Veo punteros como mi dedo índice, solemos hacer un par de cosas:

  1. cuando alguien te pide algo que no puedes llevar, como dónde está la calle, yo "señalaría" esta calle, y eso es lo que hacemos en caso de argumentos por referencia
  2. al contar o atravesar algo, usaríamos el mismo dedo, y eso es lo que hacemos en los arreglos

por favor perdona esta pobre respuesta


0

Como su pregunta está etiquetada como C ++, responderé su pregunta para ese lenguaje.

En C ++ hay una distinción entre punteros y referencias, por lo tanto, hay dos escenarios en los que los punteros (o punteros inteligentes) son necesarios para facilitar ciertos comportamientos. Se pueden usar en otras circunstancias, sin embargo, se pregunta cómo "es mejor usarlos", y en todas las demás circunstancias hay mejores alternativas.

1. Polimorfismo

Un puntero de clase base le permite llamar a un método virtual que depende del tipo de objeto al que apunta el puntero.

2. Crear objetos persistentes

Los punteros son necesarios al crear un objeto dinámicamente (en el montón en lugar de la pila). Esto es necesario cuando desea que la vida útil de los objetos sea más larga que el alcance en el que se creó.

En términos de "buenos proyectos o problemas para resolver", como ya han dicho otros aquí, cualquier proyecto no trivial utilizará punteros.


Quizás alguien preferiría cortar el objeto que usar el puntero. Feliz depuración: D
deadalnix
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.