Bagaimana cara mendeklarasikan struktur di header yang akan digunakan oleh banyak file di c?

115

Jika saya memiliki file source.c dengan struct:

struct a { 
    int i;
    struct b {
        int j;
    }
};

Bagaimana struct ini dapat digunakan di file lain (yaitu func.c)?

Haruskah saya membuat file header baru, mendeklarasikan struct di sana dan memasukkan header itu ke dalam func.c?

Atau haruskah saya mendefinisikan seluruh struct dalam file header dan memasukkannya ke dalam source.cdan func.c? Bagaimana struct dideklarasikan externdi kedua file?

Haruskah saya typedefmelakukannya? Jika ya, bagaimana caranya?

Insinyur Perangkat Lunak
sumber
Perhatikan bahwa definisi struktur tidak valid C. Minimal harus ada titik koma setelah kurung kurawal tutup untuk struct b, tapi kemudian struktur Anda amenyatakan tipe yang tidak digunakan (Anda mungkin harus mendefinisikan nama anggota, mungkin k, setelah kurung kurawal tutup dan sebelum titik koma.
Jonathan Leffler

Jawaban:

140

jika struktur ini akan digunakan oleh beberapa file func.c lainnya bagaimana melakukannya?

Ketika sebuah tipe digunakan dalam sebuah file (yaitu func.c file), itu harus terlihat. Cara terburuk untuk melakukannya adalah menyalin dan menempelkannya di setiap file sumber yang membutuhkannya.

Cara yang benar adalah meletakkannya di file header, dan menyertakan file header ini kapan pun diperlukan.

haruskah kita membuka file header baru dan mendeklarasikan strukturnya di sana dan memasukkan header itu ke dalam func.c?

Ini adalah solusi yang lebih saya sukai, karena membuat kode sangat modular. Saya akan mengkodekan struct Anda sebagai:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

struct a
{ 
    int i;
    struct b
    {
        int j;
    }
};

#endif

Saya akan meletakkan fungsi menggunakan struktur ini di header yang sama (fungsi yang "secara semantik" bagian dari "antarmuka").

Dan biasanya, saya dapat menamai file tersebut setelah nama struktur, dan menggunakan nama itu lagi untuk memilih definisi pelindung header.

Jika Anda perlu mendeklarasikan fungsi menggunakan pointer ke struct, Anda tidak memerlukan definisi struct lengkap. Deklarasi maju sederhana seperti:

struct a ;

Akan cukup, dan itu mengurangi kopling.

atau dapatkah kita mendefinisikan struktur total dalam file header dan memasukkannya ke dalam source.c dan func.c?

Ini adalah cara lain, agak lebih mudah, tetapi kurang modular: Beberapa kode yang hanya membutuhkan struktur Anda untuk bekerja tetap harus menyertakan semua jenis.

Di C ++, ini bisa menyebabkan komplikasi yang menarik, tetapi ini di luar topik (tidak ada tag C ++), jadi saya tidak akan menjelaskannya.

lalu bagaimana mendeklarasikan struktur itu sebagai eksternal di kedua file. ?

Saya gagal untuk memahami intinya, mungkin, tapi Greg Hewgill memiliki jawaban yang sangat bagus dalam postingnya Bagaimana cara mendeklarasikan struktur di header yang akan digunakan oleh banyak file di c? .

haruskah kita mengetiknya lalu bagaimana?

  • Jika Anda menggunakan C ++, jangan.
  • Jika Anda menggunakan C, Anda harus.

Alasannya adalah bahwa pengelolaan struct C bisa menyebalkan: Anda harus mendeklarasikan kata kunci struct di mana pun ia digunakan:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

void doSomething(struct MyStruct * p) /* parameter */
{
   struct MyStruct a ; /* variable */
   /* etc */
}

Sementara typedef akan memungkinkan Anda untuk menulisnya tanpa kata kunci struct.

struct MyStructTag ; /* Forward declaration */

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

void doSomething(MyStruct * p) /* parameter */
{
   MyStruct a ; /* variable */
   /* etc */
}

Penting bagi Anda untuk tetap menggunakan nama struct tersebut. Penulisan:

typedef struct
{
   /* etc. */
} MyStruct ;

hanya akan membuat struct anonim dengan nama typedef-ed, dan Anda tidak akan bisa meneruskan-mendeklarasikannya. Jadi pertahankan format berikut:

typedef struct MyStructTag
{
   /* etc. */
} MyStruct ;

Dengan demikian, Anda akan dapat menggunakan MyStruct di mana pun Anda ingin menghindari penambahan kata kunci struct, dan masih menggunakan MyStructTag ketika typedef tidak berfungsi (yaitu deklarasi maju)

Edit:

Mengoreksi asumsi yang salah tentang deklarasi struct C99, seperti yang dikatakan oleh Jonathan Leffler .

Sunting 01-06-2018:

Craig Barnes mengingatkan kita dalam komentarnya bahwa Anda tidak perlu memisahkan nama untuk nama struct "tag" dan nama "typedef", seperti yang saya lakukan di atas untuk kejelasan.

Memang, kode di atas bisa juga ditulis sebagai:

typedef struct MyStruct
{
   /* etc. */
} MyStruct ;

IIRC, ini sebenarnya yang dilakukan C ++ dengan deklarasi struct yang lebih sederhana, di belakang layar, agar tetap kompatibel dengan C:

// C++ explicit declaration by the user
struct MyStruct
{
   /* etc. */
} ;
// C++ standard then implicitly adds the following line
typedef MyStruct MyStruct;

Kembali ke C, saya telah melihat kedua penggunaan (nama terpisah dan nama yang sama), dan tidak ada yang memiliki kekurangan yang saya ketahui, jadi menggunakan nama yang sama membuat pembacaan lebih sederhana jika Anda tidak menggunakan C "ruang nama" terpisah untuk struct dan simbol lainnya .

paercebal.dll
sumber
2
Dapatkah Anda mengomentari, atau menunjukkan bagian dari standar C99 yang menjamin "Jika Anda menggunakan C99, jangan" berkomentar?
Jonathan Leffler
Kamu benar. Saya menguji C99 baru-baru ini dan terkejut melihat bahwa struct untypedefed saya tidak dikenali saat menggunakan cara C ++. Saya mencari opsi kompiler, dan kemudian, di semua dokumen standar yang dapat saya gunakan, tetapi tidak menemukan apa pun yang dapat menjelaskannya. Saya yakin itu mungkin ...
paercebal
2
... Jadi, terima kasih atas komentarnya. Saya mengoreksinya sekarang.
paercebal
Tidak perlu menggunakan nama yang berbeda untuk structtag dan typedefnama. C menggunakan namespace yang berbeda untuk structtag, sehingga Anda dapat menggunakan MyStructkeduanya.
Craig Barnes
1
@CraigBarnes Anda benar, tapi saya ingin itu menjadi jelas hanya dengan membaca kodenya. Seandainya saya memberikan nama yang sama, pemula C bisa bingung tentang perlunya menulis nama * dua kali "dalam" deklarasi "yang sama. Saya akan menambahkan catatan yang menyebutkan komentar Anda. Terima kasih!
paercebal
34

Untuk definisi struktur yang akan digunakan di lebih dari satu file sumber, Anda harus meletakkannya di file header. Kemudian sertakan file header itu di file sumber apa pun yang membutuhkan struktur.

The externdeklarasi tidak digunakan untuk definisi struktur, tetapi malah digunakan untuk deklarasi variabel (yaitu, beberapa nilai data dengan tipe struktur yang telah ditentukan). Jika Anda ingin menggunakan variabel yang sama di lebih dari satu file sumber, deklarasikan seperti externdi file header seperti:

extern struct a myAValue;

Kemudian, dalam satu file sumber, tentukan variabel sebenarnya:

struct a myAValue;

Jika Anda lupa melakukan ini atau secara tidak sengaja mendefinisikannya dalam dua file sumber, linker akan memberi tahu Anda tentang ini.

Greg Hewgill
sumber
Anda mungkin mendapatkan kesalahan penaut, atau mungkin tidak. Di C, model keterkaitan memungkinkan definisi 'umum', sehingga beberapa definisi tanpa penginisialisasi (dan mungkin dengan penginisialisasi yang sama) dapat berfungsi. Ini adalah 'ekstensi umum'. Beberapa kompiler mendukung definisi tentatif (hilang) juga, saya yakin.
Jonathan Leffler
Kemudian, dalam satu file sumber, tentukan variabel aktual:; ... atau secara tidak sengaja mendefinisikannya dalam dua file sumber ... :)
Johannes Schaub - litb
Salah satu teknik yang saya gunakan untuk mendeklarasikan / mendefinisikan masalah adalah dengan mendefinisikan secara bersyarat GLOBALsebagai externatau tidak sama sekali di bagian atas tajuk dan kemudian mendeklarasikan variabel sebagai GLOBAL struct a myAValue;. Dari sebagian besar file sumber, Anda mengatur #define GLOBAL externversi yang akan digunakan ( mendeklarasikan variabel) dan dari satu file sumber yang tepat, hal itu menyebabkan definisi kosong digunakan sehingga variabel ditentukan .
TripeHound
Anda dapat memiliki nama struktur yang sama dengan nama typedef di C, tetapi tidak di C ++.
xuhdev
6

ah:

#ifndef A_H
#define A_H

struct a { 
    int i;
    struct b {
        int j;
    }
};

#endif

begitulah, sekarang Anda hanya perlu menyertakan ah ke file tempat Anda ingin menggunakan struktur ini.

fmsf
sumber