Duda sobre una parte del libro de k&r

Iniciado por fruz, 1 Mayo 2019, 00:04 AM

0 Miembros y 2 Visitantes están viendo este tema.

fruz

Hola a todos. Tengo una duda:
En un ejemplo del libro de k&r se divide un programa en varios archivos fuente. Uno de ellos es getch.c:

#include <stdio.h>
#define BUFSIZE 100

char buf[BUFSIZE];
int bufp = 0;
itn getch(void) {
   ...
}

void ungetch(int) {
   ...
}

Dentro de getch y de ungetch se usan tanto buf como bufp.
Más adelante dice:
"La declaración static, aplicada a una variable o función externa, limita el alcance de ese objeto al resto del archivo fuente que se está compilando. Así las variables static externas proporcionan una forma de ocultar nombres como buf y bufp en la combinación getch-ungetch, que deben ser externas para que puedan ser compartidas, aunque no deben ser visibles a los usuarios de getch y ungetch."

¿Qué significa "ocultar nombres" y que "no son visibles"?


MAFUS

Cuando creas una librería para que otros puedan usarla te interesa exponer ciertos identificadores, cómo las funciones, para que se puedan usar. printf, scanf, etc. son ejemplo de ello.

Pero no sólo existen esas funciones y por debajo usan muchas otras para poder realizar su trabajo que no quieres que tus clientes ni siquiera sepan que existen porque no necesitan usarlas o podrían poner en riesgo el buen funcionamiento de tu librería. Por eso, todo y que deben existir deben estar ocultas. Por eso se usa static. También funciona para los tipos de datos.

El que mejor representa esto es FILE, un tipo de dato incompleto para el cliente, es decir, no hay forma de que sepa que miembros tiene y sólo se puede trabajar con él a través de funciones que hacen referencia a un puntero. Otro tipo dato oculto es el usado por strtok: devuelve un puntero a una cadena pero ¿dónde está? ¿cuándo la creó? ¿cuándo fue dimensionada? Lo que si está claro es que si fuera local de strtok no podría regresarlo pues se destruiría nada mas volver de la función, pero está ahí aunque no se puede acceder a él por su nombre, sólo por su puntero. El dato está oculto.

Loretz

#2
Citar¿Qué significa "ocultar nombres" y que "no son visibles"?

En general creo que conviene tener algún cuidado y no olvidar que lo que se está leyendo es una traducción; así que al estilo de los autores, no siempre la mejor prosa posible, naturalmente, también hay que añadir la buena o mala suerte del traductor.

Yo entiendo que en la frase que citas, la relación es:
Las variables no son visibles (a los meros usuarios de getch y de ungetch) porque tienen sus nombres ocultos (gracias a que han sido declaradas "static").

El problema que veo en el ejemplo es que ni buf ni bufp están declaradas "static", lo que no ayuda mucho al entendimiento. Pero si pones:

#include <stdio.h>
#define BUFSIZE 100

static char buf[BUFSIZE];  ///< ahora buf esta declarada "static"
static int bufp = 0;       ///< bufp tambien.            
itn getch(void) {
  ...
}

void ungetch(int) {
  ...
}


Pero... aunque las variables no fueran declaradas "static", de todos modos sus nombres tampoco serían "visibles" fuera del archivo getch.c, a no ser que se las declare como "extern" en un header, por ejemplo.

Como ves, la cosa no es muy lineal, ahora además de "static" apareció "extern". Bueno, creo que hubiera convenido presentar antes la declaración "extern", que hace que se puedan compartir variables entre archivos. ¿Me disculpas? Te lo dejo a ti.

De todos modos, la idea es que:
Al declarar las variables globales como "static", se está prohibiendo su acceso desde otros archivos (se ocultan sus nombres, ya no serán visibles desde otros archivos, incluso aunque algún pícaro o descuidado las declare "extern")

[Nota]
- Pero, ¿a quién se le ocultan esos nombres, si yo los estoy viendo y supongo que tú también?
- Al compilador hombre, ni a tí ni a mí, al compilador.
[/Nota]

fruz

No entiendo. ¿Qué significaría que SON visibles?

RayR

#4
Kernighan y Ritchie llaman "external" a las variables globales. Para las variables declaradas como extern usan, precisamente, la palabra "extern". En español se tradujeron ambas como "externa(s)", y de ahí que pudiera haber confusión.

Ahora, no sé si copiaste el código incorrecto, pero en mi libro sí aparecen las variables como static:

Citarstatic char buf[BUFSIZE]; /* buffer for ungetch */
static int bufp = 0; /* next free position in buf */

int getch(void) { ... }
void ungetch(int c) { ... }

En cuanto a tu duda: cuando declaras variables globales no static, de forma implícita tienen enlace externo, es decir, son accesibles ("visibles") fuera del archivo fuente en el que están declaradas (aunque no hayas usado la palabra extern). Esto quiere decir que si declaras variables globales no static con el mismo nombre en distintos archivos, en realidad se refieren a la misma variable. Cualquier modificación que hagas dentro de uno, afectará los otros. En esencia, estás compartiendo la variable a lo largo de más de un archivo. Al declararlas static, como pasa con buf y bufp, se "ocultan", es decir, sólo existen en el archivo en que se declararon. Cualquier otra variable global declarada en otro archivo de tu programa sería independiente de éstas.

Loretz

Tú dices:
CitarMás adelante dice:
"La declaración static, aplicada a una variable o función externa, ...

En esta porción de tu pregunta encuentro dos problemas:

1.- Un error del traductor:
Donde dice:
CitarLa declaración static, aplicada a una variable o función externa, ... "
El original dice:
Citar"The static declaration, applied to an external variable or function, ...."

El problema aquí es que las variables pueden ser "externas" (en el sentido de que no están definidas en el cuerpo de una función sino fuera), pero no las funciones. Quizá pudo haberse traducido por:
La declaración static, aplicada a una variable externa o a una función, ..."

Este punto no hace a la cuestión, pero me pareció que valía la pena aclararlo. El otro problema que encuentro es:

2.- Un descuido tuyo:
Comienzas diciendo
Citar"Más adelante dice: ..."

El problema aquí es que esa frase que citas está incompleta, la primera parte es importante a la idea esta de nombres de cosas "visibles" u "ocultas" para quién y desde dónde.

La frase de la versión en inglés que veo ahora comienza diciendo
Citar"The variables sp and val in stack.c , and buf and bufp in getch.c , are for the private
use of the functions in their respective source files, and are not meant to be accessed by
anything else."

Que aunque no tengo tu traducción, supongo que debe estar de alguna manera. Mal, bien o más o menos, como siempre, pero algo debe decir ¿verdad?. Supongo que si lees esa primera parte te podrás formar una mejor idea sobre a qué se refieren los autores con "visible" o "no visible" en este contexto.



fruz

En el texto dice que las variables buf y bufp "deben ser externas para que puedan ser compartidas, aunque no deben ser visibles a los usuarios de getch y ungetch."
¿Quiénes son los usuarios de getch y ungetch?
¿Cómo es que al declararlas static no son visibles en getch y ungetch, estando en el mismo archivo fuente?

MAFUS

#7
Las variables y funciones globales marcadas como static se comportan cómo las private en JAVA. Sirven para todo el código del archivo fuente pero fuera de él no se puede acceder a ellas.

En cambio las variables y funciones globales marcadas como extern se pueden usar en cualquier sitio del programa.

Por defecto las variables son static y las funciones extern.

http://www.it.uc3m.es/pbasanta/asng/course_notes/variables_es.html
https://www.learncpp.com/cpp-tutorial/42-global-variables/
Te dije que no te quedaras sólo con ese texto porqué es complicado, busca en otras fuentes.

fruz

La parte del libro que dice que con static las variables buf y bufp no son visibles en getch y ungetch, ¿está mal? (Tanto las variables como las funciones están en el mismo archivo fuente.

Serapis

#9
Vamos a lo prosaico...

...tienes un vehículo, lo llenas de combustible y recorres varios días con él el país... de repente el vehículo se para... tu llamas al mecánico y le dices "el motor se ha parado". Abres el capó del motor, y ves eso, una mostruosa masa de metal, que genéricamente llamamos motor. ¿Acaso no se te ocultan todas las piezas internas que tiene?.

El vehículo como tal solo precisa del motor unas pocas cosas; aquellas a las que encesita estar conectadas, para 'recibir' o enviar' (algo). El eje de transmisión, el embrague, el acelerador, el motor de arranque engranando al motor, etc... sin embargo el motor dentro tiene muchas piezas internamente. Cada pieza tiene su función y la función básica del motor es dar vueltas, cad apieza que se accede desde fuera, tiene una funcionalidad precisamente relaccionada de alguna manera con el motor, uno lo pone en marcha, otra lo para, otro le cambia la velocidad de giro, aún otra sin pararlo lo desconecta de la transmisión de giro... toda la funcionalidad de cada pieza interna, está 'invisible' al resto del vehículo. Al vehículo, en sí le importa 3 narices como el motor consigue hacer que dé vueltas, todo lo que precisa son esas 'conexiones' EnMarcha, Paro, Acelera, Frena, Puntomuerto, etc... allá el motor se las vea como conseguir todo eso.

El mecánico no dirá el vehículo-motor se ha parado, el mecánico podría decir más acertadamente, por ejemplo algo como: "se quedó sin combustible", "no le llega 'chispa' para la ignición", "el filtro de aire está obstruído", "las válvulas están desgastadas", "un segmento del segundo cilindro se ha roto y ha tenido consecuencias...", etc...

Cuando el mecánico retire la tapa del motor, verás todas las piezas que tiene, están ocultas, hacen su trabajo y no precisan ser consideradas desde fuera, porque desde fuera nada tiene necesidad de interaccionar con ellas, luego por ello conviene que queden ocultas, evitando en lo posible que alguien (por ejemplo), dejare caer alguna cosa y lo dañara).

En programación viene a ser lo mismo. Para hacer una tarea específíca, para cumplir una determinada funcionalidad puedes necesitar ninguna o muchas variables/funciones adicionales, pero la mayoría quedarán ocultas al que deba usar la funcionalidad, así finalmente lo que haces público, quizás alguna variable y constantes, o alguna función con los parámetros que precise y si acaso una devolución, todo el intríngulis interno que precise para llevar a cabo dicha funcionalidad debe quedar oculta, por varias razones, desde la simple: 'no aburrir al personal con miles de variables (que al final ni podrá cambiar)' hasta la lógica: 'una alteración de cualquiera de ellas arbitrariamente defenestraría los resultados de lo que deba hacer la función'.

Un ejemplo claro:

enumeracion operaciones
  DEC = -1
  RET = 0
  ADD = 1
fin enumeracion

static int total=0
static int n=0

int = public function Acumulador(int valor, operaciones op)
   
   si op = ADD
       total = (total + valor)
       // return 0
   si op =  DEC
       total = total - valor
       // return 0
   ysino
       n = total
       total = valor  // si es un 'suma y sigue', valor sería distinto de 0 y si no sería conforme recibir en valor =0, cuando se pida bascular el resultado.
       return n
   fin si
fin funcion


Esta función (puede resultar tonta como tal, pero que vale para explicar), simplemente sirve como un acumulador, vas sumando o restando valores (positivos o negativos), y en algún momento, cuando ya has terminado... quieres obtener el total, pasas en el parámetro de Op el valor 'RET' (0), y te devuelve el resultado... si la variable 'total'  fueran accesibles desde fuera, se podría cambiar su valor desde fuera en cualquier momento, luego todas las operaciones que has estado, serían malogradas porque el resultado no reflejaría el cómputo de las operaciones que has estado haciendo, no habría forma de garantizar el resultado final a ser lo que las operaciones postulan hacer, pués fue cambiado desde fuera...

Fíjate que el único propósito de 'n' es devolver el resultado, aunque 'n' no fuera declarada estática y fuera accesible desde fuera, su funcionalidad no podría quedar alterada, porque sea el valor que sea que le otorgase desde fuera, cuando se usa internamente se le asigna el valor total y acto seguido se entrega, luego la 'integridad de su funcionalidad' queda a salvo... ahora bien, para qué rayos se precisa dejar accesible esa variable desde fuera????... la  función es: Acumular(23, 1) ... desde fuera 'n' ni tiene utilidad práctca ni su nombre refleja nada útil, luego procede ocultarla... y al caso incluso podría estar encerrada dentro de la propia función ni siquiera en el nivel del módulo.

Todo eso, forma parte de lo que se conoce como 'mecanismos de encapsulación' y que consiste en ocultar todos los detalles que son prescindibles para su uso. Ojo, nota la diferencia entre ocultar y eliminar, ocultar implica que hacen su trabajo, pero desde fuera no se ve... visto desde fuera es casi lo mismo que si no existiera, pero no se ha eliminado. Décadas atrás se tenía muy claro el asunto, pero hasta la llegada de la programación oprientada a objetos, no ha quedado tan patente, la necesidad de la encapsulación y abordarlo desde una perspectiva más ambiciosa... de una forma u otra cada lenguaje tenía sus sentencias, palabras clave, etc... para hacer exactamente eso, encapsular y separar/dividir cada problema en partes lógicas en sí mismas.

Así que ya sabes dos propiedades que tienen las variables:
- Una variable contiene algún dato (sea dle tipo que sea).
- Una variable tiene un 'alcance' determinado. Ese alcance es la visibilidad, es decir desde que otras partes puede ser visto y cambiado.
Fíjate que visto y cambiado son cosas distintas... lo primero hace relación a la 'lectura' la segunda a su 'escritura', así cuando una variable se separa claramente en esos dos aspectos se tiene una 'propiedad' donde existe una variable 'privada' inaccesible desde fuera dle módulo y donde sendas funciones permiten leer y/o escribir su valor... dichas funciones a su vez pueden tener distinto alcance (visibilidad)...

...tu sigue leyendo y ya irás comprendiendo. Puede parecerte un puzzle, pero en algún momento con la colocación de una nueva pieza en el puzzle, acabas por ver y entender la 'imagen' que dicho 'puzzle' encierra... incluso aunque te falten piezas por colocar. ...de repente todo cobra sentido... sigue...