ساختارها (Structs)
ساختارها یکی از قدرتمندترین انواع داده مشتقشده در زبان C هستند. یک ساختار به شما امکان میدهد چندین متغیر با انواع متفاوت را در یک واحد واحد گروهبندی کنید. این ویژگی برای مدلسازی موجودیتهای دنیای واقعی (مانند یک دانشجو، یک کتاب، یا یک حساب بانکی) بسیار کاربردی است.
تعریف ساختار
سینتکس پایه برای تعریف یک ساختار:
struct structName { dataType member1; dataType member2; dataType member3; // ... };
مثال:
struct Student { int id; char name[50]; float gpa; int age; };
این ساختار، یک دانشجو را با چهار ویژگی (شماره دانشجویی، نام، معدل و سن) مدلسازی میکند.
تعریف متغیر از نوع ساختار
سه روش برای تعریف متغیر از یک ساختار وجود دارد:
روش اول: تعریف جداگانه
struct Student student1; // تعریف متغیر از نوع struct Student
روش دوم: همزمان با تعریف ساختار
struct Student { int id; char name[50]; float gpa; } student1, student2; // دو متغیر بهصورت همزمان
روش سوم: بدون نام (ناشناس) - یکبار مصرف
struct { int id; char name[50]; } student1; // فقط یک متغیر از این نوع قابل تعریف است
مقداردهی اولیه ساختار
مقداردهی در زمان تعریف:
struct Student student1 = {12345, "Ali Rezaei", 18.5, 22};
مقداردهی با نامگذاری اعضا (C99):
struct Student student1 = { .id = 12345, .name = "Ali Rezaei", .gpa = 18.5, .age = 22 };
مقداردهی جزئی:
struct Student student1 = {12345, "Ali Rezaei"}; // gpa و age صفر میشوند
مقداردهی صفر:
struct Student student1 = {0}; // همه اعضا صفر میشوند
دسترسی به اعضای ساختار
برای دسترسی به اعضای یک ساختار، از عملگر نقطه (.) استفاده میشود:
#include <stdio.h> #include <string.h> struct Student { int id; char name[50]; float gpa; int age; }; int main() { struct Student student1; // مقداردهی به اعضا student1.id = 12345; strcpy(student1.name, "Ali Rezaei"); student1.gpa = 18.5; student1.age = 22; // چاپ اطلاعات printf("شماره دانشجویی: %d\n", student1.id); printf("نام: %s\n", student1.name); printf("معدل: %.2f\n", student1.gpa); printf("سن: %d\n", student1.age); return 0; }
نکته مهم: برای مقداردهی به رشتهها (آرایههای کاراکتری) در ساختار، باید از توابعی مانند
strcpy()استفاده کنید. نمیتوانید مستقیماً از عملگر انتساب (=) استفاده کنید.
آرایهای از ساختارها
میتوانید آرایهای از ساختارها تعریف کنید:
struct Student class[30]; // آرایهای از ۳۰ دانشجو // مقداردهی به اولین دانشجو class[0].id = 12345; strcpy(class[0].name, "Ali Rezaei"); class[0].gpa = 18.5; // پیمایش آرایه for (int i = 0; i < 30; i++) { printf("دانشجو %d: %s\n", i + 1, class[i].name); }
مثال کامل:
#include <stdio.h> #include <string.h> struct Student { int id; char name[50]; float gpa; }; int main() { struct Student class[3] = { {101, "Ali Rezaei", 18.5}, {102, "Sara Mohammadi", 19.2}, {103, "Reza Karimi", 17.8} }; printf("لیست دانشجویان:\n"); for (int i = 0; i < 3; i++) { printf("ID: %d, نام: %s, معدل: %.2f\n", class[i].id, class[i].name, class[i].gpa); } return 0; }
ساختارهای تو در تو
یک ساختار میتواند شامل ساختار دیگری باشد:
struct Date { int day; int month; int year; }; struct Student { int id; char name[50]; struct Date birthDate; // ساختار تو در تو float gpa; }; int main() { struct Student student1 = { 12345, "Ali Rezaei", {15, 8, 2002}, // مقداردهی به ساختار داخلی 18.5 }; // دسترسی به اعضای ساختار داخلی printf("تاریخ تولد: %d/%d/%d\n", student1.birthDate.day, student1.birthDate.month, student1.birthDate.year); return 0; }
اشارهگر به ساختار
میتوانید اشارهگری به یک ساختار تعریف کنید و با استفاده از عملگر -> به اعضا دسترسی پیدا کنید:
#include <stdio.h> #include <string.h> struct Student { int id; char name[50]; float gpa; }; int main() { struct Student student1 = {12345, "Ali Rezaei", 18.5}; struct Student *ptr = &student1; // اشارهگر به ساختار // دسترسی با عملگر -> (ترجیح داده میشود) printf("نام: %s\n", ptr->name); printf("معدل: %.2f\n", ptr->gpa); // دسترسی با عملگر نقطه و اشارهگر (روش جایگزین) printf("ID: %d\n", (*ptr).id); // تغییر مقدار از طریق اشارهگر ptr->gpa = 19.0; printf("معدل جدید: %.2f\n", ptr->gpa); return 0; }
تفاوت بین . و ->:
| عملگر | زمان استفاده | مثال |
|---|---|---|
. | وقتی متغیر از نوع خود ساختار باشد | student1.id |
-> | وقتی اشارهگر به ساختار داشته باشیم | ptr->id |
ارسال ساختار به تابع
روش اول: ارسال با مقدار (کپی)
void printStudent(struct Student s) { printf("نام: %s, معدل: %.2f\n", s.name, s.gpa); } int main() { struct Student s = {12345, "Ali", 18.5}; printStudent(s); // یک کپی از ساختار ارسال میشود return 0; }
روش دوم: ارسال با اشارهگر (کارآمدتر)
void updateGpa(struct Student *s, float newGpa) { s->gpa = newGpa; // تغییرات روی ساختار اصلی اعمال میشود } int main() { struct Student s = {12345, "Ali", 18.5}; updateGpa(&s, 19.0); // آدرس ساختار ارسال میشود printf("معدل جدید: %.2f\n", s.gpa); return 0; }
نکته حرفهای: برای ساختارهای بزرگ، همیشه از اشارهگر استفاده کنید تا از کپیکردن غیرضروری دادهها جلوگیری کنید و کارایی برنامه را بهبود بخشید.
استفاده از typedef با ساختارها
typedef به شما امکان میدهد یک نام مستعار (alias) برای ساختار تعریف کنید و از کلمه struct در تعریف متغیرها صرفنظر کنید:
typedef struct { int id; char name[50]; float gpa; } Student; // نام مستعار int main() { Student s1; // بدون نیاز به کلمه struct s1.id = 12345; strcpy(s1.name, "Ali"); s1.gpa = 18.5; return 0; }
مقایسه با و بدون typedef:
| بدون typedef | با typedef |
|---|---|
struct Student s1; | Student s1; |
struct Student* ptr; | Student* ptr; |
کاربردهای عملی ساختارها
۱. مدیریت کتابخانه:
typedef struct { int id; char title[100]; char author[50]; int year; int isAvailable; } Book; Book library[1000]; // آرایهای از کتابها
۲. سیستم حقوق و دستمزد:
typedef struct { char name[50]; int employeeId; float baseSalary; float bonus; float deductions; } Employee; float calculateNetSalary(Employee *emp) { return emp->baseSalary + emp->bonus - emp->deductions; }
۳. گرافیک و بازیسازی:
typedef struct { float x; float y; float z; } Point3D; typedef struct { Point3D position; Point3D velocity; float mass; char color[10]; } Particle;
نکات پیشرفته
کپی کردن ساختارها:
در C، میتوانید ساختارها را بهراحتی با عملگر انتساب کپی کنید:
struct Student s1 = {12345, "Ali", 18.5}; struct Student s2 = s1; // کپی عمیق (مقادیر همه اعضا کپی میشوند) s2.name = "Reza"; // مشکل! آرایهها را نمیتوان مستقیم انتساب داد
اما اگر ساختار شامل اشارهگر باشد، کپی سطحی (shallow copy) انجام میشود و هر دو اشارهگر به یک آدرس اشاره میکنند.
ساختارهای خودارجاع (Self-Referential):
ساختارها میتوانند به خودشان اشاره کنند که برای ساختن لیستهای پیوندی و درختها استفاده میشود:
typedef struct Node { int data; struct Node* next; // اشارهگر به خود ساختار } Node; int main() { Node n1 = {10, NULL}; Node n2 = {20, NULL}; n1.next = &n2; // اتصال دو گره return 0; }
خطاهای رایج با ساختارها
۱. فراموشی نقطهویرگول بعد از تعریف:
struct Student { int id; char name[50]; } // خطا: نقطهویرگول فراموش شده
۲. استفاده از عملگر = برای رشتهها:
struct Student s; s.name = "Ali"; // خطا: آرایهها را نمیتوان مستقیم انتساب داد strcpy(s.name, "Ali"); // صحیح
۳. فراموشی عملگر & در scanf:
struct Student s; scanf("%d", s.id); // خطا: باید &s.id باشد scanf("%d", &s.id); // صحیح
۴. مقایسه مستقیم ساختارها:
struct Student s1, s2; if (s1 == s2) { // خطا: نمیتوان ساختارها را مستقیم مقایسه کرد // ... }
خلاصه نکات کلیدی
| مفهوم | توضیح |
|---|---|
| تعریف | struct Student { int id; char name[50]; }; |
| متغیر | struct Student s1; یا با typedef سادهتر |
| دسترسی به اعضا | s1.id یا ptr->id |
| مقداردهی | {12345, "Ali", 18.5} یا با نام اعضا |
| آرایه | struct Student class[30]; |
| اشارهگر | struct Student* ptr; |
| تابع | ارسال با اشارهگر برای کارایی بهتر |
| کاربرد اصلی | گروهبندی دادههای مرتبط و مدلسازی موجودیتهای دنیای واقعی |