Introducción

Introducción

Hemos llegado a un punto crucial en nuestra serie de artículos sobre el desarrollo C. También es, no casualmente, esa parte de C que da muchos dolores de cabeza a los principiantes. Aquí es donde entramos, y el propósito de este artículo (uno de ellos, de todos modos), es desacreditar los mitos sobre punteros y sobre C como un idioma difícil/imposible de aprender y leer. Sin embargo, recomendamos una mayor atención y una pequeña paciencia y verá que los punteros no son tan alucinantes como dicen las leyendas.

Definiciones y advertencias

Parece sentido natural y común que debamos comenzar con las advertencias, y le recomendamos sinceramente que las recuerde: mientras que los punteros hacen que su vida sea más fácil, también poder Introducir errores difíciles de encontrar y un código incomprensible. Verá, si continúa leyendo, de qué estamos hablando y la seriedad de dichos insectos, pero la conclusión es, como se dijo antes, tener mucho cuidado.

Una definición simple de un puntero sería "una variable cuyo valor es la dirección de otra variable". Probablemente sepa que los sistemas operativos tratan las direcciones al almacenar valores, al igual que etiquetar las cosas dentro de un almacén para tener una manera fácil de encontrarlas cuando sea necesario. Por otro lado, una matriz se puede definir como una colección de elementos identificados por índices. Verá más tarde por qué los punteros y las matrices generalmente se presentan juntos, y cómo ser eficientes en C utilizándolos. Si tiene antecedentes en otros idiomas de nivel superior, está familiarizado con el tipo de datos de cadena. En C, las matrices son el equivalente de las variables tipo cuerda, y se argumenta que este enfoque es más eficiente.



Puntería

Has visto la definición de un puntero, ahora comencemos con algunas explicaciones en profundidad y, por supuesto, ejemplos. Una primera pregunta que puede hacerte es "¿por qué debo usar consejos??". Aunque podría ser inflamado sobre esta comparación, me arriesgaré: ¿usas enlaces simbólicos en tu sistema Linux?? Incluso si no ha creado algunos usted mismo, su sistema los supone y hace que el trabajo sea más eficiente. He escuchado algunas historias de terror sobre desarrolladores de S senior que juran que nunca usaron consejos porque son "complicados", pero eso solo significa que el desarrollador es incompetente, nada más. Además, hay situaciones en las que tendrás que usar punteros, por lo que no deben ser tratados como opcionales, porque no son. Como antes, creo en el aprendizaje con el ejemplo, así que aquí va:

int x, y, z; x = 1; y = 2; int *ptoi; /* PTOI es, y representa, puntero a Integer*/ ptoi = & x; / * PTOI señala X */ z = *ptoi; / * Z es ahora el valor de 1, hacia el que PTOI señala */ ptoi = & y; / *PTOI ahora señala y */

Si te estás rascando la cabeza confusión, no huyas: solo duele la primera vez, ya sabes. Vamos en línea por línea y veamos lo que hicimos aquí. Primero declaramos tres enteros, eso es X, Y y Z, y dimos valores X e Y 1 e 2, respectivamente. Esta es la parte simple. El nuevo elemento viene junto con la declaración de la variable PTOI, que es un puntero a un entero, por lo que puntos hacia un entero. Esto se logra utilizando el asterisco antes del nombre de la variable y se dice que es un operador de redirección. La línea 'ptoi = & x;' significa "PTOi ahora apunta hacia X, que debe ser un entero, según la declaración de PTOI anterior". Ahora puedes trabajar con PTOi como lo harías con X (bueno, casi). Sabiendo esto, la siguiente línea es el equivalente de 'z = x;'. Luego, nosotros desertor PTOI, lo que significa que "deja de señalar a X y comience a señalar a Y". Aquí es necesaria una observación importante: el operador y el operador solo se puede usar en objetos residente de la memoria, las variables (excepto el registro [1]) y los elementos de la matriz.

[1] Las variables de tipo registradas son uno de los elementos de C que existen, pero la mayoría de los programadores las rechazan. Una variable con esta palabra clave adjunta sugiere al compilador que se usará con frecuencia y debe almacenarse en un registro de procesador para un acceso más rápido. La mayoría de los compiladores modernos ignoran esta pista y deciden por sí mismos de todos modos, por lo que si no está seguro de que necesite registrarse, no.

Dijimos que PTOI debe señalar a un entero. ¿Cómo debemos proceder si queríamos un puntero genérico, por lo que no tendremos que preocuparnos por los tipos de datos?? Ingrese el puntero al vacío. Esto es todo lo que le diremos, y la primera asignación es averiguar qué usos puede tener el puntero a la nula y cuáles son sus 'limitaciones.



Matrices

Verá en este subcapítulo por qué insistimos en presentar consejos y matrices en un artículo, a pesar del riesgo de sobrecargar el cerebro del lector. Es bueno saber que, cuando se trabaja con matrices, no tiene que usar consejos, pero es bueno hacerlo, porque las operaciones serán más rápidas, con la desventaja de un código menos comprensible. Una declaración de matriz tiene el resultado de declarar una serie de elementos consecutivos disponibles a través de índices, como así:

int a [5]; int x; a [2] = 2; x = a [2];

A es una matriz de 5 elementos, con el tercer elemento de 2 (la numeración de índice comienza con cero!), y x se define como también ser 2. Muchos errores y errores cuando se trata por primera vez con matrices es que uno olvida el problema del índice 0. Cuando dijimos "elementos consecutivos", queremos decir que está garantizado que los elementos de la matriz tienen ubicaciones consecutivas en la memoria, no que si un [2] es 2, entonces un [3] es 3. Hay una estructura de datos en C llamada Enum que hace eso, pero todavía no lo trataremos. Encontré un programa antiguo que escribí mientras aprendía C, con ayuda de mi amigo Google, que invierte a los personajes de una cadena. Aquí lo tienes:

#include #include int main () char stringy [30]; int i; char c; printf ("Escriba una cadena .\ n "); fgets (stringy, 30, stdin); printf (" \ n "); para(i = 0; yo < strlen(stringy); i++) printf("%c", stringy[i]); printf("\n"); para(i = strlen (stringy); i> = 0; i--) printf ("%c", fibroso [i]); printf ("\ n"); devolver 0;  

Esta es una forma de hacer esto sin usar punteros. Tiene defectos en muchos aspectos, pero ilustra la relación entre cadenas y matrices. Stringy es una matriz de 30 caracteres que se utilizará para contener la entrada del usuario, seré el índice de matriz y C será el carácter individual en el que se trabajará. Así que pedimos una cadena, la guardamos en la matriz usando FGETS, imprime la cadena original comenzando desde Fligy [0] y continuando, usando un bucle de forma incremental, hasta que la cadena finaliza. La operación inversa proporciona el resultado deseado: nuevamente obtenemos la longitud de la cadena con strlen () y iniciamos una cuenta regresiva hasta cero y luego imprime el carácter de la cadena por carácter. Otro aspecto importante es que cualquier matriz de caracteres en C termina con el carácter nulo, representado gráficamente por '\ 0'.

¿Cómo haríamos todo esto usando punteros?? No se sienta tentado a reemplazar la matriz con un puntero a Char, eso no funcionará. En su lugar, use la herramienta adecuada para el trabajo. Para programas interactivos como el anterior, use matrices de caracteres de longitud fija, combinadas con funciones seguras como fgets (), por lo que no será mordido por los desbordamientos del búfer. Para las constantes de cadena, sin embargo, puede usar

char * myname = "David";

y luego, usando las funciones que se le proporcionan en la cadena.h, manipular datos como mejor le. Hablando de eso, ¿qué función elegiría agregar myname a las cadenas que abordan al usuario?? Por ejemplo, en lugar de "por favor ingrese un número", debe tener "David, ingrese un número".



Punteros y matrices

Puede, y se le anima a usar matrices junto con punteros, aunque al principio puede ser sorprendido por la sintaxis. En términos generales, puede hacer cualquier cosa relacionada con los punteros, con la ventaja de la velocidad a su lado. Puede pensar que con el hardware de hoy, usar punteros con matrices solo para ganar algo de velocidad no vale la pena. Sin embargo, a medida que sus programas crecen en tamaño y complejidad, dicha diferencia comenzará a ser más obvia, y si alguna vez piensa en portar su aplicación a una plataforma integrada, se felicitará. En realidad, si entendiste lo que se dijo hasta este punto, no tendrás razones para sorprenderte. Digamos que tenemos una variedad de enteros y queremos declarar un puntero a uno de los elementos de la matriz. El código se vería así:

int myArray [10]; int *myPtr; int x; myPtr = y myArray [0]; x = *myPtr;

Entonces, tenemos una matriz llamada MyArray, que consta de diez enteros, un puntero a un entero, que obtiene la dirección del primer elemento de la matriz, y X, que obtiene el valor de dicho primer elemento a través de un puntero. Ahora puedes hacer todo tipo de trucos ingeniosos para moverse a través de la matriz, como

*(myPtr + 1);

que apuntará hacia el siguiente elemento de MyArray, a saber, MyArray [1].

Una cosa importante a saber, y al mismo tiempo que ilustra perfectamente la relación entre punteros y matrices, es que el valor de un objeto de tipo matriz es la dirección de su elemento 'primero (cero), así que si myPtr = y myArray [ 0], entonces myPtr = myArray. Como un ejercicio, lo invitamos a estudiar esta relación un poco y codificar algunas situaciones en las que cree que lo hará/podría ser útil. Esto es lo que encontrarás como aritmética de puntero.

Consideraciones sobre cadenas en C y llamadas

Antes de haber visto que puedes hacer

char *mystring; myString = "Esta es una cadena."

o puedes hacer lo mismo usando

char mystring [] = "Esta es una cadena.";

En el segundo caso, como habrá inferido, MyString es una matriz lo suficientemente grande como para mantener los datos atribuidos a él. La diferencia es que al usar matrices puede operar en caracteres individuales dentro de la cadena, mientras que al usar el enfoque de puntero no puede. Es un tema muy importante recordar que lo salvará del compilador que tiene hombres grandes que vienen a su casa y le hará cosas terribles a su abuela. Yendo un poco más lejos, otro problema que debe tener en cuenta es que si se olvida de los punteros, se realizan llamadas en C por valor. Entonces, cuando una función necesita algo de una variable, se realiza una copia local y se realiza un trabajo en eso. Pero si la función altera la variable, los cambios no se reflejan, porque el original permanece intacto. Al usar punteros, puede usar llamadas por referencia, Como verá en nuestro ejemplo a continuación. Además, llamar por valor podría convertirse en intensivo en recursos si los objetos en los que se trabajan son grandes. Técnicamente, también hay una llamada de Pointer, pero mantengámoslo simple por ahora.

Digamos que queremos escribir una función que tome un entero como argumento y lo incrementa con algún valor. Probablemente estarás tentado a escribir algo como esto:

vacío inc (int a) a+= 20; 

Ahora, si intenta esto, verá que el entero no se incrementará, porque solo la copia local será. Si hubieras escrito

vacío inc (int & a) a+= 20; 

Tu argumento entero se incrementará con veinte, que es lo que quieres. Entonces, si todavía tenía algunas dudas sobre la utilidad de los punteros, aquí hay un ejemplo simple pero significativo.



Temas algo avanzados

Pensamos en poner estos temas en una sección especial porque son un poco más difíciles de entender para los principiantes, pero son partes útiles de la programación C. Entonces…

Consejos para consejos

Sí, los punteros son variables como cualquier otra, por lo que pueden tener otras variables que les apunten. Mientras que los punteros simples como se vieron anteriormente tienen un nivel de "apuntar", los punteros a los punteros tienen dos, por lo que una variable apunta a otra que apunta a otro. Crees que esto es enloquecedor? Puedes tener consejos para consejos para consejos para consejos para .. .ad infinitum, pero ya cruzaste el umbral de la cordura y la utilidad si tienes tales declaraciones. Recomendamos usar CDECL, que es un pequeño programa generalmente disponible en la mayoría de las distribuciones de Linux que "traduce" entre C y C ++ e inglés y al revés. Entonces, se puede declarar un puntero a un puntero como

int ** ptrtoptr;

Ahora, según cómo se usan los punteros de nivel múltiple, hay situaciones en las que tiene funciones, como la comparación anterior, y desea obtener un puntero como valor de retorno. También es posible que desee una variedad de cuerdas, que es una característica muy útil, como verá en un capricho.

Matrices multidimensionales

Las matrices que has visto hasta ahora son unidimensionales, pero eso no significa que estés limitado a eso. Por ejemplo, se puede imaginar una matriz biidimensional en su mente como una matriz de matrices. Mi consejo sería usar matrices multidimensionales si siente la necesidad, pero si eres bueno con uno simple, bueno, unidimensional, usa eso para que tu vida como codificador sea más simple. Para declarar una matriz biidimensional (usamos dos dimensiones aquí, pero no está limitado a ese número), lo hará

 int bidimarray [4] [2];

que tendrá el efecto de declarar una matriz entera de 4 por 2. Para acceder al segundo elemento verticalmente (piense en un crucigrama si eso ayuda!) y el primer horizontalmente, puedes hacer

bidimarray [2] [1];

Recuerde que estas dimensiones son solo para nuestros ojos: el compilador asigna la memoria y funciona con la matriz de la misma manera, por lo que si no ve la utilidad de esto, no la use. Ergo, nuestra matriz anterior puede declararse como

int bidimarray [8]; / * 4 por 2, como se dijo */


Argumentos de la línea de comandos

En nuestra entrega anterior de la serie, hablamos sobre Main y cómo se puede usar con o sin argumentos. Cuando su programa lo necesita y tiene argumentos, son Char Argc y Char *argv []. Ahora que sabes qué son las matrices y los consejos, las cosas comienzan a tener mucho más sentido. Sin embargo, pensamos en tener un poco de detalle aquí. Char *argv [] se puede escribir como char ** argv también. Como algo de comida para pensar, ¿por qué crees que es posible?? Recuerde que Argv significa "Vector de argumentos" y es una variedad de cuerdas. Siempre puede confiar en el hecho de que Argv [0] es el nombre del programa en sí, mientras que Argv [1] es el primer argumento y así sucesivamente. Entonces, un programa corto para ver su nombre y los argumentos se verían así:

#include #include int main (int argc, char ** argv)  mientras(argc--) printf ("%s \ n", *argv ++); devolver 0; 

Conclusión

Elegimos las partes que parecían las más esenciales para la comprensión de los punteros y las matrices, e intencionalmente dejó a algunos temas como punteros a las funciones. No obstante, si trabaja con la información presentada aquí y resuelve los ejercicios, tendrá un comienzo bastante bueno en esa parte de C que se considera la principal fuente de código complicado e incomprensible.

Aquí hay una excelente referencia con respecto a los punteros de C ++. Aunque no es C, los idiomas están relacionados, por lo que el artículo lo ayudará a comprender mejor los punteros.

Esto es lo que puede esperar a continuación:

  • I. C Desarrollo en Linux - Introducción
  • II. Comparación entre C y otros lenguajes de programación
  • III. Tipos, operadores, variables
  • IV. Control de flujo
  • V. Funciones
  • VI. Punteros y matrices
  • VII. Estructuras
  • VIII. E/S básica
  • Ix. Estilo de codificación y recomendaciones
  • X. Construyendo un programa
  • Xi. Embalaje para Debian y Fedora
  • Xii. Obtener un paquete en los repositorios oficiales de Debian

Tutoriales de Linux relacionados:

  • Una introducción a la automatización, herramientas y técnicas de Linux
  • Cosas para instalar en Ubuntu 20.04
  • Mastering Bash Script Loops
  • Bucles anidados en guiones Bash
  • Mint 20: Mejor que Ubuntu y Microsoft Windows?
  • Manipulación de Big Data para la diversión y las ganancias Parte 1
  • Cosas que hacer después de instalar Ubuntu 20.04 fossa focal Linux
  • Tutorial de depuración de GDB para principiantes
  • Registro avanzado y auditoría en Linux
  • ¿Con qué frecuencia tiene que reiniciar su servidor de Linux??