usar sirven que punteros puntero para operaciones memoria matrices los lenguaje dev con como asignar aritmetica c++ c pointers

c++ - sirven - ¿Por qué usar punteros?



punteros void lenguaje c (18)

Sé que esta es una pregunta muy básica, pero acabo de comenzar con una programación básica en C ++ después de codificar algunos proyectos con lenguajes de alto nivel.

Básicamente tengo tres preguntas:

  1. ¿Por qué usar punteros sobre variables normales?
  2. ¿Cuándo y dónde debo usar punteros?
  3. ¿Cómo se usan los punteros con matrices?

  1. Los punteros le permiten referirse al mismo espacio en la memoria desde múltiples ubicaciones. Esto significa que puede actualizar la memoria en una ubicación y el cambio se puede ver desde otra ubicación en su programa. También ahorrará espacio al poder compartir componentes en sus estructuras de datos.
  2. Debe usar punteros en cualquier lugar donde necesite obtener y pasar la dirección a un punto específico en la memoria. También puede utilizar punteros para navegar por las matrices:
  3. Una matriz es un bloque de memoria contigua que se ha asignado con un tipo específico. El nombre de la matriz contiene el valor del punto de inicio de la matriz. Cuando sumas 1, te lleva al segundo puesto. Esto le permite escribir bucles que incrementan un puntero que se desliza hacia abajo en la matriz sin tener un contador explícito para usar al acceder a la matriz.

Aquí hay un ejemplo en C:

char hello[] = "hello"; char *p = hello; while (*p) { *p += 1; // increase the character by one p += 1; // move to the next spot } printf(hello);

huellas dactilares

ifmmp

porque toma el valor de cada carácter y lo incrementa en uno.


Aquí está mi respuesta, y no voy a prometer ser un experto, pero he encontrado sugerencias para ser excelente en una de mis bibliotecas que estoy tratando de escribir. En esta biblioteca (es una API de gráficos con OpenGL :-)) puede crear un triángulo con objetos de vértice pasados ​​en ellos. El método de dibujo toma estos objetos de triángulos, y bueno ... los dibuja en función de los objetos de vértice que he creado. Bueno, esta bien.

Pero, ¿qué pasa si cambio una coordenada de vértice? ¿Moverlo o algo con moveX () en la clase de vértice? Bueno, bueno, ahora tengo que actualizar el triángulo, agregar más métodos y el rendimiento se está desperdiciando porque tengo que actualizar el triángulo cada vez que se mueve un vértice. Todavía no es un gran problema, pero no es tan bueno.

Ahora, ¿qué pasa si tengo una malla con toneladas de vértices y toneladas de triángulos, y la malla gira y se mueve, y tal. Tendré que actualizar todos los triángulos que usan estos vértices y, probablemente, todos los triángulos de la escena porque no sabría cuáles usan qué vértices. Eso es muy intensivo en computación, y si tengo varias mallas encima de un paisaje, ¡oh Dios! Estoy en problemas, porque estoy actualizando cada triángulo casi todos los cuadros porque estos vértices están cambiando a la vez.

Con los punteros, no tienes que actualizar los triángulos.

Si tuviera tres * objetos de vértice por clase de triángulo, no solo estoy ahorrando espacio porque un millón de triángulos no tienen tres objetos de vértice que sean grandes en sí mismos, sino que estos punteros siempre apuntarán a los vértices a los que están destinados, sin importar Con qué frecuencia cambian los vértices. Dado que los punteros aún apuntan al mismo vértice, los triángulos no cambian, y el proceso de actualización es más fácil de manejar. Si te confundiera, no lo dudaría, no pretendo ser un experto, simplemente arrojando mis dos centavos a la discusión.


Aquí hay una perspectiva ligeramente diferente, pero perspicaz, de por qué muchas características de C tienen sentido: http://steve.yegge.googlepages.com/tour-de-babel#C

Básicamente, la arquitectura de CPU estándar es una arquitectura de Von Neumann, y es tremendamente útil poder referirse a la ubicación de un elemento de datos en la memoria, y hacer aritmética con él, en tal máquina. Si conoce alguna variante del lenguaje ensamblador, verá rápidamente cuán crucial es esto en el nivel bajo.

C ++ hace que los punteros sean un poco confusos, ya que a veces los administra por ti y oculta su efecto en forma de "referencias". Si usa C directa, la necesidad de punteros es mucho más obvia: no hay otra forma de hacer llamada por referencia, es la mejor manera de almacenar una cadena, es la mejor manera de iterar a través de una matriz, etc.


Con respecto a su segunda pregunta, generalmente no necesita usar punteros al programar, sin embargo, hay una excepción a esto y es cuando crea una API pública.

El problema con las construcciones en C ++ que la gente generalmente usa para reemplazar los punteros depende en gran medida del conjunto de herramientas que use, lo cual está bien cuando tiene todo el control que necesita sobre el código fuente. Sin embargo, si compila una biblioteca estática con Visual Studio 2008, por ejemplo. e intente usarlo en un visual studio 2010 obtendrá una tonelada de errores de vinculador porque el nuevo proyecto está vinculado con una versión más nueva de STL que no es compatible con versiones anteriores. Las cosas se vuelven aún más desagradables si compilas una DLL y das una biblioteca de importación que la gente usa en un conjunto de herramientas diferente porque, en ese caso, tu programa se bloqueará tarde o temprano sin ninguna razón aparente.

Por lo tanto, con el fin de mover grandes conjuntos de datos de una biblioteca a otra, podría considerar asignar un puntero a una matriz a la función que se supone que debe copiar los datos si no desea forzar a otros a usar las mismas herramientas que utiliza. . Lo bueno de esto es que ni siquiera tiene que ser una matriz de estilo C, puedes usar un std :: vector y dar el puntero dando la dirección del primer elemento y vector [0], por ejemplo, y usar El std :: vector para gestionar la matriz internamente.

Otra buena razón para usar punteros en C ++ nuevamente se relaciona con las bibliotecas, considere tener un dll que no se puede cargar cuando se ejecuta su programa, por lo que si usa una biblioteca de importación, la dependencia no se satisface y el programa se bloquea. Este es el caso, por ejemplo, cuando proporciona una API pública en una DLL junto con su aplicación y desea acceder a ella desde otras aplicaciones. En este caso, para usar la API, debe cargar el dll desde su ubicación (generalmente está en una clave de registro) y luego debe usar un puntero de función para poder llamar a funciones dentro de la DLL. A veces, las personas que hacen la API son lo suficientemente agradables como para brindarle un archivo .h que contiene funciones de ayuda para automatizar este proceso y darle todas las funciones de punteros que necesita, pero si no puede usar LoadLibrary y GetProcAddress en windows y dlopen y dlsym en unix para obtenerlos (considerando que conoce la firma completa de la función).


Déjame intentar y responder a esto también.

Los punteros son similares a las referencias. En otras palabras, no son copias, sino una forma de referirse al valor original.

Antes que nada, un lugar donde normalmente tendrá que usar mucho los punteros es cuando se trata de hardware integrado . Tal vez necesite cambiar el estado de un pin de IO digital. Tal vez esté procesando una interrupción y necesite almacenar un valor en una ubicación específica. Te dan la imagen. Sin embargo, si no estás tratando con hardware directamente y solo te estás preguntando qué tipos usar, sigue leyendo.

¿Por qué usar punteros en lugar de variables normales? La respuesta se vuelve más clara cuando se trata de tipos complejos, como clases, estructuras y matrices. Si utilizara una variable normal, podría terminar haciendo una copia (los compiladores son lo suficientemente inteligentes para evitar esto en algunas situaciones y C ++ 11 también ayuda, pero por ahora nos mantendremos alejados de esa discusión).

Ahora, ¿qué pasa si quieres modificar el valor original? Podrías usar algo como esto:

MyType a; //let''s ignore what MyType actually is right now. a = modify(a);

Eso funcionará bien y si no sabes exactamente por qué estás usando punteros, no deberías usarlos. Cuidado con la razón "probablemente son más rápidos". Ejecute sus propias pruebas y, si son más rápidas, úselas.

Sin embargo, supongamos que está resolviendo un problema en el que necesita asignar memoria. Cuando asignas memoria, necesitas desasignarla. La asignación de memoria puede o no ser exitosa. Aquí es donde los punteros son útiles: le permiten probar la existencia del objeto que ha asignado y le permiten acceder al objeto para el que se asignó la memoria eliminando la referencia al puntero.

MyType *p = NULL; //empty pointer if(p) { //we never reach here, because the pointer points to nothing } //now, let''s allocate some memory p = new MyType[50000]; if(p) //if the memory was allocated, this test will pass { //we can do something with our allocated array for(size_t i=0; i!=50000; i++) { MyType &v = *(p+i); //get a reference to the ith object //do something with it //... } delete[] p; //we''re done. de-allocate the memory }

Esta es la clave de por qué usaría punteros: las referencias suponen que el elemento al que hace referencia ya existe . Un puntero no lo hace.

La otra razón por la que usarías punteros (o al menos terminar teniendo que lidiar con ellos) es porque son un tipo de datos que existía antes que las referencias. Por lo tanto, si terminas usando bibliotecas para hacer las cosas que sabes que son mejores, verás que muchas de estas bibliotecas usan punteros por todas partes, simplemente por el tiempo que llevan funcionando (muchas de ellos fueron escritos antes de C ++).

Si no usó ninguna biblioteca, puede diseñar su código de tal manera que pueda mantenerse alejado de los punteros, pero dado que los punteros son uno de los tipos básicos del lenguaje, cuanto más rápido se sienta cómodo al usarlos, más portátiles serían tus habilidades en C ++.

Desde el punto de vista de la capacidad de mantenimiento, también debo mencionar que cuando usa punteros, tiene que probar su validez y manejar el caso cuando no son válidos, o simplemente asumir que son válidos y aceptar el hecho de que su programa se estrellará o peor cuando se rompe la suposición. Dicho de otra manera, su elección con punteros es introducir la complejidad del código o un mayor esfuerzo de mantenimiento cuando algo se rompe y está intentando rastrear un error que pertenece a toda una clase de errores que introducen los punteros, como la corrupción de memoria.

Entonces, si controla todo su código, manténgase alejado de los punteros y, en su lugar, utilice referencias, manteniéndolos constantes cuando pueda. Esto te obligará a pensar en la vida útil de tus objetos y terminará manteniendo tu código más fácil de entender.

Solo recuerda esta diferencia: una referencia es esencialmente un puntero válido. Un puntero no siempre es válido.

Entonces, ¿estoy diciendo que es imposible crear una referencia inválida? No. Es totalmente posible, porque C ++ te permite hacer casi cualquier cosa. Simplemente es más difícil de hacer involuntariamente y te sorprenderás de cuántos errores son involuntarios :)


En C ++, si desea utilizar el polymorphism subtipo, debe utilizar punteros de usuario. Ver este post: Polimorfismo en C ++ sin punteros .

Realmente, cuando lo piensas, esto tiene sentido. Cuando utiliza el polimorfismo de subtipo, en última instancia, no sabe de antemano qué implementación de método de clase o subclase se invocará porque no sabe cuál es la clase real.

Esta idea de tener una variable que contiene un objeto de una clase desconocida es incompatible con el modo predeterminado (no puntero) de C ++ de almacenar objetos en la pila, donde la cantidad de espacio asignado corresponde directamente a la clase. Nota: si una clase tiene 5 campos de instancia en lugar de 3, será necesario asignar más espacio.

Tenga en cuenta que si está utilizando ''&'' para pasar argumentos por referencia, la indirección (es decir, los punteros) todavía está involucrada detrás de la escena. El ''&'' es solo azúcar sintáctico que (1) le ahorra la molestia de usar la sintaxis de puntero y (2) permite que el compilador sea más estricto (como prohibir los punteros nulos).

En gran parte, los punteros son matrices (en C / C ++), son direcciones en la memoria, y se puede acceder a ellas como una matriz si se desea (en casos "normales").

Dado que son la dirección de un elemento, son pequeñas: ocupan solo el espacio de una dirección. Como son pequeños, enviarlos a una función es barato. Y luego permiten que esa función funcione en el elemento real en lugar de una copia.

Si desea realizar una asignación de almacenamiento dinámico (por ejemplo, para una lista enlazada), debe usar punteros, ya que son la única forma de tomar memoria del montón.


En java y C # todas las referencias de objetos son punteros, lo que con c ++ es que tienes más control sobre dónde apuntas los puntos. Recordar Con gran poder viene la gran responsabilidad.


La necesidad de punteros en lenguaje C se describe here

La idea básica es que muchas limitaciones en el lenguaje (como el uso de matrices, cadenas y la modificación de múltiples variables en las funciones) podrían eliminarse mediante la manipulación de las ubicaciones de memoria de los datos. Para superar estas limitaciones, los punteros fueron introducidos en C.

Además, también se ve que al usar punteros, a veces puede ejecutar su código más rápido y ahorrar memoria (en los casos en los que pasa grandes tipos de datos como estructuras a una función. Hacer una copia de dichos tipos de datos antes de pasar tomaría tiempo y consumiría memoria). Esta es otra razón por la que los programadores prefieren los punteros para los tipos de datos grandes.

PD: Consulte el here para una explicación detallada con código de ejemplo.


Los punteros son importantes en muchas estructuras de datos cuyo diseño requiere la capacidad de vincular o encadenar un "nodo" a otro de manera eficiente. No "elegiría" un puntero sobre, digamos, un tipo de datos normal como float, simplemente tienen diferentes propósitos.

Los punteros son útiles cuando se requiere un alto rendimiento y / o una huella de memoria compacta.

La dirección del primer elemento en su matriz se puede asignar a un puntero. Esto le permite acceder directamente a los bytes asignados subyacentes. El punto central de una matriz es evitar que tengas que hacer esto.


Los punteros son una forma de obtener una referencia indirecta a otra variable. En lugar de mantener el valor de una variable, le indican su dirección . Esto es particularmente útil cuando se trata de arrays, ya que al usar un puntero al primer elemento de una matriz (su dirección), puede encontrar rápidamente el siguiente elemento incrementando el puntero (a la siguiente ubicación de la dirección).

La mejor explicación de los punteros y la aritmética de punteros que he leído está en El lenguaje de programación C de K&R. Un buen libro para comenzar a aprender C ++ es C ++ Primer .


Porque copiar objetos grandes en todos los lugares es una pérdida de tiempo y memoria.


Un uso de punteros (no mencionaré las cosas que ya se cubrieron en las publicaciones de otras personas) es acceder a la memoria que no ha asignado. Esto no es muy útil para la programación de PC, pero se usa en la programación integrada para acceder a dispositivos de hardware mapeados en memoria.

En los viejos tiempos de DOS, solía poder acceder directamente a la memoria de video de la tarjeta de video declarando un puntero a:

unsigned char *pVideoMemory = (unsigned char *)0xA0000000;

Muchos dispositivos integrados todavía utilizan esta técnica.


Una forma de usar punteros sobre variables es eliminar la memoria duplicada requerida. Por ejemplo, si tiene algún objeto complejo grande, puede usar un puntero para apuntar a esa variable para cada referencia que haga. Con una variable, necesita duplicar la memoria para cada copia.


Una razón para usar punteros es para que una variable o un objeto se pueda modificar en una función llamada.

En C ++ es una mejor práctica usar referencias que punteros. Aunque las referencias son esencialmente punteros, C ++ en cierta medida oculta el hecho y hace que parezca que está pasando por valor. Esto facilita cambiar la forma en que la función de llamada recibe el valor sin tener que modificar la semántica de pasarlo.

Considere los siguientes ejemplos:

Utilizando referencias:

public void doSomething() { int i = 10; doSomethingElse(i); // passes i by references since doSomethingElse() receives it // by reference, but the syntax makes it appear as if i is passed // by value } public void doSomethingElse(int& i) // receives i as a reference { cout << i << endl; }

Usando punteros:

public void doSomething() { int i = 10; doSomethingElse(&i); } public void doSomethingElse(int* i) { cout << *i << endl; }


los punteros se utilizan porque el rendimiento aumenta en la asignación y la recuperación de datos del área de almacenamiento dinámico.


  • ¿Por qué usar punteros sobre variables normales?

La respuesta corta es: no. ;-) Los punteros se deben usar donde no se puede usar nada más. Se debe a la falta de funcionalidad adecuada, a los tipos de datos faltantes o al rendimiento puro. Más abajo ...

  • ¿Cuándo y dónde debo usar punteros?

La respuesta corta aquí es: donde no puedes usar nada más. En C, no tiene soporte para tipos de datos complejos, como una cadena. Tampoco hay forma de pasar una variable "por referencia" a una función. Ahí es donde tienes que usar punteros. También puede hacer que apunten a prácticamente cualquier cosa, listas enlazadas, miembros de estructuras, etc. Pero no entremos en eso aquí.

  • ¿Cómo se usan los punteros con matrices?

Con poco esfuerzo y mucha confusión. ;-) Si hablamos de tipos de datos simples, como int y char, hay poca diferencia entre una matriz y un puntero. Estas declaraciones son muy similares (pero no son lo mismo, por ejemplo, sizeof devolverá valores diferentes):

char* a = "Hello"; char a[] = "Hello";

Puedes alcanzar cualquier elemento en la matriz como este

printf("Second char is: %c", a[1]);

Índice 1 ya que la matriz comienza con el elemento 0. :-)

O igualmente podrías hacer esto

printf("Second char is: %c", *(a+1));

El operador del puntero (el *) es necesario ya que le estamos diciendo a printf que queremos imprimir un carácter. Sin el *, se imprimirá la representación de caracteres de la dirección de memoria. Ahora estamos usando el propio personaje en su lugar. Si hubiéramos usado% s en lugar de% c, le habríamos pedido a printf que imprimiera el contenido de la dirección de memoria señalada por ''a'' más uno (en este ejemplo anterior), y no hubiéramos tenido que poner el * Al frente:

printf("Second char is: %s", (a+1)); /* WRONG */

Pero esto no solo habría impreso el segundo carácter, sino todos los caracteres en las siguientes direcciones de memoria, hasta que se encontró un carácter nulo (/ 0). Y aquí es donde las cosas empiezan a ponerse peligrosas. ¿Qué sucede si intenta accidentalmente imprimir una variable del tipo entero en lugar de un puntero de carácter con el formateador% s?

char* a = "Hello"; int b = 120; printf("Second char is: %s", b);

Esto imprimirá lo que se encuentre en la dirección de memoria 120 y continuará imprimiendo hasta que se encuentre un carácter nulo. Es incorrecto e ilegal realizar esta declaración printf, pero probablemente funcionaría de todos modos, ya que un puntero en realidad es del tipo int en muchos entornos. Imagine los problemas que podría causar si utilizara sprintf () en su lugar y asigne de esta forma una "char array" demasiado larga a otra variable, que solo tiene un cierto espacio limitado asignado. Lo más probable es que termine escribiendo sobre otra cosa en la memoria y haga que su programa se bloquee (si tiene suerte).

Ah, y si no asigna un valor de cadena a la matriz / puntero de caracteres cuando lo declara, DEBE asignarle suficiente memoria antes de asignarle un valor. Utilizando malloc, calloc o similar. Esto ya que solo declaró un elemento en su matriz / una dirección de memoria única para señalar. Así que aquí hay algunos ejemplos:

char* x; /* Allocate 6 bytes of memory for me and point x to the first of them. */ x = (char*) malloc(6); x[0] = ''H''; x[1] = ''e''; x[2] = ''l''; x[3] = ''l''; x[4] = ''o''; x[5] = ''/0''; printf("String /"%s/" at address: %d/n", x, x); /* Delete the allocation (reservation) of the memory. */ /* The char pointer x is still pointing to this address in memory though! */ free(x); /* Same as malloc but here the allocated space is filled with null characters!*/ x = (char *) calloc(6, sizeof(x)); x[0] = ''H''; x[1] = ''e''; x[2] = ''l''; x[3] = ''l''; x[4] = ''o''; x[5] = ''/0''; printf("String /"%s/" at address: %d/n", x, x); /* And delete the allocation again... */ free(x); /* We can set the size at declaration time as well */ char xx[6]; xx[0] = ''H''; xx[1] = ''e''; xx[2] = ''l''; xx[3] = ''l''; xx[4] = ''o''; xx[5] = ''/0''; printf("String /"%s/" at address: %d/n", xx, xx);

Tenga en cuenta que aún puede usar la variable x después de haber realizado una memoria libre () de la memoria asignada, pero no sabe qué hay allí. También tenga en cuenta que los dos printf () pueden darle direcciones diferentes, ya que no hay garantía de que la segunda asignación de memoria se realice en el mismo espacio que la primera.


  • En algunos casos, se requiere que los punteros de función usen funciones que están en una biblioteca compartida (.DLL o .so). Esto incluye realizar tareas en todos los idiomas, donde a menudo se proporciona una interfaz DLL.
  • Haciendo compiladores
  • Haciendo calculadoras científicas, ¿dónde tiene una matriz o vector o mapa de cadenas de punteros de función?
  • Intentando modificar la memoria de video directamente - haciendo su propio paquete de gráficos
  • Haciendo un API!
  • Estructuras de datos: punteros de enlace de nodo para árboles especiales que está creando.

Hay muchas razones para los punteros. Tener un nombre en C especialmente es importante en las DLL si desea mantener la compatibilidad entre idiomas.