C: البنى (struct)

البنية كنموذج مُخصص — تُغلف أنواعًا مختلفة من المعلومات (الاسم، العمر، الدرجة) معًا في وحدة واحدة يمكنك إدارتها ككل.

1. تعريف البنية والإعلان عنها

(1) تعريف نوع بنية

C
struct Student {
    char name[20];
    int age;
    float score;
};

هذا يُعرّف نوعًا اسمه struct Student. لاحظ أن الفاصلة المنقوطة في النهاية مطلوبة.

(2) الإعلان عن متغيرات بنية

C
struct Student s1;
struct Student s2, s3;

يمكنك أيضًا الإعلان عن متغيرات في نفس وقت تعريف النوع:

C
struct Point {
    int x;
    int y;
} p1, p2;

(3) بنية مجهولة

إذا احتجتها مرة واحدة فقط، يمكنك حذف اسم النوع:

C
struct {
    int width;
    int height;
} box;
⚠️ ملاحظة: البنية المجهولة لا يمكن استخدامها للإعلان عن متغيرات جديدة لاحقًا. تُستخدم عادةً للحالات التي لا تتكرر.


2. تهيئة متغير البنية

(1) التهيئة المتسلسلة

TEXT 📖 للعرض فقط
struct Student s1 = {"Zhang", 20, 89.5};

(2) التهيئة المُعيّنة (C99)

C
struct Student s2 = {.name = "Li", .score = 92.0, .age = 21};
💡 نصيحة: التهيئة المُعيّنة لا تحتاج لاتباع ترتيب الإعلان. الأعضاء غير المحددين يُصفّرون تلقائيًا.

(3) التهيئة الجزئية

TEXT 📖 للعرض فقط
struct Student s3 = {"Wang"};

يُهيّأ name؛ وبقية الأعضاء تُضبط تلقائيًا إلى 0.


3. الوصول للأعضاء

(1) عامل النقطة .

الوصول للأعضاء عبر متغير البنية:

C
struct Student s = {"Zhao", 19, 78.5};
printf("Name: %s\n", s.name);
printf("Age: %d\n", s.age);
s.score = 85.0;

(2) عامل السهم ->

الوصول للأعضاء عبر مؤشر البنية:

TEXT 📖 للعرض فقط
struct Student s = {"Zhao", 19, 78.5};
struct Student *ps = &s;
printf("Name: %s\n", ps->name);
ps->age = 20;

ps->name يكافئ (*ps).name. عامل -> هو سكر نحوي — أكثر إيجازًا عند العمل مع المؤشرات.

💡 نصيحة: طريقة سهلة: استخدم نقطة للمتغيرات، واستخدم سهمًا للمؤشرات.

▶ مثال

C
#include <stdio.h>

struct Book {
    char title[50];
    char author[30];
    float price;
};

int main(void) {
    struct Book b1 = {.title = "The C Programming Language", .author = "K&R", .price = 45.0};
    struct Book *pb = &b1;

    printf("Title: %s\n", pb->title);
    printf("Author: %s\n", pb->author);
    printf("Price: %.1f\n", pb->price);

    pb->price = 55.0;
    printf("After discount: %.1f\n", b1.price);
    return 0;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Title: The C Programming Language
Author: K&R
Price: 45.0
After discount: 55.0

4. مصفوفة البنى

وضع بنى متعددة في مصفوفة للإدارة الموحدة.

TEXT 📖 للعرض فقط
struct Student class[3] = {
    {"Zhang", 20, 89.5},
    {"Li", 21, 92.0},
    {"Wang", 19, 78.0}
};

for (int i = 0; i < 3; i++) {
    printf("%s %d %.1f\n", class[i].name, class[i].age, class[i].score);
}
TEXT 📖 للعرض فقط
Zhang 20 89.5
Li 21 92.0
Wang 19 78.0

التنقل في مصفوفة بنى بمؤشر:

C
struct Student *p = class;
for (int i = 0; i < 3; i++, p++) {
    printf("%s %.1f\n", p->name, p->score);
}

5. مؤشر البنية

أشهر استخدام لمؤشرات البنى: تجنب نسخ البنية بالكامل عند تمريرها بين الدوال.

TEXT 📖 للعرض فقط
void print_student(struct Student *ps) {
    printf("%s, age %d, score %.1f\n", ps->name, ps->age, ps->score);
}

int main(void) {
    struct Student s = {"Zhang", 20, 89.5};
    print_student(&s);
    return 0;
}
TEXT 📖 للعرض فقط
Zhang, age 20, score 89.5
⚠️ ملاحظة: التمرير بالقيمة ينسخ البنية بالكامل. إذا كان للبنية أعضاء كثيرون أو تحتوي مصفوفات كبيرة، فالعبء كبير. تمرير مؤشر ينسخ عنوانًا واحدًا فقط.


6. البنية كوسيط دالة

(1) التمرير بالقيمة

C
void add_score(struct Student s, float bonus) {
    s.score += bonus;
}
⚠️ ملاحظة: عند التمرير بالقيمة، التعديلات داخل الدالة تؤثر على نسخة — المتغير الأصلي لا يتغير.

(2) التمرير بالمؤشر

TEXT 📖 للعرض فقط
void add_score(struct Student *s, float bonus) {
    s->score += bonus;
}

التعديلات عبر المؤشر داخل الدالة تؤثر مباشرةً على المتغير الأصلي.

▶ مثال

C
#include <stdio.h>

struct Score {
    char name[20];
    int chinese;
    int math;
    int english;
};

int total(struct Score *s) {
    return s->chinese + s->math + s->english;
}

void boost(struct Score *s, int bonus) {
    s->chinese += bonus;
    s->math += bonus;
    s->english += bonus;
}

int main(void) {
    struct Score stu = {.name = "Zhou", .chinese = 80, .math = 75, .english = 88};
    printf("Original total: %d\n", total(&stu));
    boost(&stu, 5);
    printf("After bonus: %d\n", total(&stu));
    return 0;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Original total: 243
After bonus: 258

7. إسناد البنى ومقارنتها

(1) الإسناد

البنى من نفس النوع يمكن إسنادها مباشرة — يُنسخ الأعضاء واحدًا تلو الآخر:

TEXT 📖 للعرض فقط
struct Student s1 = {"Zhang", 20, 89.5};
struct Student s2;
s2 = s1;

(2) المقارنة

⚠️ ملاحظة: لا يمكن مقارنة البنى بـ == أو !=. يجب مقارنة الأعضاء فرديًا:

C
int equal(struct Student *a, struct Student *b) {
    return a->age == b->age && a->score == b->score;
}

8. البنى المتداخلة

يمكن أن يكون عضو البنية نفسه بنية أخرى:

C
struct Date {
    int year;
    int month;
    int day;
};

struct Employee {
    char name[20];
    struct Date birthday;
    float salary;
};

struct Employee e = {"Chen", {1995, 6, 15}, 8000.0};
printf("Birthday: %d-%02d-%02d\n", e.birthday.year, e.birthday.month, e.birthday.day);
TEXT 📖 للعرض فقط
Birthday: 1995-06-15

❓ أسئلة شائعة

س ما الفرق بين . و ->؟
ج . تصل للأعضاء عبر متغير البنية؛ -> تصل للأعضاء عبر مؤشر البنية. p->name تكافئ (*p).name.
س هل يمكن مقارنة البنى بـ ==؟
ج لا. لغة C لا تدعم المقارنة بـ == للبنى. يجب مقارنة الأعضاء فرديًا أو استخدام memcmp (لكن احذر بايتات الحشو).
س هل أمرر البنية بالقيمة أم بالمؤشر؟
ج عمومًا مرر بالمؤشر — أكثر كفاءة ويسمح بتعديل البيانات الأصلية. لسيناريوهات القراءة فقط، أضف حماية const: const struct Student *s.
س لماذا يجب أن ينتهي تعريف البنية بفاصلة منقوطة؟
ج لأن إعلانات المتغيرات يمكن أن تتبع القوس المغلق مباشرة. الفاصلة المنقوطة تخبر المترجم بأن قائمة الإعلان اكتملت — حذفها يسبب خطأ نحوي.

📖 ملخص

📝 تمارين

  1. عرّف struct Rectangle بالعرض والارتفاع. اكتب دالتين float area(struct Rectangle *r) و float perimeter(struct Rectangle *r)، ثم احسب واطبع النتائج في main
  2. عرّف struct Date واكتب دالة int date_compare(struct Date *a, struct Date *b) تُرجع -1/0/1 تدل على أن a أقدم من / يساوي / أحدث من b
  3. عرّف مصفوفة بنى طلاب (5 طلاب)، رتّبها حسب الدرجة من الأعلى إلى الأقل، واطبع النتائج
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%