ti-enxame.com

redefinição de typedef

Possivelmente, estou fazendo isso incorretamente e isso é muito uma pergunta sobre por que ele funciona em um compilador e não no outro.

Eu tenho um aplicativo C grande e estou tentando seguir o estilo de não incluir arquivos de cabeçalho de outros arquivos de cabeçalho. Em vez disso, usando declarações avançadas; portanto, estou tentando o seguinte.

// 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) {
    ...
}

Esse paradigma compila e roda no Ubuntu 11.10 gcc - mas fornece erros de compilação no OpenSUSE gcc que dizem "redefinição de typedef".

Eu tenho feito meu desenvolvimento em Ubunutu e, portanto, não havia percebido que esse paradigma pode estar incorreto. Será que isso está errado e o gcc do Ubuntu está sendo bom demais?

20
BrT

Fiquei surpreso com isso porque tenho certeza de que redeclarar o mesmo typedef no mesmo escopo é legal em C++, mas aparentemente não é legal em C.

Primeiro, os nomes de typedef não têm ligação:

ISO/IEC 9899: 1999 + TC3 6.2.6/6:

Os seguintes identificadores não têm vínculo: um identificador declarado como algo que não seja um objeto ou uma função [...]

e 6,7/3:

Se um identificador não tiver vínculo, não deverá haver mais de uma declaração do identificador (em um declarador ou especificador de tipo) com o mesmo escopo e no mesmo espaço de nome, exceto os tags especificados em 6.7.2.3.

Portanto, você precisa garantir que cada declaração typedef apareça apenas uma vez no escopo do arquivo em cada unidade de tradução.

24
CB Bailey

Falta uma parte do idioma. As declarações de encaminhamento são independentes das definições, portanto, elas devem estar em um arquivo de cabeçalho separado.

// 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

Agora é sempre seguro incluir cabeçalhos em qualquer ordem.


É ilegal ter duas definições de qualquer coisa. Um typedef é uma definição, não apenas uma declaração; portanto, o compilador estava sendo bastante relaxado para permitir a redundância.

5
Potatoswatter

O compilador ubuntu está sendo excessivamente suave; você não pode digitar a mesma coisa duas vezes. No estilo a que você se refere, a ordem das inclusões é importante e geralmente é mencionada como um comentário no arquivo de cabeçalho ou na documentação. Nesse caso, você teria:

//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){ ... }

Você pode notar que isso ficará confuso no caso de dependências cíclicas.

1
Dave

Por uma questão de estilo, eu colocaria o typedef após a estrutura. ou seja:

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

Dessa forma, você está dizendo: "aqui está B_ e agora quero me referir a ele como B". Pode ser que, ao contrário, engane algo no compilador.

1
Rob

Você está redefinindo A e B escrevendo a mesma instrução em vários arquivos de cabeçalho. Uma solução seria remover os typedef de A e B dos A.h e B.h e usar seu C.h como ele é.

0
Jan Henke

Como outros já declararam, você não pode redefinir os tipos em C. Esse erro indica principalmente que pode haver inclusões em loop ou outra falha lógica. Para evitar isso, a melhor prática é bloquear arquivos de inclusão, ou seja,.

#ifndef __HEADER_H__
#define __HEADER_H__

// Your code goes here

#endif

Dessa forma, inclusões desnecessárias serão omitidas por esse bloqueio.
No seu exemplo, você precisaria incluir B em A e A em C. Incluir B em C não teria efeito e satisfaria o compilador

0
friendzis