2009-07-20 21 views
9

En la pregunta Why should we typedef a struct so often in C?, descansar respondió que:¿Cómo puedo ocultar la declaración de una estructura en C?

En este último caso, no se puede volver el Punto de valor, desde su declaración se oculta a los usuarios de el archivo de cabecera. Esta es una técnica ampliamente utilizada en GTK +, por ejemplo.

¿Cómo se cumple la declaración? ¿Por qué no puedo devolver el Punto por valor?

ADD:

entendí por qué no puede devolver el struct por valor, pero, todavía es difícil ver por qué no puedo deferencia este momento de 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 otra vez.

Respuesta

26

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 private.h:

struct Point 
{ 
    int x; 
    int y; 
} 

En archivo private.c:

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

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

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

En cuanto a su otra pregunta: No se puede eliminar la referencia debido a que el programa usando la biblioteca no sólo tienen la información de private.h, que no contiene las declaraciones de 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, al igual que declararía que los miembros de datos de una clase C++ son privados.

5

Lo que quiere decir es que no puede devolver el valor por valor de struct en el encabezado, porque para eso, la estructura debe declararse completamente. Pero eso ocurre en el archivo C (la declaración que hace que X un tipo completo esté "oculto" en el archivo C y no esté expuesto en el encabezado), en su ejemplo.A continuación se declara solamente un tipo incompleto, si esa es la primera declaración de la estructura

struct X; 

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

struct X f(void); 

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

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

El error ocurre porque "x" todavía está incompleto. Ahora, si solo incluye el encabezado con la declaración incompleta en él, entonces no puede llamar a esa función, porque la expresión de la llamada a la función daría como resultado un tipo incompleto, que está prohibido.

Si se va a presentar una declaración del tipo completo struct X en el medio, que sería válida

struct X; 
struct X f(void); 

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

Sería el caso de la forma usando typedef también: Ambos nombre de la misma, (posiblemente incompleta) tipo. Una vez usando un identificador ordinario X, y otra vez usando una etiqueta struct X.

5

En el archivo de cabecera:

typedef struct _point * Point; 

Después de que el compilador ve esto se sabe:

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

El compilador no sabe:

  • lo que la estructura se parece a _point
  • lo que los miembros que contiene
  • lo grande que es

Y no sólo el compilador no lo saben - que como programadores no lo saben ya sea . 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 que el código anterior, se puede escribir funciones como:

Point f() { 
    .... 
} 

porque Point es un puntero y punteros son todos del mismo tamaño & el compilador no necesita saber nada más sobre ellos. Pero no se puede escribir una función que devuelve por valor:

_point f() { 
    .... 
} 

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

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

3

Lo que significa que es posterior: Si ve la cabecera

typedef struct _Point Point; 

Point * point_new(int x, int y); 

entonces usted no conoce los detalles de implementación de Point.

1

Como alternativa al uso de punteros opacos (como han mencionado otros), en su lugar puede devolver una bolsa opaca de bytes si se quiere evitar el uso de la memoria de pila:

// 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 pila en el código del cliente, pero el cliente no sabe qué contiene, lo único que sabe es que se trata de una burbuja de bytes con un tamaño determinado. Por supuesto, todavía 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 las estructuras pthreads uso de la biblioteca de bytes opacos para los tipos como pthread_t, pthread_cond_t, etc. - todavía se puede crear instancias de los de la pila (y por lo general lo hacen), pero no tengo idea de lo que hay en ellos. Solo eche un vistazo a su /usr/include/pthreads.h y los diversos archivos que incluye.

+1

Vas a tener problema de alineación, pública 'struct Point' alineación nativa requisito se basa en el tipo de datos uint8_t, por ejemplo. 1 byte, mientras que el requisito de alineación nativa 'struct 'nativo se basa en' int', probablemente 4 bytes. Intenté explicar el problema en respuesta http://stackoverflow.com/a/17619016/611560 Saludos. – ydroneaud

2

pregunta antiguo, mejor respuesta:

En encabezado del archivo:

typedef struct _Point Point; 

En C del archivo:

struct _Point 
{ 
    int X; 
    int Y; 
}; 
Cuestiones relacionadas