Preguntas frecuentes sobre lenguaje C

¿Qué son las FAQs?

Preguntas sobre funciones

  1. Ejemplos sencillos
  2. ¿Qué son los argumentos de una función?
  3. ¿Qué es pasar un argumento por valor?
  4. ¿Qué es pasar un argumento por referencia?
  5. ¿Por qué a scanf() se le pasan los argumentos por referencia y a printf() no?
  6. ¿Por qué se dice que los vectores, las matrices y las cadenas de caracteres se pasan siempre por referencia?
  7. ¿Cuándo un resultado de una función se devuelve como argumento y cuándo como valor de retorno?

Preguntas sobre punteros

  1. ¿Qué es un puntero?
  2. ¿Por qué son importantes los punteros?

Preguntas sobre memoria dinámica

  1. ¿Qué es la memoria estática y qué es la memoria dinámica?
  2. ¿Para qué sirve la memoria dinámica?
  3. ¿Qué son las funciones calloc() y malloc()? ¿Es lo mismo utilizar new?



Respuestas a las preguntas sobre funciones

Ejemplos sencillos Top

Para poder explicar algunos conceptos importantes de este apartado se van a presentar dos ejemplos sencillos de funciones, una con valor de retorno y argumentos pasados por valor y la otra sin valor de retorno y con los argumentos pasados por referencia.:

  1. Función maximo de dos valores. Esta función devuelve como valor de retorno el mayor valor de los dos argumentos actuales que se le pasan en la llamada. Su definición es la siguiente:
    1. double max(double a, double b) {
          if (a>=b)
              return a;
          else
              return b; 
      }
  2. Función permutar dos valores. A esta función se le pasan por referencia dos variables double como argumento y permuta sus valores. No tiene valor de retorno. Su declaración es:

Se utilizarán estas funciones en las explicaciones que siguen.

¿Qué son los argumentos de una función? Top

Una función es una porción de código independiente del exterior (es decir de otros programas) y que se puede re-utilizar (es decir llamar muchas veces con datos diferentes). Aunque el concepto de función es general y existe en todos los lenguajes de programación, aquí se hará referencia a las funciones de C/C++.

Se distingue entre argumentos formales y argumentos actuales. Los argumentos formales son los que aparecen en la definición de la función, mientras que los actuales son los que aparecen en la llamada a la función. Se podría también decir que los argumentos formales son los argumentos vistos desde dentro de la función, y los argumentos actuales son los argumentos vistos desde el programa que llama a la función, esto es desde fuera de la función. Los argumentos formales son siempre variables de la función que recogen los valores que se les pasan desde el exterior; los argumentos actuales pueden ser variables o expresiones cuyos valores se pasan a los argumentos formales.

Cuando una función tiene valor de retorno puede ser utilizada en una sentencia aritmética (por ejemplo, la función seno en la expresión: y=sin(x)*x-1.0;). En este caso el valor de retorno se sustituye en el lugar ocupado por la llamada a la función en dicha sentencia aritmética. Cuendo una función no tiene valor de retorno, se llama simplemente colocando la llamada en una línea del programa seguida de punto y coma (por ejemplo: permutar(&x, &y);).

¿Qué es pasar un argumento por valor? Top

En C/C++ el paso de argumentos a una función se hace de la siguiente manera. En el programa que llama a la función, se evalúan los argumentos actuales y sus valores se pasan a las variables de la función que constituyen los argumentos formales. En realidad siempre se pasan copias de dichos valores. Los argumentos formales (los argumentos vistos desde dentro de la función son variables nuevas, que se crean cuando la función es llamada, y que toman los valores que les pasan los argumentos actuales.

Cuando de dice que en C los argumentos de las funciones se pasan siempre por valor, lo que se quiere decir es que las variables argumentos formales reciben copias de los valores de los argumentos actuales, pero son siempre variables diferentes, propias de la función. Por eso, si se modifica un argumento formal dentro de la función, se está modificando una copia del argumento actual y si el argumento actual es una variable, dicha variable no queda modificada. Esto es un mecanismo de seguridad de C, de modo que una función no pueda modificar facilmente las variables externas a ella.

¿Qué es pasar un argumento por referencia? Top

En muchas ocasiones, las funciones tienen que modificar las variables pasadas como argumentos actuales. Como el valor de retorno es único, ésta es la única forma por ejemplo de que una función trasmita al exterior varios resultados en una sola llamada. Cuando se desea que la función modifique los argumentos actuales (las variables externas a la función que aparecen en la llamada), hay que pasárselos por referencia. ¿Qué quiere decir esto: que a veces se pasan copias de los argumentos actuales y otras veces se pasan los originales? Ya se verá en otro lugar que en C++ es así (a través de un tipo de variables que no tiene C: las variables reference), pero en C a las funciones siempre se les pasan copias de los argumentos actuales.

¿Como puede una función modificar una variable externa si las funciones sólo pueden recibir copias de las variables del programa que llama a la función? Esto se puede conseguir por medio de los punteros. La forma de modificar una variable externa es disponer de su dirección en memoria. Las funciones de C son capaces de recibir copias de las direcciones en memoria de las variables externas, pero para modificar el contenido de una dirección de memoria la dirección o una copia de esa dirección son igual de válidas.

¿Por qué a scanf() se le pasan los argumentos por referencia y a printf() no? Top

La función printf() imprime los valores de las variables o expresiones que se le pasan como argumentos. La función printf() no modifica los valores de sus argumentos, simplemente los imprime por pantalla. Por tanto, el paso de argumentos por valor es perfectamente satisfactorio para dicha función.

El caso de scanf() es muy diferente: esta función lee valores del teclado y asigna dichos valores a las variables del programa principal que se le han pasado como argumentos. Es decir, la función scanf() modifica las variables que se le pasan como argumentos (recibe unas variables sin valor asignado y guarda en ellas lo que el usuario teclea). Por eso, a la función printf() hay que pasarle siempre los argumentos por referencia.

Por este motivo, mientras que a printf() se le pueden pasar expresiones además de variables, a scanf() sólo se le pueden pasar direcciones de variables. nunca expresiones.

¿Por qué se dice que los vectores, las matrices y las cadenas de caracteres se pasan siempre por referencia? Top

En C/C++ el nombre de un vector, de una matriz o de una cadena de caracteres es siempre un puntero (al primer elemento del vector, al primer elemento del vector de punteros que contiene las direcciones de los primeros elementos de cada fila de la matriz, y al primer carácter de la cadena, respectivamente). Cuando dentro de una función se dispone de la dirección de una variable externa, se puede cambiar el valos de dicha variable por medio del operador indirección (*). Por tanto, las funciones siempre pueden modificar el valor de los elementos de un vector, de una matriz, o de una cadena de caracteres cuando sus nombres le han sido pasados como argumentos.


Respuestas a las preguntas sobre punteros

¿Qué es un puntero? Top

Un puntero es una variable que contiene o almacena la dirección en memoria de otra variable. De ordinario no interesa el valor de la variable puntero, sino el valor de la variable cuya dirección se almacena en el puntero. Los punteros constituyen una forma alternativa de manejar la información (con una dirección de memoria, en vez de con un nombre de variable).

¿Por qué son importantes los punteros? Top

En C los punteros son importantes, entre otras, por las siguientes razones:

  1. porque las cadenas de caracteres, los vectores y las matrices están basadas en punteros. De hecho el nombre de cada uno de estos tipos de variables es un puntero. Esto es válido tanto para reserva estática de memoria como con reserva dinámica, pero en este último caso el programador tiene toda la responsabilidad y debe hacer un uso más cuidadoso de los punteros.
  2. porque el paso de argumentos por referencia a funciones está basado en punteros. Para que una función pueda modificar una variable externa (una variable que no sea local de la función) hay que pasarle la dirección de dicha variable por medio de un puntero.


Respuestas a las preguntas sobre memoria dinámica

¿Cuándo un resultado de una función se devuelve como argumento y cuándo como valor de retorno? Top

Una función se comunica con el exterior a través de los argumentos (entrada de datos en la funcion y salida de resultados) y del valor de retorno (sólo un valor resultado de la función, devuelto por medio de la sentencia return). Así pues, una función C/C++ tiene una forma de recibir datos (los argumentos), pero dos para dar resultados (los argumentos pasados por referencia y el valor de retorno). En muchos casos no hay una única forma de programar la función (ni tampoco una forma claramente superior a las demás); en otras ocasiones, sí existe una forma más natural y apropiada de programarla..

Cuando una función tiene valor de retorno puede ser utilizada en una sentencia aritmética (por ejemplo, la función seno en la expresión: y=sin(x)*x-1.0;). En este caso el valor de retorno se sustituye en el lugar ocupado por la llamada a la función en dicha sentencia aritmética. Cuando una función no tiene valor de retorno, se llama simplemente colocando la llamada en una línea del programa seguida de punto y coma (por ejemplo: permutar(&x, &y);).

Por ejemplo, la función cos(x) también podría utilizarse sin valor de retorno y devolviendo el resultado por medio de un argumento pasado por referencia. La función podría declararse en la forma:

y ser llamada del modo:

donde la variable c, pasada por referencia, recogería el valor de coseno de x. Es evidente que, puesta en esta forma, la función calcula el coseno, pero no puede utilizarse en una expresión aritmética porque no tiene valor de retorno.

¿Qué es la memoria estática y qué es la memoria dinámica? Top

La memoria estática es memoria que se reserva en el momento de la compilación, antes de comenzar a ejecutarse el programa. Por ejemplo, para una resolución de un sistema de ecuaciones lineales, la memoria estática se puede reservar con sentencias del tipo:

El inconveniente de la reserva estática es que la cantidad de memoria se reserva siempre antes de conocer los datos concretos del problema a resolver. Eso lleva a reservar siempre un máximo de memoria que en la mayor parte de las ocasiones no se va a necesitar. La reserva estática tampoco se adapta bien a la memoria real disponible en el ordenador en que se está ejecutando el programa.

Por el contrario, la reserva dinámica de memoria se hace en tiempo de ejecución, después de leer los datos y de conocer el tamaño exacto del problema a resolver. Como consecuencia, la reserva dinámica de memoria se adapta mucho mejor a las necesidades de cada caso. Como contrapartida, es algo más difíicil de programar.

Cuando se reserva memoria dinámicamente, es muy importante liberar la memoria que ya no se necesita. De otra forma los programas van creciendo a lo largo de una ejecución hasta agotar la memoria disponible.

¿Para qué sirve la memoria dinámica? Top

La memoria dinámica sirve para que los programas se adapten siempre al tamaño del problema que tienen que resolver, sin desperdiciar recursos de memoria. Esto se traduce asimismo en una mayor eficiencia en la ejecución de los programas.

¿Qué son las funciones calloc() y malloc()? ¿Es lo mismo utilizar new? Top

Las funciones calloc() y malloc() son la forma clasica de reservar memoria dinámicamente en C. Ambas devuelven un puntero al comienzo de la memoria reservada. La función calloc() tiene 2 argumentos (el número de datos a reservar y los bytes que ocupa cada dato), mientras que malloc() sólo tiene uno (el total bytes de memoria a reservar).

Todo lo que hacen estas funciones lo puede realizar el operador new de C++, que tiene además otras ventajas que resultan patentes en la programación orientada a objetos de C++.



¿Qué son las FAQs (Frequently Asked Questions)?

Las FAQs (Frequently Asked Questions) son un tipo de documento típico de la Internet. A medida que nuevos usuarios se iban incorporando a la red, las preguntas que dirigían a los usuarios veteranos eran casi siempre las mismas. Para no estar siempre repitiendo lo mismo, los usuarios expertos realizaron compilaciones de esas preguntas más frecuentes con las correspondientes respuestas.

El resultado ha sido un tipo de documento muy didáctico y cómodo de utilizar. El deseo de los profesores de esta asignatura es ir poco a poco recogiendo las dudas más frecuentes sobre C con las respuestas adecuadas. Las preguntas se irán clasificando por temas