разыменование указателя на неполный тип - PullRequest
59 голосов
/ 23 апреля 2010

Я видел много вопросов по этому поводу, но я собираюсь задать вопрос по-другому, без специального кода. Есть ли способ EASILY определить, что является причиной неполного типа? В моем случае я использую чей-то другой код, и я полностью уверен, что у меня нет правильных заголовков, но (поскольку компьютеры делают это намного быстрее и лучше, чем человеческие глаза), есть способ заставить компилятор сказать "Эй, ты думаю у тебя есть тип X в строке 34, но на самом деле отсутствует ". Сама ошибка появляется только при назначении, что не очень полезно.

Ответы [ 7 ]

46 голосов
/ 23 апреля 2010

Я видел вопрос на днях, когда кто-то непреднамеренно использовал неполный тип, указав что-то вроде

struct a {
    int q; 
}; 
struct A *x; 
x->q = 3;

Компилятор знал, что struct A является структурой, несмотря на то, что A является полностью неопределенным, благодаря ключевому слову struct.

Это было в C ++, где такое использование struct нетипично (и, оказывается, может привести к стрельбе из стопы). В С, если вы делаете

typedef struct a {
    ...
} a;

тогда вы можете использовать a в качестве имени типа и опустить struct позже. Это приведет к тому, что компилятор выдаст вам неопределенную ошибку идентификатора позже, а не неполный тип, если вы неправильно наберете имя или забудете заголовок.

10 голосов
/ 23 апреля 2010

Что вы имеете в виду, ошибка появляется только при назначении? Например, в GCC, без назначения в поле зрения:

int main() {
    struct blah *b = 0;
    *b; // this is line 6
}

incompletetype.c:6: error: dereferencing pointer to incomplete type.

Ошибка - это в строке 6, вот где я использовал неполный тип, как если бы он был полным типом. Я был в порядке до тех пор.

Ошибка в том, что вы должны были включить любой заголовок, определяющий тип. Но компилятор не может угадать, в какую строку он должен был быть включен: любая строка вне функции была бы в порядке, во многом. Он также не будет перебирать каждый текстовый файл в вашей системе, искать заголовок, который его определяет, и предлагать вам включить это.

В качестве альтернативы (хорошая точка, potatoswatter) ошибка находится на строке, где было определено b, когда вы имели в виду , чтобы указать какой-то тип, который действительно существует, но фактически указывает blah. Поиск определения переменной b не должен быть слишком сложным в большинстве случаев. Среды разработки обычно могут сделать это за вас, предупреждения компилятора могут не беспокоить. Однако это довольно отвратительный код, если вы не можете найти определения того, что вы используете.

9 голосов
/ 01 марта 2017

Другая возможная причина - косвенная ссылка. Если код ссылается на структуру, которая не включена в текущий файл c, компилятор будет жаловаться.

a-> b-> c // ошибка, если b не включен в текущий файл c

7 голосов
/ 23 апреля 2010

Я не совсем понимаю, в чем проблема. Неполный тип - это не тот тип, который «отсутствует». Неполный тип - это тип, который объявлен , но не определен (в случае структурных типов). Найти неопределяемое объявление легко. Что касается поиска отсутствующего определения ... компилятор вам здесь не поможет, так как именно это и вызвало ошибку в первую очередь.

Основной причиной неполных ошибок типов в C являются опечатки в именах типов, которые не позволяют компилятору сопоставлять одно имя с другим (как при сопоставлении объявления с определением). Но опять же, компилятор не может вам здесь помочь. Компилятор не догадывается о опечатках.

1 голос
/ 09 января 2012

эта ошибка обычно показывает, отличается ли имя вашей структуры от инициализации вашей структуры в коде, поэтому обычно c находит имя структуры, которую вы поместили, и если исходная структура не найдена, это обычно или если вы наведете указатель на этот указатель, появится ошибка.

0 голосов
/ 13 февраля 2017

Вне возможных сценариев, включающих оптимизацию всей программы, код, сгенерированный для чего-то вроде:

struct foo *bar;
struct foo *test(struct foo *whatever, int blah)
{
  return blah ? whatever: bar;
}

не будет зависеть от того, что могут содержать члены struct foo. Поскольку утилиты make обычно перекомпилируют любой модуль компиляции, в котором появляется полное определение структуры, даже когда такие изменения не могут фактически повлиять на сгенерированный для них код, часто пропускают полные определения структуры из модулей компиляции, которые на самом деле не нужны. их, и такое упущение, как правило, не заслуживает предупреждения.

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

Между прочим, есть еще одна ситуация, когда стандарт позволил бы компилятору требовать, чтобы полное определение объединения было видимым, но не требовал диагностики: если две структуры начинаются с общей начальной последовательности, и вид объединения, содержащий оба, является видимым когда компилятор обрабатывает код, который использует указатель одного из типов структуры для проверки члена этой общей начальной последовательности, компилятор должен распознать, что такой код может обращаться к соответствующему члену структуры другого типа. Я не знаю, какие компиляторы соответствуют стандарту, когда виден полный тип объединения, но не тогда, когда его нет [gcc склонен генерировать несоответствующий код в любом случае, если не используется флаг -fno-strict-aliasing, в в этом случае он будет генерировать соответствующий код в обоих случаях], но если кто-то хочет написать код, который использует правило CIS таким образом, чтобы гарантировать правильное поведение на соответствующих компиляторах, может потребоваться убедиться, что полное определение типа объединения видно; Невыполнение этого требования может привести к тому, что компилятор автоматически создаст фиктивный код.

0 голосов
/ 13 февраля 2017

A - Раствор

Говоря о языке C, я только что нашел эмпирически, что следующий код объявления будет решением;

typedef struct ListNode
{
    int data;
    ListNode * prev;
    ListNode * next;
} ListNode;

Так что, как правило, я даю одно и то же имя как для определения типа, так и для имени структуры;

typedef struct X
{
    // code for additional types here
    X* prev; // reference to pointer
    X* next; // reference to pointer
} X;

B - Проблемные образцы

Где следующие объявления считаются неполными компилятором gcc при выполнении следующего оператора. ;

removed->next->prev = removed->prev;

И я получаю ту же ошибку для кода разыменования, указанного в выводе ошибки;

>gcc Main.c LinkedList.c -o Main.exe -w
LinkedList.c: In function 'removeFromList':
LinkedList.c:166:18: error: dereferencing pointer to incomplete type 'struct ListNode'
     removed->next->prev = removed->prev;

Для обоих заголовочных файлов объявлений, перечисленных ниже;

typedef struct
{
    int data;
    ListNode * prev;
    ListNode * next;
} ListNode;

Плюс этот;

typedef struct ListNodeType
{
    int data;
    ListNode * prev;
    ListNode * next;
} ListNode;
...