C: البنى (struct)
البنية كنموذج مُخصص — تُغلف أنواعًا مختلفة من المعلومات (الاسم، العمر، الدرجة) معًا في وحدة واحدة يمكنك إدارتها ككل.
1. تعريف البنية والإعلان عنها
(1) تعريف نوع بنية
struct Student {
char name[20];
int age;
float score;
};
هذا يُعرّف نوعًا اسمه struct Student. لاحظ أن الفاصلة المنقوطة في النهاية مطلوبة.
(2) الإعلان عن متغيرات بنية
struct Student s1;
struct Student s2, s3;
يمكنك أيضًا الإعلان عن متغيرات في نفس وقت تعريف النوع:
struct Point {
int x;
int y;
} p1, p2;
(3) بنية مجهولة
إذا احتجتها مرة واحدة فقط، يمكنك حذف اسم النوع:
struct {
int width;
int height;
} box;
2. تهيئة متغير البنية
(1) التهيئة المتسلسلة
struct Student s1 = {"Zhang", 20, 89.5};
(2) التهيئة المُعيّنة (C99)
struct Student s2 = {.name = "Li", .score = 92.0, .age = 21};
(3) التهيئة الجزئية
struct Student s3 = {"Wang"};
يُهيّأ name؛ وبقية الأعضاء تُضبط تلقائيًا إلى 0.
3. الوصول للأعضاء
(1) عامل النقطة .
الوصول للأعضاء عبر متغير البنية:
struct Student s = {"Zhao", 19, 78.5};
printf("Name: %s\n", s.name);
printf("Age: %d\n", s.age);
s.score = 85.0;
(2) عامل السهم ->
الوصول للأعضاء عبر مؤشر البنية:
struct Student s = {"Zhao", 19, 78.5};
struct Student *ps = &s;
printf("Name: %s\n", ps->name);
ps->age = 20;
ps->name يكافئ (*ps).name. عامل -> هو سكر نحوي — أكثر إيجازًا عند العمل مع المؤشرات.
▶ مثال
#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;
}
Title: The C Programming Language
Author: K&R
Price: 45.0
After discount: 55.0
4. مصفوفة البنى
وضع بنى متعددة في مصفوفة للإدارة الموحدة.
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);
}
Zhang 20 89.5
Li 21 92.0
Wang 19 78.0
التنقل في مصفوفة بنى بمؤشر:
struct Student *p = class;
for (int i = 0; i < 3; i++, p++) {
printf("%s %.1f\n", p->name, p->score);
}
5. مؤشر البنية
أشهر استخدام لمؤشرات البنى: تجنب نسخ البنية بالكامل عند تمريرها بين الدوال.
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;
}
Zhang, age 20, score 89.5
6. البنية كوسيط دالة
(1) التمرير بالقيمة
void add_score(struct Student s, float bonus) {
s.score += bonus;
}
(2) التمرير بالمؤشر
void add_score(struct Student *s, float bonus) {
s->score += bonus;
}
التعديلات عبر المؤشر داخل الدالة تؤثر مباشرةً على المتغير الأصلي.
▶ مثال
#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;
}
Original total: 243
After bonus: 258
7. إسناد البنى ومقارنتها
(1) الإسناد
البنى من نفس النوع يمكن إسنادها مباشرة — يُنسخ الأعضاء واحدًا تلو الآخر:
struct Student s1 = {"Zhang", 20, 89.5};
struct Student s2;
s2 = s1;
(2) المقارنة
== أو !=. يجب مقارنة الأعضاء فرديًا:
int equal(struct Student *a, struct Student *b) {
return a->age == b->age && a->score == b->score;
}
8. البنى المتداخلة
يمكن أن يكون عضو البنية نفسه بنية أخرى:
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);
Birthday: 1995-06-15
❓ أسئلة شائعة
. و ->؟. تصل للأعضاء عبر متغير البنية؛ -> تصل للأعضاء عبر مؤشر البنية. p->name تكافئ (*p).name.==؟== للبنى. يجب مقارنة الأعضاء فرديًا أو استخدام memcmp (لكن احذر بايتات الحشو).const struct Student *s.📖 ملخص
structتُغلف أعضاء من أنواع مختلفة في نوع مُخصص- التهيئة يمكن أن تستخدم قيمًا متسلسلة أو تهيئة مُعيّنة من C99 بصيغة
.name = ... - استخدم
.للوصول للأعضاء عبر المتغير، و->عبر المؤشر - مصفوفات البنى تسمح بالإدارة المجمعة للبيانات من نفس النوع
- يُفضّل تمرير مؤشرات البنى كوسائط دوال لتجنب عبء النسخ
- البنى يمكن تداخلها — يمكن أن يكون العضو نفسه بنية
📝 تمارين
- عرّف
struct Rectangleبالعرض والارتفاع. اكتب دالتينfloat area(struct Rectangle *r)وfloat perimeter(struct Rectangle *r)، ثم احسب واطبع النتائج في main - عرّف
struct Dateواكتب دالةint date_compare(struct Date *a, struct Date *b)تُرجع -1/0/1 تدل على أن a أقدم من / يساوي / أحدث من b - عرّف مصفوفة بنى طلاب (5 طلاب)، رتّبها حسب الدرجة من الأعلى إلى الأقل، واطبع النتائج