¿Cómo puedo ocultar la statement de una estructura en C?

En la pregunta ¿Por qué deberíamos tipearf una estructura tan a menudo en C? , desenvuelto contestó que:

En este último caso, no puede devolver el Punto por valor, ya que su statement está oculta para los usuarios del archivo de encabezado. Esta es una técnica utilizada ampliamente en GTK +, por ejemplo.

¿Cómo se realiza la ocultación de declaraciones? ¿Por qué no puedo devolver el punto por valor?

AÑADIR:

Comprendí por qué no puedo devolver la estructura por valor, pero aún es difícil ver por qué no puedo deferir este punto en mi función. es decir, si mi estructura tiene un miembro llamado y, ¿por qué no puedo hacerlo?

pointer_to_struct->y = some_value; 

¿Por qué debería usar métodos para hacerlo? (Como Gtk +)

Gracias chicos, y lo siento por mi mal inglés de nuevo.

Eche un vistazo a este ejemplo de una biblioteca, utilizando un archivo de encabezado público, un archivo de encabezado privado y un archivo de implementación.

En archivo public.h :

 struct Point; Point* getSomePoint(); 

En archivo privado.h :

 struct Point { int x; int y; } 

En el archivo private.c :

 Point* getSomePoint() { /* ... */ } 

Si combinas estos tres archivos en una biblioteca, solo le das public.h y el archivo de objeto de biblioteca al consumidor de la biblioteca.

getSomePoint tiene que devolver un puntero a Point , porque public.h no define el tamaño del Point , solo que es una estructura y que existe. Los consumidores de la biblioteca pueden usar punteros para Point , pero no pueden acceder a los miembros ni copiarlos, porque no conocen el tamaño de la estructura.

Con respecto a su pregunta adicional: No puede desreferenciarse porque el progtwig que usa la biblioteca solo tiene la información de private.h , que no contiene las declaraciones de los miembros. Por lo tanto, no puede acceder a los miembros de la estructura de puntos.

Puede ver esto como la característica de encapsulación de C, tal como declararía que los miembros de datos de una clase de C ++ son privados.

Lo que quiere decir es que no puede devolver la estructura por valor en el encabezado, porque para eso, la estructura debe estar completamente declarada. Pero eso sucede en el archivo C (la statement que hace que X sea ​​un tipo completo está “oculta” en el archivo C, y no está expuesta en el encabezado), en su ejemplo. Lo siguiente declara solo un tipo incompleto, si esa es la primera statement de la estructura

 struct X; 

A continuación, puede declarar la función.

 struct X f(void); 

Pero no puede definir la función, porque no puede crear una variable de ese tipo, y mucho menos devolverla (se desconoce su tamaño).

 struct X f(void) { // <- error here // ... } 

El error ocurre porque "x" todavía está incompleta. Ahora, si solo incluye el encabezado con la statement incompleta, entonces no puede llamar a esa función, ya que la expresión de la llamada de función produciría un tipo incompleto, lo que está prohibido.

Si tuviera que proporcionar una statement del tipo completo de struct X en medio, sería válido

 struct X; struct X f(void); // ... struct X { int data; }; struct X f(void) { // valid now: struct X is a complete type // ... } 

Esto se aplicaría a la forma en que también se usa typedef : Ambos nombran el mismo tipo (posiblemente incompleto). Una vez usando un identificador ordinario X , y otra vez usando una struct X etiqueta struct X

En el archivo de cabecera:

 typedef struct _point * Point; 

Después de que el comstackdor ve esto, sabe:

  • hay una estructura llamada _point
  • Hay un tipo de puntero que puede referirse a un punto.

El comstackdor no sabe:

  • cómo se ve la estructura _point
  • qué miembros contiene
  • qué tan grande es

Y no solo el comstackdor no lo sabe, nosotros como progtwigdores tampoco lo sabemos. Esto significa que no podemos escribir código que dependa de esas propiedades de _point, lo que significa que nuestro código puede ser más portátil.

Dado el código anterior, puede escribir funciones como:

 Point f() { .... } 

porque Point es un puntero y los punteros son todos del mismo tamaño y el comstackdor no necesita saber nada más sobre ellos. Pero no puedes escribir una función que devuelva por valor:

 _point f() { .... } 

porque el comstackdor no sabe nada sobre _point, específicamente su tamaño, lo que necesita para construir el valor de retorno.

Por lo tanto, solo podemos referirnos a _point mediante el tipo de Punto, que es realmente un puntero. Esta es la razón por la cual el Estándar C tiene tipos como ARCHIVO, a los que solo se puede acceder mediante un puntero: no puede crear una instancia de estructura de ARCHIVO en su código.

Lo que significa ese post es: si ves el encabezado

 typedef struct _Point Point; Point * point_new(int x, int y); 

entonces no conoces los detalles de implementación de Point .

Echa un vistazo a esto: puntero opaco

Vieja pregunta, mejor respuesta:

En el archivo de encabezado:

 typedef struct _Point Point; 

En el archivo C:

 struct _Point { int X; int Y; }; 

Como alternativa al uso de punteros opacos (como han mencionado otros), puede devolver una bolsa de bytes opaca si desea evitar el uso de memoria de stack:

 // In public.h: struct Point { uint8_t data[SIZEOF_POINT]; // make sure this size is correct! }; void MakePoint(struct Point *p); // In private.h: struct Point { int x, y, z; }; void MakePoint(struct Point *p); // In private.c: void MakePoint(struct Point *p) { p->x = 1; p->y = 2; p->z = 3; } 

Luego, puede crear instancias de la estructura en la stack en el código del cliente, pero el cliente no sabe qué contiene, todo lo que sabe es que es un blob de bytes con un tamaño determinado. Por supuesto, aún puede acceder a los datos si puede adivinar las compensaciones y los tipos de datos de los miembros, pero nuevamente tiene el mismo problema con los punteros opacos (aunque los clientes no conocen el tamaño del objeto en ese caso).

Por ejemplo, las diversas estructuras utilizadas en la biblioteca pthreads usan estructuras de bytes opacos para tipos como pthread_t , pthread_cond_t , etc. – todavía puede crear instancias de las que están en la stack (y normalmente lo hace), pero no tiene idea de qué en ellos. Simplemente eche un vistazo a su /usr/include/pthreads.h y los diversos archivos que incluye.