الدوال Function في لغة سي
الدوال ( Functions ) إن مصطلح دالة قد يكون مألوفاً في الرياضيات لكن في البرمجة له معاني اعمق من الرياضيات, سوف نتعرَّف على كيفية إنشاء دالة Function و انواعها و كيفية إستعمال بعض الدوال Functions الموجودة في لغة سي ثم سنتعرف على مفهوم جديد و هو المصفوفات Arrays و كيفية إنشائها و كيف نتعامل مع النصوص في لغة سي.
اغلب البرامج التي نراها هي اكبر بكثير مما كنا نقوم بالعمل به في الدروس السابقة و افضل طريقة لإنشاء و صيانة البرامج الكبيرة. هي تقسيم البرنامج إلى اكثر من قطعه برمجية, حيث أن كل قطعة تقوم بأداء مهام خاصة و بذلك تكون صيانة كل قطعه على حدى اسهل بكثير من لو وقعت مشكلة في برنامج كبير ذو قطعه واحدة.
فوائد الدوال في لغة سي
يوجد العديد من الأمور التي تفيدنا بها و يجب أن نستخدم الدوال بها وهي:
- إستدعاء الوظائف المدمجة في وظائف فليس على المبرمج كتابة الوظائف بنفسه.
- تسهل إعادة استخدام الكود (بحيث يمكن إستدعاء الدالة نفسها من أجل أداء مهمة ما في أماكن مختلفة من البرنامج أو خارج البرنامج).
- تجعل الدوال البرمجة أسهل و امتع عن طريق إلغاء تكرار الكود.
مثال 1
#include <stdio.h>
#include <stdlib.h>
void printMuhammad(){
printf("Muhammad Alush \n python \n javascript \n java \n c++ \n \n");
}
void printSaja(){
printf("Saja Osman \n C \n PHP \n \n");
}
void printAMVIIP(){
printf("Ahmed AMVIIP \n html \n css \n laravel \n \n");
}
int main(int argc, char** argv) {
printMuhammad();
printSaja();
printAMVIIP();
return 0;
}
نُلاحظ أننا قُمنا بتعريف ثلاثة دوال void و هذه الدوال هي:
الدالة Function الأولى إسمها printMuhammad تقوم هذه الدالة بطباعة إسم محمد و اللغات التي يقدمها على هذا الموقع. الدالة Function الثانية هي printSaja و هذه تقوم بطباعة إسم سجى و اللغات التي تقدمها على موقعنا. الدالة Function الثالثة هي printAMVIIP تقوم هذه الدالة بطباعة اللغات التي يقدمها أحمد على موقعنا.
ثم دخلنا للدالة main و قمنا بإستدعاء كل دالة بإسمها كما كتبناه في الأعلى مع وضع القوسين بنهاية كل دالة للتعريف بها و عند تشغيل الكود السابق سنحصل على النتيجة التالية
Muhammad Alush
python
javascript
java
c++
Saja Osman
C
PHP
Ahmed AMVIIP
html
css
laravel
خُلاصة هذا المثال هو بدلاً من كتابة كل الأوامر في الدالة main قمنا بتوزيعها في دوال منفصلة, بيانات كل إسم في دالة لوحده ثم قُمنا بإستدعائها فقط من خلال الدالة main.
مثال 2
اكتب برنامج يحتوي على دالة إسمها printLink تقوم بطباعة رابط nusur tech.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv) {
printLink();
return 0;
}
void printLink()
{
printf("www.nusurtech.ca \n");
}
لقد قُمنا ببناء دالة printLink و كتبنا لها أن مُهمتها هي طباعة رابط موقعنا ثم النزول سطر جديد, هذا كان في خارج الدالة main. ثم في داخل الدالة main قُمنا بكتابة أمر إستدعاء هذه الدالة عبر كتابة إسمها, و عند تشغيل هذا الكود سوف يُعطينا خطأ. و لا يطبَع لنا النتيجة التي نُريدها و يكون الخطأ هو أن الدالة main لم تتعرَّف على أمر إستدعاء. الدالة printLink بإسمها و أنها غير معرّفة. و السبب يعود هو أن في برامج السي C التعليمات البرمجية تتنفذ سطراً سطراً من الأعلى إلى الأسفل فيبدأ في include ثم int main و غيره إلى نهاية البرنامج.
لكن في كودنا السابق سوف يأتي المترجم إلى أمر الإستدعاء printLink فيقول المحرر أنا لم ارى هذه الدالة. في الأعلى و لن يستطيع التعرّف عليها لأنها في الأسفل, و الحل هو نقلها للأعلى فوق int main.
#include <stdio.h>
#include <stdlib.h>
void printLink()
{
printf("www.nusurtech.ca \n");
}
int main(int argc, char** argv) {
printLink();
return 0;
}
و عند نقله و تشغيله, البرنامج سوف يَمُر على تعريف الدالة في الأعلى ثم بعد ذلك سوف يمر على دالة. main و سيرى. الدالة و يقول لقد قمت برؤيتها في الأعلى و عند التشغيل ستكون النتيجة هي
www.nusurtech.ca
ملاحظة
كما قُلنا بأن الدالة main هي نقطة البداية لأي برنامج فلنمثلها بالمدير و هناك بعض الدوال المعرفة في البرنامج مثل myLink فلنمثلهم الموظفين فإذا قامت الدالة main بإستدعاء الموظف لأداء مهمة ما تقوم الدالة main بإنتظار الموظف حتى ينتهي من عمله و لا تبالي بكيفية اداء عمله, المهم أن يقوم بالإنتهاء من المهمة
الدوال التي تعيد قيمة
القيم المعادة من الدوال ( Return type functions ) و لفهم هذا النوع أكثر يجب أن نأخذ مثال حي عن هذا الأمر لذا تخيل معي ما يلي:
أن أب يستدعي الإبن للذهاب إلى السوق و الأب هو main و الإبن هو دالة اُخرى تقوم بالذهاب إلى السوق. فإذا استدعى الأب إبنه و قال إذهب إلى السوق فهو لم يقٌم بإعطاءه أي مٌعطيات أو مُعاملات و لم يطلُب منه إرجاع أي شيئ و هذا هو اول نوع من الدوال "حيث لا يوجد مُعطيات و لا يتطلّب أي مُرجعات". فإذا قال الأب للإبن إذهب إلى السوق و خُذ دينار فهو قام بتمرير مٌتغير و هو الدينار لكن لا يزال لم يطلُب من الإبن بإرجاع أي شيئ و هذا النوع الثاني من الدوال "حيث توجد مُعطيات و هي المال و لا يتطلب أي مُرجعات".
أما إذا قال إذهب إلى السوق و خُذ دينار و آتني ببطاطا فإن الأب يطلُب من الإبن أن يأخذ الدينار و يقوم بالذهاب إلى السوق ثم يرجع للأب مع البطاطا أي أنه أرجَع له قيمة و هذا النوع الثالث من الدوال. و قد يقول الأب إذهب إلى السوق و آتني ببطاطا و لم يُعطِه أي شيئ فمعنى هذا أن الإبن يجب أن يتصرَّف أن يأخذ مال من أي مكان آخر و يذهَب إلى السوق وهذا هو النوع الرابع من الدوال.
أنواع الدوال التي تعيد قيمة في لغة C
- يكون بدون معطيات وبدون مرجعات.
- مع معطيات وبدون مرجعات.
- مع معطيات ومع مرجعات.
- بدون معطيات ومع مرجعات.
الدوال التي تعيد قيمة (بدون معطيات وبدون مرجعات)
#include <stdio.h>
#include <stdlib.h>
void potato()
{
printf("potato \n");
}
int main(int argc, char** argv) {
potato();
return 0;
}
لقد قُمنا بإنشاء دالة من نوع void و إسمها potato و كانت هذه الدالة مُهمتها طباعة كلمة بطاطا potato فقط. ثم تقوم بالنزول سطراً جديداً و ثم قُمنا بالدخول إلى الدالة main و قُمنا بإستدعاء هذه الدالة عبر كتابة إسمها فقط potato(). و عند تشغيل الكود السابق سوف نحصل على النتيجة.
potato
نلاحظ بأن هذا النوع لم نقوم بإعطاءه أية معطيات و لم نقم بالطلب منه إرجاع أي شيئ
الدوال التي تعيد قيمة (مع معطيات وبدون مرجعات)
#include <stdio.h>
#include <stdlib.h>
void potato()
{
printf("potato \n");
}
void printNumberPotato(int number)
{
for(int i = 0; i < number; i++)
printf("potato \n");
}
int main(int argc, char** argv) {
potato();
printNumberPotato(4);
return 0;
}
انشأنا دالة void تقوم بطلب معطيات عددية int و إسم هذه الدالة هو printNumberPotato. م دخلنا لحلقة for loop و قُلنا i تساوي 0 و i اصغر من المُتغير number و تقوم بزيادة 1 في كل دورة و يقوم في كل دورة بطباعة كلمة potato و ينزل سطراً جديداً. ومن ثم دخلنا للدالة main لتشغيلها عبر إستدعائها بإسمها printNumberPotato و مررنا لها القيمة 4 لأنه في الأعلى وضعنا أنه يلزَم تمرير قيمة.
وعند تشغيل هذا البرنامج سوف يطبَع لنا كلمة potato خمس مرات و عند تشغيله سنجد نتيجته.
potato
potato
potato
potato
potato
هذا هو النوع الثاني بحيث أنه من نوع void مع قيمة مُدخلة
الدوال التي تعيد قيمة (مع معطيات ومع مرجعات)
#include <stdio.h>
#include <stdlib.h>
void potato()
{
printf("potato \n");
}
void printNumberPotato(int number)
{
for(int i = 0; i < number;
printf("potato \n");
}
int power(int number)
{
return number * number;
}
int main(int argc, char** argv) {
int result = power(4);
printf("%d", result);
return 0;
}
أنشأنا دالة إسمها power يعني التربيع من اجل إرجاع قيمة مربعة و طلبنا أن القيمة الممررة هي عددية number و تقوم هذه الدالة بطباعة إرجاع قيمة القيمة الممررة مضروبة بنفسها number*number أي إرجاع القيمة المربعة لهذا المعامل. وفي الدالة main وضعنا متغير عددي result يقوم بإستقبال القيمة المُرجعة من الدالة power و ضربِها بنفس القيمة. ثم طَلَبنا طِباعة القيمة المُرجعه فعلى سبيل المثال مرَّرنا له القيمة 6 سوف تقوم بضربها في 6 و يكون الناتج 36 لكن في برنامجنا مرّرنا له القيمة 4.
و عند التشغيل نحصل على النتيجة
potato
كما نلاحظ لقد طبع لنا الرقم 16 لأن القيمة كان 4 و ضربها في نفسها
الدوال التي تعيد قيمة (بدون معطيات ومع مرجعات)
#include <stdio.h>
#include <stdlib.h>
int power(int number)
{
return number * number;
}
void power2(int number)
{
printf("%d", number * number);
}
int main(int argc, char** argv) {
power2(6);
return 0;
}
قمنا بنسخ الدالة power في المثال السابق و أسميناها power2 و تقوم بطباعة قيمة عددية و هي number*number. و قمنا في البرنامج الرئيسي بإستدعائها بإسمها و تمرير الرقم 6 لها. وعند تشغيل البرنامج ستكون النتيجة.
9
الدالة ذات القيمة المُرجعة يجب إستقبال قيمتها من الدالة التي قامت بإستدعائها و هي الدالة main و يجب تعريفها حسب نوع القيمة المُراد إرجاعها. ويمكننا تعريف الدوال بأي نوع من انواع البيانات التي تعلمناها مسبقاً مثل (int, float, doubel, boolean) و غيرها. لكن لا يجب ابداً نسيان وضع return في نهاية تعريف كل دالة تقوم بإرجاع قيمة.