ahmed.o.mohamed

[C] الدوال, الملفات و مجالات الرؤية

1 مشاركة في هذا الموضوع

فهرس الأسئلة :

  1. الدوال (Functions) :
    • مالسبيل إلى تغيير قيم وسائط الدالة ؟
    • ما هي الوسائط الافتراضية (Default parameters) ؟
    • ما هي فائدة الدوال الخطية (Inline Function) و أين يتم تخزينها ؟
    • ماهو النموذج المُصغر (prototype) ؟
    • مالفرق بين الإعلان عن الدالة أسفل أو أعلى الــ main ؟
    • ما هي التواقيع الصحيحة للدالة الرئيسية ؟
    • كيف تستطيع printf استقبال عدد غير محدود من مُختلف أنواع المتغيرات ؟
    • عند استخدام الرمز %d مع الأعداد الحقيقية تظهر نتائج غريبة !, ما السبب ؟
    • ما هي أهم الرموز المُستخدمة مع الدالة printf ؟
    • ما هي أهم الرموز المُستخدمة مع الدالة scanf ؟
    • ماهي أهم دوال المكتبة math.h ؟
    • ماهي أهم دوال المكتبة ctype.h ؟
    • كيف تُعيد الدالة أكثر من قيمة ؟
    • كيف يتم الإعلان عن دالة تُعيد سلسلة محارف ؟
    • كيف نمرر مصفوفة إلى دالة ؟
    • لماذا لا يُنصح باسخدام المؤشرات المحلية كقيمة مُعادة من طرف الدوال ؟
    • كيف يتم الإعلان عن مؤشر يشير إلى دالة ؟
    • كيف يتم الإعلان عن مصفوفة دوال ؟
    • كيف تتم إعادة مؤشر يشير إلى دالة ؟

[*]الملفات (Files) :

  • كيف أتاكد من وجود ملف ؟
  • كيف أعرف حجم ملف ؟
  • كيف يتم نسخ الملفات ؟
  • كيف أحذف أسطراً من ملف نصي ؟
  • كيف أحذف ملفا ؟
  • كيف أعرض محتوى مجلد ؟

[*]مدى المتغيرات أو مجالات الرؤية (Variables scope) :

  • مالفرق بين المتغيرات المحلية (local) و المتغيرات العامة (global) ؟
  • ماذا تعني الكلمة static ؟
  • كيف يتم استخدام متغير عام مُعرف في ملف مصدري آخر (another source file) ؟

1. الدوال (Functions) :

مالسبيل إلى تغيير قيم وسائط الدالة ؟

في C, يتم تمرير نُسخ من الوسائط, تُخزن داخل المكدس (Stack), أو على الأقل هذا ما يحدث في أغلب الــ implementations !, عند الوصول إلى return و انتهاء عمل الدالة يتم حذف جميع القيم المُخزنة في المكدس و بالتالي تضيع التغييرات التي حدثت على قيم الوسائط.

للتغلب على هذه المشكلة يكفي إرسال الوسائط عن طريق العنوان بدلا من القيمة, انظر المثال:

void echange(int * a, int * b) {
int tmp = *a;
*a = *b;
*b = tmp;
}

ما هي فائدة الوسائط الافتراضية (Default parameters) ؟

تظهر فائدة الوسيط الإفتراضي إذا لم تُمرر له قيمة, حيث يحتفظ بقيمته الإفتراضية.

يتم الإعلان عن الوسائط الإفتراضية عن طريق إسناد قيم افتراضية لها في النموذج المصغر للدالة, مثلاً:

int myFunc(int x= 10) ;

فهذه الدالة يمكن استدعاؤها هكذا:

c= myFunc(25) ;

لتكون قيمة x مساوية لـ 25. أو هكذا:

c= myFunc() ;

لتكون قيمة x مساوية لـ 10 لأنه تم تجاهل تمرير قيمة لهذا الوسيط, و بالتالي احتفظ الوسيط بقيمته الإفتراضية.

لاحظ أن القيم المفترضة توضع في النموذج المصغر فقط, أما ترويسة الدالة فتبقى:

int myFunc(int x)

أخيرا, هناك 3 ملاحظات مهمة :

  • يتم تعيين القيم المفترضة بالموقع وليس بالاسم, لذلك ليس من الضروري أن يطابق اسم الوسيط في النموذج المصغر اسمه في التعريف.
  • عند تعيين قيمة مفترضة لوسيط ما, يجب تعيين قيم مفترضة لكل الوسائط بعده.
  • في حال كان هناك أكثر من وسيط مفترض في دالة واحدة, فلا يمكن عند الاستدعاء تمرير قيمة لأحد هذه الوسائط المفترضة دون تمرير قيمة للوسيط المفترض الذي يسبقه.

ما هي فائدة الدوال الخطية (Inline Function) و أين يتم تخزينها ؟

الدوال الخطية تُنسخ شفرتها كما هي وتوضع في المكان الذي تم استدعاؤها منه, أي أنها تصبح جزءاً من الدالة المُستدعية.

تظهر فائدتها عندما تكون الدالة صغيرة جداً, وذلك لتوفير الوقت المُستهلك لاستدعاء الدالة من مكانها في الذاكرة لو كانت عادية.

عند تعريف دالة, يقوم المترجم بإنشاء نسخة منها في الذاكرة, ويقفز إليها كلما تم استدعاء هذه الدالة.

بعض الدوال تكون صغيرة, وسيكون القفز إليها في كل استدعاء مضيعة للوقت, لذلك يمكن جعلها خطية Inline بحيث يقوم المترجم عند التنفيذ بنسخ تعليماتها كما هي, ووضعها في كل مكان يتم استدعاؤها فيه, وذلك بوضع كلمة inline قبل نوع إرجاع الدالة في النموذج المصغر كالتالي على سبيل المثال:

inline int myFunc(int) ;

قد يتجاهل المترجم كون دالة ما خطية ويتعامل معها على أنها دالة عادية في حال وجد حجمها كبيراً جداً.

ماهو النموذج المُصغر (prototype) ؟

الــ prototype هو النموذج المـُصغر للدالة أو توقيع الدالة كما يُسميه البعض, هذا التوقيع يشمل تعريف الدالة (نوع "الكائن" المـُعاد + اسم الدالة + الوسائط), فمثلا لو أردنا كتابة دالة تقوم بإجراء القسمة بين عددين و تُعيد بنية تحتوي على قيمة الناتج و باقي القسمة أيضا, سيكون توقيع الدالة هكذا :

struct Mod(int x, int y) ;

مثال :

#include<stdio.h>

struct Div {
int quot;
int rem;
};

Div Mod(int x, int y) {
Div Result;
Result.quot = x / y;
Result.rem = x % y;
return Result;
}

int main() {
Div test = Mod(38, 5);
printf("38 div 5 => %d, remainder %d.\n", test.quot, test.rem);
return 0;
}

أيهما أفضل, الإعلان عن الدالة أسفل أو أعلى الــ main ؟

يُفضل دائما وضع prototype الدوال فوق الــ main ثم كتابة تفاصيل الدوال أسفل الدالة الرئيسية.

يُمكننا تلخيص فوائد وضع الــ prototype في أربع نقاط :

  1. تنظيم الشفرة وتسهيل تصحيحها.
  2. لو كانت لديك دالتان تستدعيان بعضهما البعض, فحينها يجب أن يكون هناك نموذج مصغر لإحداهما على الأقل, لكي تستطيع القيام بالعملية, حيث أن الدالة حين تستدعي دالة أخرى, فإنها تبحث عنها فوقها في الشفرة, فإما أن تجد تعريف الدالة, أو أن تجد تصريحها (نموذجها المصغر Prototype), أو لا تجد ما تريده, وحينها لن تعمل.
  3. لا يمكن تعريف القيم الافتراضية لبعض الوسائط إلا في النموذج المصغر.
  4. لا تستطيع تحديد إن كانت الدالة خطية Inline أم لا إلا في النموذج المصغر.

ما هي التواقيع الصحيحة للدالة الرئيسية ؟

الــ main تُعيد int دائما, الكتابات السليمة تكون هكذا :

int main(void);
int main(int argc, char * argv[]);

كل توقيع آخر ليس بالضرورة أن يكون قياسيا, و لا يُنصح باستخدامه حتى لو قبله المترجم الذي تعمل عليه !

أيضا, القيمة المـُعادة من طرف الدالة الرئيسية يجب أن تكون موجبة أو معدومة, القيم القياسية المـُستخدمة في الإعادة هي 0, EXIT_SUCCESS Or EXIT_FAILURE.

يُمكن أيضا أن تجد في Unix التوقيع التالي:

int main (int argc, char* argv[], char** arge);

عند الحاجة إلى استخدام Shell Environment Variables, هذا النوقيع ليس محمولا (portable) و لا قياسيا أيضا (standard).

كيف تستطيع printf استقبال عدد غير محدود من مُختلف أنواع المتغيرات ؟

printf هي دالة غير محددة الوسائط, توقيعها كالتالي:

int printf(const char * format, ...); /* C 90 */
int printf(const char * restrict format, ...); /* C 99

لاحظ أن نوع و عدد الوسائط غير مُحدد في التوقيع.

عند استخدام الرمز %d مع الأعداد الحقيقية تظهر نتائج غريبة !, ما السبب ؟

لا تحاول استخدام الرمز %d مع النوع float أو ما شابه لأن هذا سينتج عنه الـ Undefined Behavior أو السلوك الغير معروف و هو تصرف غير مسؤولة عنه الدالة التي تناديها أو الشيء (Construct) الذي تقوم باستعماله في اللغة.مثلاً, من المعروف أن القسمة على صفر خطأ منطقي. في لغات أخرى, يكون هناك ما يسمى بالـ Exception في اللغة اسمه Divide by Zero أو ما شابه ليخبرك أنك قسمت على صفر, في C هناك شروط يجب أن تتوفر قبل عمل أي دالة بشكل صحيح تسمى أحياناً بـ Preconditions , ببساطة أكثر, صاحب الدالة التي سوف تستعملها يقول لك يجب أن تنادي الدالة بالطريقة التالية, و إن لم تناديها بالطريقة المعطاة فالدالة ليست مسؤولة عن التحقق من صحة ما يمرر لها, بالتالي صاحب الدالة لا يقوم بالتحقق من شيء و إنما يفترض أنك أنت المسؤول عن المناداة بشكل صحيح. و أنت قمت بمناداة الدالة printf بشكل خاطئ. هذا هو السلوك الغير المعروف بكل بساطة, و لا يمكنك بأي حال من الأحوال أن تقول ظهرت لي نتيجة ما عند تجربة الكود, إذاً فهو صحيح. لأن الدالة نفسها لا تتحقق أصلاً من شيء, بالتالي الناتج خاطئ حتى لو كان صحيحاً على مترجم معين بنسخة معينة على نظام تشغيل معين باستخدام مكتبات معينة و جميع الظروف التي تحيط لحظة تشغيل الكود !

ما هي أهم الرموز المُستخدمة مع الدالة printf ؟

الجدول التالي يبين أهم الرموز المُستخدمة مع الدالة printf :

post-219439-036783600 1345303561_thumb.p

ما هي أهم الرموز المُستخدمة مع الدالة scanf؟

الجدول التالي يبين أهم الرموز المُستخدمة مع الدالة scanf :

post-219439-067942200 1345303671_thumb.p

ماهي أهم دوال المكتبة math.h ؟

الجدول التالي يبين دوال المكتبة math.h, الأكثر استخداما :

post-219439-078414300 1345303718_thumb.p

ماهي أهم دوال المكتبة ctype.h ؟

الجدول التالي يبين دوال المكتبة ctype.h, الأكثر استخداما :

post-219439-093200500 1345303749_thumb.p

كيف تُعيد الدالة أكثر من قيمة ؟

لغة C لا تسمح للدوال بإعادة أكثر من كائن, لكن توجد عدة حلول لهذه المشكلة, أحد أبرز هذه الحلول هو التمرير بالمرجع لأن التغييرات ستحدث على مستوى العنوان و ليس القيمة, يوجد أيضا حل آخر و هو تمرير بنية أو مؤشر بنية تحوي مجموعة العناصر التي نريد تغيير قيمها.

كيف يتم الإعلان عن دالة تُعيد سلسلة محارف ؟

توجد 3 طرق لمثل هذا الأمر, الطريقة الأولى تتعلق بتمرير مؤشر يشير إلى المصفوفة الحرفية :

#include <stdio.h>
#include <string.h>

void get_string(char * Buffer, size_t BufferLen) {
/* On ne peut pas utiliser strcpy car cette fonction
ne permet pas d'indiquer le nombre max de caracteres qu'on veut copier */
strncpy(Buffer, "MaChaine", BufferLen);
/* Il est possible que Buffer n'ait pas pu contenir toute la chaine ... */
Buffer[BufferLen - 1] = '\0';
}

int main(void) {
char Buffer[100];
get_string(Buffer, sizeof (Buffer));
printf("%s\n", Buffer);
return 0;
}

الطريقة الثانية تعتمد على إعادة مؤشر يشير إلى سلسلة محارف موجودة في global memory و يمكننا فعلها بإحدى طريقتين :

  1. باستخدام Static string :
    #include <stdio.h>

    const char * get_string(void) {
    return "MaChaine";
    }

    int main(void) {
    printf("%s\n", get_string());
    return 0;
    }


  2. باستخدام Dynamic string :
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    char * alloc_string(void) {
    char * Buffer, s[] = "MaChaine";
    Buffer = malloc(strlen(s) + 1);
    if (Buffer != NULL)
    strcpy(Buffer, s);
    return Buffer;
    }

    int main(void) {
    char * Buffer = alloc_string();
    if (Buffer != NULL) {
    printf("%s\n", Buffer);
    free(Buffer);
    }
    return 0;
    }


كيف نمرر مصفوفة إلى دالة ؟

التعامل مع المصفوفات في الدوال يتم على أساس العنوان (عنوان المصفوفة). عادة, نقوم بتمرير مؤشر (يشير إلى أول عناصر المصفوفة) و متغير صحيح عبارة عن حجم المصفوفة (إذا كانت هذه الخطوة ضرورية), انظر المثال:

#include <stdio.h>

void initialiser_tab(int * ptr, size_t n_elements) {
size_t i;
for (i = 0; i < n_elements; i++)
ptr[i] = (int) i;
}

int main() {
int t[10];
size_t i, n_elements = sizeof (t) / sizeof (t[0]);
/* t peut egalement s'ecrire &(t[0]) */
initialiser_tab(t, n_elements);
for (i = 0; i < n_elements; i++)
printf("%d\n", t[i]);
return 0;
}

عنوان المصفوفة هو عنوان أول عناصرها, وبالتالي فالكتابات التالية متكافئة:

void initialiser_tab(int * ptr, size_t n_elements)
void initialiser_tab(int ptr[], size_t n_elements)
void initialiser_tab(int ptr[10], size_t n_elements) /* Le 10 ne sert absolument a rien */

هذا مثال مع مصفوفة ثنائية البعد:

#include <stdio.h>
/* initialiser_tab_2 : initialise un tableau de N elements ou */

/* chaque element est un tableau de M entiers . */
void initialiser_tab_2(int * ptr, size_t N, size_t M) {
/* ptr = &(t[0][0]) d'ou : */
/* - ptr[i] <=> t[0][i] */
/* - ptr[M * j] <=> t[j][0] */
/* - ptr[M * j + i] <=> t[j][i] */
size_t j, i;
for (j = 0; j < N; j++) {
for (i = 0; i < M; i++) {
ptr[M * j + i] = (int) j;
}
}
}

int main() {
int t[10][4];
size_t j, i;
size_t N = sizeof (t) / sizeof (t[0]), M = sizeof (t[0]) / sizeof (t[0][0]);
/* &(t[0][0]) peut egalement s'ecrire t[0] ou encore (int *)t */
initialiser_tab_2(&(t[0][0]), N, M);
for (j = 0; j < N; j++) {
for (i = 0; i < M; i++)
printf("%d\t", t[j][i]);
printf("\n");
}
return 0;
}

لماذا لا يُنصح باسخدام المؤشرات المحلية كقيمة مُعادة من طرف الدوال ؟

لأن المتغيرات المحلية لا يتم "تحريرها" عند الخروج من الـ Scope الخاص بالدالة, بل يتم استخدام Frame مختلف عن طريق زيادة الـ Stack pointer مما يؤدي إلى جعل الـ Frame السابق قابل للكتابة عليه عن طريق تعريف متغيرات محلية أخرى في الـ Parent Routine.

في هذه الحالة سيظل المؤشر يشير الى ذاكرة صالحة للاستخدام لكننا لا نضمن أنه لن يتم الكتابة عليها Overwrite عن طريق حجز متغير محلي آخر.

لم أفهم شيئا مما قلت !! :blush:

بعبارة أبسط, الدالة التي تحتوى على المتغير من نوع مؤشر قد انتهى عملها و تمت العودة منها ليتم تنفيذ باقي البرنامج, في هذه الحالة المكان الموجود داخل ذاكرة الـ stack و الذي كان مستخدما من قبل المتغير من نوع المؤشر سيظل محتفظ بالقيمة التي به طالما لم يُستخدم ذلك المكان من قبل دالة أخرى.

كيف يتم الإعلان عن مؤشر يشير إلى دالة ؟

في البداية, نقوم بتعريف الدالة التي سيشير إليها المؤشر:

typedef int F(void); /* F : type de la fonction f. */

/* Voici la fonction f en question : */
int f(void) {
return 1;
}

ثم نعلن عن المؤشر P الذي يشير إلى f هكذا:

F * p = &f; 

يقوم المترجم بتحويل اسم الدالة (f في هذه الحالة) إلى &f, إذا يمكننا استبدال الكتابة السابقة بالكتابة التالية:

F * p = f; 

ولاستدعاء الدالة f يمكننا استخدام إحدى الكتابتين:

y = p();
y = (*p)();

أما بدون استخدام typedef سيكون إعلان المؤشر p هكذا :

int (*p)(void) = f; 

المؤشر p يشير إلى الدالة f التي لا تأخذ أي وسائط و تعيد int و بالتالي فإن *p يمثل عنوان الدالة, إذا يُمكننا كتابة تعريف الدالة هكذا:

int (*p)(void) ;

كيف يتم الإعلان عن مصفوفة دوال ؟

توجد حالتان لمثل هذا الأمر, إذا كانت الدوال تملك نفس الــ prototype, يكفي الإعلان هكذا:

extern char * f(int, int); /* une fonction */
char * (*fp[N])(int, int); /* Un tableau de N fonctions */
char * sz;
fp[4] = f; /* affectation de f dans le tableau */
sz = fp[4](42, 12); /* utilisation */

أما إذا كانت الدوال تختلف في الــ prototype فيجب الإعلان عن مصفوفة من الــ Generic Functions. هذه الأخيرة لا توجد بشكل حقيقي في اللغة, لذا يجب استخدام دالة بدون وسائط و تعيد int :

int (*fp[N])(); /* Un tableau de N fonctions quelconques*/

الكتابة السابقة تصلح لجميع الدوال باستثناء تلك الغير محدودة الوسائط مثل printf.

كيف تتم إعادة مؤشر يشير إلى دالة ؟

إليك هذا المثال:

int (*func(void))(const char *) {
return puts;
}

الدالة func لا تأخذ وسيطا, و تُعيد مؤشرا يشير إلى دالة من نوع int f(const char * s).

لفهم المثال السابق, يمكنك قراءته كالتالي:

func تُعيد مؤشرا, الكتابة func(void) تُمثل المؤشر, الكتابة *func(void) تُمثل الشيء الذي يُشير إليه المؤشر, هذا الشيء عبارة عن دالة تأخذ const char * كوسيط و تُعيد int.

إذا النموذج المُصغر لــ f هو : int (*func(void))(const char *).

بالطبع, يُمكننا تخفيف الكتابة السابقة باستخدام typedef :

#include <stdio.h>
typedef int F(const char *);
F * func(void);

int main(void) {
func()("Bonjour."); /* <=> puts("Bonjour."); */
return 0;
}

F * func(void) {
return puts;
}

2. الملفات (Files) :

كيف أتاكد من وجود ملف ؟

بشكل عام, لا توجد طريقة قياسية لمعرفة سبب عدم فتح الملف. في معيار POSIX, توجد الدالة access ولكن بعض الأنظمة لا يطبق هذه الواجهة.

في معيار C ISO, الحل الوحيد لاختبار وجود ملف هو محاولة فتحه:

FILE *fp = fopen("fichier.txt", "r");
if (fp == NULL) {
fputs("Le fichier n’existe pas,\n"
"ou vous n’avez pas les droits necessaires\n"
"ou il est inaccessible en ce moment\n"
, stderr);
} else {
/* ... operation sur le fichier */
fclose(fp);
}

مع أن هذه الطريقة ليست سليمة 100% لأنه إذا لم يتم فتح الملف فهذا لا يعني بالضرورة أن الملف غير موجود !, توجد عدة أسباب أخرى :

  1. قد لا تملك الصلاحيات الضرورية لفتح الملف
  2. قد لا يمكن الوصول إلى محتوى الملف في تلك اللحظة (ملف مُشفر مثلا)
  3. قد لا يملك النظام مساحة الذاكرة الكافية لفتح الملف

في نظام DOS/Windows و أنظمة *Nix يمكننا فحص قيمة errno, هل هي مُساوية لـــ ENOENT (no such file or directory).

كيف أعرف حجم ملف ؟

لسوء الحظ, الدالتان stat و fstat التابعتان للمعيار POSIX غير مُدرجتان في معيار ISO.

الحل القياسي الوحيد يكمن في استخدام الدالتين fseek و ftell :

int fsize(const char * fname, long * ptr) {
/* Cette fonction retourne 0 en cas de succes, une valeur differente dans le cas contraire. */
/* La taille du fichier, si elle a pu etre calculee, est retournee dans *ptr */
FILE * f;
int ret = 0;
f = fopen(fname, "rb");
if (f != NULL) {
fseek(f, 0, SEEK_END); /* aller a la fin du fichier */
*ptr = ftell(f); /* lire l'offset de la position courante par rapport au debut du fichier */
fclose(f);
} else
ret = 1;
return ret;
}

الحل السابق يصلح فقط عندما يكون حجم الملف أقل من القيمة العظمى لــ int.

كيف يتم نسخ الملفات ؟

لا توجد في C دالة تنسخ الملفات, لكن يمكننا كتابة دالة تقرأ الملف ثم تقوم بنسخه في الملف المطلوب :

int copier_fichier(char const * const source, char const * const destination) {
FILE* fSrc;
FILE* fDest;
char buffer[512];
int NbLus;
if ((fSrc = fopen(source, "rb")) == NULL) {
return 1;
}
if ((fDest = fopen(destination, "wb")) == NULL) {
fclose(fSrc);
return 2;
}
while ((NbLus = fread(buffer, 1, 512, fSrc)) != 0)
fwrite(buffer, 1, NbLus, fDest);
fclose(fDest);
fclose(fSrc);
return 0;
}

من السهل جدا تغيير عمل الدالة السابقة من أجل دمج محتوى الملف المراد نسخه في محتوى الملف المطلوب عن طريق فتح الملف على النمط ("ab").

في Windows, توجد الدالة CopyFile التي تغنينا عن كتابة دالة جديدة للنسخ في كل مرة:

BOOL CopyFile(LPCTSTR lpExistingFileName, /* Nom du fichier source */
LPCTSTR lpNewFileName, /* Nom du fichier destination */
BOOL bFailIfExists /* Si != 0, la copie sera annulée si le fichier existe déjà */
);

كيف أحذف أسطراً من ملف نصي ؟

لغة C لا تُوفر دالة لفعل هذا الأمر, و بالتالي يجب قراءة الملف ونسخ جميع الأسطر باستثناء تلك التي نريد إزالتها, المثال التالي يحذف الأسطر التي يبدأ بــ @, لتبسيط الكود, اعتبرتُ أن طول كل سطر لا يتجاوز 255 حرف:

#include <stdio.h>
#include <stdlib.h>

int main(void) {
char ligne[256];
FILE * fIn;
FILE * fOut;
if ((fIn = fopen("texte.txt", "r")) == NULL)
return EXIT_FAILURE;
if ((fOut = fopen("texte.tmp", "w")) == NULL) {
fclose(fIn);
return EXIT_FAILURE;
}
while (fgets(ligne, sizeof ligne, fIn)) {
if (ligne[0] != '@')
fputs(ligne, fOut);
}
fclose(fIn);
fclose(fOut);
rename("texte.tmp", "texte.txt");
return 0;
}

نفس الشيء بالنسبة لحذف سجل من ملف ثنائي (binary file).

كيف أحذف ملفا ؟

في Windows, استخدم الدالة DeleteFile الموجودة في المكتبة windows.h و في UNIX (بشكل عام, جميع الأنظمة التي تتوافق مع المعيار POSIX), استخدم الدالة unlink الموجودة في المكبتة unistd.h.

المكتبة القياسية لــ C توفر أيضا الدالة remove التي تستقبل مسار الملف المراد حذفه و تعيد قيمة صحيحة تدل على نجاح أو فشل العملية:

#include <stdio.h>
int remove(const char * pathname);

كيف أعرض محتوى مجلد ؟

في Windows, استخدم الدالتين FindFirstFile و FindNextFile للبحث عن الملفات باستخدام التعابير المنطقية (*.* تعني جميع الملفات و جميع الامتدادات). المقبض (HANDLE) الذي تُعيده الدالة FindFirstFile ينبغي إغلاقه حالما لم يعد ضروريا باستخدام الدالة FindClose.

هذه الدوال لا تبحث في المجلدات الفرعية, المثال التالي يعرض محتوى Current Directory :

#include <stdio.h>
#include <windows.h>

int main(void) {
WIN32_FIND_DATA File;
HANDLE hSearch;
hSearch = FindFirstFile("*.*", &File);
if (hSearch != INVALID_HANDLE_VALUE) {
do {
printf("%s\n", File.cFileName);
} while (FindNextFile(hSearch, &File));
FindClose(hSearch);
}
return 0;
}

إن كنت تعمل في بيئة متوافقة مع المعيار POSIX فاستخدم الدوال opendir, readdir and closedir :

#include <stdio.h>
#include <dirent.h>

int main(void) {
DIR * rep = opendir(".");
if (rep != NULL) {
struct dirent * ent;
while ((ent = readdir(rep)) != NULL) {
printf("%s\n", ent->d_name);
}
closedir(rep);
}
return 0;
}

3. مدى المتغيرات أو مجالات الرؤية (Variables scope) :

مالفرق بين المتغيرات المحلية (local) و المتغيرات العامة (global) ؟

يتم الإعلان عن المتغيرات المحلية داخل دالة معينة, و لا يمكن استخدامها إلا داخل الدالة أو الإطار التي أُعلنت بداخله أما المتغيرات العامة فيتم الإعلان عنها خارج جميع الدوال, ويمكن لأي دالة في البرنامج استعمال هذا النوع المتغيرات.

بالنسبة للمتغيرات العامة فتنتهي أعمارها عند انتهاء تنفيذ البرنامج بينما ينتهي عمر المتغيرات الخاصة عند الانتهاء من تنفيذ الدالة الموجودة بها.

ماذا تعني الكلمة static ؟

تُستخدم هذه الكلمة مع المتغيرات الساكنة, و عند إضافتها إلى أي متغير فهذا يعني أنك قد أضفت له صفتين رئيسيتين هما:

  • أصبح عمر المتغير مثل عمر المتغيرات العامة التي لا تُمسح من الذاكرة إلا إذا طلبت ذلك برمجيا, أو بانتهاء تنفيذ البرنامج .. لأن المتغيرات العامة تكون مُعرضة للقراءة في أي وقت ومن أي دالة.
  • لا ُيمكن رؤيتها إلا داخل الإطار التي أُعلنت بداخله.

المتغيرات الساكنة تأخذ مزايا النوعين السابقين .. فهي مثل المتغيرات الخاصة لأن هناك دالة وحيدة تستطيع رؤيتها وهي الدالة التي تم الإعلان عن المتغير بداخلها ,و من ناحية أُخرى فالمتغيرات الساكنة مثل المتغيرات العامة لأنها لا تنتهي أو تُمسح من الذاكرة عندما ينتهي تنفيذ الدالة التابعة لها .. بل تظل مخزنة في الذاكرة (جاهزة للاستدعاء) حتى ينتهي تنفيذ البرنامج.

أيضا, إضافة الكلمة static إلى الدوال تعني أنه لا يُمكن رؤية تلك الدوال خارج الملف الذي أعلن عنهم فيه.

كيف يتم استخدام متغير عام مُعرف في ملف مصدري آخر (another source file) ؟

تسمح الكلمة المُعرفة مُسبقا extern بالإعلان عن متغير عام سبق تعريفه في نفس الملف المصدري أو في ملف آخر. في المثال التالي, يتكون المشروع من ملفين هما globales.c و main.c, الملف الأول يحتوي على الإعلان عن متغيرين عامَّيْن و الملف الثاني يستخدم تلك المتغيرات :

globales.c :

int globale_1 = 1;
int globale_2 = 2;

main.c :

#include <stdio.h>
extern int globale_1;
int get_globale_2(void);

int main(void) {
printf("globale_1 = %d\n", globale_1);
printf("globale_2 = %d\n", get_globale_2());
return 0;
}

int get_globale_2(void) {
extern int globale_2;
return globale_2;
}

تم تعديل بواسطه أحمد الشنقيطي
إضافة أسئلة جديدة.
2

شارك هذا الرد


رابط المشاركة
شارك الرد من خلال المواقع ادناه

  • يستعرض القسم حالياً   0 members

    لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .