views:

6342

answers:

6

Hello,

I have a header file port.h, port.c, and my main.c

I get the following error: 'ports' uses undefined struct 'port_t'

I thought as I have declared the struct in my .h file and having the actual structure in the .c file was ok.

I need to have the forward declaration as I want to hide some data in my port.c file.

In my port.h I have the following:

/* port.h */
struct port_t;

port.c:

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

main.c:

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

int main(void)
{
struct port_t ports;

return 0;
}

Many thanks for any suggestions,

+13  A: 

Unfortunately, the compiler needs to know the size of port_t (in bytes) while compiling main.c, so you need the full type definition in the header file.

Matthew
apparently you are right. C seem to handle this quite differently than C++.
Johannes Schaub - litb
@litb: what do you mean? I believe in c++, main.cpp would still need a full definition of port_t in order to create the "ports" instance.
Evan Teran
@Matthew: it may be worth clarifying that it's the full _type_ defintion that needs to be in the header file; the variable definition goes in the source file.
Steve Melnikoff
Steve is correct, of course.C++ is the same, even with classes. You need the full class definition in the header so the compiler knows how much space it should reserve on the stack for variables of that type. Only the implementation of member functions need not be in the header file.
Matthew
Evan, in C it is not a struct definition, but a struct declaration if you do struct f { ... }; in C++, that is called a definition, in C there is no such distinction between a struct definition and a struct declaration: both a declarations. the first ("struct f;") declares an incomplete type...
Johannes Schaub - litb
while the second ("struct f { int a; };") declares a complete type struct f. so his first wording "full declaration" was quite correct (for C) i think. for C++, "full definition" would be better because that's precisely what "struct f;" is not in C++.
Johannes Schaub - litb
Evan, C also handles it different regarding incomplete types used in object definition: struct c; struct c f; is completely valid in C, as long as later in the translation unit a declaration of struct c provides the complete type. (until then, struct c remains an incomplete type).
Johannes Schaub - litb
and in between struct c f; and the declaration that provides the complete type, f can be used in limited ways (e.g its address can be taken, but sizeof cannot be applied). In C++, however, "struct c; struct c f;" would be wrong because struct c is an incomplete type. c++ is more restrictive here.
Johannes Schaub - litb
so i said C handles that differently than C++. (declarations can be definitions for objects, enum consts, functions and typedef-names - not for structs/unions in C. But in C++, there is also a class definition (with class/struct class-key). that's kinda confusing, but sadly, that's how it is :)
Johannes Schaub - litb
You can read about that in C99 (i've got the TC2 n1124 document) 6.7 (especially, 6.7/5). i think C surprises me everytime again. rules in it are sometimes a bit surprising for me. but maybe that is just because they are different from the rules of c++.
Johannes Schaub - litb
+6  A: 

If you want to hide the internal data of the port_t structure you can use a technique like how the standard library handles FILE objects. Client code only deals with FILE* items, so they do not need (indeed, then generally can't) have any knowlege of what is actually in the FILE structure. The drawback to this method is that the client code can't simply declare a variable to be of that type - they can only have pointers to it, so the object needs to be created and destroyed using some API, and all uses of the object have to be through some API.

The advantage to this is that you have a nice clean interface to how port_t objects must be used, and lets you keep private things private (non-private things need getter/setter functions for the client to access them).

Just like how FILE I/O is handled in the C library.

Michael Burr
Even the FILE struct is clearly laid out in stdio.h. At least, on my system. "typedef struct __sFILE { ... } FILE;" Via FILE pointers we never need to see their internals (which are very interesting - function pointers for how to read/write/etc.) but they ARE still there.
Chris Lutz
Also note: To deal with FILE * pointers and not struct FILE * pointers, you need to use the "typedef" version. The above code requires "struct port_t". If it were "typedef struct port_t { ... } PORT" then it could use a PORT * pointer similar to FILE * pointers.
Chris Lutz
@Chris, it doesn't have to be declared fully, an implementation could technically define a FILE struct as char[X] since there's nothing in the standard that dictates what the structure contains - so it could be hidden.
paxdiablo
But, sure enough, Chris is right - it seems all my compilers have the full FILE structure defined in stdio.h. I had always assumed they wouldn't - I wonder if there's something that makes them have to, or if it's just convenient and the implementers don't care that the struct is fully available.
Michael Burr
The reason the file structure is laid out in <stdio.h> is so that the macros like getchar() can be defined - the compiler needs the internals.
Jonathan Leffler
A: 

I would recommend a different way:

/* 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;
}

It is generally no good to define variables in a header file.

eaanon01
he wasn't defining a variable in a header, he was trying to forward declare the port_t type.
Evan Teran
Why he is not using get/set or simulair functions beats me... But yea you are correct... I was just giving a suggestion...
eaanon01
+1  A: 

A common solution that I use:

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

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

You use the port_p in function interfaces. You will need to create special malloc (and free) wrappers in port.h as well:

port_p portAlloc(/*perhaps some initialisation args */);
portFree(port_p);
Hans van Eck
A: 

I was trying to debug some open source C code used for sound drivers ( ALSA )... and I could not display the members of a specific struct because the ALSA code was purposely obfuscated:

They defined the struct definition in a .c file... and made the struct definition publicly available by typedef in the .h file!

I did not like their "purposely obfuscation" trick... but maybe it is what you are trying to do.

Trevor Boyd Smith
+1  A: 

Hi, I am having a similar question, I need to provide a C static library to the client and need to be able to make a struct definition unavailable. On top of that I need to be able to execute code before the main at library initialization using a global variable.

Here's my code:

private.h


#ifndef PRIVATE_H
#define PRIVATE_H

typedef struct TEST test;

#endif


private.c (this should end up in a static library)

#include "private.h"
#include <stdio.h>

struct TEST
{
    TEST()
    {
        printf("Execute before main and have to be unavailable to the user.\n");
    }

    int a; // Can be modified by the user
    int b; // Can be modified by the user
    int c; // Can be modified by the user

} TEST;


main.c

test t;

int main( void )
{
    t.a = 0;
    t.b = 0;
    t.c = 0;

    return 0;
}

Obviously this code doesn't work... but show what I need to do... Anybody knows how to make this work? I google quite a bit but can't find an answer, any help would be greatly appreciated.

TIA!

BobMcLaury