C'de birden çok dosya tarafından kullanılacak bir başlıktaki bir yapı nasıl bildirilir?


115

Struct içeren bir source.c dosyam varsa:

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

Bu yapı başka bir dosyada (yani func.c) nasıl kullanılabilir ?

Yeni bir başlık dosyası oluşturmalı, yapıyı orada bildirmeli ve bu başlığı da eklemeli func.cmiyim?

Veya tüm yapıyı bir başlık dosyasında tanımlamalı ve bunu hem source.cve hem de eklemeli miyim func.c? Yapı externher iki dosyada nasıl bildirilebilir ?

Yapmalı mıyım typedef? Öyleyse nasıl?


Yapı tanımı En azından geçerli C. için yakın ayracı sonra noktalı virgül olmalıdır olmadığını Not struct bama sonra yapı akullanılmayan bir tür bildirir (muhtemelen belki bir üye adı tanımlamalıdır kiç yakın ayracı sonra ve noktalı virgülden önce.
Jonathan Leffler

Yanıtlar:


140

bu yapı başka bir dosya tarafından kullanılacaksa func.c nasıl yapılır?

Bir dosyada (yani func.c dosyasında) bir tür kullanıldığında, görünür olması gerekir. Bunu yapmanın en kötü yolu, onu ihtiyaç duyulan her kaynak dosyaya kopyalayıp yapıştırmaktır.

Doğru yol, onu bir başlık dosyasına koymak ve gerektiğinde bu başlık dosyasını dahil etmektir.

yeni bir başlık dosyası açıp buradaki yapıyı bildirip bu başlığı func.c'ye ekleyelim mi?

Bu, daha çok sevdiğim çözüm, çünkü kodu oldukça modüler hale getiriyor. Yapınızı şu şekilde kodlardım:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

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

#endif

Bu yapıyı kullanan işlevleri aynı başlığa koyardım ("arayüzünün" anlamsal olarak parçası olan işlev).

Ve genellikle, dosyayı yapı adından sonra adlandırabilir ve bu adı yeniden tanımlayıcı başlık korumalarını seçmek için kullanabilirim.

Yapıya bir işaretçi kullanarak bir işlev bildirmeniz gerekiyorsa, tam yapı tanımına ihtiyacınız olmayacaktır. Şunun gibi basit bir ileriye dönük beyan:

struct a ;

Yeter ve kuplajı azaltır.

veya başlık dosyasında toplam yapıyı tanımlayabilir ve bunu hem source.c hem de func.c'ye ekleyebilir miyiz?

Bu başka bir yoldur, biraz daha kolaydır, ancak daha az modülerdir: Çalışması için yalnızca sizin yapınıza ihtiyaç duyan bazı kodların yine de tüm türleri içermesi gerekir.

C ++ 'da, bu ilginç bir karmaşıklığa yol açabilir, ancak bu konu dışı (C ++ etiketi yok), bu yüzden ayrıntıya girmeyeceğim.

sonra bu yapının her iki dosyada da extern olarak nasıl ilan edileceği. ?

Belki konuyu göremedim ama Greg Hewgill'in gönderisinde çok iyi bir cevabı var. C'de birden fazla dosya tarafından kullanılacak bir başlıktaki bir yapı nasıl ilan edilir? .

daktiloyla yazalım o zaman nasıl?

  • C ++ kullanıyorsanız, kullanmayın.
  • C kullanıyorsanız, kullanmalısınız.

C struct yönetiminin bir acı olmasının nedeni: struct anahtar sözcüğünü kullanıldığı her yerde bildirmeniz gerekir:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

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

Typedef ise struct anahtar sözcüğü olmadan yazmanızı sağlar.

struct MyStructTag ; /* Forward declaration */

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

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

Yapı için hala bir isim tutmanız önemlidir . Yazı:

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

sadece yazılı bir adla anonim bir yapı oluşturacak ve bunu ileri-bildiremeyeceksiniz. Bu nedenle, aşağıdaki biçimi koruyun:

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

Böylece, struct anahtar sözcüğünü eklemekten kaçınmak istediğiniz her yerde MyStruct'ı kullanabilir ve bir typedef çalışmadığında (yani ileriye dönük bildirim) MyStructTag'i kullanmaya devam edebilirsiniz.

Düzenle:

Jonathan Leffler'ın haklı olarak belirttiği gibi, C99 yapı bildirimi hakkındaki yanlış varsayım düzeltildi .

2018-06-01'i düzenleyin:

Craig Barnes yorumunda bize yapı "etiket" adı ve "typedef" adı için ayrı adlar tutmanıza gerek olmadığını hatırlatır, yukarıda açıklık uğruna yaptığım gibi.

Aslında, yukarıdaki kod şu şekilde yazılabilir:

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

IIRC, bu aslında C ++ 'nın C ile uyumlu kalmasını sağlamak için perde arkasında daha basit yapı bildirimiyle yaptığı şeydir:

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

C'ye dönersek, her iki kullanımı da gördüm (ayrı isimler ve aynı isimler) ve hiçbirinin bildiğim dezavantajları yok, bu yüzden aynı ismi kullanmak , yapılar ve diğer semboller için C ayrı "isim alanları" kullanmıyorsanız okumayı kolaylaştırır .


2
C99 standardının "C99 kullanıyorsanız, yapmayın" yorumunuzu garanti eden bölümü hakkında yorum yapabilir veya işaret edebilir misiniz?
Jonathan Leffler

Haklısın. Yakın zamanda bir C99'u test ettim ve tiplenmemiş yapımın C ++ yolu kullanıldığında tanınmadığını görünce şaşırdım. Derleyici seçeneklerini aradım ve sonra, tüm standart belgelerde ellerimi elime koydum, ancak bunun mümkün olduğuna inandığımı açıklayacak hiçbir şey bulamadım ...
paercebal

2
... Her neyse, yorumunuz için teşekkürler. Şimdi düzelttim.
paercebal

structEtiket ve isim için farklı isimler kullanmaya gerek yoktur typedef. C, structetiketler için farklı bir ad alanı kullanır , böylece MyStructher ikisi için de kullanabilirsiniz .
Craig Barnes

1
@CraigBarnes Haklısın, ama sadece kodu okuyarak bunun net olmasını istedim. Aynı adı vermiş olsaydım, C yeni başlayanları aynı "beyanda" adı iki kez "iki kez yazmaları gerektiği konusunda kafalarını karıştırabilirdi.
Yorumunuzu

34

Birden fazla kaynak dosyada kullanılacak bir yapı tanımı için mutlaka bir başlık dosyasına koymalısınız. Ardından, bu başlık dosyasını, yapıya ihtiyaç duyan herhangi bir kaynak dosyaya ekleyin.

externBildirimi yapısı tanımları için kullanılmaz, fakat bunun yerine (olduğunu, bir yapı türü ile bir veri değeri tanımlandığı ki) değişken bildirimleri için kullanılır. Aynı değişkeni birden fazla kaynak dosyada kullanmak istiyorsanız, aşağıdaki gibi externbir başlık dosyasında olduğu gibi bildirin:

extern struct a myAValue;

Ardından, bir kaynak dosyada gerçek değişkeni tanımlayın:

struct a myAValue;

Bunu yapmayı unutursanız veya yanlışlıkla iki kaynak dosyada tanımlarsanız, bağlayıcı size bu konuda bilgi verecektir.


Bir bağlayıcı hatası alabilirsiniz veya almayabilirsiniz. C'de, bağlantı modeli 'ortak' tanımlara izin verir, bu nedenle başlatıcı içermeyen (ve belki aynı başlatıcıyla) birden çok tanım çalışabilir. Bu, "ortak bir uzantı" dır. Bazı derleyicilerin geçici (eksik) tanımları da desteklediğine inanıyorum.
Jonathan Leffler

Ardından, bir kaynak dosyada gerçek değişkeni tanımlayın :; ... ya da yanlışlıkla iki kaynak dosyada tanımlayın ... :)
Johannes Schaub - litb

Açıklama / tanımlama sorunu için kullandığım bir teknik , başlığın üstünde koşullu GLOBALolarak externveya hiçbir şey tanımlamamak ve ardından değişkenleri olarak bildirmektir GLOBAL struct a myAValue;. Çoğu kaynak dosyadan, #define GLOBAL externsürümün kullanılmasını ( değişkenleri bildirerek ) düzenlersiniz ve tam olarak tek bir kaynak dosyadan, değişkenlerin tanımlanması için boş tanımlamanın kullanılmasına neden olur .
TripeHound

Yapı adını C'deki typedef adıyla aynı olabilir, ancak C ++ 'da alamazsınız.
xuhdev

5

Ah:

#ifndef A_H
#define A_H

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

#endif

işte böyle, şimdi bu yapıyı kullanmak istediğiniz dosyalara ah eklemeniz gerekiyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.