переопределение typedef - PullRequest
       15

переопределение typedef

16 голосов
/ 03 декабря 2011

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

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

// in A.h
typedef struct A_ A;
typedef struct B_ B;
struct A_ {
    double a;
    B *b;
};

// in B.h
typedef struct B_ B;
struct B_ {
    int c;
};

// in C.h
typedef struct A_ A;
typedef struct B_ B;
void function_do_something(A*, B*);

// in C.c
#include "A.h"
#include "B.h"
#include "C.h"
void function_do_something(A* a, B* b) {
    ...
}

Эта парадигма компилируется и запускается в Ubuntu 11.10 gcc - но она дает ошибки компилятора в OpenSUSE gcc, которые говорят "переопределение typedef".

IЯ занимался моим развитием в Убунуту и ​​не осознавал, что эта парадигма может быть неверной.Это просто, что это неправильно и gcc Ubuntu слишком хорош?

Ответы [ 7 ]

22 голосов
/ 03 декабря 2011

Я был удивлен этим, потому что я вполне уверен, что повторное выделение одного и того же typedef в той же области допустимо в C ++, но, очевидно, это недопустимо в C.

Во-первых, имена typedef не имеют связи:

ИСО / МЭК 9899: 1999 + TC3 6.2.6 / 6:

Следующие идентификаторы не имеют связи: идентификатор, объявленный как что-либо, кроме объекта или функции[...]

и 6.7 / 3:

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

Поэтому необходимо убедиться, что каждое объявление typedef появляется только один раз в области действия файла вкаждая единица перевода.

5 голосов
/ 03 декабря 2011

Одна часть идиомы отсутствует.Предварительные объявления не зависят от определений, поэтому они должны находиться в отдельном заголовочном файле.

// a_fwd.h

#ifndef A_FWD_H
#define A_FWD_H

typedef struct A_ A;

#endif

// a.h

#ifndef A_H
#define A_H

#include "a_fwd.h"

struct A_ {
};

#endif

Теперь всегда безопасно включать любые заголовки в любом порядке.


Этонезаконно иметь два определения чего-либо.Typedef - это определение, а не просто объявление, поэтому один компилятор был довольно слаб, чтобы разрешить избыточность.

1 голос
/ 03 декабря 2011

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

//A.h
typedef struct A A;
struct A {
    double a;
    B* b;
};

// B.h
typedef struct B B;
struct B {
    int c;
};

// C.h
void function_do_something(A*, B*);

// C.c
#include "B.h"
#include "A.h"
#include "C.h"

void function_do_something(A* a, B* b){ ... }

Вы можете заметить, что в случае циклических зависимостей это может стать грязным.

1 голос
/ 03 декабря 2011

Так же, как стиль, я бы поместил typedef после структуры.то есть:

struct B_ {
    int c;
};
typedef struct B_ B;

Таким образом, вы говорите: «здесь B_, и теперь я хочу обозначить его как B».Может быть, что-то наоборот обманывает что-то в компиляторе.

0 голосов
/ 03 декабря 2011

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

#ifndef __HEADER_H__
#define __HEADER_H__

// Your code goes here

#endif

Таким образом, этот замок будет исключать ненужные включения.
В вашем примере вам нужно будет включить B в A и A в C. Включение B в C не будет иметь никакого эффекта и удовлетворит компилятор

0 голосов
/ 03 декабря 2011

Вы определяете несколько раз одно и то же.

Вы можете распределить его по нескольким заголовочным файлам, просто убедитесь, что перед тем, как будет определена структура _A, виден какой-то B.

Этот код работает:

#include <stdio.h>

typedef struct _B B;
typedef struct _A A;

struct _A {
    double a;
    B *b;
};

struct _B {
    int c;
};

void function_do_something(A* a, B* b)
{
    printf("a->a (%f) b->c (%d)\n", a->a, b->c);
}

int main()
{
   A a;
   B b;

  a.a = 3.4;
  b.c = 34;

  function_do_something(&a, &b);

  return 0;
}

Выход:

> ./x
a->a (3.400000) b->c (34)

РЕДАКТИРОВАТЬ : обновлено для C

РЕДАКТИРОВАТЬ 2 : разбить на несколько файлов заголовков

b.h:

#ifndef B_H
#define B_H

struct _B {
    int c;
};

#endif

хиджра:

#ifndef A_H
#define A_H

typedef struct _B B;

struct _A {
    double a;
    B *b;
};

typedef struct _A A;

#endif

main.c:

#include <stdio.h>

#include "a.h"
#include "b.h"

void function_do_something(A* a, B* b)
{
    printf("a->a (%f) b->c (%d)\n", a->a, b->c);
}

int main()
{
   A a;
   B b;

  a.a = 3.4;
  b.c = 34; 

  function_do_something(&a, &b);

  return 0;
}
0 голосов
/ 03 декабря 2011

Вы переопределяете A и B, написав одно и то же утверждение в нескольких заголовочных файлах.Одним из решений было бы сбросить typedef A и B с Ah и Bh и использовать Ch как есть.

...