تخطَّ إلى المحتوى

🇨 شرح لغة C

أفضل الممارسات والترجمة

الدرس 21 من 27· ⏱ 3 دقائق قراءة· 🗓 آخر تحديث: ١٨ أكتوبر ٢٠٢٦

لماذا تحتاج C انضباطًا خاصًا؟

في معظم اللغات الحديثة، الخطأ الخطير يعطيك رسالة واضحة فورًا. في C، تجاوز حدود مصفوفة أو استخدام ذاكرة محرّرة لا يُعلن عن نفسه: البرنامج يعمل اليوم وينهار غدًا على جهاز آخر. لذلك ممارسات C الجيدة تدور حول فكرة واحدة: اجعل الأدوات تكتشف الأخطاء بدلًا عنك.

الترجمة بـ gcc

gcc program.c -o program     # ترجمة
./program                    # تشغيل (Linux/Mac)
gcc program.c -o program -lm # مع مكتبة الرياضيات

فعّل التحذيرات دائمًا

gcc -std=c17 -Wall -Wextra -Wpedantic program.c -o program
  • -Wall -Wextra: مجموعة واسعة من التحذيرات المفيدة (محدّد printf خاطئ، متغيّر غير مهيّأ، = داخل if، سقوط في switch...). رغم اسمه، -Wall لا يفعّل "كل" التحذيرات.
  • -std=c17: حدّد المعيار صراحة بدل الاعتماد على افتراضي المترجم.
  • -Wpedantic: يحذّر من الإضافات الخاصة بـ gcc التي لن تعمل في مترجم آخر.

اعتبر كل تحذير خطأً لم يحدث بعد. في المشاريع الجماعية وCI أضف -Werror ليرفض البناء أي تحذير.

الفاحصات أثناء التطوير (Sanitizers)

gcc -g -fsanitize=address,undefined program.c -o program
./program
  • AddressSanitizer (address): يكتشف تجاوز حدود المصفوفات، والاستخدام بعد free، والتحرير المزدوج، والتسرّب.
  • UndefinedBehaviorSanitizer (undefined): يكتشف تجاوز حدود int الموقّع، والإزاحة الخاطئة، والقسمة على صفر، وغيرها.
  • -g يضيف معلومات التصحيح فتشير الرسائل لاسم الملف ورقم السطر.

البرنامج يصبح أبطأ مع الفاحصات، فهي لنسخ التطوير والاختبار لا للإنتاج. كل أمثلة مسار C في هذا الموقع مجرّبة بهذه الخيارات.

مثال: هذا البرنامج "يعمل" بدون الفاحص ويطبع رقمًا ما، لكن مع الفاحص يتوقّف ويخبرك بالسطر:

int main(void) {
    int a[3] = {1, 2, 3};
    int sum = 0;
    for (int i = 0; i <= 3; i++) sum += a[i];   // ❌ i = 3 خارج المصفوفة
    printf("%d\n", sum);
    return 0;
}

valgrind: فحص بلا إعادة ترجمة

على Linux، valgrind يشغّل برنامجك تحت المراقبة ويبلّغ عن أخطاء الذاكرة والتسرّب:

valgrind --leak-check=full ./program

أبطأ بكثير من AddressSanitizer، لكنه مفيد حين لا تستطيع إعادة الترجمة، أو لتأكيد نتيجة ثانية.

ممارسات أساسية في الكود

  • هيّئ كل متغيّر عند تعريفه. المتغيّرات المحلّية غير المهيّأة تحمل قيمًا عشوائية.
  • افحص قيمة إرجاع كل دالة قد تفشل: malloc وfopen وscanf وfgets (درس معالجة الأخطاء).
  • لكل malloc مالك واحد يعرف أنه مسؤول عن free، ووثّق ذلك فوق الدالة.
  • استخدم الدوال المحدودة الحجم: snprintf وfgets بدل sprintf وgets (الأخيرة حُذفت من المعيار كليًا لأنه لا يمكن استخدامها بأمان).
  • ثوابت بأسماء بدل الأرقام السحرية: const int MAX_USERS = 100;.
  • const لكل ما لا يتغيّر، خصوصًا معاملات المؤشّرات: void print(const char *s).
  • دوال صغيرة تفعل شيئًا واحدًا، وstatic لكل دالة مساعدة لا يحتاجها ملف آخر.
  • قسّم المشروع إلى ملفات .h و.c حسب المسؤولية، كما في الدرس التالي.

أمر الترجمة الذي يستحق الحفظ

gcc -std=c17 -Wall -Wextra -Wpedantic -g -fsanitize=address,undefined main.c -o main

هذا أمر التطوير اليومي. ولنسخة الإصدار استبدل الفاحصات بالتحسين: -O2، وأبقِ التحذيرات. وانتبه: بعض أخطاء السلوك غير المعرّف لا تظهر إلا مع -O2، لأن المحسِّن يعتمد على افتراض أنها لا تحدث. لهذا شغّل اختباراتك بالنسختين.

أخطاء شائعة في C

  • تجاوز حدود المصفوفات والنصوص بلا مكان لـ \0 (المصفوفات والنصوص).
  • المؤشّرات المعلّقة والفارغة (المؤشّرات).
  • تسرّب الذاكرة والاستخدام بعد free (إدارة الذاكرة).
  • تجاهل التحذيرات لأن البرنامج "يعمل".
  • الاختبار بدون فاحصات: الخطأ موجود لكنك لا تراه.

🎯 التالي: تنظيم البرنامج عبر ملفات متعددة.

شرح أفضل الممارسات والترجمة — لغة C بالعربي
أفضل الممارسات والترجمة — لغة C بالعربي · The Code Fix

📚 لمزيد من التعمّق في لغة C، راجِع مرجع لغة C الرسمي.

هل كان هذا الدرس مفيدًا؟