Почему этот указатель разыменования указывает на неполную ошибку типа?

Я знаю, что этот вопрос устарел, но я не могу понять, что не так с моим кодом.

У меня есть файл tree.c со следующими struct tree, и этот файл включает файл заголовка, в котором объявлен указатель на этот тип структуры:

дерево.с

#include "tree.h"

typedef struct tree
{
    char desig[200];
    int num;
    tree_ptr left, right, subtree;
} Tree;

дерево.ч

#ifndef ___TREE_H___
#define ___TREE_H___

typedef struct tree *tree_ptr;

#endif

Когда я пытаюсь получить доступ к некоторому экземпляру этой структуры в другом исходном файле, компилятор выдает ошибку "указатель разыменования на неполный тип":

insert(..., instance->subtree);

Что случилось?


person PablodeAcero    schedule 20.03.2015    source источник
comment
Возможно, код, вызывающий insert(), видит typedef, но не фактическую структуру?   -  person mah    schedule 20.03.2015
comment
может быть, лучше переместить объявление типа в заголовок.   -  person Jason Hu    schedule 20.03.2015
comment
Когда вы включаете tree.h, скажем, в treeUser.c, спросите себя: видел ли компилятор какое-либо упоминание subtree в treeUser.c? Или subtree это совершенно неизвестная концепция при компиляции treeUser.c?   -  person    schedule 20.03.2015
comment
Вы говорите, что я должен включить заголовок после объявления структуры?   -  person PablodeAcero    schedule 20.03.2015
comment
Не используйте имена, начинающиеся с двойного подчеркивания (или даже с одинарного подчеркивания). Они зарезервированы для использования «реализацией», и вы будете знать, реализуете ли вы «реализацию» (но, подсказка, если вы не пишете компилятор C и его библиотеку поддержки, вы не реализуете « реализация').   -  person Jonathan Leffler    schedule 20.03.2015
comment
Вам необходимо создать MCVE (минимальный, полный, проверяемый пример) или SSCCE (Короткий, автономный, правильный пример), чтобы мы могли воспроизвести вашу проблему. Если вы пытаетесь получить доступ в tree.c, проблем быть не должно. Если вы пытаетесь получить доступ к instance->subtree в каком-то другом исходном файле, а instance является tree_ptr, то вы нарушаете правила, и компилятор прав, наказывая вас.   -  person Jonathan Leffler    schedule 20.03.2015
comment
@JonathanLeffler Да, я пытаюсь точно так же получить доступ к другому исходному файлу. Я включаю заголовок в этот исходный файл. Что я могу сделать, чтобы исправить проблему? Спасибо.   -  person PablodeAcero    schedule 20.03.2015
comment
См. Почему я могу использовать typedef несуществующего типа. По сути, у вас могут быть непрозрачные типы, в которых вы передаете указатели на эти типы, но вы не можете разыменовывать указатели, или у вас могут быть полные типы, детали структуры которых известны. Если вашему коду требуется доступ к внутренностям структуры, вам необходимо включить определение структуры в свой заголовок. Если вы не хотите, чтобы код вне файла реализации имел доступ к деталям, предоставьте функции доступа. См. также Скрыть определение типа.   -  person Jonathan Leffler    schedule 20.03.2015


Ответы (3)


Попробуйте сделать следующий файл tree.h:

   #ifndef  tree_h_
   #define  tree_h_

   typedef struct tree
   {
       char desig[200];
       int num;
       struct tree *left, *right, *subtree;
    } Tree;

   typedef struct tree *tree_ptr;

   #endif

и удаление объявления дерева из файла реализации. Из-за того, что не отображается небольшой работающий MCVE или SSCCE, я не могу протестировать вышеуказанное решение в вашем конкретном случае, однако следующая упрощенная программа компилируется (gcc -ansi -pedantic -Wall tree.c -o tree_test) без предупреждения или ошибки. :

файл tree.c

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


    int main(int argc, char* argv[])
    {
         Tree  root;

         root.left = malloc(sizeof(Tree));
         root.right = malloc(sizeof(Tree));
         root.subtree = NULL;

         if((NULL != root.left) && (NULL != root.right))
         {
             root.left->left = NULL;
             root.left->left = NULL;

             root.right->left = NULL;
             root.right->right = NULL;
         }

         return 0;
    }

Это было протестировано с использованием gcc версии 4.8.2 в системе Centos 7.

Н.Б. Я представил только минималистскую проверку ошибок, без обработки ошибок и кода очистки, чтобы сделать пример коротким. Конечно, в вашем коде вы должны обеспечить полную проверку ошибок, обработку ошибок и очистку.

person thurizas    schedule 20.03.2015

В вашем заголовочном файле есть определение только для типа указателя "tree_ptr". Указатели всегда имеют размер 4 байта на 32-битных машинах, независимо от того, на что они указывают. Итак, пока вы не обращаетесь к членам «struct tree», все в порядке, т.е. когда вы присваиваете значение указателю типа «tree_ptr» или когда проверяете, не является ли указатель типа «tree_ptr» NULL. Такие операции не требуют полного определения структуры. Но если вы хотите получить доступ к членам этой структуры, вашему коду необходимо определение структуры в области видимости. Структуры не похожи на функции, поэтому вы не можете объявить их в заголовке и определить в файле .c. Поэтому я предлагаю вам переместить определение структуры в файл заголовка.

person binW    schedule 20.03.2015

Другие исходные файлы не могут видеть ваш tree.c и абсолютно ничего не знают о том, что находится внутри вашего tree.c. Все, что они могут видеть, это tree.h, который объявляет

typedef struct tree *tree_ptr;

И это объявляет struct tree неполным типом.

Ваш struct tree является полным типом внутри tree.c и только внутри tree.c. Во всех других файлах .c это неполный тип. Если вы хотите, чтобы он был полным во всех исходных файлах, содержащих tree.h, вам придется переместить полное объявление struct tree в tree.h.

person AnT    schedule 20.03.2015