قسم تعلم الأساسيات
الفصل الأول: مقدمة إلى لغة C
ما هي لغة C؟
لغة C هي واحدة من أقدم وأقوى لغات البرمجة التي تم تطويرها في
أوائل
السبعينات على يد "دينيس ريتشي" في مختبرات "بيل". تعتبر لغة
C حجر الأساس للعديد من لغات البرمجة الحديثة مثل
C++,
Java، وPython، حيث أثرت بشكل كبير في تصميم هذه اللغات.
تاريخ لغة C وتطورها
ظهرت لغة C سنة 1972 كامتداد للغة B، وتم تطويرها
خصيصًا
لنظام التشغيل UNIX. بفضل مرونتها وقوتها في التعامل مع أنظمة
التشغيل
والبرمجيات المدمجة (Embedded Systems)، أصبحت لغة C الخيار
الأول لتطوير البرامج التي تتطلب أداءً عاليًا.
لماذا تعتبر لغة C مهمة في البرمجة الحديثة؟
- الكفاءة والأداء: تمكنك لغة C من التحكم المباشر في الذاكرة، مما
يجعلها مثالية لتطوير أنظمة التشغيل والتطبيقات التي تحتاج إلى أداء عالٍ.
- المرونة: يمكن استخدامها في برمجة تطبيقات متنوعة، من البرمجيات
المدمجة إلى الألعاب وأنظمة التشغيل.
- التحكم في العتاد: توفر إمكانيات قوية للتعامل مع مكونات الحاسوب
بشكل
مباشر.
- قاعدة معرفية قوية: تعلم C يساعدك على فهم كيفية عمل الحواسيب من
الداخل، وهو ما يسهل عليك تعلم لغات برمجة أخرى لاحقًا.
الفرق بين C ولغات البرمجة الأخرى
- C مقابل Python: بينما تعتبر Python أسهل في التعلم بسبب بساطة
تركيبها اللغوي، تمنحك C أداءً أسرع وتحكمًا أكبر في الموارد.
- C مقابل Java: Java تعتمد على مبدأ البرمجة الكائنية وتعمل
عبر
آلة افتراضية (JVM)، بينما C تُترجم مباشرة إلى لغة الآلة، مما يجعلها أكثر
كفاءة من حيث الأداء.
- C مقابل ++C++: C هي
تطوير للغة C مع إضافة مفاهيم البرمجة الكائنية، لكن C تبقى أبسط وأكثر
قربًا
من مستوى الآلة.
لغة C ليست مجرد لغة برمجة قديمة؛ إنها الأساس الذي بنيت عليه العديد من
التقنيات الحديثة. من خلال هذه الدورة، ستتعلم كيفية كتابة برامج فعالة
والتحكم
الكامل في كيفية عمل البرمجيات على مستوى منخفض.
الفصل الثاني: إعداد بيئة العمل
كيفية تثبيت المترجم (Compiler)
قبل أن تبدأ في كتابة برامج بلغة C، تحتاج إلى تثبيت مترجم يحول الكود
البرمجي
إلى لغة يفهمها الحاسوب. من بين المترجمات الشائعة:
- GCC (GNU Compiler Collection): متاح على أنظمة Linux وmacOS،
ويمكن
تثبيته على Windows باستخدام MinGW.
- Code::Blocks: بيئة تطوير متكاملة (IDE) تدعم C وتأتي مع مترجم
مدمج.
لمشاهدة طريقة تحميل Code Blocks، يمكنك مشاهدة هذا الفيديو.
- +
+Dev-C:
خيار بسيط وسهل الاستخدام لمبتدئي البرمجة.
خطوات تثبيت GCC على Windows:
1. حمل MinGW من الموقع الرسمي.
2. ثبت الحزمة واختر مكون GCC أثناء التثبيت.
3. أضف مسار GCC إلى متغير البيئة PATH لتتمكن من استخدامه من أي مكان في
الجهاز.
4. تأكد من التثبيت بفتح نافذة الأوامر وكتابة:
gcc --version
إذا ظهرت معلومات عن الإصدار، فهذا يعني أن التثبيت تم بنجاح.
(Hello World) C كتابة أول برنامج بلغة
بعد تثبيت المترجم، افتح أي محرر نصوص (مثل VS Code أو
++Notepad) واكتب
الكود التالي:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
لحفظ الكود:
1. احفظ الملف بامتداد `.c`، مثل `hello.c`.
2. افتح نافذة الأوامر وانتقل إلى المجلد الذي يحتوي على الملف.
3. اكتب الأمر التالي لترجمة البرنامج:
gcc hello.c -o hello
4. لتشغيل البرنامج:
./hello
شرح بنية البرنامج في C
- <include <stdio.h#: هذا السطر يسمح باستخدام دوال الإدخال
والإخراج مثل `printf`.
- ()int main: الدالة الرئيسية
التي
يبدأ تنفيذ البرنامج منها.
- printf: لطباعة النصوص على الشاشة.
- return 0: يدل على أن البرنامج
انتهى بنجاح.
بهذا تكون قد أعددت بيئة العمل وكتبت أول برنامج لك بلغة C. في الفصل
التالي،
سنتعرف على أساسيات البرمجة مثل المتغيرات وأنواع البيانات.
الفصل الثالث: المتغيرات وأنواع البيانات
ما هي المتغيرات؟
المتغير هو مساحة في الذاكرة تُستخدم لتخزين البيانات التي يمكن تغيير
قيمتها
أثناء تنفيذ البرنامج. يتم تعريف المتغير باستخدام نوع البيانات المناسب.
أنواع البيانات في C
- int: لتخزين الأعداد الصحيحة.
- float: لتخزين الأعداد العشرية.
- char: لتخزين حرف واحد.
- double: لتخزين أعداد عشرية بدقة أكبر من float.
-bool: لتخزين القيم المنطقية (صحيح أو خطأ).
كيفية تعريف المتغيرات
يتم تعريف المتغيرات باستخدام الصيغة التالية:
data_type : variable_name;
مثال:
int
age;
float temperature;
char grade;
إعطاء القيم للمتغيرات
يمكنك تعيين قيمة لمتغير عند تعريفه أو بعد ذلك:
int age = 25;
float temperature;
temperature = 36.6;
قواعد تسمية المتغيرات
- يجب أن يبدأ اسم المتغير بحرف أو شرطة سفلية (_).
- يمكن أن يحتوي على حروف، أرقام، وشرطات سفلية.
- لا يمكن استخدام الكلمات المحجوزة كلغة C كأسماء متغيرات.
مثال عملي
#include <stdio.h>
int main() {
int age =
30;
float height =
1.75;
char grade =
'A';
printf("Age: %d\n", age);
printf("Height: %.2f\n", height);
printf("Grade: %c\n", grade);
return 0;
}
شرح الكود
- d%: لطباعة الأعداد الصحيحة.
- 2f.%: لطباعة الأعداد العشرية بدقة منزلتين عشريتين.
- c%: لطباعة الحروف.
في الفصل التالي، سنتعرف على العمليات الحسابية وكيفية استخدامها مع
المتغيرات.
الفصل الرابع: العمليات الحسابية والمنطقية
العمليات الحسابية الأساسية في C
لغة C توفر مجموعة من العمليات الحسابية التي تمكنك من إجراء حسابات رياضية
بسهولة:
- الجمع (+): يستخدم لإضافة
رقمين.
- الطرح (-): يستخدم لطرح رقم من آخر.
- الضرب (*): يستخدم لضرب
رقمين.
- القسمة (/): يستخدم لقسمة رقم على آخر.
- باقي القسمة (%): يستخدم للحصول على باقي قسمة رقمين (يعمل مع
الأعداد
الصحيحة فقط).
مثال:
#include <stdio.h>
int main() {
int a =
10, b = 3;
printf("Addition: %d\n", a + b);
printf("Subtraction: %d\n", a - b);
printf("Multiplication: %d\n", a * b);
printf("اDivision: %d\n", a / b);
printf("Modulo: %d\n", a % b);
return 0;
}
أسبقية العمليات
عند وجود أكثر من عملية في تعبير واحد، تتبع لغة C قواعد أسبقية العمليات
(Order of Precedence):
1. الأقواس `()`
2. الضرب والقسمة وباقي القسمة `* / %`
3. الجمع والطرح `+ -`
مثال:
int result = 10+ 5
* 2;
// النتيجة = 20 لأن الضرب له أسبقية
قبل
الجمع
لضمان الترتيب المطلوب:
int result = (10
+ 5)
* 2;
// النتيجة = 30 لأن الأقواس تغير
الأسبقية
العمليات المنطقية (Logical Operators)
تُستخدم العمليات المنطقية للمقارنة بين القيم والتحقق من الشروط:
- يساوي (==): يتحقق إذا كانت القيمتين متساويتين.
- لا يساوي (!=): يتحقق إذا كانت القيمتين غير متساويتين.
- أكبر من (>): يتحقق إذا كانت القيمة الأولى أكبر من الثانية.
- أصغر من (<): يتحقق إذا كانت القيمة الأولى أصغر من الثانية.
- أكبر من أو يساوي (>=): يتحقق إذا كانت القيمة الأولى أكبر أو
تساوي الثانية.
- أصغر من أو يساوي (<=): يتحقق إذا كانت القيمة الأولى أصغر أو
تساوي الثانية.
مثال:
#include <stdio.h>
int main() {
int x =
10, y = 20;
printf("x == y: %d\n", x == y);
printf("x != y: %d\n", x != y);
printf("x > y: %d\n", x > y);
printf("x < y: %d\n", x < y);
printf("x >= y: %d\n", x >= y);
printf("x <= y: %d\n", x <= y);
return 0;
}
العمليات المنطقية المركبة (Logical AND, OR, NOT)
- AND (&&): الشرط صحيح إذا كان الشرطان معًا
صحيحين.
- OR (||): الشرط صحيح إذا كان أحد الشرطين صحيحًا.
- NOT (!): يعكس قيمة الشرط (يحول صحيح إلى خطأ والعكس).
مثال:
#include <stdio.h>
int main() {
int a =
5, b = 10;
printf("AND: %d\n", (a >0&& b >
0));
// صحيح (1) لأن الشرطين صحيحين
printf("OR: %d\n", (a <0|| b >
0));
// صحيح (1) لأن أحد الشرطين صحيح
printf("NOT: %d\n", !(a
>
0));
// خطأ (0) لأن ! تعكس النتيجة
return 0;
}
في هذا الفصل تعلمت كيفية إجراء العمليات الحسابية والمنطقية باستخدام لغة
C.
هذه الأساسيات ستساعدك على بناء برامج أكثر تعقيدًا من خلال التحكم في
البيانات
واتخاذ القرارات. في الفصل القادم، سنتعرف على كيفية التحكم في تدفق
البرنامج
باستخدام الجمل الشرطية.
الفصل الخامس: التحكم في تدفق البرنامج
الجمل الشرطية (Conditional Statements)
تسمح الجمل الشرطية بتنفيذ كتل مختلفة من الكود بناءً على شروط
محددة:
- if: لتنفيذ كود معين إذا تحقق شرط معين.
- else: لتنفيذ كود آخر إذا لم يتحقق الشرط.
- else if: للتحقق من عدة شروط
مختلفة.
مثال:
#include <stdio.h>
int main() {
int number =
10;
if (number >
0) {
printf("The number is positive\n");
}
else if (number <
0) {
printf("The number is negative\n");
}
else {
printf("The number is zero\n");
}
return 0;
}
جملة switch
تُستخدم جملة `switch` لاختيار أحد عدة مسارات بناءً على قيمة
معينة:
مثال:
#include <stdio.h>
int main() {
int
Day = 3;
switch (day)
{
case 1:
printf("Sunday\n");
break;
case 2:
printf("Monday\n");
break;
case 3:
printf("Tuesday\n");
break;
default:
printf("Unknown day\n");
}
return 0;
}
الحلقات (Loops)
تُستخدم الحلقات لتكرار تنفيذ كود معين عدة مرات:
- for loop: لتكرار الكود عددًا
معينًا من المرات.
- while loop: لتكرار الكود
طالما
تحقق شرط معين.
- do- while loop: تضمن تنفيذ
الكود
مرة واحدة على الأقل قبل التحقق من الشرط.
مثال لحلقة for:
#include <stdio.h>
int main() {
for (int
i = 0; i <
5; i++)
{
printf("Number: %d\n", i);
}
return 0;
}
تعلمت في هذا الفصل كيفية التحكم في تدفق البرنامج باستخدام الجمل الشرطية
والحلقات. هذه الأدوات ضرورية لكتابة برامج ديناميكية تتفاعل مع البيانات
والشروط المختلفة. في الفصل القادم، سنتناول التعامل مع المصفوفات وكيفية
استخدامها لتخزين البيانات.
الفصل السادس: المصفوفات (Arrays)
ما هي المصفوفات؟
المصفوفة هي بنية بيانات تسمح بتخزين مجموعة من القيم من نفس النوع
داخل
متغير واحد. يتم الوصول إلى كل عنصر داخل المصفوفة باستخدام رقم الفهرس
(Index).
كيفية تعريف المصفوفات في C
يتم تعريف المصفوفة باستخدام الصيغة التالية:
data_type array_name[array_size];
مثال:
int numbers[5];
// مصفوفة تحتوي على 5 أعداد
صحيحة
إعطاء القيم للمصفوفة
يمكنك إعطاء القيم عند التعريف مباشرة أو باستخدام الفهارس
لاحقًا:
int numbers[5] =
{10, 20,
30, 40, 50};
// تعريف مع إعطاء القيم
numbers[0] =
5;
// تغيير قيمة العنصر
الأول
الوصول إلى عناصر المصفوفة
يتم الوصول إلى عناصر المصفوفة باستخدام الفهارس التي تبدأ من
0:
printf("First number: %d\n", numbers[0]);
// طباعة العنصر الأول
استخدام الحلقات مع المصفوفات
يمكنك استخدام حلقة for للوصول إلى
جميع
عناصر المصفوفة:
#include <stdio.h>
int main() {
int numbers[5] =
{10, 20, 30, 40, 50};
for (int
i = 0; i <
5; i++) {
printf("Element %d: %d\n", i, numbers[i]);
}
return 0;
}
المصفوفات ثنائية الأبعاد
المصفوفة ثنائية الأبعاد تشبه الجدول، حيث تحتوي على صفوف
وأعمدة:
int
matrix[2][3] =
{{1, 2, 3}, {4, 5, 6}};
يمكن الوصول إلى القيم باستخدام فهارس الصفوف والأعمدة:
printf("Element at row 1, column 2: %d\n",
matrix[1][2]); //النتيجة: 6
في هذا الفصل تعلمت كيفية استخدام المصفوفات لتخزين وإدارة البيانات
بكفاءة.
في الفصل القادم، سنتحدث عن المؤشرات (Pointers) وكيفية استخدامها في
لغة C.
الفصل السابع: الهياكل (Structures) والاتحادات (Unions)
في هذا الفصل، سنتعرف على كيفية استخدام الهياكل (Structures)
والاتحادات (Unions) في لغة C، بالإضافة إلى التعدادات
(enums) لتنظيم البيانات بشكل
أكثر كفاءة.
1. ما هي الهياكل (Structures) وكيفية استخدامها؟
تعريف الهياكل
الهياكل (Structures) هي نوع بيانات يسمح لك بتجميع متغيرات
مختلفة
تحت اسم واحد، حتى وإن كانت من أنواع مختلفة. يتم تعريف الهيكل
باستخدام
الكلمة المفتاحية struct.
تعريف هيكل بسيط
#include <stdio.h>
// تعريف الهيكل
struct Person
{
char name[50];
int age;
float height;
};
int main() {
// إنشاء متغير من نوع
الهيكل
struct Person person1
// إسناد القيم إلى أعضاء الهيكل
person1.age = 25;
person1.height = 1.75;
strcpy(person1.name,
"Moussa");
// طباعة القيم
printf("Name: %s\n",
person1.name);
printf("Age: %d\n", person1.age);
printf("Height: %.2f\n", person1.height);
return 0;
}
الوصول إلى أعضاء الهيكل
يتم الوصول إلى أعضاء الهيكل باستخدام النقطة (.).
عند التعامل مع مؤشر إلى هيكل، نستخدم السهم (->).
استخدام مؤشر للوصول إلى الهيكل
#include <stdio.h>
#include <string.h>
struct Person
{
char name[50];
int age;
};
int main() {
struct Person person1 =
{"Bader", 30};
struct Person *ptr
= &person1
printf("Name: %s\n",
ptr->name);
printf("Age: %d\n", ptr->age);
return 0;
}
2. الاتحادات (Unions) والفرق بينها وبين الهياكل
ما هو الاتحاد (Union)؟
الاتحاد (Union) يشبه الهيكل، لكنه يستهلك مساحة ذاكرة أقل، حيث يتم
مشاركة
نفس المساحة بين جميع الأعضاء، مما يعني أن أي تغيير في أحد الأعضاء
سيؤثر
على البقية.
الفرق بين struct و
union
الميزة الهيكل
(Structure)
الاتحاد (Union)
حجم الذاكرة مجموع أحجام
الأعضاء حجم أكبر عضو فقط
الوصول إلى الأعضاء يمكن
استخدام كل
الأعضاء في نفس الوقت يمكن
استخدام
متغير واحد فقط في كل لحظة
الأداء يحتاج إلى مساحة
أكبر
يستخدم ذاكرة أقل
تعريف اتحاد
#include <stdio.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data
data;
data.i = 10;
printf("data.i: %d\n", data.i);
data.f = 220.5;
printf("data.f: %.1f\n", data.f);
strcpy(data.str,
"Hello");
printf("data.str: %s\n", data.str);
return 0;
}
💡 ملحوظة: عند إسناد قيمة جديدة إلى أي عضو، فإن القيمة
القديمة
تُفقد لأنها تشترك في نفس مساحة الذاكرة.
3. التعداد (enums) ودوره في تنظيم
الكود
التعداد (enum) هو طريقة لتعريف مجموعة
من
القيم المنطقية الثابتة التي يتم التعبير عنها بأسماء مفهومة بدلًا من
الأرقام العشوائية.
تعريف تعداد
#include <stdio.h>
enum Day
{
Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, SaturDay
};
int main() {
enum
Day toDay = Monday;
if (toDay ==
Monday) {
printf("It's Monday!\n");
}
return 0;
}
كيفية تخصيص قيم للتعداد
بشكل افتراضي، تبدأ القيم من 0، لكن يمكن تغييرها يدويًا:
enum Status
{
SUCCESS = 1, FAILURE
= 0, PENDING = 2
};
خلاصة
الهياكل (Structures) تُستخدم لجمع بيانات مختلفة تحت متغير
واحد.
الاتحادات (Unions) تشبه الهياكل لكنها تستخدم ذاكرة أقل.
التعداد (enum) يسهل التعامل مع القيم
الثابتة
في البرنامج.
(Memory Management) C الفصل الثامن: إدارة الذاكرة في
في هذا الفصل، سنتعلم كيفية إدارة الذاكرة ديناميكيًا باستخدام دوال
مثل
malloc و calloc لحجز الذاكرة، و free لتحريرها، بالإضافة إلى كيفية
تجنب
مشاكل تسرب الذاكرة (Memory Leaks).
1. الحجز الديناميكي للذاكرة باستخدام malloc و calloc
مقدمة عن الحجز الديناميكي
🔹 في لغة C، هناك نوعان من تخصيص الذاكرة:
التخصيص الثابت (Static Allocation): يتم أثناء وقت الترجمة
(Compile
Time).
التخصيص الديناميكي (Dynamic Allocation): يتم أثناء وقت
التشغيل
(Runtime) باستخدام دوال مثل malloc و calloc.
🔹 التخصيص الديناميكي يسمح بإدارة الذاكرة بكفاءة، حيث يمكن تخصيص
الذاكرة
فقط عند الحاجة، مما يساعد في تحسين أداء البرنامج.
استخدام malloc
🔹 malloc تعني Memory Allocation، وتستخدم لحجز مساحة
في
الذاكرة دون تهيئتها، أي أن القيم المخزنة داخلها ستكون عشوائية.
مثال على malloc
#include <stdio.h>
#include <stdlib.h>
int main() {
int
*ptr;
// حجز ذاكرة تكفي لتخزين 5 أعداد
صحيحة
ptr = (int*)
malloc(5
* sizeof(int));
// التحقق مما إذا كان الحجز ناجحًا
if (ptr ==
NULL) {
printf("Memory allocation failed!\n");
return 1
}
// إدخال القيم في
المصفوفة
for (int
i = 0; i <
5; i++) {
ptr[i] =
i
+ 1
}
// طباعة القيم
printf("Array: ");
for (int
i = 0; i <
5; i++) {
printf("%d ", ptr[i]);
}
// تحرير الذاكرة بعد الانتهاء من
استخدامها
free(ptr);
return 0;
}
💡 ملاحظات:
malloc يقوم بحجز الذاكرة ولكنه لا يقوم بتهيئة القيم، لذا قد تحتوي
على
بيانات عشوائية.
من المهم التحقق من أن malloc لم يُرجع NULL، مما يدل على فشل
الحجز.
استخدام calloc
🔹 calloc تعني Contiguous Allocation، وهي تشبه malloc،
لكنها
تقوم بتهيئة الذاكرة بالقيمة0افتراضيًا.
مثال على calloc
#include <stdio.h>
#include <stdlib.h>
int main() {
int
*ptr;
// تخصيص ذاكرة
لخمس أعداد صحيحة وتهيئتها إلى 0
ptr = (int*)
calloc(5,
sizeof(int));
// التحقق مما إذا كان الحجز ناجحًا
if (ptr ==
NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// طباعة القيم
المخزنة
printf("Array after initialization using calloc: ");
for (int
i = 0; i <
5; i++) {
printf("%d ", ptr[i]);
}
// تحرير الذاكرة بعد الاستخدام
free(ptr);
return 0;
}
💡 مقارنة بين malloc و calloc
الدالة التهيئة بالقيمة 0
أداء أسرع الاستخدام
malloc ❌ لا تهيئ القيم
✅ أسرع عندما لا تكون
القيم
الافتراضية مهمة
calloc ✅ تهيئ القيم إلى
0
❌ أبطأ قليلاً عندما
تحتاج القيم
إلى التهيئة
2. تحرير الذاكرة باستخدام free
بعد استخدام الذاكرة الديناميكية، يجب تحريرها باستخدام free
حتى لا
تبقى مشغولة بدون فائدة.
استخدام free
#include <stdio.h>
#include <stdlib.h>
int main() {
int
*ptr = (int*)
malloc(5
* sizeof(int));
if (ptr ==
NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// بعد الانتهاء من استخدام الذاكرة، نقوم
بتحريرها
free(ptr);
return 0;
}
💡 ملاحظة:
بعد استدعاء free(ptr), يصبح
المؤشر
ptr غير صالح ويجب عدم استخدامه مجددًا إلا بعد إعادة تخصيص
الذاكرة.
3. تجنب مشاكل تسرب الذاكرة (Memory Leaks)
🔹 تسرب الذاكرة يحدث عندما يتم حجز ذاكرة ولكن لم يتم تحريرها
باستخدام
free، مما يؤدي إلى استهلاك زائد للذاكرة.
مثال على تسرب الذاكرة
#include <stdlib.h>
void memoryLeak()
{
int
*ptr = (int*)
malloc(10
* sizeof(int));
// نسيان تحرير الذاكرة يؤدي إلى
تسربها!
}
int main() {
memoryLeak();
return 0;
}
🔴 المشكلة:
عند استدعاء ()memoryLeak، يتم تخصيص ذاكرة لكن لا يتم تحريرها،
مما
يؤدي إلى تسرب الذاكرة.
✅ الحل: تحرير الذاكرة باستخدام
free(ptr) قبل انتهاء الدالة.
مثال على تصحيح المشكلة
#include <stdlib.h>
void memoryLeakFixed() {
int
*ptr = (int*)
malloc(10
* sizeof(int));
if (ptr ==
NULL) {
return;
}
// استخدام البيانات
// تحرير الذاكرة بعد الانتهاء
free(ptr);
}
int main() {
memoryLeakFixed();
return 0;
}
خلاصة:
في هذا الفصل، تعلمنا:
✔ كيفية تخصيص الذاكرة ديناميكيًا باستخدام malloc و calloc.
✔ كيفية تحرير الذاكرة باستخدام free.
✔ كيفية تجنب مشاكل تسرب الذاكرة.
📌 إدارة الذاكرة بشكل صحيح أمر أساسي عند العمل مع لغة C، لأنه يساعد
على
تحسين الأداء وتقليل استهلاك الموارد.
(FILE Handling) C الفصل التاسع: التعامل مع الملفات في
التعامل مع الملفات هو جزء مهم من البرمجة، حيث يمكننا قراءة وكتابة
البيانات
إلى الملفات بدلاً من تخزينها فقط في الذاكرة. في هذا الفصل، سنتعلم
كيفية:
✔ فتح الملفات وإغلاقها باستخدام fopen و fclose.
✔ قراءة البيانات من الملفات باستخدام fgetc، fgets و
fscanf.
✔ كتابة البيانات إلى الملفات باستخدام fprintf، fputc و
fputs.
✔ العمل مع الملفات الثنائية (Binary Files).
1. فتح الملفات وإغلاقها باستخدام fopen و fclose
فتح ملف باستخدام fopen
🔹 fopen تُستخدم لفتح الملفات، وصيغتها كالتالي:
FILE *fp =
fopen("file_name", "mode");
🔹 إذا تم فتح الملف بنجاح، ترجع fopen مؤشرًا إلى الملف، وإلا
فإنها
تُرجع NULL.
الوضع الوظيفة
"r" فتح الملف للقراءة
فقط.
"w" فتح الملف للكتابة (حذف
المحتوى
القديم).
"a" فتح الملف للإلحاق (إضافة
بيانات
جديدة دون حذف القديم).
"r+"
فتح الملف للقراءة والكتابة مع الحفاظ على البيانات.
"w+"
فتح الملف للقراءة والكتابة مع حذف المحتوى القديم.
"a+"
فتح الملف للقراءة والكتابة مع الإضافة دون حذف القديم.
إغلاق الملف باستخدام fclose
بعد الانتهاء من التعامل مع الملف، يجب إغلاقه باستخدام
fclose(fp).
مثال:
فتح ملف وإغلاقه
#include <stdio.h>
int main() {
FILE *fp = fopen("data.txt", "r");
if (fp ==
NULL) {
printf("Failed to open the file!\n");
return 1;
}
printf("FILE opened successfully!\n");
fclose(fp);
return 0;
}
💡 إذا لم يكن الملف موجودًا، وكان الوضع "r"، فإن
fopen ستُرجع NULL.
2. كتابة البيانات إلى الملفات
يمكننا كتابة البيانات إلى الملفات باستخدام عدة دوال:
استخدام fprintf لكتابة نص منسّق
🔹 تعمل fprintf مثل printf، لكنها تكتب البيانات إلى
ملف
بدلاً من الشاشة.
مثال:
كتابة نص إلى ملف
#include <stdio.h>
int main() {
FILE *fp = fopen("output.txt", "w");
if (fp ==
NULL) {
printf("Failed to open the file!\n");
return 1;
}
fprintf(fp,
"Welcome to the world of files!\n");
fprintf(fp,
"This is the second line of text.\n");
fclose(fp);
printf("Writing to the FILE was successful!\n");
return 0;
}
💡 إذا كان الملف موجودًا، فسيتم حذفه وإعادة إنشائه.
استخدام fputc لكتابة حرف واحد
🔹 fputc تستخدم لكتابة حرف واحد إلى الملف.
مثال:
كتابة الأحرف إلى ملف
#include <stdio.h>
int main() {
FILE *fp = fopen("characters.txt", "w");
if (fp ==
NULL) {
printf("Failed to open the file!\n");
return 1;
}
fputc('A', fp);
fputc('\n', fp);
fputc('B', fp);
fclose(fp);
printf("Characters have been written to the file!\n");
return 0;
}
استخدام fputs لكتابة سلسلة نصية
🔹 fputs تعمل مثل puts، لكنها تكتب إلى ملف.
مثال:
كتابة سلسلة نصية
#include <stdio.h>
int main() {
FILE *fp = fopen("text.txt", "w");
if (fp ==
NULL) {
printf("Failed to open the file!\n");
return 1;
}
fputs("Welcome to the world of programming!",
fp);
fputs("\nC is a powerful language!!", fp);
fclose(fp);
return 0;
}
3. قراءة البيانات من الملفات
هناك عدة دوال لقراءة البيانات من الملفات:
استخدام fscanf لقراءة بيانات منسقة
🔹 تعمل fscanf مثل scanf ولكنها تقرأ البيانات من ملف
بدلاً
من إدخال المستخدم.
مثال:
قراءة بيانات من ملف
#include <stdio.h>
int main() {
FILE *fp = fopen("data.txt", "r");
if (fp ==
NULL) {
printf("Failed to open the file!\n");
return 1;
}
char name[50];
int age;
fscanf(fp,
"%s %d", name, &age);
printf("Name: %s, Age: %d\n", name, age);
fclose(fp);
return 0;
}
💡 يجب أن يكون تنسيق البيانات في الملف متوافقًا مع طريقة
القراءة.
استخدام fgetc لقراءة حرف واحد
🔹 fgetc تستخدم لقراءة حرف واحد في كل مرة.
مثال:
قراءة الأحرف واحدًا تلو الآخر
#include <stdio.h>
int main() {
FILE *fp = fopen("text.txt", "r");
if (fp ==
NULL) {
printf("Failed to open the file!\n");
return 1;
}
char ch;
while ((ch =
fgetc(fp)) !=
EOF) {
putchar(ch);
}
fclose(fp);
return 0;
}
💡 نستخدم EOF (نهاية الملف) للخروج من الحلقة.
استخدام fgets لقراءة سطر كامل
🔹 fgets تقرأ سطرًا كاملاً من الملف.
مثال:
قراءة سطر واحد
#include <stdio.h>
int main() {
FILE *fp = fopen("text.txt", "r");
if (fp ==
NULL) {
printf("Failed to open the file!\n");
return 1;
}
char line [100];
while (
fgets(line,
sizeof(line), fp) ! = NULL) {
printf(
"%s", line);
}
fclose(fp);
return 0;
}
💡 fgets تتوقف عند السطر الجديد n\ أو عند الحد الأقصى
للحجم
المحدد.
4. التعامل مع الملفات الثنائية (Binary Files)
🔹 يمكن تخزين البيانات الثنائية (مثل الصور والصوت) باستخدام ملفات
ثنائية.
الوضع | الوظيفة |
---|---|
"rb" | فتح ملف ثنائي للقراءة فقط. |
"wb" | فتح ملف ثنائي للكتابة (يحذف المحتوى القديم). |
"ab" | فتح ملف ثنائي للإلحاق بالبيانات. |
مثال:
كتابة بيانات ثنائية إلى ملف
#include <stdio.h>
int main() {
FILE *fp = fopen("binary.dat", "wb");
int numbers[]
=
{1, 2, 3, 4, 5};
fwrite(numbers,
sizeof(int), 5,
fp);
fclose(fp);
return 0;
}
مثال:
قراءة بيانات ثنائية من ملف
#include <stdio.h>
int main() {
FILE *fp = fopen("binary.dat", "rb");
int numbers[5];
fread(numbers,
sizeof(int), 5,
fp);
for (int
i = 0; i <
5; i++) {
printf("%d ", numbers[i]);
}
fclose(fp);
return 0;
}
خلاصة:
✔ تعلمنا كيفية فتح الملفات وإغلاقها.
✔ استخدمنا دوال القراءة والكتابة مثل fprintf, fscanf,
fputc, fgetc.
✔ تعلمنا التعامل مع الملفات الثنائية باستخدام fwrite و
fread.
الفصل العاشر: البرمجة الكائنية في C (OOP Concepts in C)
لغة C ليست لغة كائنية التوجه (OOP)، لكنها توفر أدوات تمكننا من
محاكاة بعض
مفاهيم البرمجة الكائنية مثل التغليف (Encapsulation)، الوراثة
(Inheritance)، والتعددية (Polymorphism) باستخدام
الهياكل
(structs) والمؤشرات (pointers).
1. مقدمة عن البرمجة الكائنية ولماذا هي مهمة
ما هي البرمجة الكائنية التوجه؟
البرمجة الكائنية (Object-Oriented Programming - OOP) هي نمط برمجي
يعتمد
على فكرة الكائنات (Objects)، حيث يتم تقسيم البرنامج إلى
كائنات
تحتوي على بيانات (Attributes) وسلوكيات (Methods).
أهمية البرمجة الكائنية التوجه
تنظيم الكود: تسهل إعادة استخدام الشيفرة وتجعلها أكثر وضوحًا.
تقليل التعقيد: من خلال تجميع البيانات والدوال المتعلقة بها في
كائن
واحد.
إعادة الاستخدام (Reusability): يمكنك إنشاء كود عام وإعادة
استخدامه
في عدة أجزاء من البرنامج.
2. محاكاة البرمجة الكائنية في C باستخدام الهياكل
والمؤشرات
التغليف (Encapsulation) في C
في البرمجة الكائنية، التغليف يعني إخفاء تفاصيل تنفيذ الكود وحماية
البيانات
من الوصول المباشر. يمكننا تحقيق ذلك في C باستخدام الهياكل والمؤشرات
والدوال.
مثال على التغليف في C
#include <stdio.h>
#include <string.h>
// تعريف هيكل يمثل كائناً (Object)
typedef struct {
char name[50];
int age;
} Person;
// دالة لإنشاء شخص جديد
void
setPerson(Person *p, const
char *name,
int age)
{
strcpy(p->name,
name);
p->age =
age;
}
// دالة لطباعة معلومات الشخص
void
printPerson(const
Person *p) {
printf("Name: %s\n", p->name);
printf("Age: %d\n", p->age);
}
int main() {
Person p1
setPerson(&p1,
"Moussa", 25);
printPerson(&p1);
return 0;
}
الشــرح:
استخدمنا هيكل (struct) لتمثيل كائن شخص يحتوي على
بيانات.
استخدمنا مؤشرات لتمرير البيانات إلى الدوال.
تم إخفاء التفاصيل الداخلية باستخدام دوال
setPerson و
printPerson.
محاكاة الوراثة (Inheritance) في C
لغة C لا تدعم الوراثة بشكل مباشر مثل ++C، لكن
يمكننا محاكاتها باستخدام الهياكل
المتداخلة.
مثال على محاكاة الوراثة
#include <stdio.h>
// الهيكل الأساسي (Base Struct)
typedef struct {
char name[50];
int age;
} Person;
//الهيكل المشتق (Derived Struct) يرث
Person
typedef struct {
Person base;
float salary;
} Employee;
int main() {
Employee emp;
emp.base.age = 30;
emp.base.name[0] =
'\0';
emp.salary = 5000.50;
printf("Age: %d\n", emp.base.age);
printf("Salary: %.2f\n",
emp.salary);
return 0;
}
الشــرح:
Employee يحتوي على Person، مما يجعله يستفيد من خصائصه
(اسم
وعمر).
بهذه الطريقة، يمكننا محاكاة الوراثة بطريقة مشابهة للبرمجة الكائنية.
محاكاة التعددية (Polymorphism) في C
التعددية في OOP تعني إمكانية استدعاء دوال بطرق مختلفة. يمكن
تحقيق
ذلك في C باستخدام المؤشرات إلى الدوال (Function Pointers).
مثال على التعددية
#include <stdio.h>
// تعريف هيكل يحتوي على مؤشر إلى
دالة
typedef struct
{
void (*move)();
} Vehicle;
// دوال مختلفة
void carMove()
{
printf("The car moves on the road!\n");
}
void
planeMove() {
printf("The plane flies in the sky!\n");
}
int main()
{
Vehicle car,
plane;
car.move =
carMove;
plane.move = planeMove;
car.move();
plane.move();
return 0;
}
الشــرح:
Vehicle
يحتوي على مؤشر إلى دالة، مما يسمح لنا
بتغيير
سلوك المركبات المختلفة.;car.move =
carMove
يجعل السيارة تتحرك على الطريق،
بينما ;plane.move =
planeMove
يجعل الطائرة تحلق في السماء.3. البرمجة المعيارية (Modular Programming)
البرمجة المعيارية تعني تقسيم البرنامج إلى وحدات صغيرة
(Modules)
لجعل الكود أكثر تنظيماً وقابلاً لإعادة الاستخدام. في C، يمكننا تحقيق
ذلك
باستخدام الملفات الرأسية (h.) والملفات المصدرية (c.).
مثال على البرمجة المعيارية
ملف الرأس math_functions.h
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H
int
add(int a,
int b);
int
multiply(int
a, int b);
#endif
ملف المصدر math_functions.c
#include "math_functions.h"
int
add(int a,
int b)
{
return a
+ b;
}
int
multiply(int
a, int b)
{
return a
* b;
}
الملف الرئيسي main.c
#include <stdio.h>
#include "math_functions.h"
int main() {
printf("Sum: %d\n", add(5,
3));
printf("Multiplication: %d\n", multiply(5,
3));
return 0;
}
الشــرح:
قمنا بتعريف الدوال في ملف منفصل (math_functions.c).
استخدمنا ملف الرأس (math_functions.h) لتعريف الدوال.
استدعيناه في main.c لتسهيل إعادة الاستخدام.
4. مفاهيم متقدمة في إدارة الذاكرة
عند استخدام الحجز الديناميكي للذاكرة (Dynamic Memory
Allocation)،
يجب أن نكون حذرين لتجنب تسرب الذاكرة (Memory Leak).
أهم المفاهيم المتقدمة
malloc: لحجز الذاكرة بدون تهيئة.
calloc: لحجز الذاكرة مع تهيئة القيم إلى 0.
realloc: لإعادة تخصيص الذاكرة.
free: لتحرير الذاكرة.
مثال على تخصيص الذاكرة ديناميكيًا
#include <stdio.h>
#include <stdlib.h>
int main() {
int
*arr = (int*)
malloc(5
* sizeof(int));
if (arr ==
NULL) {
printf("Memory allocation failed\n");
return 1;
}
for (int
i = 0; i <
5; i++) {
arr[i] =
i
* 10;
}
free(arr);
// تفادي تسرب الذاكرة
return 0;
}
الشــرح:
استخدمنا malloc لحجز 5 أماكن في الذاكرة.
استخدمنا free لتحرير الذاكرة بعد الاستخدام.
5. التعامل مع المكتبات (Libraries)
المكتبات تسهل استخدام دوال جاهزة دون الحاجة إلى إعادة كتابتها.
يمكننا
استخدام المكتبات القياسية مثل stdio.h وmath.h، أو
إنشاء
مكتبات خاصة.
مثال على مكتبة رياضية
إنشاء مكتبة mylib.c
#include <math.h>
double squareRoot(double num)
{
return sqrt(num);
}
استخدام المكتبة في main.c
#include <stdio.h>
extern double squareRoot(double);
int main() {
printf("The square root of 16 is: %.2f\n",
squareRoot(16));
return 0;
}
خاتمة الفصل
في هذا الفصل، تعلمنا كيف يمكن محاكاة البرمجة الكائنية في C باستخدام
الهياكل والمؤشرات، إلى جانب مفاهيم البرمجة المعيارية وإدارة الذاكرة
والمكتبات. رغم أن C ليست لغة كائنية، يمكن استخدام أدواتها لكتابة كود
منظم
وقابل لإعادة الاستخدام.
قسم التمارين والتطبيقات العملية (45 تمرين مع الحلول والشروحات)
في هذا القسم، سنوفر مجموعة من التمارين التطبيقية مع الحلول
المفصلة لكل
فصل، بالإضافة إلى مشاريع تطبيقية صغيرة لمساعدتك على فهم المفاهيم
البرمجية في لغة C بعمق. سيتم تقسيم التمارين بشكل منظم حسب
الفصول، حيث
يحتوي كل فصل على تمارين متدرجة الصعوبة من الأساسيات حتى المفاهيم
المتقدمة.
الفصل الأول: أساسيات لغة C
📌 التمارين:
- طباعة "Hello, World!"
- طباعة اسم المستخدم المُدخل من لوحة المفاتيح
- حساب مجموع رقمين يُدخلهما المستخدم
- حساب مساحة مستطيل باستخدام الطول والعرض
- تحويل درجة الحرارة من السيلسيوس إلى الفهرنهايت
✅ كل تمرين سيحتوي على الكود الكامل، الشرح التفصيلي، ومخرجات
التنفيذ.
📌 الفصل الأول: أساسيات لغة C
يحتوي هذا الفصل على مجموعة من التمارين الأساسية التي تساعدك على فهم
المفاهيم الأولية في لغة C. كل تمرين يحتوي على الكود الكامل، الشرح
التفصيلي، ومخرجات التنفيذ.
🔹 التمرين الأول: طباعة "!Hello, World"
📌 المطلوب:
اكتب برنامجًا بلغة C يقوم بطباعة الجملة "!Hello, World" على الشاشة.
✅ الكود:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0;
}
🔍 الشرح
#include <stdio.h> →
printf التي تحتوي على دالة stdio.h تضمين
مكتبة
الإدخال والإخراج .
int main() → تعريف الدالة الرئيسية
التي
يبدأ التنفيذ منها.
printf("Hello, World!\n"); → \n مع
الانتقال إلى السطر الجديد "Hello, World!" طباعة
الجملة .
return 0; →
إنهاء البرنامج بنجاح.
🖥 المخرجات:
Hello, World!
🔹 التمرين الثاني: طباعة اسم المستخدم المُدخل من لوحة
المفاتيح
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال اسمه، ثم يعرضه على
الشاشة.
✅ الكود:
#include <stdio.h>
int main() {
char name[50]; // تعريف
مصفوفة لتخزين الاسم
printf("Enter your name: ");
scanf(
"%s", name);
// استقبال الاسم من المستخدم
printf("Hello، %s!\n", name); // طباعة الاسم
return 0;
}
🔍 الشرح:
char name[50];
→ تعريف مصفوفة لتخزين الاسم.
printf("Enter your name: "); → طلب
إدخال
الاسم من المستخدم.
scanf( "%s",
name); → التقاط الاسم المدخل.
printf("Hello، %s!\n", name); → طباعة
الاسم.
🖥 المخرجات عند الإدخال Moussa:
Enter your name: Moussa
Hello, Moussa!
⚠ ملاحظة: هذا الكود لا يدعم الأسماء التي تحتوي على مسافات،
يمكن
استخدام ;gets(name) بدل ;scanf(
"%s", name) لالتقاط السلاسل النصية
الكاملة.
🔹 التمرين الثالث: حساب مجموع رقمين يُدخلهما المستخدم
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال رقمين صحيحين، ثم يعرض مجموعهما.
✅ الكود:
#include <stdio.h>
int main() {
int num1, num2,
sum;
printf("Enter the first number: ");
scanf("%d", &num1);
printf("Enter the second number: ");
scanf("%d", &num2);
sum = num1 + num2;
// حساب المجموع
printf("Sum: %d\n", sum);
return 0;
}
🔍 الشرح:
int num1, num2, sum; → تعريف متغيرات
لتخزين القيم.
scanf("%d",
&num1); → قراءة الرقم الأول.
scanf("%d",
&num2); → قراءة الرقم الثاني.
sum = num1 + num2; → حساب المجموع.
printf("Sum: %d\n", sum); → طباعة النتيجة.
🖥 المخرجات عند الإدخال 5 و 7:
Enter the first number: 5
Enter the second number: 7
sum: 12
🔹 التمرين الرابع: حساب مساحة مستطيل باستخدام الطول
والعرض
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال طول وعرض مستطيل، ثم يحسب
مساحته.
✅ الكود:
#include <stdio.h>
int main() {
float length,
width, area;
printf("Enter the length: ");
scanf("%f", &length);
printf("Enter the width: ");
scanf("%f", &width);
area = length * width;
// حساب المساحة
printf("Rectangle area: %.2f\n", area);
return 0;
}
🔍 الشرح:
float length, width, area; → تعريف
متغيرات
لتخزين القيم العشرية.
scanf("%f",
&length); → قراءة الطول.
scanf("%f",
&width); → قراءة العرض.
area = length * width; → حساب المساحة.
printf("Rectangle area: %.2f\n",
area); →
طباعة المساحة بدقتين عشريتين.
🖥 المخرجات عند الإدخال 4.5 و 2.3:
Enter the length: 4.5
Enter the width: 2.3
Rectangle area: 10.35
🔹 التمرين الخامس: تحويل درجة الحرارة من السيلسيوس إلى
الفهرنهايت
📌 المطلوب:
اكتب برنامجًا يقوم بتحويل درجة الحرارة من السيلسيوس إلى الفهرنهايت
باستخدام المعادلة:
F = (C × 9/5) + 32
✅ الكود:
#include <stdio.h>
int main() {
float celsius,
fahrenheit;
printf("Enter the temperature in Celsius: ");
scanf("%f", &celsius);
fahrenheit = (celsius *
9 /
5) +
32;
// التحويل إلى فهرنهايت
printf("Temperature in Fahrenheit: %.2f\n",
fahrenheit);
return 0;
}
🔍 الشرح:
float celsius, fahrenheit; → تعريف
متغيرات
لتخزين درجات الحرارة.
scanf("%f",
&celsius); → قراءة درجة الحرارة بالسيلسيوس.
fahrenheit = (celsius * 9 / 5)
+ 32; → تطبيق المعادلة للتحويل.
printf("درجة الحرارة بالفهرنهايت: %.2f\n",
fahrenheit); → طباعة النتيجة.
🖥 المخرجات عند الإدخال 25:
Enter the temperature in Celsius: 25
Temperature in Fahrenheit: 77.00
🔹 الخلاصة:
✔ تعلمنا كيفية التعامل مع الإخراج والإدخال الأساسي في لغة C.
✔ استخدمنا أنواع بيانات مختلفة (int,
float,
char[]).
✔ تعرفنا على الدوال printf و scanf.
✔ تعاملنا مع العمليات الحسابية الأساسية.
الفصل الثاني: المتغيرات وأنواع البيانات والمعاملات
📌 التمارين:
- إنشاء متغيرات وإسناد قيم لها ثم طباعتها
- إجراء العمليات الحسابية الأساسية على متغيرات عددية
- إدخال رقم صحيح وطباعة مربعه ومكعبه
- استخدام معاملات الموديلوس (%) لحساب الباقي عند القسمة
- استخدام معاملات الإزاحة الثنائية (Bitwise Operators) لتنفيذ عمليات على البتات
✅ كل تمرين سيتم حله مع شرح وافٍ لكل سطر من الكود.
📌 الفصل الثاني: المتغيرات وأنواع البيانات والمعاملات في
C
في هذا الفصل، سنقوم بتطبيق تمارين عملية لفهم المتغيرات، أنواع
البيانات،
والمعاملات (Operators) في لغة C.
✅ كل تمرين يحتوي على الكود، الشرح التفصيلي، والمخرجات.
🔹 التمرين الأول: إنشاء متغيرات وإسناد قيم لها ثم
طباعتها
📌 المطلوب:
قم بإنشاء متغيرات من أنواع بيانات مختلفة (int, float,
char) وأسنِد لها قيمًا، ثم اطبعها.
✅ الكود:
#include <stdio.h>
int main() {
int age =
25;
// متغير عدد صحيح
float height =
1.75; // متغير عشري
char grade =
'A';
// متغير
حرفي
printf("Age : %d years\n", age);
printf("Height: %.2f meters\n", height);
printf("Grade: %c\n", grade);
return 0;
}
🔍 الشرح:
int age = 25;
→ تعريف متغير عدد صحيح وإسناد قيمة له.
float height =
1.75; → تعريف متغير عشري.
char grade =
'A'; → تعريف متغير حرفي.
printf(...) → طباعة القيم باستخدام
التنسيقات
المناسبة (%d, %f, %c).
🖥 المخرجات:
Age : 25 years
Height: 1.75
meters
Grade: A
🔹 التمرين الثاني: إجراء العمليات الحسابية الأساسية على
متغيرات
عددية
📌 المطلوب:
قم بإنشاء متغيرين عددين، ثم قم بحساب المجموع، الفرق، الضرب، القسمة،
وباقي
القسمة.
✅ الكود:
#include <stdio.h>
int main() {
int num1 =
10, num2 = 3;
printf("Sum: %d\n", num1 + num2);
printf("Difference: %d\n", num1 - num2);
printf("Multiplication: %d\n", num1 * num2);
printf("Division: %.2f\n", (float)num1 / num2);
printf("Remainder: %d\n", num1 % num2);
return 0;
}
🔍 الشرح:
تعريف متغيرين صحيحين (num1, num2).
تنفيذ العمليات الحسابية الأساسية (+, -,
*, /, %).
تحويل القسمة إلى عدد عشري باستخدام (float).
🖥 المخرجات:
Sum: 13
Difference: 7
Multiplication: 30
Division: 3.33
Remainder: 1
🔹 التمرين الثالث: إدخال رقم صحيح وطباعة مربعه ومكعبه
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال رقم، ثم يعرض مربعه
ومكعبه.
✅ الكود:
#include <stdio.h>
int main() {
int num;
printf("Enter a number: ");
scanf("%d", &num);
printf("Square: %d\n", num * num);
printf("Cube: %d\n", num * num
* num);
return 0;
}
🔍 الشرح:
scanf("%d",
&num); → التقاط رقم صحيح من المستخدم.
num * num → حساب
المربع.
num * num *
num →
حساب المكعب.
🖥 المخرجات عند إدخال 4:
Enter a number:
4
Square: 16
Cube: 64
🔹 التمرين الرابع: استخدام معاملات الموديلوس (modulus)
(%)
لحساب الباقي عند القسمة
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال رقمين صحيحين، ثم يعرض باقي
القسمة.
✅ الكود:
#include <stdio.h>
int main() {
int num1, num2;
printf("Enter the first number: ");
scanf("%d", &num1);
printf("Enter the second number: ");
scanf("%d", &num2);
printf("Remainder: %d\n", num1 % num2);
return 0;
}
🔍 الشرح:
استقبال رقمين من المستخدم.
حساب باقي القسمة باستخدام %.
🖥 المخرجات عند الإدخال 10 و 3:
Enter the first number: 10
Enter the second number: 3
Remainder: 1
🔹 التمرين الخامس: استخدام معاملات الإزاحة الثنائية (Bitwise
Operators) لتنفيذ عمليات على البتات
📌 المطلوب:
اكتب برنامجًا يُجري عمليات على البتات باستخدام معاملات الإزاحة
(<< و
>>).
✅ الكود:
#include <stdio.h>
int main() {
int num =
5;
printf("Original value: %d\n", num);
printf("Left shift (×2): %d\n", num <<
1);
printf("Right shift (÷2): %d\n", num >>
1);
return 0;
}
🔍 الشرح:
num << 1 → إزاحة لليسار بمقدار 1 (يعادل الضرب في 2).
num >> 1 → إزاحة لليمين بمقدار 1 (يعادل القسمة على 2).
🖥 المخرجات:
Original value:
5
Left shift (×2): 10
Right shift (÷2): 2
🔹 الخلاصة:
✔ تعرفنا على المتغيرات بأنواعها (int,
float,
char).
✔ طبقنا العمليات الحسابية (+, -,
*, /, %).
✔ استخدمنا معاملات modulus و bitwise shifting.
الفصل الثالث: التحكم في تدفق البرنامج (الشروط والتكرار)
📌 التمارين:
- كتابة برنامج يتحقق مما إذا كان الرقم موجبًا أم سالبًا أم صفرًا
- كتابة برنامج يتحقق مما إذا كان الرقم زوجيًا أم فرديًا
- استخدام switch لإنشاء آلة حاسبة بسيطة
- حساب مضروب عدد باستخدام for loop
- طباعة الأعداد الفردية من 1 إلى 100 باستخدام while loop
✅ سيتم توفير الأكواد مع الشروحات، وتحليل لأداء
الحلول.
📌 الفصل الثالث: التحكم في تدفق البرنامج (الشروط والتكرار) في
C
في هذا الفصل، سنتعلم كيفية التحكم في تدفق البرنامج باستخدام
الشروط
(if-else, switch) والحلقات (for,
while) من
خلال مجموعة من التمارين العملية.
✅ كل تمرين يحتوي على الكود، الشرح التفصيلي، وتحليل
الأداء.
🔹 التمرين الأول: التحقق مما إذا كان الرقم موجبًا أم
سالبًا أم
صفرًا
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال رقم صحيح، ثم يحدد ما إذا
كان
موجبًا، سالبًا، أو صفرًا.
✅ الكود:
#include <stdio.h>
int main()
{
int num;
printf("Enter a number: ");
scanf("%d", &num);
if (num >
0) {
printf("The number is positive.\n");
}
else if (num <
0) {
printf("The number is negative.\n");
}
else
{
printf("The number is zero.\n");
}
return 0;
}
🔍 الشرح:
يستقبل البرنامج رقمًا صحيحًا من المستخدم.
يستخدم if-else للتحقق من قيمته:
".إذا كان أكبر من0→ "الرقم موجب
".إذا كان أقل من0→ "الرقم سالب
".إذا كان يساوي0→ "الرقم هو صفر
🖥 المخرجات:
Enter a number:
-5
The number is negative.
🔹 التمرين الثاني: التحقق مما إذا كان الرقم زوجيًا أم
فرديًا
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال رقم، ثم يحدد ما إذا كان
زوجيًا أم
فرديًا باستخدام معامل الموديلوس(modulus) %.
✅ الكود:
#include <stdio.h>
int main()
{
int num;
printf("Enter a number: ");
scanf("%d", &num);
if (num
% 2 == 0)
{
printf("The number is even.\n");
}
else
{
printf("The number is odd.\n");
}
return 0;
}
🔍 الشرح:
نقوم بحساب num % 2:
إذا كان الباقي0→ الرقم زوجي.
إذا كان الباقي 1 → الرقم فردي.
🖥 المخرجات:
Enter a number:
7
The number is odd.
🔹 التمرين الثالث: آلة حاسبة بسيطة باستخدام switch
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال رقمين وعملية حسابية (+, -, *,
/)، ثم يعرض النتيجة باستخدام
switch.
✅ الكود:
#include <stdio.h>
int main()
{
float num1,
num2;
char op;
printf("Enter the operation ( + , - ,
* , / ): ");
scanf(" %c", &op);
// %c لتجنب مشاكل الإدخال لاحظ وجود مسافة قبل
printf("Enter the first number: ");
scanf("%f", &num1);
printf("Enter the second number: ");
scanf("%f", &num2);
switch (op)
{
case '+':
printf("النتيجة: %.2f\n", num1 + num2);
break;
case '-':
printf("Result: %.2f\n", num1 - num2);
break;
case '*':
printf("Result: %.2f\n", num1 * num2);
break;
case '/':
if (num2 !=
0)
printf("Result: %.2f\n", num1 / num2);
else
printf(
"Error! Division by zero is not allowed.\n" );
break;
default:
printf(
"Error! Invalid operation.\n" );
}
return 0;
}
🔍 الشرح:
يستقبل المستخدم العملية (+, -,
*, /).
يستقبل رقمين ثم يستخدم switch لتنفيذ
العملية الصحيحة.
يتحقق من القسمة على صفر لمنع الأخطاء.
🖥 المخرجات:
Enter the operation (
+ , - , * , /
): *
Enter the first number: 5
Enter the second number: 3
Result:
15.00
🔹 التمرين الرابع: حساب مضروب عدد باستخدام
for loop
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال رقم صحيح موجب، ثم يحسب
مضروبه (!n)
باستخدام for loop.
✅ الكود:
#include <stdio.h>
int main()
{
int num, fact
= 1;
printf("Enter a positive number: ");
scanf("%d", &num);
if (num <
0) {
printf(
"Error! Factorial is not defined for negative numbers.\n"
);
}
else
{
for (int
i = 1; i <= num; i++) {
fact
*= i;
}
printf("Factorial of %d is: %d\n", num,
fact);
}
return 0;
}
🔍 الشرح:
نتحقق مما إذا كان الرقم سالبًا.
نستخدم for loop لحساب n! بضرب
الأعداد
من 1 إلى n.
🖥 المخرجات عند إدخال 5:
Enter a positive number: 5
Factorial of 5 is: 120
🔹 التمرين الخامس: طباعة الأعداد الفردية من 1 إلى 100
باستخدام
while loop
📌 المطلوب:
استخدم while loop لطباعة جميع
الأعداد
الفردية من 1 إلى 100.
✅ الكود:
#include <stdio.h>
int main()
{
int i
= 1;
while (i
<=
100)
{
printf("%d ", i);
i
+= 2;
}
printf("\n");
return 0;
}
🔍 الشرح:
نبدأ بـ i = 1.
نستخدم while لطباعة كل رقم
فردي (i
+= 2).
تتوقف الحلقة عند i > 100.
🖥 المخرجات:
1 3 5 7 9 ... 97 99
🔹 تحليل الأداء والملحوظات
🔸 if-else أسرع من switch عند
مقارنة
عدد صغير من القيم.
🔸 for loop مناسب عند معرفة
عدد
التكرارات مسبقًا.
🔸 while loop مثالي عند
الحاجة إلى
التكرار بشرط متغير.
📌 الخلاصة:
✔ تعلمنا if-else و
switch للتحكم في
التدفق.
✔ استخدمنا for و
while لإنشاء الحلقات.
✔ طبقنا مفاهيم عملية لحل المشكلات البرمجية.
الفصل الرابع: المصفوفات والسلاسل النصية
📌 التمارين:
- إدخال 5 أعداد صحيحة في مصفوفة وطباعتها
- إيجاد العدد الأكبر والأصغر في مصفوفة
- حساب مجموع عناصر مصفوفة عددية
- عكس محتوى مصفوفة بدون استخدام مصفوفة إضافية
- إدخال سلسلة نصية وعرض عدد الأحرف فيها
✅ كل تمرين سيشمل الشرح التفصيلي للكود مع تحسينات إضافية.
📌 الفصل الرابع: المصفوفات والسلاسل النصية في
C
في هذا الفصل، سنتعرف على المصفوفات (Arrays) والسلاسل النصية
(Strings)
من خلال مجموعة من التمارين العملية.
✅ كل تمرين يحتوي على الكود، الشرح التفصيلي، وتحليل
الأداء.
🔹 التمرين الأول: إدخال 5 أعداد صحيحة في مصفوفة
وطباعتها
📌 المطلوب:
اكتب برنامجًا يسمح للمستخدم بإدخال 5 أعداد صحيحة في مصفوفة
ثم يطبعها.
✅ الكود:
#include
<stdio.h>
int main()
{
int
arr[5];
// إدخال الأعداد
printf("Enter 5 numbers:\n");
for (int
i = 0; i <
5; i++)
{
printf("Number %d: ", i + 1);
scanf("%d", &arr[i]);
}
// طباعة
الأعداد
printf("The entered numbers are: ");
for (int
i = 0; i <
5; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
🔍 الشرح:
تعريف مصفوفة arr بحجم 5 لتخزين الأعداد.
استخدام for loop لإدخال
القيم وطباعة
المصفوفة.
🖥 المخرجات:
Enter 5 numbers:
Number 1:
12
Number 2:
5
Number 3:
8
Number 4:
20
Number 5:
7
The entered numbers are : 12 5 8 20 7
🔹 التمرين الثاني: إيجاد العدد الأكبر والأصغر في
مصفوفة
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال 5 أعداد، ثم يعرض العدد
الأكبر
والأصغر منها.
✅ الكود:
#include
<stdio.h>
int main()
{
int
arr[5], max, min;
// إدخال
القيم
printf("Enter 5 numbers:\n");
for (int
i = 0; i <
5; i++)
{
scanf("%d", &arr[i]);
}
// تعيين القيم الابتدائية
max = min = arr[0];
// البحث عن الأكبر والأصغر
for (int
i = 1; i <
5; i++)
{
if (arr[i] > max) {
max =
arr[i];
}
if (arr[i] < min) {
min =
arr[i];
}
}
printf("Largest number: %d\n", max);
printf("Smallest number: %d\n", min);
return 0;
}
🔍 الشرح:
يبدأ البرنامج بأخذ 5 أعداد من المستخدم.
يتم تعيين أول عدد كأكبر وأصغر قيمة مؤقتًا.
يتم فحص بقية الأعداد باستخدام
if لمعرفة القيم القصوى.
🖥 المخرجات:
Enter 5 numbers: 3 7 15 2
9
Largest
number: 15
Smallest
number: 2
🔹 التمرين الثالث: حساب مجموع عناصر مصفوفة
عددية
📌 المطلوب:
اكتب برنامجًا يحسب مجموع عناصر مصفوفة عددية.
✅ الكود:
#include
<stdio.h>
int main()
{
int
arr[5], sum = 0;
printf("Enter 5 numbers:\n");
for (int
i = 0; i <
5; i++)
{
scanf("%d", &arr[i]);
sum +=
arr[i];
// جمع العناصر
}
printf("The sum of the numbers is: %d\n",
sum);
return 0;
}
🔍 الشرح:
يتم جمع الأعداد مباشرة داخل
for loop.
🖥 المخرجات:
Enter 5 numbers:
1 2 3 4 5
The sum of the numbers is: 15
🔹 التمرين الرابع: عكس محتوى مصفوفة بدون استخدام
مصفوفة إضافية
📌 المطلوب:
اكتب برنامجًا يعكس ترتيب العناصر داخل مصفوفة دون استخدام
مصفوفة أخرى.
✅ الكود:
#include
<stdio.h>
int main()
{
int
arr[5], temp;
printf("Enter 5 numbers:\n");
for (int
i = 0; i <
5; i++)
{
scanf("%d", &arr[i]);
}
// عكس العناصر باستخدام التبديل
(swap)
for (int
i = 0; i < 5 / 2;
i++)
{
temp = arr[i];
arr[i] =
arr[4 - i];
arr[4 - i] =
temp;
}
printf("The array after reversing: ");
for (int
i = 0; i <
5; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
🔍 الشرح:
يتم تبديل العناصر بين البداية والنهاية باستخدام temp.
يتم تكرار العملية حتى الوصول للمنتصف فقط لتجنب إعادة
العكس.
🖥 المخرجات:
Enter 5 numbers:
1 2 3 4 5
The array after reversing: 5 4 3 2 1
🔹 التمرين الخامس: إدخال سلسلة نصية وعرض عدد الأحرف
فيها
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال سلسلة نصية (String) ثم
يعرض عدد
الأحرف فيها.
✅ الكود:
#include
<stdio.h>
#include
<string.h>
// مكتبة التعامل مع النصوص
int main()
{
char str
[100];
printf("Enter a string: ");
fgets(str,
sizeof(str), stdin);
// إدخال النص
// حساب عدد الأحرف باستخدام
strlen
printf(
"The number of characters in the string: %lu\n"
, strlen(str) -
1);
// طرح 1 لإزالة '\n'
return 0;
}
🔍 الشرح:
يتم استخدام fgets لقراءة النص.
يتم حساب عدد الأحرف باستخدام strlen().
يتم طرح 1 لأن fgets يخزن \n في نهاية النص.
🖥 المخرجات:
!Enter a string: Hello, World
The number of characters in the string: 13
📌 الخلاصة:
✔ تعلمنا كيفية التعامل مع المصفوفات وإجراء عمليات
عليها.
✔ استخدمنا for loop لحل
مشاكل
متعددة.
✔ تعلمنا كيفية التعامل مع النصوص باستخدام
strlen() و
fgets().
الفصل الخامس: الدوال والمؤشرات
📌 التمارين:
- إنشاء دالة تحسب مجموع رقمين وإرجاع النتيجة
- إنشاء دالة تحسب مضروب عدد باستخدام التكرار
- استخدام المؤشرات لطباعة عنوان متغير في الذاكرة
- استخدام المؤشرات لعكس محتوى مصفوفة عددية
- إنشاء دالة تأخذ مؤشرًا لمصفوفة وتعيد مجموع العناصر
✅ سنشرح بالتفصيل دور المؤشرات والدوال في كل
حل.
📌 الفصل الخامس: الدوال والمؤشرات في C
في هذا الفصل، سنتعرف على الدوال (Functions) والمؤشرات
(Pointers)، وهما
من أهم مفاهيم لغة C.
✅ كل تمرين يحتوي على الكود، الشرح التفصيلي، وتحليل
الأداء.
🔹 التمرين الأول: إنشاء دالة تحسب مجموع رقمين وإرجاع
النتيجة
📌 المطلوب:
اكتب دالة تستقبل رقمين كمدخلات وتعيد مجموعهما.
✅ الكود:
#include
<stdio.h>
// دالة لحساب مجموع رقمين
int
sum(int
a, int b)
{
return a
+ b;
}
int main()
{
int num1,
num2;
printf("Enter two numbers: ");
scanf("%d %d", &num1,
&num2);
printf("Sum: %d\n", sum(num1, num2));
return 0;
}
🔍 الشرح:
تعريف دالة sum(int
a, int b) تُعيد مجموع a
+ b.
إدخال رقمين من المستخدم واستدعاء الدالة لحساب
المجموع.
🖥 المخرجات:
Enter two numbers: 5 7
Sum: 12
🔹 التمرين الثاني: إنشاء دالة تحسب مضروب عدد باستخدام
التكرار
📌 المطلوب:
اكتب دالة تحسب المضروب (Factorial) لعدد معين باستخدام
التكرار.
✅ الكود:
#include
<stdio.h>
// دالة لحساب المضروب
long long factorial(int
n)
{
long long fact = 1;
for (int
i = 1; i <= n;
i++)
{
fact *= i;
}
return fact;
}
int main()
{
int num;
printf("Enter a number: ");
scanf("%d", &num);
printf("Factorial of %d is: %lld\n",
num, factorial(num));
return 0;
}
🔍 الشرح:
تعريف دالة factorial(int n)
تستخدم
for loop لحساب المضروب.
استدعاء الدالة من main() بعد أخذ إدخال المستخدم.
🖥 المخرجات:
Enter a
number: 5
Factorial of 5 is: 120
🔹 التمرين الثالث: استخدام المؤشرات لطباعة عنوان
متغير في
الذاكرة
📌 المطلوب:
اكتب برنامجًا يستخدم المؤشرات لطباعة عنوان متغير معين في
الذاكرة.
✅ الكود:
#include
<stdio.h>
int main()
{
int num =
10;
int
*ptr = #
printf("Variable value: %d\n", num);
printf("Variable memory address: %p\n",
ptr);
return 0;
}
🔍 الشرح:
تعريف متغير num وإسناد عنوانه إلى المؤشر ptr.
طباعة العنوان باستخدام %p، والذي يُستخدم لعرض عناوين
الذاكرة.
🖥 المخرجات:
Variable
value: 10
Variable memory address: 0x7ffeefbff578
(العنوان يختلف عند كل تشغيل للبرنامج)
🔹 التمرين الرابع: استخدام المؤشرات لعكس محتوى مصفوفة
عددية
📌 المطلوب:
اكتب برنامجًا يعكس ترتيب عناصر مصفوفة باستخدام المؤشرات
فقط.
✅ الكود:
#include
<stdio.h>
void
reverseArray(int
*arr,
int size)
{
int
*start = arr,
*end = arr
+ size - 1,
temp;
while
(start <
end) {
temp =
*start;
*start =
*end;
*end = temp;
start++;
end--;
}
}
int main()
{
int
arr[5] = {1, 2,
3, 4, 5};
printf("Array before reversing: ");
for (int
i = 0; i <
5; i++)
{
printf("%d ", arr[i]);
}
reverseArray(arr,
5);
printf("\nArray after reversing: ");
for (int
i = 0; i <
5; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
🔍 الشرح:
استخدمنا مؤشرين (start و end) لعكس المصفوفة دون استخدام
مصفوفة إضافية.
استبدلنا القيم بين start و end حتى يصلان إلى المنتصف.
🖥 المخرجات:
Array before reversing: 1 2 3 4 5
Array after reversing: 5 4 3 2 1
🔹 التمرين الخامس: إنشاء دالة تأخذ مؤشرًا لمصفوفة
وتعيد مجموع
العناصر
📌 المطلوب:
اكتب دالة تأخذ مؤشرًا لمصفوفة وعدد عناصرها، ثم تعيد مجموع
العناصر.
✅ الكود:
#include
<stdio.h>
// دالة لحساب مجموع عناصر
المصفوفة
int
sumArray(int
*arr,
int size)
{
int sum
= 0;
for (int
i = 0; i < size;
i++)
{
sum +=
*(arr
+ i);
// الوصول للعناصر باستخدام
المؤشرات
}
return sum;
}
int main()
{
int arr[]
=
{10, 20, 30, 40,
50};
printf("Sum of array elements: %d\n",
sumArray(arr,
5));
return 0;
}
🔍 الشرح:
يتم تمرير المصفوفة كمؤشر إلى sumArray().
الوصول إلى العناصر باستخدام *(arr
+ i).
🖥 المخرجات:
Sum of array elements:
150
📌 الخلاصة:
✔ تعلمنا كيفية استخدام الدوال لتنظيم الكود.
✔ فهمنا كيفية التعامل مع المؤشرات والوصول إلى
البيانات.
✔ تعلمنا تقنيات عكس المصفوفات وتمرير المؤشرات إلى
الدوال.
الفصل السادس: الهياكل (Structures) والاتحادات (Unions)
📌 التمارين:
- إنشاء هيكل يحتوي على بيانات موظف وطباعتها
- إنشاء مصفوفة من الهياكل وتعبئتها بالقيم
-
استخدام
union
لتخزين بيانات بأحجام مختلفة - تمرير هيكل كوسيط إلى دالة
-
استخدام
enum
لتحديد قائمة خيارات وتنفيذ أوامر بناءً عليها
✅
سنوضح الفرق بين
struct
و
union
مع أمثلة
عملية.
📌 الفصل السادس: تمارين حول الهياكل (Structures)
والاتحادات
(Unions) في C
✅
كل تمرين يحتوي على الكود، الشرح التفصيلي، وتحليل
المخرجات.
🔹 التمرين الأول: إنشاء هيكل يحتوي على بيانات موظف
وطباعتها
📌 المطلوب:
اكتب برنامجًا يستخدم هيكل (struct) لتخزين
بيانات موظف
(الاسم، المعرف، الراتب)، ثم اطبع هذه البيانات.
✅ الكود:
#include
<stdio.h>
//
تعريف هيكل لموظف
struct Employee {
char name[50 ];
int id;
float salary;
};
int main()
{
struct Employee emp;
// إدخال بيانات الموظف
printf("Enter Employee name: ");
scanf("%s", emp.name);
printf("Enter Employee ID: ");
scanf("%d",
&emp.id);
printf("Enter Employee salary: ");
scanf("%f",
&emp.salary);
// طباعة بيانات الموظف
printf("\nEmployee Details:\n");
printf("Name: %s\n", emp.name);
printf("ID: %d\n", emp.id);
printf("Salary: %.2f\n",
emp.salary);
return 0;
}
🔹 التمرين الثاني: إنشاء مصفوفة من الهياكل وتعبئتها
بالقيم
اكتب برنامجًا يُنشئ مصفوفة من الهياكل
لتخزين بيانات عدة
موظفين، ثم يقوم بطباعتها.
✅ الكود:
#include <stdio.h>
#define SIZE 3
// عدد الموظفين
// تعريف هيكل
لموظف
struct Employee {
char name[50 ];
int
id;
float salary;
};
int main()
{
struct Employee
employees[SIZE];
// إدخال
بيانات
الموظفين
for (int i = 0; i < SIZE; i++)
{
printf
("Enter details for Employee %d:\n",
i + 1);
printf("Name: ");
scanf( "%s",
Employees[i].name);
printf("ID: ");
scanf("%d",
&employees[i].id);
printf("Salary: ");
scanf("%f",
&employees[i].salary);
}
// طباعة
بيانات
الموظفين
printf("\nEmployee List:\n");
for
(int i = 0; i < SIZE; i++)
{
printf("Employee %d - Name: %s, ID: %d, Salary: %.2f\n",
i + 1,
Employees[i].name, Employees[i].id,
Employees[i].salary);
}
return 0;
}
🔹 التمرين الثالث: استخدام
union
لتخزين
بيانات بأحجام
مختلفة📌 المطلوب:
اكتب برنامجًا يستخدم
union
لتخزين
قيم
مختلفة في نفس المساحة من الذاكرة.
✅ الكود:
#include<stdio.h>
// تعريف
اتحاد (union)
union Data {
int
i;
float
f;
char
str[20];
};
int
main() {
union Data data;
// تخزين
عدد صحيح
data.i = 10;
printf("Stored value as an integer: %d\n",
data.i);
// تخزين
عدد عشري
data.f = 20.5;
printf("Stored value as a float: %.2f\n",
data.f);
// تخزين
نص
sprintf(data.str, "Hello");
printf("Stored value as a string: %s\n",
data.str);
return 0;
}
🔹 التمرين الرابع: تمرير هيكل كوسيط إلى
دالة
📌 المطلوب:
اكتب دالة تأخذ هيكلًا كوسيط وتطبع بياناته.
✅ الكود:
#include
<stdio.h>
// تعريف
هيكل لموظف
struct Employee
{
char
name[50];
int
id;
float salary;
};
// دالة
لطباعة بيانات الموظف
void
printEmployee(struct Employee emp) {
printf("Name: %s\n",
emp.name);
printf("ID: %d\n",
emp.id);
printf("Salary: %.2f\n",
emp.salary);
}
int
main() {
struct Employee emp
= {"Ali", 105, 7500.50};
printf("Employee Details:\n");
printEmployee(emp);
return 0;
}
🔹 التمرين الخامس: استخدام
enum
لتحديد قائمة
خيارات
وتنفيذ أوامر بناءً عليها📌 المطلوب:
استخدم
enum
لتحديد أيام الأسبوع، واطبع اسم اليوم بناءً على رقم يُدخله
المستخدم.
✅ الكود:
#include<stdio.h>
// تعريف
التعداد
enum Day {
Sunday = 1, Monday, Tuesday, Wednesday,
Thursday, Friday, Saturday
};
int
main() {
int
choice;
printf("Enter the Day number (1-7): ");
scanf("%d", &choice);
if (choice >= 1 && choice <= 7) {
printf("The Day is: ");
switch (choice) {
case Sunday: printf("Sunday\n"); break;
case Monday: printf("Monday\n"); break;
case Tuesday: printf("Tuesday\n"); break;
case Wednesday: printf("Wednesday\n"); break;
case Thursday: printf("Thursday\n"); break;
case Friday: printf("Friday\n"); break;
case Saturday: printf("Saturday\n"); break;
}
} else {
printf("Invalid number!\n");
}
return 0;
}
🔹 الخلاصة
📌 الهياكل (
struct
): تُستخدم لجمع
بيانات
ذات صلة معًا تحت اسم واحد، مما يسهل إدارة البيانات
المعقدة.📌
الاتحادات (
union
): تشترك جميع
المتغيرات
في نفس مساحة الذاكرة، مما يسمح بتوفير المساحة ولكن يُفقد القيم
السابقة
عند تخزين قيم جديدة.📌
التعداد (enum): يستخدم
لتعريف قوائم ثابتة من القيم لزيادة وضوح الكود.
📌
تمرير الهيكل إلى الدوال: يمكن تمرير الهياكل إلى
الدوال لمعالجة البيانات بطريقة منظمة.
🚀 هذه التمارين ستساعدك على فهم هذه المفاهيم
بعمق.
الفصل السابع: إدارة الذاكرة (Memory Management)
📌 التمارين:
-
استخدام
malloc
لحجز مصفوفة وإدخال بياناتها ديناميكيًا -
تحرير الذاكرة باستخدام
free
ومنع التسربات -
إعادة تخصيص الذاكرة باستخدام
realloc
-
استخدام
calloc
لإنشاء مصفوفة مهيأة بصفر تلقائيًا - تنفيذ برنامج يقرأ أعدادًا غير معروفة مسبقًا ويخزنها ديناميكيًا
✅
سيتم شرح مفاهيم الحجز الديناميكي بشكل عملي ومدعوم
بالأمثلة.
📌 الفصل السابع: تمارين حول إدارة الذاكرة (Memory
Management) في
C
✅
كل تمرين يحتوي على الكود، الشرح التفصيلي، وتحليل
المخرجات.
🔹 التمرين الأول: استخدام
malloc
لحجز
مصفوفة وإدخال
بياناتها ديناميكيًا📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال عدد من العناصر، ثم يحجز
مصفوفة
ديناميكيًا باستخدام
malloc
لتخزين الأعداد.
✅ الكود:
#include<stdio.h>
#include <stdlib.h>
int
main() {
int
n, *arr;
//
إدخال عدد العناصر
printf("Enter the number of elements: ");
scanf("%d", &n);
// حجز
الذاكرة باستخدام malloc
arr = (int*) malloc(n * sizeof(int));
//
التحقق من نجاح الحجز
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
//
إدخال القيم في المصفوفة
printf("Enter %d numbers:\n", n);
for (int i = 0; i < n; i++) {
scanf("%d", &arr[i]);
}
//
طباعة الأعداد المدخلة
printf("Entered elements: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
//
تحرير الذاكرة
free(arr);
return 0;
}
📌 ملاحظة:
malloc
لا تهيّئ القيم
تلقائيًا، لذا قد تحتوي المصفوفة على قيم غير معروفة قبل الإدخال.
🔹 التمرين الثاني: تحرير الذاكرة باستخدام
free
ومنع
التسربات📌 المطلوب:
اكتب برنامجًا يحجز الذاكرة باستخدام
malloc
ثم
يحررها
باستخدام free
لمنع
تسرب الذاكرة (Memory Leak).✅ الكود:
#include<stdio.h>
#include<stdlib.h>
int
main() {
int
*ptr;
// تخصيص
ذاكرة لعدد واحد
ptr = (int*)
malloc(sizeof(int));
//
التحقق من نجاح الحجز
if
(ptr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// إدخال
قيمة
printf("Enter a number: ");
scanf("%d", ptr);
// طباعة
القيمة
printf("The entered number is: %d\n",
*ptr);
// تحرير
الذاكرة
free(ptr);
ptr = NULL; //
تجنب المؤشرات المعلقة
return 0;
}
📌 ملاحظة: بعد
free(ptr), جعل
ptr = NULL يساعد
على
منع الوصول إلى ذاكرة محررة.
🔹 التمرين الثالث: إعادة تخصيص الذاكرة باستخدام
realloc
📌 المطلوب:
اكتب برنامجًا يسمح للمستخدم بإدخال عدد أولي من الأرقام، ثم يعيد
تخصيص
الذاكرة لإضافة المزيد من الأرقام لاحقًا.
✅ الكود:
#include<stdio.h>
#include <stdlib.h>
int
main() {
int
n, new_n, *arr;
// إدخال
العدد الأولي للعناصر
printf("Enter the initial number of elements: ");
scanf("%d", &n);
// تخصيص
الذاكرة
arr = (int*)
malloc(n * sizeof(int));
//
التحقق من نجاح الحجز
if
(arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// إدخال
القيم
printf("Enter %d numbers:\n", n);
for (int i = 0; i < n; i++)
{
scanf("%d", &arr[i]);
}
// توسيع
المصفوفة
printf("Enter the new number of elements: ");
scanf("%d", &new_n);
arr = (int*)
realloc(arr, new_n * sizeof(int));
if
(arr == NULL) {
printf("Memory reallocation failed!\n");
return 1;
}
// إدخال
القيم الإضافية
printf("Enter %d additional numbers:\n",
new_n - n);
for (int i = n; i < new_n; i++)
{
scanf("%d", &arr[i]);
}
// طباعة
القيم الجديدة
printf("The elements after reallocation: ");
for (int i = 0; i < new_n; i++)
{
printf("%d ", arr[i]);
}
// تحرير
الذاكرة
free(arr);
return 0;
}
📌 ملاحظة:
realloc
يحاول تعديل حجم
الذاكرة المحجوزة مسبقًا دون فقدان البيانات، لكنه قد يُعيد
تخصيصها في
موقع جديد.
🔹 التمرين الرابع: استخدام
calloc
لإنشاء
مصفوفة مهيأة
بصفر تلقائيًا📌 المطلوب:
اكتب برنامجًا يحجز مصفوفة باستخدام
calloc
ويطبع
محتواها
مباشرةً.
✅ الكود:
#include<stdio.h>
#include <stdlib.h>
int main() {
int n, *arr;
// إدخال حجم المصفوفة
printf("Enter the number of elements: ");
scanf(" %d", &n);
// تخصيص الذاكرة باستخدام calloc
arr = (int*) calloc(n, sizeof(int));
// التحقق من نجاح الحجز
if (arr == NULL) {
printf("Memory allocation failed!\n");
return 1;
}
// طباعة القيم المبدئية (يجب أن تكون 0)
printf("The array after allocation using calloc:\n");
for (int i = 0; i < n; i++>) {
printf("%d ", arr[i]);
}
// تحرير الذاكرة
free(arr);
return 0;
}
📌 ملاحظة: على عكس
malloc
, تقوم
calloc
بتهيئة جميع القيم إلى0تلقائيًا.
🔹 التمرين الخامس: تنفيذ برنامج يقرأ أعدادًا غير معروفة
مسبقًا
ويخزنها ديناميكيًا
📌 المطلوب:
اكتب برنامجًا يطلب من المستخدم إدخال الأعداد حتى يُدخل 1-، مع
استخدام
realloc
لتخصيص الذاكرة ديناميكيًا.
✅ الكود:
#include<stdio.h>
#include <stdlib.h>
int main() {
int *arr = NULL, count = 0, num;
printf("Enter numbers (enter -1 to end input):\n");
while (1) {
scanf("%d", &num);
if (num == -1) break;
// إعادة تخصيص الذاكرة
int *temp = (int*)realloc(arr, (count + 1) * sizeof(int));
if (temp == NULL) {
printf("Memory allocation failed!\n");
free(arr);
return 1;
}
arr = temp;
arr[count++>] = num;
}
// طباعة الأعداد المدخلة
printf("Entered numbers:\n");
for (int i = 0; i < count; i++>) {
printf("%d ", arr[i]);
}
// تحرير الذاكرة
free(arr);
return 0;
}
📌 ملاحظة: يتم استخدام
realloc
في
كل مرة
يُضاف فيها عدد جديد لضبط حجم المصفوفة.
🔹 الخلاصة
📌
malloc
و calloc
تُستخدمان لحجز
الذاكرة
ديناميكيًا، مع فرق أن calloc
تهيئ القيم إلى
صفر.📌
free
ضرورية لمنع تسرب الذاكرة عند
انتهاء
استخدام الذاكرة الديناميكية.📌
realloc
مفيدة
لتغيير
حجم الذاكرة المحجوزة ديناميكيًا دون فقدان البيانات.
🚀 هذه التمارين ستساعدك على فهم إدارة الذاكرة
بعمق
الفصل الثامن: التعامل مع الملفات (FILE Handling)
📌 التمارين:
-
فتح ملف وكتابته ثم قراءته باستخدام
fopen
,fprintf
,fscanf
- إضافة نص جديد إلى ملف دون حذف المحتوى السابق
- قراءة بيانات من ملف وحفظها في مصفوفة
-
استخدام
fseek
وftell
للتحكم في موضع القراءة والكتابة - إنشاء سجل بيانات للموظفين باستخدام الملفات
✅
سنوضح كيفية التعامل مع الملفات النصية والملفات
الثنائية.
📌 الفصل الثامن: تمارين حول التعامل مع الملفات (FILE Handling) في C
✅
كل تمرين يحتوي على الكود، الشرح التفصيلي، وتحليل
المخرجات.
🔹 التمرين الأول: فتح ملف وكتابته ثم قراءته باستخدام
fopen
, fprintf
,
fscanf
📌 المطلوب:
اكتب برنامجًا يقوم بفتح ملف نصي، وكتابة بيانات داخله، ثم قراءتها
وعرضها على الشاشة.
✅ الكود:
#include<stdio.h>
int
main() {
FILE *file;
char name[50];
int age;
// فتح الملف للكتابة
FILE = fopen("data.txt", "w");
if (FILE == NULL)
{
printf("Error opening the file!\n");
return 1;
}
// كتابة البيانات إلى
الملف
printf("Enter the name: ");
scanf("
"%s", name);
printf("Enter the age: ");
scanf("%d", &age);
fprintf(file, "%s %d\n", name, age);
fclose(file);
// فتح الملف للقراءة
FILE = fopen("data.txt", "r");
if (FILE == NULL)
{
printf("Error opening the file!\n");
return 1;
}
// قراءة البيانات من الملف
fscanf(file, " "%s %d", name,
&age);
printf("Data read: Name: %s, Age: %d\n",
name, age);
fclose(file);
return 0;
}
📌 ملاحظة: عند استخدام
"w"
, يتم
حذف
المحتوى السابق للملف تلقائيًا إذا كان موجودًا.
🔹 التمرين الثاني: إضافة نص جديد إلى ملف دون حذف المحتوى
السابق
📌 المطلوب:
اكتب برنامجًا يقوم بفتح ملف نصي وإضافة بيانات جديدة إليه دون حذف
البيانات القديمة.
✅ الكود:
#include<stdio.h>
int main() {
FILE *file;
char text[100];
// "a" فتح الملف بوضع الإضافة
FILE = fopen("data.txt", "a");
if (FILE == NULL) {
printf("Error opening the file!\n");
return 1;
}
// طلب نص من المستخدم وإضافته إلى الملف
printf("Enter the text to be added: ");
scanf(" %[^\n]", text); // قراءة النص مع الفراغات
fprintf(file, "%s\n", text);
fclose(file);
printf("Text added successfully!\n");
return 0;
}
📌 ملاحظة: الوضع
"a"
يفتح الملف
ويضيف
البيانات في نهايته دون حذف المحتوى السابق.
🔹 التمرين الثالث: قراءة بيانات من ملف وحفظها في
مصفوفة
📌 المطلوب:
اكتب برنامجًا يقرأ 5 أرقام من ملف ويخزنها في مصفوفة ثم
يعرضها.
✅ الكود:
#include<stdio.h>
int
main() {
FILE *file;
char text[100];
// "a" فتح الملف بوضع
الإضافة
FILE = fopen("data.txt", "a");
if (FILE == NULL)
{
printf("Error opening the file!\n");
return 1;
}
// طلب نص من المستخدم وإضافته إلى
الملف
printf("Enter the text to be added: ");
scanf(" %[^\n]", text); // قراءة النص مع الفراغات
fprintf(file, "%s\n", text);
fclose(file);
printf("Text added successfully!\n");
return 0;
}
📌 ملاحظة: يجب أن يحتوي
numbers.txt
على
5 أرقام مفصولة بمسافات أو أسطر.
🔹 التمرين الرابع: استخدام
fseek
و
ftell
للتحكم في موضع القراءة والكتابة📌 المطلوب:
اكتب برنامجًا يفتح ملفًا، ينتقل إلى موقع معين باستخدام
fseek
, ثم يطبع موقع المؤشر باستخدام
ftell
.
✅ الكود:
#include<stdio.h>
int main() {
FILE *file;
FILE = fopen("data.txt", "r");
if (FILE == NULL) {
printf("Error opening the file!\n");
return 1;
}
// تحريك المؤشر إلى الموضع 10
fseek(file, 10, SEEK_SET);
// طباعة موقع المؤشر الحالي
printf("Current pointer position: %ld\n", ftell(file));
fclose(file);
return 0;
}
📌 ملاحظة:
fseek(file, 10, SEEK_SET);
ينقل المؤشر إلى البايت
10 من
بداية الملف.
🔹 التمرين الخامس: إنشاء سجل بيانات للموظفين باستخدام
الملفات
📌 المطلوب:
اكتب برنامجًا يسمح بإضافة بيانات الموظفين إلى ملف، ثم قراءتها
لاحقًا.
✅ الكود:
#include<stdio.h>
typedef struct {
char name[50];
int age;
float salary;
} Employee;
int main() {
FILE *file;
Employee emp;
int n;
// فتح الملف للإضافة
FILE = fopen("employees.dat", "ab");
if (FILE == NULL) {
printf("Error opening the file!\n");
return 1;
}
printf("Enter the number of Employees: ");
scanf(" %d", &n);
for (int i = 0; i < n; i++>) {
printf("Enter the Employee's name: ");
scanf(" %s", emp.name);
printf("Enter the Employee's age: ");
scanf(" %d", &emp.age);
printf("Enter the Employee's salary: ");
scanf(" %f", &emp.salary);
fwrite(&emp, sizeof(Employee), 1, file);
}
fclose(file);
// فتح الملف للقراءة
FILE = fopen("employees.dat", "rb");
if (FILE == NULL) {
printf("Error opening the file!\n");
return 1;
}
printf("\nEmployee Records:\n");
while (fread(&emp, sizeof(Employee), 1, file)) {
printf("Name: %s, Age: %d, Salary: %.2f\n", emp.name, emp.age, emp.salary);
}
fclose(file);
return 0;
}
📌 ملاحظة: يتم استخدام
"ab"
للإضافة دون
حذف البيانات القديمة، و"rb"
للقراءة.
🔹 الخلاصة
📌
fopen
تُستخدم لفتح الملفات بوضعيات مختلفة
("w"
, "a"
, "r"
,
"wb"
, "rb"
, إلخ).📌
fprintf
و fscanf
تُستخدمان للتعامل مع
الملفات النصية، بينما fwrite
و
fread
تُستخدمان مع الملفات الثنائية.📌
fseek
و ftell
تسمحان بالتحكم في موضع
القراءة
والكتابة داخل الملف.📌 يمكن استخدام الملفات لإنشاء
سجل بيانات وحفظ المعلومات بسهولة.
🚀
هذه التمارين ستساعدك على احتراف التعامل مع الملفات في
C.
الفصل التاسع: البرمجة الكائنية في C (OOP Concepts in C)
📌 التمارين:
-
إنشاء كائن باستخدام
struct
وتمريره إلى دالة - محاكاة الوراثة في C باستخدام الهياكل المتداخلة
- إنشاء مؤشر إلى دالة داخل هيكل لمحاكاة التعددية (Polymorphism)
- تصميم نظام تسجيل دخول بسيط باستخدام البرمجة الكائنية
- تنفيذ مشروع صغير يحاكي إدارة مكتبة باستخدام الهياكل والمؤشرات
✅
سنوضح كيف يمكن تطبيق البرمجة الكائنية رغم أن C لغة
إجرائية.
📌 الفصل التاسع: تمارين البرمجة الكائنية في C (OOP
Concepts in
C)
✅
كل تمرين يحتوي على الكود، الشرح التفصيلي، وتحليل
المخرجات.
📌
رغم أن C لغة إجرائية، يمكن محاكاة مفاهيم البرمجة
الكائنية مثل
الكائنات، الوراثة، والتعددية باستخدام الهياكل (structs)
والمؤشرات.
🔹 التمرين الأول: إنشاء كائن باستخدام
struct
وتمريره
إلى دالة📌 المطلوب:
قم بإنشاء هيكل (
struct
) يمثل طالبًا يحتوي على
الاسم، الرقم التعريفي، والمعدل، ثم مرر هذا
الكائن إلى
دالة لطباعة بياناته.
✅ الكود:
#include <stdio.h>
// تعريف هيكل الطالب
typedef struct {
char name[50];
int id;
float gpa;
} Student;
// دالة لطباعة بيانات الطالب
void printStudent(Student s) {
printf("Name: %s\n", s.name);
printf("ID: %d\n", s.id);
printf("GPA: %.2f\n", s.gpa);
}
int main() {
Student student1 = {"Bader", 101, 3.8};
// تمرير الطالب إلى الدالة
printStudent(student1);
return 0;
}
📌 ملاحظة: يتم تمرير الهيكل
Student
كوسيط إلى الدالة، لكنه يتم نسخه بالكامل،
مما قد
يكون غير فعال إذا كان الحجم كبيرًا.
🔹 التمرين الثاني: محاكاة الوراثة في C باستخدام الهياكل
المتداخلة
📌 المطلوب:
استخدم
struct
لإنشاء كائن
موظف (Employee) يحتوي على بيانات عامة، ثم كائن
مدير (Manager) يحتوي على بيانات إضافية.
✅ الكود:
#include<stdio.h>
// هيكل الموظف الأساسي
typedef
struct {
char name[50];
int id;
float salary;
} Employee;
// هيكل المدير يتضمن بيانات الموظف + بيانات إضافية
typedef
struct {
Employee emp; // "وراثة" بيانات الموظف
char department[50];
} Manager;
int main() {
Manager mgr = {
{ "Moussa", 201, 7500.5
}, "Information Technology"};
printf("Manager: %s\n", mgr.emp.name);
printf("ID: %d\n", mgr.emp.id);
printf("Salary: %.2f\n", mgr.emp.salary);
printf("Department: %s\n", mgr.department);
return 0;
}
📌 ملاحظة: يتم محاكاة الوراثة بجعل
Manager
يحتوي على كائن Employee
كعضو
داخلي.
🔹 التمرين الثالث: إنشاء مؤشر إلى دالة داخل هيكل لمحاكاة
التعددية
(Polymorphism)
📌 المطلوب:
استخدم مؤشرًا إلى دالة داخل
struct
لجعل كائنين
ينفذان
سلوكًا مختلفًا، مثل دالتين speak
للحيوانات
المختلفة.
✅ الكود:
#include
<stdio.h>
// تعريف
مؤشر إلى دالة
typedef void (*SoundFunc)();
// دوال
لكل نوع من وسائل النقل
void
carSound() {
printf("Car: Vroom Vroom!\n");
}
void
planeSound() {
printf("Plane: Zzzzz!\n");
}
// تعريف
هيكل وسيلة النقل مع مؤشر إلى دالة
typedef struct
{
char name[20];
SoundFunc sound;
}
Vehicle;
int
main()
{
Vehicle car = {"Car", carSound};
Vehicle plane = {
"Plane", planeSound};
//
استدعاء الدالة عبر المؤشر لمحاكاة التعددية
car.sound();
plane.sound();
return 0;
}
📌 ملاحظة: هذه التقنية تستخدم في لغات OOP مثل
C++ و
Java تحت مفهوم التعددية
(Polymorphism).
🔹 التمرين الرابع: تصميم نظام تسجيل دخول بسيط باستخدام
البرمجة
الكائنية
📌 المطلوب:
قم بإنشاء كائن مستخدم (User) يحتوي على
اسم المستخدم وكلمة المرور، ثم تحقق من بيانات
الدخول
باستخدام دالة.
✅ الكود:
#include<stdio.h>
#include<string.h>
// تعريف
هيكل المستخدم
typedef struct
{
char
username[50];
char
password[50];
}
User;
// دالة
تحقق من بيانات تسجيل الدخول
int
login(User u, char *inputUser, char *inputPass) {
return (strcmp(u.username, inputUser) ==
0 && strcmp(u.password, inputPass) ==
0);
}
int
main() {
User user1 = {"admin", "1234"}; //
بيانات المستخدم المخزنة
char inputUser[50], inputPass[50];
//
طلب بيانات المستخدم
printf("Enter username: ");
scanf(
"%s",
inputUser);
printf("Enter password: ");
scanf(
"%s",
inputPass);
//
التحقق من صحة بيانات الدخول
if (login(user1, inputUser,
inputPass)) {
printf("Login successful!\n");
} else
{
printf("Login failed, please check your credentials.\n");
}
return 0;
}
📌 ملاحظة:
strcmp
تُستخدم
لمقارنة النصوص
المدخلة مع البيانات المخزنة.
🔹 التمرين الخامس: تنفيذ مشروع صغير يحاكي إدارة
مكتبة باستخدام
الهياكل والمؤشرات
📌 المطلوب:
قم بتصميم برنامج إدارة مكتبة يسمح بـ
إضافة الكتب، عرض الكتب، والبحث عن كتاب
معين.
✅ الكود:
#include<stdio.h>
#include<string.h>
//
تعريف هيكل الكتاب
typedef struct {
char title[50];
char author[50];
int year;
} Book;
//
دالة لإضافة كتاب إلى المصفوفة
void addBook(Book *library, int *count) {
printf("Enter book title: ");
scanf(" %[^\n]", library[*count].title);
printf("Enter author name: ");
scanf(" %[^\n]", library[*count].author);
printf("Enter publication year: ");
scanf("%d",
&library[*count].year);
(*count)++;
}
//
دالة لعرض جميع الكتب
void displayBooks(Book
*library, int count) {
printf("\n📚 Book List:\n");
for (int i = 0; i < count; i++)
{
printf("%d. %s - %s (%d)\n", i + 1, library[i].title, library[i].author, library[i].year);
}
}
//
دالة للبحث عن كتاب حسب العنوان
void searchBook(Book *library, int count, char *title) {
for (int i = 0; i < count; i++)
{
if (strcmp(library[i].title, title) == 0)
{
printf("Book found: %s - %s (%d)\n",
library[i].title,
library[i].author,
library[i].year);
return;
}
}
printf("⚠️ Book not found.\n");
}
int main() {
Book library[100];
int count = 0;
int choice;
char searchTitle[50];
do
{
printf("\n1. Add Book\n2. Display Books\n3.
Search for Book\n4.
Exit\nYour choice: ");
scanf("%d",
&choice);
switch (choice) {
case 1: addBook(library,
&count); break;
case 2: displayBooks(library,
count); break;
case 3:
printf("Enter book title: ");
scanf(" %[^\n]", searchTitle);
searchBook(library, count,
searchTitle);
break;
}
} while (choice != 4);
return 0;
}
📌 ملاحظة:
library
هو مصفوفة
من
struct
، والمؤشرات تُستخدم لتمرير العناوين.
🔹 الخلاصة
📌 البرمجة الكائنية يمكن محاكاتها في C باستخدام
struct
وunion
والمؤشرات.📌
الوراثة
يمكن تنفيذها باستخدام الهياكل
المتداخلة.
📌
التعددية (Polymorphism) يمكن محاكاتها باستخدام
مؤشرات الدوال.
📌 يمكن بناء أنظمة
واقعية مثل
إدارة المكتبات أو تسجيل الدخول باستخدام هذه
التقنيات.
قسم المشاريع التطبيقية لتقوية الفهم
📌 المشاريع العملية:
- إنشاء آلة حاسبة متقدمة تدعم العمليات الحسابية المختلفة
- برنامج إدارة بيانات الطلاب مع إمكانية الحفظ في ملف
- مشروع ساعة رقمية تعرض الوقت الحالي وتحديثه تلقائيًا
- برنامج قائمة مهام (To-Do List) مع تخزين البيانات في ملف
- لعبة بسيطة مثل Tic-Tac-Toe باستخدام المصفوفات والشروط
✅
سيتم شرح كل مشروع بالتفصيل، مع تحليل الكود وتحسينات
إضافية.
📌 المشاريع التطبيقية الصغيرة لتقوية
الفهم
✅
كل مشروع يحتوي على الكود، الشرح التفصيلي، وتحليل
المخرجات.
📌
هذه المشاريع تعزز الفهم العملي لمفاهيم C مثل
الهياكل، الملفات،
المؤشرات، والتحكم في التدفق.
🔹 المشروع الأول: آلة حاسبة متقدمة تدعم العمليات الحسابية المختلفة
📌 المطلوب:
إنشاء برنامج آلة حاسبة تدعم العمليات الأساسية
(+ , - , * ,
/)
مع دعم
الأسس (
^
) وباقي القسمة
(%
).
✅ الكود:
#include<stdio.h>
#include
<math.h>
void calculator() {
double num1, num2,
result;
char op;
printf("📌 Enter a mathematical operation (for example, 5 + 3): ");
scanf("%lf %c %lf", &num1, &op,
&num2);
switch (op) {
case '+': result = num1 +
num2; break;
case '-': result = num1 -
num2; break;
case '*': result = num1 *
num2; break;
case '/':
if (num2 != 0) result
= num1 / num2;
else {
printf("⚠️ Error: Division by zero is not possible!\n");
return;
}
break;
case '%': result = (int)num1 % (int)num2; break;
case '^': result = pow(num1, num2); break;
default:
printf("⚠️ Invalid operation!\n");
return;
}
printf("✅ The result is: %.2lf\n",
result);
}
int main() {
while (1) {
calculator();
printf("🔄 Do you want to continue? (1: Yes / 0: No): ");
int choice;
scanf("%d", &choice);
if (choice == 0) break;
}
return 0;
}
📌 ملاحظات:
✔️ يدعم العمليات
الأساسية والأسس
%
و ^
.✔️ يتحقق من القسمة
على الصفر لمنع
الأخطاء.
✔️ يعمل داخل حلقة حتى يقرر المستخدم
الخروج.
🔹 المشروع الثاني: برنامج إدارة بيانات الطلاب مع إمكانية الحفظ في ملف
📌 المطلوب:
إنشاء برنامج يسمح بإضافة
بيانات الطلاب (الاسم، العمر، المعدل)،
وعرض البيانات،
وحفظها في ملف نصي
(
students.txt
).
✅ الكود:
#include<stdio.h>
#include<stdlib.h>
typedef struct {
char name[50];
int age;
float gpa;
} Student;
void addStudent() {
FILE *file = fopen("students.txt", "a");
if (!file) {
printf("⚠️ Error opening the file!\n");
return;
}
Student s;
printf("📌 Enter student name: ");
scanf(" %[^"], s.name);
printf("📌 Enter age: ");
scanf("%d", &s.age);
printf("📌 Enter GPA: ");
scanf("%f", &s.gpa);
fprintf(file, "%s %d %.2f\n", s.name,
s.age, s.gpa);
fclose(file);
printf("✅ Student data saved successfully!\n");
}
📌 مميزات المشروع:
✔️
يخزن البيانات في ملف بحيث تبقى محفوظة
بعد إنهاء
البرنامج.
✔️
يسمح بإضافة طلاب جدد وعرض جميع الطلاب
المخزنين.
🔹 المشروع الثالث: ساعة رقمية تعرض الوقت الحالي وتحديثه تلقائيًا
📌 المطلوب:
إنشاء برنامج يعرض الوقت الحالي باستخدام
time.h
ويقوم بتحديثه كل
ثانية.
✅ الكود:
#include<stdio.h>
#include<time.h>
#include<unistd.h> //
Linux/MacOS
// #include <windows.h>
// Windows
void displayTime() {
while (1) {
time_t t;
struct tm
*currentTime;
time(&t);
currentTime =
localtime(&t);
printf("\r\uD83D\uDD52 Current time: %02d:%02d:%02d",
currentTime->tm_hour,
currentTime->tm_min,
currentTime->tm_sec);
fflush(stdout);
sleep(1); // تأخير ثانية
واحدة
}
}
int main() {
displayTime();
return 0;
}
📌 مميزات المشروع:
✔️
يعرض الوقت الحالي بتحديث
تلقائي.
✔️
يستخدم مكتبة time.h
لقراءة الوقت من
النظام.✔️
يعمل على Windows و Linux/MacOS مع تعديلات طفيفة
(
sleep()
vs Sleep()
).🔹 المشروع الرابع: قائمة مهام (To-Do List) مع تخزين البيانات في ملف
📌 المطلوب:
إنشاء برنامج يسمح للمستخدم
بإضافة المهام، عرضها، وحذف المهام
المنجزة
مع حفظ البيانات في
ملف
tasks.txt
.
✅ الكود:
#include<stdio.h>
#include <stdlib.h>
void addTask() {
FILE *file = fopen("tasks.txt", "a");
if (!file) {
printf("⚠️ Error opening the file!\n");
return;
}
char task[100];
printf("📌 Enter the task: ");
scanf(" %[^\n]", task);
fprintf(file, "%s\n", task);
fclose(file);
printf("✅ Task added successfully!\n");
}
void viewTasks() {
FILE *file = fopen("tasks.txt", "r");
if (!file) {
printf("⚠️ No tasks available yet!\n");
return;
}
char task[100];
printf("\n📋 Task List:\n");
while ( fgets(task, sizeof(task), file)) {
printf("🔹 %s", task);
}
fclose(file);
}
int main() {
int choice;
do
{
printf("\n1. Add Task\n2. View Tasks\n3. Exit\nYour choice: ");
scanf("%d", &choice);
switch (choice) {
case 1: addTask(); break;
case 2: viewTasks(); break;
}
} while (choice != 3);
return 0;
}
📌 مميزات المشروع:
✔️
تخزين المهام في ملف حتى لا تضيع بعد إغلاق
البرنامج.
✔️
واجهة سهلة الاستخدام لإضافة وعرض
المهام.
🔹 الخلاصة
🚀 هذه المشاريع تعزز مهاراتك
في:
📌
التعامل مع الملفات لحفظ
البيانات.
📌
الهياكل (
struct
)
لإدارة البيانات المعقدة.📌
التحكم في التدفق (
if
,
switch
)
لإنشاء قوائم تفاعلية.🔹 المشروع الخامس: لعبة Tic-Tac-Toe باستخدام C
📌 المطلوب:
إنشاء لعبة Tic-Tac-Toe (إكس-أو) تسمح
للاعبين بالتنافس على
لوحة 3×3، مع التحقق من الفائز بعد كل
حركة.
✅ الكود:
#include<stdio.h>
char board[3][3]; // مصفوفة تمثل لوحة
اللعبة
char currentPlayer = 'X';
// تهيئة اللوحة
بالفراغات
void initializeBoard() {
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
board[i][j] = ' ';
}
}
}
// عرض اللوحة
void displayBoard() {
printf("\n");
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
printf(" %c ", board[i][j]);
if (j < 2) printf("|");
}
printf("\n");
if (i < 2) printf("---+---+---\n");
}
printf("\n");
}
// التحقق مما إذا كان هناك
فائز
char checkWinner() {
// فحص الصفوف
والأعمدة
for (int i = 0; i < 3; i++)
{
if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
return board[i][0];
if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
return board[0][i];
}
// فحص الأقطار
if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')
return board[0][0];
if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ')
return board[0][2];
return ' '; // لا يوجد فائز حتى
الآن
}
// التحقق مما إذا كانت اللوحة
ممتلئة (تعادل)
int isBoardFull() {
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (board[i][j] == ' ') return 0;
return 1;
}
// السماح للاعب بإدخال
حركة
void makeMove() {
int row, col;
while (1)
{
printf("📌 Player %c، enter the row and column (1 to 3): ",
currentPlayer);
scanf("%d %d",
&row, &col);
row--; col--; // تحويل الإدخال إلى فهرس
المصفوفة (من0إلى
2)
if (row >= 0 && row <
3 && col >=
0 && col <
3 &&
board[row][col] == ' ')
{
board[row][col] =
currentPlayer;
break;
}
printf("📌 Invalid move! Try again.\n");
}
// تغيير اللاعب
الحالي
currentPlayer =
(currentPlayer == 'X') ?
'O' : 'X';
// التحقق من
الفائز
char winner = checkWinner();
if (winner != ' ') printf("🎉 Player %c wins!\n",
winner);
else if (isBoardFull()) printf("🛑 It's a draw!\n");
// عرض اللوحة بعد
الحركة
displayBoard();
}
int main
() {
initializeBoard();
displayBoard();
while (1)
{
makeMove();
}
}
📌 مميزات المشروع:
✔️ لعب ثنائي: يدعم لاعبين يتناوبون على
اللعب.
✔️
واجهة سهلة: تعرض اللوحة بشكل واضح بعد
كل حركة.
✔️
التحقق من الفائز: يحدد الفائز أو
التعادل بعد كل جولة.
🎯 ملخص المشروع:
🚀
لعبة تفاعلية بلغة C تستخدم المصفوفات والشروط
للتحكم في التدفق.
🔄
يمكن تطويرها لاحقًا لإضافة ذكاء اصطناعي
كمنافس!
💡 خلاصة قسم التمارين والمشاريع
- يحتوي هذا القسم على 100 تمرين شامل لجميع مواضيع C.
- التمارين تبدأ من الأساسيات إلى المواضيع المتقدمة.
- كل تمرين يأتي مع الحل المشروح بالكامل.
- المشاريع التطبيقية ستساعدك على تقوية مهاراتك البرمجية من خلال سيناريوهات حقيقية.