неопределенное предварительное объявление структуры C

У меня есть заголовочный файл port.h, port.c и мой main.c

Я получаю следующую ошибку: "ports" использует неопределенную структуру "port_t"

Я подумал, что, поскольку я объявил структуру в своем файле .h, и фактическая структура в файле .c была в порядке.

Мне нужно иметь предварительное объявление, так как я хочу скрыть некоторые данные в моем файле port.c.

В моем port.h у меня есть следующее:

/* port.h */
struct port_t;

порт.c:

/* port.c */
#include "port.h"
struct port_t
{
    unsigned int port_id;
    char name;
};

основной.с:

/* main.c */
#include <stdio.h>
#include "port.h"

int main(void)
{
struct port_t ports;

return 0;
}

Большое спасибо за любые предложения,


person ant2009    schedule 07.03.2009    source источник
comment
Обратите внимание, что односимвольные имена портов не будут очень интересными!   -  person Jonathan Leffler    schedule 07.03.2009


Ответы (4)


К сожалению, компилятору необходимо знать размер port_t (в байтах) при компиляции main.c, поэтому вам нужно полное определение типа в заголовочном файле.

person Matthew    schedule 07.03.2009
comment
видимо ты прав. Кажется, C справляется с этим совершенно иначе, чем C++. - person Johannes Schaub - litb; 07.03.2009
comment
@litb: что ты имеешь в виду? Я считаю, что в С++ main.cpp все равно потребуется полное определение port_t для создания экземпляра портов. - person Evan Teran; 07.03.2009
comment
@Matthew: возможно, стоит уточнить, что полное определение type должно быть в заголовочном файле; определение переменной находится в исходном файле. - person Steve Melnikoff; 07.03.2009
comment
Стив прав, конечно. С++ такой же, даже с классами. Вам нужно полное определение класса в заголовке, чтобы компилятор знал, сколько места он должен зарезервировать в стеке для переменных этого типа. Только реализация функций-членов не обязательно должна быть в заголовочном файле. - person Matthew; 08.03.2009
comment
Эван, в C это не определение структуры, а объявление структуры, если вы делаете struct f { ... }; в C++ это называется определением, в C нет такого различия между определением структуры и объявлением структуры: оба являются объявлениями. первый (struct f;) объявляет неполный тип... - person Johannes Schaub - litb; 08.03.2009
comment
а второй (struct f { int a; };) объявляет полный тип struct f. так что его первая формулировка полной декларации была вполне правильной (для C), я думаю. для C++ полное определение было бы лучше, потому что это именно то, что struct f; нет в С++. - person Johannes Schaub - litb; 08.03.2009
comment
Эван, C также по-разному обрабатывает неполные типы, используемые в определении объекта: struct c; структура cf; полностью действителен в C, если позже в единице перевода объявление struct c предоставляет полный тип. (до этого struct c остается неполным типом). - person Johannes Schaub - litb; 08.03.2009
comment
и между struct c f; и объявление, предоставляющее полный тип, f может использоваться ограниченным образом (например, его адрес может быть взят, но sizeof не может быть применен). Однако в C++ struct c; структура cf; было бы неправильно, потому что struct c является неполным типом. С++ здесь более строгий. - person Johannes Schaub - litb; 08.03.2009
comment
поэтому я сказал, что C обрабатывает это иначе, чем C++. (объявления могут быть определениями для объектов, констант enum, функций и имен typedef - не для структур/объединений в C. Но в C++ также есть определение класса (с class/struct class-key). это немного сбивает с толку, но к сожалению, это так :) - person Johannes Schaub - litb; 08.03.2009
comment
Вы можете прочитать об этом в C99 (у меня есть документ TC2 n1124) 6.7 (особенно 6.7/5). я думаю, что C удивляет меня каждый раз снова. правила в нем меня иногда немного удивляют. но, возможно, это просто потому, что они отличаются от правил С++. - person Johannes Schaub - litb; 08.03.2009

Если вы хотите скрыть внутренние данные структуры port_t, вы можете использовать метод, аналогичный тому, как стандартная библиотека обрабатывает объекты FILE. Клиентский код имеет дело только с элементами FILE*, поэтому ему не нужно (на самом деле, как правило, и не может быть) знать, что на самом деле находится в структуре FILE. Недостатком этого метода является то, что клиентский код не может просто объявить переменную такого типа — он может иметь только указатели на нее, поэтому объект необходимо создавать и уничтожать с помощью некоторого API, и all< /em> использование объекта должно осуществляться через какой-либо API.

Преимущество этого заключается в том, что у вас есть хороший чистый интерфейс для того, как должны использоваться объекты port_t, и позволяет вам сохранять конфиденциальность личных вещей (не частные вещи нуждаются в функциях получения/установки, чтобы клиент мог получить к ним доступ).

Точно так же, как FILE I/O обрабатывается в библиотеке C.

person Michael Burr    schedule 07.03.2009
comment
Даже структура FILE четко выложена в stdio.h. По крайней мере, в моей системе. typedef struct __sFILE { ... } FILE; С помощью указателей FILE нам никогда не нужно видеть их внутренности (которые очень интересны - указатели функций для чтения/записи/и т. д.), но они все еще существуют. - person Chris Lutz; 07.03.2009
comment
Также обратите внимание: чтобы иметь дело с указателями FILE *, а не с указателями struct FILE *, вам нужно использовать версию typedef. Приведенный выше код требует struct port_t. Если бы это был typedef struct port_t { ... } PORT, то он мог бы использовать указатель PORT *, аналогичный указателям FILE *. - person Chris Lutz; 07.03.2009
comment
@Chris, его не нужно объявлять полностью, реализация может технически определить структуру FILE как char [X], поскольку в стандарте нет ничего, что диктовало бы, что содержит структура, поэтому ее можно было бы скрыть. - person paxdiablo; 07.03.2009
comment
Но, конечно же, Крис прав - кажется, все мои компиляторы имеют полную структуру FILE, определенную в stdio.h. Я всегда предполагал, что они не будут - интересно, есть ли что-то, что заставляет их это делать, или это просто удобно, а разработчикам все равно, что структура полностью доступна. - person Michael Burr; 07.03.2009
comment
Причина, по которой файловая структура размещена в ‹stdio.h›, заключается в том, что макросы, такие как getchar(), могут быть определены - компилятору нужны внутренние компоненты. - person Jonathan Leffler; 07.03.2009

Обычное решение, которое я использую:

/* port.h */
typedef struct port_t *port_p;

/* port.c */
#include "port.h"
struct port_t
{
    unsigned int port_id;
    char name;
};

Вы используете port_p в функциональных интерфейсах. Вам также нужно будет создать специальные обертки malloc (и бесплатные) в port.h:

port_p portAlloc(/*perhaps some initialisation args */);
portFree(port_p);
person Hans van Eck    schedule 07.03.2009

Я бы рекомендовал другой способ:

/* port.h */
#ifndef _PORT_H
#define _PORT_H
typedef struct /* Define the struct in the header */
{
    unsigned int port_id;
    char name;
}port_t;
void store_port_t(port_t);/*Prototype*/
#endif

/* port.c */
#include "port.h"
static port_t my_hidden_port; /* Here you can hide whatever you want */
void store_port_t(port_t hide_this)
{
    my_hidden_port = hide_this;
}

/* main.c */
#include <stdio.h>
#include "port.h"
int main(void)
{
    struct port_t ports;
    /* Hide the data with next function*/
    store_port_t(ports);
    return 0;
}

Обычно нецелесообразно определять переменные в заголовочном файле.

person eaanon01    schedule 07.03.2009
comment
он не определял переменную в заголовке, он пытался передать объявление типа port_t. - person Evan Teran; 07.03.2009
comment
Почему он не использует функции get/set или simulair, меня бьет... Но да, вы правы... Я всего лишь предложил... - person eaanon01; 08.03.2009