0

I split a C program into multiple files. Here is what they look like.

ListDefinition.h:

#ifndef ALGORITHM_AND_DATASTRUCTURE_LISTDEFINITION_H
#define ALGORITHM_AND_DATASTRUCTURE_LISTDEFINITION_H

extern typedef struct type DataType; //this is a struct declaration about basic data type in the list

extern typedef struct s_list SeqList, *PseqList; //this is a struct declaration about sequence list

extern typedef struct sl_list SlinkList, *PSlinkList; //this is a struct declaration about linked list

#endif //ALGORITHM_AND_DATASTRUCTURE_LISTDEFINITION_H

or I remove extern

#ifndef ALGORITHM_AND_DATASTRUCTURE_LISTDEFINITION_H
#define ALGORITHM_AND_DATASTRUCTURE_LISTDEFINITION_H

typedef struct type DataType; //this is a struct declaration about basic data type in the list

typedef struct s_list SeqList, *PseqList; //this is a struct declaration about sequence list

typedef struct sl_list SlinkList, *PSlinkList; //this is a struct declaration about linked list

#endif //ALGORITHM_AND_DATASTRUCTURE_LISTDEFINITION_H

ListDefinition.c:

#include "ListDefinition.h"
#define MAXSIZE 100
typedef struct type {
    int date;
}DataType;

typedef struct s_list {
    DataType a[MAXSIZE];
    int length;
    int top;
}SeqList, *PseqList;

typedef struct sl_list {
    DataType node;
    struct sl_list *next;
}SlinkList, *PSlinkList;

I want to use ListDefinition.h in ListFunction.h

ListFunction.h:

#ifndef ALGORITHM_AND_DATASTRUCTURE_LISTFUNCTION_H
#define ALGORITHM_AND_DATASTRUCTURE_LISTFUNCTION_H

#include "ListDefinition.h"

PseqList initial_seqList(PseqList PL);//The function be used to initialize the sequence list

int search_seqlist(PseqList PL, DataType x);//the function be used to search the x in the sequence list

#endif //ALGORITHM_AND_DATASTRUCTURE_LISTFUNCTION_H

ListFunction.c:

#include <stdio.h>
#include <stdlib.h>
#include "ListFunction.h"
PseqList initial_seqList(PseqList PL) {
    PL=malloc(sizeof(SeqList));
    if(PL == NULL) {
        exit(1);
        printf("The memory isn't allocated");
    }
    PL->length = 0;
    PL->top = -1;
    return PL;
}
int search_seqlist(PseqList PL, DataType x) {
    int i;
    for(i = 0;i < PL->length; i++) {
        if(PL->a[i].date == x.date)
            break;
    }
    if (i == PL->length)
        return 0;
    else
        return i+1;
}

You don't care about what the codes mean. Many errors appear, but when I change #include "ListDefinition.h" into #include "ListDefinition.c" in ListFunction.h. All errors go away, I want to know why? This problem seems to tell me I should use ListFunction.h. I run the codes in Clion.

Boris
  • 75
  • 8
  • I guess the thing is that `ListDefinition.c` contains the declarations that should have been in the .h file. I have never seen `extern typedef struct` before, and don't know what that should mean. – Bo Persson Nov 04 '17 at 12:15
  • `extern` keyword is for linking data object or functions after compiling. Nevertheless every information required for compilation must be available in the file. Therefore you cannot have something like an `extern` declaration of a type. How should the compiler know what a `SeqList`looks like if you actively hide the definition from it. – Gerhardh Nov 04 '17 at 12:22
  • @Gerhardh If I delete extern? – Boris Nov 04 '17 at 12:30
  • Please, **never ever** update your question in that way, making all comments useless! – Gerhardh Nov 04 '17 at 12:33
  • Removing `extern` does not help. The type definition is still missing. – Gerhardh Nov 04 '17 at 12:34
  • @Gerhardh I get it. Should I delete this question? – Boris Nov 04 '17 at 12:49
  • No. Just add your changes to the question but do not alter the initial question. – Gerhardh Nov 04 '17 at 12:50
  • Note [Is it a good idea to typedef pointers?](https://stackoverflow.com/questions/750178/) — succinctly, the answer is No (unless you're dealing with pointers to functions, perhaps). – Jonathan Leffler Nov 04 '17 at 15:38

1 Answers1

0

The combination of extern typedef is illegal in C. Both keywords are 'storage classes', and you can only have one storage class in a declaration. Drop the extern; there is no storage for a typedef (it doesn't define any object) so there's no 'storage defined elsewhere' for the extern to be useful. If you dropped the typedef, you would be attempting to declare variables of incomplete type.

Your code seems to be trying to implement 'opaque types'. This can be a good technique — if you're careful. However, being careful includes:

  • The client code never needs to dereference the internals of the structure types.
  • The client code never needs to allocate a structure of the type (it will only ever use pointers to the type).
  • The function interface is complete enough to allow the client code to do anything it needs to do.

You might, therefore, define a header like your ListFunction.h which would include the content of ListDefinition.h — the typedefs for the structure types (without the separate header) — and the function declarations. Your client code could use that.

You might have a header (not a source file) equivalent to what's in ListDefinition.c, except that the structure definitions would simply define the structure content; it would not include the typedef or the typedef names. This would be a 'private' header. It would only be used by the code that implements the functions that provide access to the opaque types declared in ListDefinition.h. You'd only create the second header if you needed that information in more than one source file. If the information is only needed in one source file, you'd keep the information in that source file (only).

Your design requires the DataType to be non-opaque. Your function interface requires the calling code to pass a copy of a DataType, and therefore the calling code must be able to allocate storage for a DataType. Further, you don't provide a suite of functions for managing DataType values. Therefore, you have to make that structure visible in the ListFunction.h header.

Using opaque structure pointers is far better than using void pointers in the interface. Using void pointers leaves the code easy to misuse — any pointer of any type can be passed to a function that expects a void * argument. By contrast, if a function expects a SeqList *, you cannot pass a FILE * or a char * or a DataType * to the function in its place (though you could pass something explicitly defined as a void * because void * can be converted, in C (but not C++), to any other object pointer type). Avoid void * when you can.

Note too the discussion in Is it a good idea to typedef pointers? — the succinct answer is 'No'.

Your interface to initial_seqlist() is dubious at best. The implementation shows that you should not take any argument (since the first thing your code does is overwrite the value passed as an argument). It should be SeqList *initial_seqlist(void).

Noting that there are no functions declared to manipulate Slinklist types, the code might become:

listdef11.h

#ifndef LISTDEF11_H_INCLUDED
#define LISTDEF11_H_INCLUDED

typedef struct DataType
{
    int date;
} DataType;

typedef struct SeqList SeqList;

extern SeqList *initial_seqList(void);
extern int search_seqlist(SeqList *PL, DataType x);

/* ... */

#endif /* LISTDEF11_H_INCLUDED */

listdef11.c

#include "listdef11.h"
#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100

struct SeqList
{
    DataType a[MAXSIZE];
    int length;
    int top;
};

SeqList *initial_seqList(void)
{
    SeqList *psl = malloc(sizeof(*psl));
    if (psl == NULL)
    {
        fprintf(stderr, "Failed to allocate %zu bytes of memory in %s()\n",
                sizeof(*psl), __func__);
        exit(1);
    }
    psl->length = 0;
    psl->top = -1;
    return psl;
}

int search_seqlist(SeqList *psl, DataType x)
{
    for (int i = 0; i < psl->length; i++)
    {
        if (psl->a[i].date == x.date)
            return i + 1;
    }
    return 0;
}

There are a number of other changes made in the code. I don't agree with all the design decisions (for example, it appears that the top member is either unused or essentially the same as length; I think it should go). But this at least compiles. Consumers of listdef11.h can do what is needful — or, if the list of support functions declared in listdef11.h was more complete, they could. The list would include a destructor and an inserter at minimum; it would probably need to include more than that. It isn't entirely obvious what users might do with the number returned by search_seqlist().

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278