จะประกาศโครงสร้างในส่วนหัวที่จะใช้โดยไฟล์หลาย ๆ ไฟล์ใน c ได้อย่างไร?


115

ถ้าฉันมีไฟล์ source.c ที่มีโครงสร้าง:

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

โครงสร้างนี้สามารถใช้ในไฟล์อื่น (เช่นfunc.c) ได้อย่างไร?

ฉันควรสร้างไฟล์ส่วนหัวใหม่ประกาศโครงสร้างที่นั่นและรวมส่วนหัวนั้นไว้ด้วยfunc.cหรือไม่

หรือฉันควรกำหนดโครงสร้างทั้งหมดในไฟล์ส่วนหัวและรวมไว้ในทั้งสองsource.cและfunc.c? โครงสร้างสามารถประกาศexternในทั้งสองไฟล์ได้อย่างไร?

ควรtypedefหรือไม่ ถ้าเป็นอย่างไร


โปรดทราบว่านิยามโครงสร้างไม่ถูกต้อง C. อย่างน้อยที่สุดควรมีเครื่องหมายอัฒภาคหลังวงเล็บปีกกาปิดstruct bแต่โครงสร้างของคุณจะaประกาศประเภทที่ไม่ได้ใช้ (คุณควรกำหนดชื่อสมาชิกkไว้หลังวงเล็บปีกกาปิดด้านในและ ก่อนอัฒภาค
Jonathan Leffler

คำตอบ:


140

ถ้าโครงสร้างนี้จะใช้กับ func.c ไฟล์อื่นจะทำอย่างไร?

เมื่อใช้ชนิดในไฟล์ (เช่นไฟล์ func.c) จะต้องมองเห็นได้ วิธีที่แย่ที่สุดคือคัดลอกวางในไฟล์ต้นฉบับแต่ละไฟล์ที่จำเป็น

วิธีที่ถูกต้องคือการวางไว้ในไฟล์ส่วนหัวและรวมไฟล์ส่วนหัวนี้ทุกครั้งที่ต้องการ

เราจะเปิดไฟล์ส่วนหัวใหม่และประกาศโครงสร้างที่นั่นและรวมส่วนหัวนั้นไว้ใน func.c หรือไม่?

นี่เป็นวิธีแก้ปัญหาที่ฉันชอบมากกว่าเพราะมันทำให้โค้ดเป็นโมดูลาร์สูง ฉันจะเขียนโครงสร้างของคุณเป็น:

#ifndef SOME_HEADER_GUARD_WITH_UNIQUE_NAME
#define SOME_HEADER_GUARD_WITH_UNIQUE_NAME

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

#endif

ฉันจะใส่ฟังก์ชันที่ใช้โครงสร้างนี้ในส่วนหัวเดียวกัน (ฟังก์ชันที่เป็นส่วน "อินเตอร์เฟส" ของมัน)

และโดยปกติฉันสามารถตั้งชื่อไฟล์ตามชื่อโครงสร้างและใช้ชื่อนั้นอีกครั้งเพื่อเลือกตัวป้องกันส่วนหัวกำหนด

หากคุณต้องการประกาศฟังก์ชันโดยใช้ตัวชี้ไปที่โครงสร้างคุณไม่จำเป็นต้องใช้นิยามโครงสร้างแบบเต็ม การประกาศล่วงหน้าง่ายๆเช่น:

struct a ;

จะเพียงพอและลดการมีเพศสัมพันธ์

หรือเราสามารถกำหนดโครงสร้างทั้งหมดในไฟล์ส่วนหัวและรวมไว้ใน source.c และ func.c ได้หรือไม่

นี่เป็นอีกวิธีหนึ่งที่ง่ายกว่า แต่แบบแยกส่วนน้อย: โค้ดบางตัวที่ต้องการเพียงโครงสร้างของคุณในการทำงานก็ยังต้องรวมทุกประเภท

ใน C ++ สิ่งนี้อาจนำไปสู่ความซับซ้อนที่น่าสนใจ แต่สิ่งนี้ไม่อยู่ในหัวข้อ (ไม่มีแท็ก C ++) ดังนั้นฉันจะไม่อธิบายอย่างละเอียด

แล้วจะประกาศโครงสร้างนั้นเป็นภายนอกในไฟล์ทั้งสองได้อย่างไร ?

บางทีฉันไม่เห็นประเด็น แต่ Greg Hewgill มีคำตอบที่ดีมากในโพสต์ของเขาจะประกาศโครงสร้างในส่วนหัวที่จะใช้โดยไฟล์หลายไฟล์ใน c ได้อย่างไร? .

แล้วเราจะพิมพ์มันยังไง?

  • หากคุณกำลังใช้ C ++ อย่า
  • หากคุณใช้ C คุณควร

เหตุผลที่การจัดการโครงสร้าง C อาจเป็นความเจ็บปวด: คุณต้องประกาศคำหลักโครงสร้างทุกที่ที่ใช้:

struct MyStruct ; /* Forward declaration */

struct MyStruct
{
   /* etc. */
} ;

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

ในขณะที่ typedef จะช่วยให้คุณสามารถเขียนได้โดยไม่ต้องใช้คีย์เวิร์ด struct

struct MyStructTag ; /* Forward declaration */

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

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

เป็นสิ่งสำคัญที่คุณจะต้องตั้งชื่อให้กับโครงสร้าง การเขียน:

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

จะสร้างโครงสร้างที่ไม่ระบุตัวตนด้วยชื่อ typedef-ed และคุณจะไม่สามารถประกาศไปข้างหน้าได้ ดังนั้นให้ใช้รูปแบบต่อไปนี้:

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

ดังนั้นคุณจะสามารถใช้ MyStruct ทุกที่ที่คุณต้องการหลีกเลี่ยงการเพิ่มคีย์เวิร์ด struct และยังคงใช้ MyStructTag เมื่อ typedef ไม่ทำงาน (เช่นการประกาศไปข้างหน้า)

แก้ไข:

แก้ไขสมมติฐานผิดเกี่ยวกับ C99 ประกาศ struct เป็นข้อสังเกตชอบธรรมโดยโจนาธาน Leffler

แก้ไข 2018-06-01:

Craig Barnesเตือนเราในความคิดเห็นของเขาว่าคุณไม่จำเป็นต้องเก็บชื่อแยกต่างหากสำหรับชื่อ struct "tag" และชื่อ "typedef" เหมือนที่ฉันทำข้างต้นเพื่อความชัดเจน

อันที่จริงรหัสข้างต้นสามารถเขียนได้เป็น:

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

IIRC นี่คือสิ่งที่ C ++ ทำกับการประกาศโครงสร้างที่ง่ายกว่าเบื้องหลังเพื่อให้เข้ากันได้กับ C:

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

กลับไปที่ C ฉันเคยเห็นการใช้งานทั้งสองแบบ (ชื่อที่แยกจากกันและชื่อเดียวกัน) และไม่มีข้อเสียใด ๆ ที่ฉันรู้ดังนั้นการใช้ชื่อเดียวกันจะทำให้การอ่านง่ายขึ้นหากคุณไม่ใช้"เนมสเปซ" แยกต่างหากสำหรับโครงสร้างและสัญลักษณ์อื่น ๆ .


2
คุณสามารถแสดงความคิดเห็นหรือชี้ให้เห็นส่วนของมาตรฐาน C99 ที่รับประกันว่า "หากคุณใช้ C99 อย่า" แสดงความคิดเห็น
Jonathan Leffler

คุณถูก. ฉันทดสอบ C99 เมื่อเร็ว ๆ นี้และรู้สึกประหลาดใจที่เห็นโครงสร้างที่ไม่ได้รับการพิมพ์ของฉันไม่รู้จักเมื่อใช้วิธี C ++ ฉันค้นหาตัวเลือกคอมไพเลอร์จากนั้นในเอกสารมาตรฐานทั้งหมดที่ฉันสามารถทำได้ แต่ไม่พบสิ่งใดที่สามารถอธิบายได้ฉันเชื่อว่าเป็นไปได้ ...
paercebal

2
... อย่างไรก็ตามขอบคุณสำหรับข้อสังเกต ตอนนี้ฉันแก้ไขแล้ว
paercebal

ไม่มีความจำเป็นต้องใช้ชื่อที่แตกต่างกันสำหรับเป็นstructแท็กและtypedefชื่อ C ใช้เนมสเปซที่แตกต่างกันสำหรับstructแท็กดังนั้นคุณสามารถใช้MyStructทั้งสองอย่างได้
Craig Barnes

1
@CraigBarnes คุณพูดถูก แต่ฉันต้องการให้ชัดเจนเพียงแค่อ่านรหัส หากฉันตั้งชื่อเดียวกันอาจทำให้มือใหม่ C สับสนว่าจำเป็นต้องเขียนชื่อ * สองครั้ง "ใน" คำประกาศ "เดียวกันฉันจะเพิ่มข้อความที่กล่าวถึงความคิดเห็นของคุณขอบคุณ!
paercebal

34

สำหรับคำนิยามโครงสร้างที่จะใช้กับซอร์สไฟล์มากกว่าหนึ่งไฟล์คุณควรใส่ไว้ในไฟล์ส่วนหัว จากนั้นรวมไฟล์ส่วนหัวนั้นไว้ในไฟล์ต้นฉบับที่ต้องการโครงสร้าง

การexternประกาศไม่ได้ใช้สำหรับนิยามโครงสร้าง แต่ใช้สำหรับการประกาศตัวแปรแทน (นั่นคือค่าข้อมูลบางอย่างที่มีประเภทโครงสร้างที่คุณกำหนดไว้) หากคุณต้องการใช้ตัวแปรเดียวกันกับซอร์สไฟล์มากกว่าหนึ่งไฟล์ให้ประกาศexternในไฟล์ส่วนหัวเช่น:

extern struct a myAValue;

จากนั้นในหนึ่งแฟ้มแหล่งที่มากำหนดตัวแปรที่เกิดขึ้นจริง:

struct a myAValue;

หากคุณลืมทำสิ่งนี้หรือกำหนดโดยไม่ได้ตั้งใจในไฟล์ต้นฉบับสองไฟล์ตัวเชื่อมโยงจะแจ้งให้คุณทราบเกี่ยวกับเรื่องนี้


คุณอาจได้รับข้อผิดพลาดตัวเชื่อมโยงหรืออาจไม่ได้รับ ใน C รูปแบบการเชื่อมโยงอนุญาตให้ใช้คำจำกัดความ "ทั่วไป" ดังนั้นคำจำกัดความหลายคำที่ไม่มีตัวเริ่มต้น (และอาจใช้ตัวเริ่มต้นเดียวกัน) อาจใช้งานได้ มันเป็น 'ส่วนขยายทั่วไป' ฉันเชื่อว่าคอมไพเลอร์บางตัวรองรับคำจำกัดความเบื้องต้น (ขาดหายไป) ด้วย
Jonathan Leffler

จากนั้นในซอร์สไฟล์หนึ่งให้กำหนดตัวแปรจริง:; ... หรือตั้งใจกำหนดในไฟล์ต้นฉบับสองไฟล์ ... :)
Johannes Schaub - litb

เทคนิคหนึ่งที่ฉันใช้สำหรับปัญหาการประกาศ / กำหนดคือการกำหนดGLOBALตามเงื่อนไขเป็นexternหรือไม่มีอะไรที่ด้านบนของส่วนหัวแล้วประกาศตัวแปรเป็นGLOBAL struct a myAValue;. จากไฟล์ที่มาส่วนใหญ่คุณจัดให้มี#define GLOBAL externรุ่นที่จะใช้ ( ประกาศตัวแปร) และจากแฟ้มแหล่งหนึ่งว่ามันทำให้เกิดที่ว่างเปล่ากำหนดที่จะนำมาใช้เพื่อให้ตัวแปรที่มีการกำหนดไว้
TripeHound

คุณสามารถมีชื่อโครงสร้างเหมือนกับชื่อ typedef ใน C แต่ไม่ใช่ใน C ++
xuhdev

5

อา:

#ifndef A_H
#define A_H

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

#endif

เอาล่ะตอนนี้คุณต้องรวม ah เข้ากับไฟล์ที่คุณต้องการใช้โครงสร้างนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.