variable son realizar que para necesita los las investigacion indicadores indicador independientes identifique entre dimensiones diferencia dependientes cuales cientifica c pointers

son - variables dependientes e independientes indicadores



¿Qué es lo que la gente encuentra difícil con respecto a los indicadores C? (29)

Agregan una dimensión extra al código sin un cambio significativo en la sintaxis. Piensa sobre esto:

int a; a = 5

Solo hay una cosa que cambiar: a . Puede escribir a = 6 y los resultados son obvios para la mayoría de las personas. Pero ahora considera:

int *a; a = &some_int;

Hay dos cosas sobre a que son relevantes en diferentes momentos: el valor real de a , el puntero y el valor "detrás" del puntero. Puedes cambiar a :

a = &some_other_int;

... y some_int todavía está por ahí con el mismo valor. Pero también puedes cambiar lo que señala:

*a = 6;

Hay una brecha conceptual entre a = 6 , que tiene solo efectos secundarios locales, y *a = 6 , lo que podría afectar a un montón de otras cosas en otros lugares. Mi punto aquí no es que el concepto de indirección sea intrínsecamente engañoso, sino que porque puedes hacer lo inmediato, lo local con a cosa indirecta con *a ... eso podría ser lo que confunde a la gente.

A partir de la cantidad de preguntas publicadas aquí, está claro que las personas tienen algunos problemas bastante fundamentales cuando buscan información sobre punteros y aritmética de punteros.

Tengo curiosidad por saber por qué. Nunca me han causado grandes problemas (aunque aprendí sobre ellos en el Neolítico). Para escribir mejores respuestas a estas preguntas, me gustaría saber qué es lo que las personas encuentran difícil.

Entonces, si estás luchando con punteros, o si recientemente, pero de repente "lo entendiste", ¿cuáles fueron los aspectos de los indicadores que te causaron problemas?


Aquí hay un ejemplo de puntero / matriz que me dio pausa. Supongamos que tiene dos matrices:

uint8_t source[16] = { /* some initialization values here */ }; uint8_t destination[16];

Y su objetivo es copiar el contenido de uint8_t del destino de origen utilizando memcpy (). Adivina cuál de los siguientes logra ese objetivo:

memcpy(destination, source, sizeof(source)); memcpy(&destination, source, sizeof(source)); memcpy(&destination[0], source, sizeof(source)); memcpy(destination, &source, sizeof(source)); memcpy(&destination, &source, sizeof(source)); memcpy(&destination[0], &source, sizeof(source)); memcpy(destination, &source[0], sizeof(source)); memcpy(&destination, &source[0], sizeof(source)); memcpy(&destination[0], &source[0], sizeof(source));

La respuesta (Spoiler Alert!) Es TODOS. "destino", "destino" y "destino [0]" tienen todos el mismo valor. "& destino" es un tipo diferente de los otros dos, pero sigue siendo el mismo valor. Lo mismo ocurre con las permutaciones de "fuente".

Como comentario aparte, personalmente prefiero la primera versión.


Aquí hay una falta de respuesta: use cdecl (o c ++ decl) para resolverlo:

eisbaw@leno:~$ cdecl explain ''int (*(*foo)(const void *))[3]'' declare foo as pointer to function (pointer to const void) returning pointer to array 3 of int


Cuando comencé a trabajar con ellos, el mayor problema que tuve fue la sintaxis.

int* ip; int * ip; int *ip;

son todos iguales.

pero:

int* ip1, ip2; //second one isn''t a pointer! int *ip1, *ip2;

¿Por qué? porque la parte "puntero" de la declaración pertenece a la variable y no al tipo.

Y luego desreferenciando la cosa usa una notación muy similar:

*ip = 4; //sets the value of the thing pointed to by ip to ''4'' x = ip; //hey, that''s not ''4''! x = *ip; //ahh... there''s that ''4''

Excepto cuando realmente necesitas un puntero ... ¡entonces usas un ampersand!

int *ip = &x;

¡Hurra por consistencia!

Entonces, al parecer solo para ser imbéciles y demostrar cuán ingeniosos son, muchos desarrolladores de bibliotecas usan punteros a punteros a punteros, y si esperan una serie de esas cosas, ¿por qué no pasar un puntero a eso también? .

void foo(****ipppArr);

para llamar a esto, necesito la dirección de la matriz de punteros a punteros a punteros de ints:

foo(&(***ipppArr));

En seis meses, cuando tenga que mantener este código, dedicaré más tiempo a tratar de descubrir qué significa todo esto que reescribir desde cero. (Sí, probablemente entendí mal la sintaxis. Ha pasado un tiempo desde que hice algo en C. Casi me echo de menos, pero luego soy un poco masajista)


Cuando se trata de punteros, las personas que se confunden están ampliamente en uno de los dos campos. He estado (¿soy?) En ambos.

La multitud del array[]

Esta es la multitud que directamente no sabe cómo traducir de la notación de puntero a la notación de matriz (o ni siquiera sabe que están relacionados). Aquí hay cuatro formas de acceder a los elementos de una matriz:

  1. notación de matriz (indexación) con el nombre de la matriz
  2. notación de matriz (indexación) con el nombre del puntero
  3. Notación de puntero (el *) con el nombre del puntero
  4. la notación del puntero (el *) con el nombre de la matriz

int vals[5] = {10, 20, 30, 40, 50}; int *ptr; ptr = vals; array element pointer notation number vals notation vals[0] 0 10 *(ptr + 0) ptr[0] *(vals + 0) vals[1] 1 20 *(ptr + 1) ptr[1] *(vals + 1) vals[2] 2 30 *(ptr + 2) ptr[2] *(vals + 2) vals[3] 3 40 *(ptr + 3) ptr[3] *(vals + 3) vals[4] 4 50 *(ptr + 4) ptr[4] *(vals + 4)

La idea aquí es que acceder a las matrices a través de punteros parece bastante simple y directo, pero se pueden hacer muchas cosas muy complicadas e ingeniosas de esta manera. Algunos de los cuales pueden dejar perplejos a los experimentados programadores de C / C ++, y mucho menos a los principiantes inexpertos.

La reference to a pointer y un pointer to a pointer multitud pointer to a pointer

This es un gran artículo que explica la diferencia y que citaré y robaré un código de :)

Como un pequeño ejemplo, puede ser muy difícil ver exactamente lo que el autor quería hacer si te encuentras con algo como esto:

//function prototype void func(int*& rpInt); // I mean, seriously, int*& ?? int main() { int nvar=2; int* pvar=&nvar; func(pvar); .... return 0; }

O, en menor medida, algo como esto:

//function prototype void func(int** ppInt); int main() { int nvar=2; int* pvar=&nvar; func(&pvar); .... return 0; }

Entonces, al final del día, ¿qué es lo que realmente resolvemos con todo este galimatías? Nada.

Ahora hemos visto la sintaxis de ptr-to-ptr y ref-to-ptr. ¿Hay alguna ventaja de una sobre la otra? Tengo miedo, no. El uso de uno de los dos, para algunos programadores, son solo preferencias personales. Algunos de los que usan ref-to-ptr dicen que la sintaxis es "más clara", mientras que otros utilizan ptr-to-ptr, por ejemplo, la sintaxis ptr-to-ptr lo hace más claro para aquellos que leen lo que estás haciendo.

Esta complejidad y la intercambiabilidad aparente (audaz) con referencias, que a menudo es otra advertencia de los indicadores y un error de los recién llegados, dificulta la comprensión de los indicadores. También es importante comprender, para fines de compleción, que los punteros a las referencias son ilegales en C y C ++ por razones confusas que lo llevan a la semántica rvalue - rvalue .

Como se comentó en una respuesta anterior, muchas veces solo tendrás estos programadores que piensan que están siendo inteligentes usando ******awesome_var->lol_im_so_clever() y la mayoría de nosotros probablemente sea culpable de escribir tales atrocidades a veces, pero simplemente no es un buen código, y ciertamente no es mantenible.

Bueno, esta respuesta resultó ser más larga de lo que esperaba ...


Culpo personalmente a la calidad de los materiales de referencia y a las personas que enseñan; la mayoría de los conceptos en C (pero especialmente los indicadores) simplemente se enseñan mal. Sigo amenazando con escribir mi propio libro en C (titulado " Lo último, las necesidades del mundo es otro libro en el lenguaje de programación C" ), pero no tengo el tiempo ni la paciencia para hacerlo. Así que salgo aquí y lanzo citas aleatorias del Estándar a las personas.

También está el hecho de que cuando C se diseñó inicialmente, se suponía que entendías la arquitectura de la máquina a un nivel bastante detallado simplemente porque no había forma de evitarlo en tu trabajo diario (la memoria era tan estrecha y los procesadores eran tan lentos tenías que entender cómo lo que escribiste afectaba el rendimiento).


Debería comenzar diciendo que C y C ++ fueron los primeros lenguajes de programación que aprendí. Comencé con C, luego hice C ++ en la escuela mucho, y luego volví a C para hablar con fluidez.

Lo primero que me confundió sobre los indicadores cuando aprendí C fue el simple:

char ch; char str[100]; scanf("%c %s", &ch, str);

Esta confusión se debió en gran medida a que se me introdujo al usar referencia a una variable para argumentos OUT antes de que los punteros me fueran presentados correctamente. Recuerdo que me salté la escritura de los primeros ejemplos en C para Dummies porque eran demasiado simples solo para nunca obtener el primer programa que escribí para trabajar (probablemente debido a esto).

Lo que era confuso acerca de esto era lo que realmente significaba &ch , y por qué str no lo necesitaba.

Después de que me familiaricé con eso, recuerdo haber estado confundido acerca de la asignación dinámica. Me di cuenta en algún momento que tener punteros a los datos no era extremadamente útil sin la asignación dinámica de algún tipo, así que escribí algo así como:

char * x = NULL; if (y) { char z[100]; x = z; }

para tratar de asignar dinámicamente un espacio. No funcionó. No estaba seguro de que funcionaría, pero no sabía de qué otra manera podría funcionar.

Más tarde aprendí sobre malloc y lo new , pero realmente parecían generadores de memoria mágicos para mí. No sabía nada sobre cómo podrían funcionar.

Algún tiempo después, me enseñaban recursividad nuevamente (ya lo había aprendido antes, pero ahora estaba en clase) y le pregunté cómo funcionaba bajo el capó, dónde estaban almacenadas las variables por separado. Mi profesor dijo "en la pila" y muchas cosas quedaron claras para mí. Había escuchado el término antes y había implementado pilas de software antes. Ya había escuchado a otros referirse a "la pila" mucho antes, pero se había olvidado de eso.

Por esta época también me di cuenta de que el uso de matrices multidimensionales en C puede ser muy confuso. Sabía cómo funcionaban, pero eran tan fáciles de enredar que decidí tratar de usarlos cada vez que podía. Creo que el problema aquí fue principalmente sintáctico (especialmente al pasar o devolverlos de las funciones).

Desde que escribí C ++ para la escuela durante uno o dos años obtuve mucha experiencia usando punteros para estructuras de datos. Aquí tuve una nueva serie de problemas: mezclar punteros. Tendría varios niveles de punteros (cosas como node ***ptr; ) y siempre me tropezaba. Desreferenciaba un puntero en el número incorrecto de veces y, finalmente, recurría a calcular cuántos * necesitaba por prueba y error.

En algún momento aprendí cómo funcionaba el montón de un programa (más o menos, pero lo suficientemente bueno como para que ya no me mantuviera despierto por la noche). Recuerdo haber leído que si miras unos pocos bytes antes del puntero que malloc en un sistema determinado, puedes ver cuántos datos se asignaron realmente. Me di cuenta de que el código en malloc podría pedir más memoria del sistema operativo y esta memoria no era parte de mis archivos ejecutables. Tener una idea de trabajo decente de cómo funciona malloc es realmente útil.

Poco después de esto tomé una clase de ensamblaje, que no me enseñó tanto sobre punteros como la mayoría de los programadores probablemente piensen. Sin embargo, me hizo pensar más sobre en qué ensamblaje se podría traducir mi código. Siempre intenté escribir un código eficiente, pero ahora tenía una mejor idea de cómo hacerlo.

También tomé un par de clases donde tuve que escribir algo de ceceo . Al escribir lisp, no me preocupaba tanto la eficiencia como en C. Tenía muy poca idea de en qué se podría traducir este código si se compilaba, pero sabía que parecía utilizar muchos símbolos locales nombrados (variables). cosas mucho más fáciles. En algún momento escribí un código de rotación de árbol AVL en un poco de ceceo que me costó mucho escribir en C ++ debido a problemas con el puntero. Me di cuenta de que mi aversión a lo que yo pensaba que eran variables locales excesivas había obstaculizado mi capacidad para escribir ese y muchos otros programas en C ++.

También tomé una clase de compiladores. Mientras estaba en esta clase pasé al material de avances y aprendí sobre la asignación estática única (SSA) y variables muertas, lo cual no es tan importante, excepto que me enseñó que cualquier compilador decente hará un trabajo decente al tratar con variables que son ya no se usa. Ya sabía que más variables (incluyendo punteros) con tipos correctos y buenos nombres me ayudarían a mantener las cosas en mi cabeza, pero ahora también sabía que evitarlas por razones de eficiencia era incluso más estúpido que decir a mis profesores menos optimizados con la micro-optimización. yo.

Entonces, para mí, conocer bastante sobre el diseño de memoria de un programa me ayudó mucho. Pensar en lo que significa mi código, tanto simbólicamente como en el hardware, me ayuda. Usar punteros locales que tengan el tipo correcto ayuda mucho. A menudo escribo código que se ve así:

int foo(struct frog * f, int x, int y) { struct leg * g = f->left_leg; struct toe * t = g->big_toe; process(t);

de modo que si arruino un tipo de puntero queda muy claro por el error del compilador cuál es el problema. Si lo hice:

int foo(struct frog * f, int x, int y) { process(f->left_leg->big_toe);

y me equivoqué con cualquier tipo de puntero, el error del compilador sería mucho más difícil de descifrar. Me sentiría tentado a recurrir a los cambios de prueba y error en mi frustración, y probablemente empeorar las cosas.


Había programado en c ++ por 2 años y luego me convertí a Java (5 años) y nunca miré hacia atrás. Sin embargo, cuando recientemente tuve que usar algunas cosas nativas, descubrí (con asombro) que no había olvidado nada sobre los punteros y que incluso los encuentro fáciles de usar. Este es un agudo contraste con lo que experimenté hace 7 años cuando intenté captar el concepto por primera vez. Entonces, ¿supongo que comprender y gustar es una cuestión de madurez de programación? :)

O

Los indicadores son como andar en bicicleta, una vez que descubres cómo trabajar con ellos, no hay que olvidarlo.

En general, difícil de comprender o no, toda la idea del puntero es MUY educativa y creo que debe ser entendida por todos los programadores, independientemente de si programa en un idioma con indicaciones o no.


Hay un gran artículo que apoya la idea de que los indicadores son difíciles en el sitio de Joel Spolsky: The Perils of JavaSchools .

[Descargo de responsabilidad: no soy un enemigo de Java per se .]


La comprensión adecuada de punteros requiere conocimiento sobre la arquitectura de la máquina subyacente.

Muchos programadores hoy en día no saben cómo funciona su máquina, al igual que la mayoría de las personas que saben conducir un automóvil no saben nada sobre el motor.


La mayoría de las cosas son más difíciles de entender si no estás basado en el conocimiento que está "debajo". Cuando enseñé CS me fue mucho más fácil cuando comencé a mis alumnos a programar una "máquina" muy simple, una computadora decimal simulada con códigos de operación decimales cuya memoria consistía en registros decimales y direcciones decimales. Pondrían programas muy cortos para, por ejemplo, agregar una serie de números para obtener un total. Luego lo pasarían solo para ver lo que estaba sucediendo. Podrían mantener presionada la tecla "enter" y verla correr "rápido".

Estoy seguro de que casi todos en SO se preguntan por qué es útil ser tan básico. Olvidamos lo que era no saber programar. Jugar con una computadora de juguete pone en práctica conceptos sin los cuales no se puede programar, como las ideas de que la computación es un proceso paso a paso, usando un pequeño número de primitivas básicas para crear programas, y el concepto de memoria variables como lugares donde se almacenan los números, en los que la dirección o el nombre de la variable es distinto del número que contiene. Existe una distinción entre el momento en que ingresa al programa y el momento en que "se ejecuta". Me gusta aprender a programar como cruzar una serie de "baches de velocidad", como programas muy simples, luego bucles y subrutinas, luego matrices, luego E / S secuenciales, luego punteros y estructura de datos. Todos estos son mucho más fáciles de aprender haciendo referencia a lo que una computadora realmente está haciendo debajo.

Finalmente, al llegar a C, los indicadores son confusos, aunque K & R hizo un muy buen trabajo al explicarlos. La forma en que los aprendí en C fue para saber leerlos, de derecha a izquierda. Como cuando veo int *p en mi cabeza, digo " p apunta a un int ". C se inventó como un paso adelante del lenguaje ensamblador y eso es lo que me gusta de él: está cerca de ese "terreno". Los punteros, como cualquier otra cosa, son más difíciles de entender si no tienes esa conexión a tierra.


La principal dificultad con los punteros, al menos para mí, es que no comencé con C. Empecé con Java. Toda la noción de punteros era realmente extraña hasta un par de clases en la universidad donde se esperaba que yo supiera C. Entonces me enseñé los principios básicos de C y cómo usar punteros en su sentido más básico. Incluso entonces, cada vez que me encuentro leyendo el código C, tengo que buscar la sintaxis del puntero.

Así que en mi experiencia muy limitada (1 año en el mundo real + 4 en la universidad), los indicadores me confunden porque nunca tuve que usarlo realmente en otra cosa que no sea la clase. Y puedo simpatizar con los estudiantes que ahora comienzan CS con JAVA en lugar de C o C ++. Como dijiste, aprendiste punteros en la era del ''Neolítico'' y probablemente lo hayan usado desde entonces. Para nosotros, las personas más nuevas, la noción de asignar memoria y hacer aritmética de puntero es realmente extraña porque todos estos lenguajes la han abstraído.

PD: Después de leer el ensayo de Spolsky, su descripción de ''JavaSchools'' no se parecía en nada a lo que pasé en la universidad en Cornell (''05 -''09). Tomé las estructuras y la programación funcional (sml), los sistemas operativos (C), los algoritmos (lápiz y papel) y una gran cantidad de otras clases que no se enseñaban en Java. Sin embargo, todas las clases introductorias y optativas se realizaron todas en Java porque es útil no reinventar la rueda cuando se trata de hacer algo más elevado que implementar una tabla hash con punteros.


Mirando hacia atrás, hubo cuatro cosas que realmente me ayudaron a entender finalmente los indicadores. Antes de esto, podía usarlos, pero no los entendía del todo. Es decir, sabía que si seguía los formularios, obtendría los resultados que deseaba, pero no entendía del todo el ''por qué'' de los formularios. Me doy cuenta de que esto no es exactamente lo que me has preguntado, pero creo que es un corolario útil.

  1. Escribir una rutina que tomó un puntero a un número entero y modificó el entero. Esto me dio las formas necesarias sobre las cuales construir cualquier modelo mental de cómo funcionan los punteros.

  2. Asignación de memoria dinámica unidimensional. Averiguar la asignación de memoria 1-D me hizo entender el concepto del puntero.

  3. Asignación de memoria dinámica bidimensional. Averiguar la asignación de memoria en 2-D reforzó ese concepto, pero también me enseñó que el puntero mismo requiere almacenamiento y debe tenerse en cuenta.

  4. Diferencias entre variables de pila, variables globales y memoria de montón. Averiguar estas diferencias me enseñó los tipos de memoria a los que apuntan los punteros.

Cada uno de estos elementos requería imaginar lo que estaba sucediendo en un nivel inferior: construir un modelo mental que satisficiera cada caso que se me ocurriera. Tomó tiempo y esfuerzo, pero valió la pena. Estoy convencido de que para entender los indicadores, debes construir ese modelo mental sobre cómo funcionan y cómo se implementan.

Ahora volvamos a tu pregunta original. Según la lista anterior, había varios elementos que tuve dificultades para comprender originalmente.

  1. Cómo y por qué uno usaría un puntero.
  2. ¿Cómo son diferentes y similares a las matrices?
  3. Comprender dónde se almacena la información del puntero.
  4. Entender qué y hacia dónde apunta el puntero.

No recibí punteros hasta que leí la descripción en K & R. Hasta ese momento, los punteros no tenían sentido. Leí un montón de cosas en las que la gente decía: "No aprendas punteros, son confusos y te harán daño a tu cabeza y te darán aneurismas", así que me alejé de él durante mucho tiempo y creé este aire innecesario de difícil concepto. .

De lo contrario, más o menos lo que pensé, ¿por qué querrías una variable que tienes que pasar por aros para obtener el valor de, y si querías asignarle algo, tenías que hacer cosas extrañas para obtener los valores para ir? en ellas. El punto total de una variable es algo para almacenar un valor, pensé, así que por qué alguien quería complicarlo estaba más allá de mí. "Entonces, con un puntero debes usar el operador * para obtener su valor" ¿Qué tipo de variable tonta es esa? " , Pensé. Inútil, sin juego de palabras.

La razón por la que fue complicado fue porque no entendía que un puntero era una dirección para algo. Si explica que es una dirección, que es algo que contiene una dirección para otra cosa, y que puede manipular esa dirección para hacer cosas útiles, creo que podría aclarar la confusión.

Una clase que requería el uso de punteros para acceder / modificar puertos en una PC, usar aritmética de punteros para abordar diferentes ubicaciones de memoria y mirar códigos C más complicados que modificaban sus argumentos me desilusionó de la idea de que los punteros eran, de hecho, inútiles.


Sospecho que las personas van demasiado profundas en sus respuestas. No es realmente necesario comprender la programación, las operaciones reales de la CPU o la gestión de la memoria de nivel de ensamblaje.

Cuando estaba enseñando, encontré los siguientes agujeros en la comprensión de los estudiantes como la fuente más común de problemas:

  1. Heap vs Stack storage. Simplemente es sorprendente cuánta gente no entiende esto, incluso en un sentido general.
  2. Marcos de pila. Solo el concepto general de una sección dedicada de la pila para variables locales, junto con la razón por la cual es una "pila" ... detalles como esconder la ubicación de devolución, los detalles del manejador de excepciones y los registros anteriores pueden dejarse de forma segura hasta que alguien intente compilar un compilador
  3. "La memoria es memoria es memoria" La conversión simplemente cambia las versiones de los operadores o la cantidad de espacio que el compilador da para un trozo de memoria particular. Sabes que estás lidiando con este problema cuando las personas hablan sobre "qué variable (primitiva) X realmente es".

La mayoría de mis alumnos pudieron comprender un dibujo simplificado de un trozo de memoria, generalmente la sección de variables locales de la pila en el alcance actual. En general, ayuda a dar direcciones ficticias explícitas a las distintas ubicaciones.

Supongo que en resumen, digo que si quieres entender los punteros, debes comprender las variables y lo que realmente son en las arquitecturas modernas.


Tuve mi "momento de puntero" trabajando en algunos programas de telefonía en C. Tuve que escribir un emulador de intercambio AXE10 usando un analizador de protocolos que solo entendía el clásico C. Todo dependía de conocer los punteros. Traté de escribir mi código sin ellos (hey, yo era "pre-puntero" me cortó algo de holgura) y fracasé por completo.

La clave para entenderlos, para mí, era el operador & (dirección). Una vez que entendí eso &i refería a la "dirección de i", entendí que *i quise decir "el contenido de la dirección señalada por i" apareció un poco más tarde. Cada vez que escribía o leía mi código siempre repetía lo que "&" quería decir y lo que significaba "*" y, finalmente, comencé a usarlos de forma intuitiva.

Para mi vergüenza, me forzaron a entrar en VB y luego en Java, así que el conocimiento de mi puntero no es tan nítido como lo era antes, pero me alegro de que soy "post-puntero". Sin embargo, no me pidas que use una biblioteca que me obligue a comprender * * p.


I have recently just had the pointer click moment, and I was surprised that I had been finding it confusing. It was more that everyone talked about it so much, that I assumed some dark magic was going on.

The way I got it was this. Imagine that all defined variables are given memory space at compile time(on the stack). If you want a program that could handle large data files such as audio or images, you wouldn''t want a fixed amount of memory for these potential structures. So you wait until runtime to assign a certain amount of memory to holding this data(on the heap).

Once you have your data in memory, you don''t want to be copying that data all around your memory bus every time you want to run an operation on it. Say you want to apply a filter to your image data. You have a pointer that starts at the front of the data you have assigned to the image, and a function runs across that data, changing it in place. If you didn''t know what you we''re doing, you would probably end up making duplicates of data, as you ran it through the operation.

At least that''s the way I see it at the moment!


I personally did not understand the pointer even after my post graduation and after my first job. The only thing I was knowing is that you need it for linked list, binary trees and for passing arrays into functions. This was the situation even at my first job. Only when I started to give interviews, I understand that the pointer concept is deep and has tremendous use and potential. Then I started reading K & R and writing own test program. My whole goal was job-driven.
At this time I found that pointers are really not bad nor difficult if they are been taught in a good way. Unfortunately when I learn C in graduation, out teacher was not aware of pointer, and even the assignments were using less of pointers. In the graduate level the use of pointer is really only upto creating binary trees and linked list. This thinking that you don''t need proper understanding of pointers to work with them, kill the idea of learning them.


I think it requires a solid foundation, probably from the machine level, with introduction to some machine code, assembly, and how to represent items and data structure in RAM. It takes a little time, some homework or problem solving practice, and some thinking.

But if a person knows high level languages at first (which is nothing wrong -- a carpenter uses an ax. a person who needs to split atom uses something else. we need people who are carpenters, and we have people who study atoms) and this person who knows high level language is given a 2 minute introduction to pointers, and then it is hard to expect him to understand pointer arithmetics, pointers to pointers, array of pointers to variable size strings, and array of array of characters, etc. A low-level solid foundation can help a lot.


I think one reason C pointers are difficult is that they conflate several concepts which are not really equivalent; yet, because they are all implemented using pointers, people can have a hard time disentangling the concepts.

In C, pointers are used to, amoung other things:

  • Define recursive data structures

In C you''d define a linked list of integers like this:

struct node { int value; struct node* next; }

The pointer is only there because this is the only way to define a recursive data structure in C, when the concept really has nothing to do with such a low-level detail as memory addresses. Consider the following equivalent in Haskell, which doesn''t require use of pointers:

data List = List Int List | Null

Pretty straightforward - a list is either empty, or formed from a value and the rest of the list.

  • Iterate over strings and arrays

Here''s how you might apply a function foo to every character of a string in C:

char *c; for (c = "hello, world!"; *c != ''/0''; c++) { foo(c); }

Despite also using a pointer as an iterator, this example has very little in common with the previous one. Creating an iterator that you can increment is a different concept from defining a recursive data structure. Neither concept is especially tied to the idea of a memory address.

  • Achieve polymorphism

Here is an actual function signature found in glib :

typedef struct g_list GList; void g_list_foreach (GList *list, void (*func)(void *data, void *user_data), void* user_data);

Whoa! That''s quite a mouthful of void* ''s. And it''s all just to declare a function that iterates over a list that can contain any kind of thing, applying a function to each member. Compare it to how map is declared in Haskell:

map::(a->b)->[a]->[b]

That''s much more straightforward: map is a function that takes a function which converts an a to a b , and applies it to a list of a ''s to yield a list of b ''s. Just like in the C function g_list_foreach , map doesn''t need to know anything in its own definition about the types to which it will be applied.

Para resumir:

I think C pointers would be a lot less confusing if people first learned about recursive data structures, iterators, polymorphism, etc. as separate concepts, and then learned how pointers can be used to implement those ideas in C , rather than mashing all of these concepts together into a single subject of "pointers".


It seems many students have a problem with the concept of indirection, especially when they meet the concept of indirection for the first time. I remember from back when I was a student that out of the +100 students of my course, only a handful of people really understood pointers.

The concept of indirection is not something that we often use in real life, and therefore it''s a hard concept to grasp initially.


Once upon a time... We had 8 bit microprocessors and everyone wrote in assembly. Most processors included some type of indirect addressing used for jump tables and kernels. When higher level languages came along we add a thin layer of abstraction and called them pointers. Over the years we have gotten more and more away from the hardware. This is not necessarily a bad thing. They are called higher level languages for a reason. The more I can concentrate on what I want to do instead of the details of how it is done the better.


Pointers (along with some other aspects of low-level work), require the user to take away the magic.

Most high level programmers like the magic.


Pointers are a way of dealing with the difference between a handle to an object and an object itself. (ok, not necessarily objects, but you know what I mean, as well as where my mind is)

At some point, you probably have to deal with the difference between the two. In modern, high-level language this becomes the distinction between copy-by-value and copy-by-reference. Either way, it is a concept that is often difficult for programmers to grasp.

However, as has been pointed out, the syntax for handling this problem in C is ugly, inconsistent, and confusing. Eventually, if you really attempt to understand it, a pointer will make sense. But when you start dealing with pointers to pointers, and so on ad nauseum, it gets really confusing for me as well as for other people.

Another important thing to remember about pointers is that they''re dangerous. C is a master programmer''s language. It assumes you know what the heck you''re doing and thereby gives you the power to really mess things up. While some types of programs still need to be written in C, most programs do not, and if you have a language that provides a better abstraction for the difference between an object and its handle, then I suggest you use it.

Indeed, in many modern C++ applications, it is often the case that any required pointer arithmetic is encapsulated and abstracted. We don''t want developers doing pointer arithmetic all over the place. We want a centralized, well tested API that does pointer arithmetic at the lowest level. Making changes to this code must be done with great care and extensive testing.


Pointers are difficult because of the indirection.


Pointers.. hah.. all about pointer in my head is that it give a memory address where the actual values of whatever its reference.. so no magic about it.. if you learn some assembly you wouldn''t have that much trouble learning how pointers works.. come on guys... even in Java everything is a reference..


Speaking as a C++ newbie here:

The pointer system took a while for me to digest not necessarily because of the concept but because of the C++ syntax relative to Java. A few things I found confusing are:

(1) Variable declaration:

A a(1);

vs.

A a = A(1);

vs.

A* a = new A(1);

and apparently

A a();

is a function declaration and not a variable declaration. In other languages, there''s basically just one way to declare a variable.

(2) The ampersand is used in a few different ways. If it is

int* i = &a;

then the &a is a memory address.

OTOH, if it is

void f(int &a) {}

then the &a is a passed-by-reference parameter.

Although this may seem trivial, it can be confusing for new users - I came from Java and Java''s a language with a more uniform use of operators

(3) Array-pointer relationship

One thing that''s a tad bit frustrating to comprehend is that a pointer

int* i

can be a pointer to an int

int *i = &n; //

o

can be an array to an int

int* i = new int[5];

And then just to make things messier, pointers and array are not interchangeable in all cases and pointers cannot be passed as array parameters.

This sums up some of the basic frustrations I had with C/C++ and its pointers, which IMO, is greatly compounded by the fact that C/C++ has all these language-specific quirks.


The problem I have always had (primarily self-taught) is the "when" to use a pointer. I can wrap my head around the syntax for constructing a pointer but I need to know under which circumstances a pointer should be used.

Am I the only one with this mindset? ;-)


El principal problema que las personas no entienden es por qué necesitan punteros. Porque no son claros acerca de la pila y el montón. Es bueno comenzar desde el ensamblador de 16 bits para x86 con el modo de memoria pequeña. Ayudó a muchas personas a tener idea de la pila, el montón y la "dirección". Y byte :) Los programadores modernos a veces no pueden decirle cuántos bytes necesita para abordar el espacio de 32 bits. ¿Cómo pueden tener idea de los punteros?

El segundo momento es la notación: declaras el puntero como *, obtienes la dirección como & y esto no es fácil de entender para algunas personas.

Y lo último que vi fue un problema de almacenamiento: ellos entienden el montón y la pila, pero no pueden entrar en la idea de "estática".