ليش C23 مهم الآن؟
بدءًا من إصدار GCC 15 صار المعيار الافتراضي عند الترجمة هو C23 (-std=gnu23) بدل C17 — يعني لما تكتب gcc program.c -o program بدون تحديد معيار صراحة، أنت فعليًا تترجم بـ C23. GCC 16 وسّع الدعم أكثر (مثل النوع _BitInt). هذا يفتح مزايا جديدة تستحق معرفتها حتى لو كنت تتبع نفس أسلوب الدروس السابقة.
💡 لو تبي تجبر معيار محدد بدل الافتراضي (مثلًا للتوافق مع كود قديم)، استخدم
gcc -std=c17 program.cأو-std=c23صراحة.
bool و true و false صارت كلمات مفتاحية
في الدرس عن أنواع البيانات تعلّمت إنه لازم تضمّن <stdbool.h> عشان تستخدم bool. من C23، bool وtrue وfalse جزء من اللغة نفسها مباشرة، بدون أي تضمين:
bool active = true; // يعمل مباشرة في C23، بدون #include <stdbool.h>
<stdbool.h> ما زال موجود ويعمل بدون مشاكل (للتوافق مع كود أقدم)، لكنه لم يعد ضروريًا بمشروع C23 جديد.
nullptr بدل NULL
NULL تقليديًا مجرد ماكرو (غالبًا ((void*)0) أو 0)، وهذا يسبب التباس أحيانًا لأنه يمكن التعامل معه كعدد صحيح بالخطأ. C23 يضيف nullptr: قيمة بنوع مخصّص nullptr_t تتحوّل تلقائيًا لأي نوع مؤشّر، ولا يمكن الخلط بينها وبين رقم:
int *p = nullptr; // أوضح من NULL، ومضمون أنه مؤشّر لا عدد صحيح
constexpr لثوابت تُحسب وقت الترجمة
constexpr int maxUsers = 100;
constexpr double pi = 3.14159;
بعكس #define (استبدال نصّي بلا نوع)، متغيّر constexpr له نوع حقيقي يفحصه المترجم. وبعكس const العادي — اللي ممكن تُهيّأ بقيمة تُحسب وقت التشغيل — قيمة constexpr يجب أن تأتي من تعبير ثابت يُحسب بالكامل وقت الترجمة.
typeof لاشتقاق نوع متغيّر موجود
int x = 5;
typeof(x) y = 10; // y من نوع int تلقائيًا، بدون كتابة int صراحة
مفيد بالماكروهات العامة حيث تحتاج تعريف متغيّر بنفس نوع متغيّر آخر دون معرفة اسم ذلك النوع مسبقًا.
فواصل رقمية لقراءة أوضح
long long distance = 299'792'458; // سرعة الضوء - أوضح من 299792458
int mask = 0xFF'FF'00'00;
علامة ' بين الأرقام تُتجاهل تمامًا وقت الترجمة، وتُستخدم فقط لتحسين قراءة الأرقام الكبيرة — لا تغيّر القيمة الفعلية بأي شكل.
سمات معيارية [[...]]
C23 يضيف سمات (attributes) موحّدة بين كل المترجمات، بدل الاعتماد على امتدادات خاصة بـ GCC أو Clang:
[[deprecated("استخدم newFunc بدلها")]]
void oldFunc(void);
[[maybe_unused]] int debugCounter = 0;
[[nodiscard]] int mustCheckResult(void);
switch (x) {
case 1:
doSomething();
[[fallthrough]]; // انزلاق متعمّد، ليس نسيان break
case 2:
doMore();
break;
}
[[deprecated]]: يحذّرك المترجم لو استدعيت دالة أو استخدمت نوعًا قديمًا.[[maybe_unused]]: يمنع تحذير "متغيّر غير مستخدم" لمتغيّر تعرف أنه قد لا يُستخدم بكل المسارات.[[nodiscard]]: يحذّرك لو تجاهلت القيمة المُرجعة من دالة (مفيد لدوال شبيهة بـ malloc أو fopen يجب فحص نتيجتها).[[fallthrough]]: يوضّح أن الانزلاق بين حالتَي switch متعمّد، لا نسيان لـ break.
أخطاء شائعة
- افتراض أن كل المترجمات تدعم C23 كاملًا — تأكد من إصدار GCC أو Clang قبل الاعتماد على هذه المزايا بمشروع فعلي، خصوصًا لو المشروع يُبنى ببيئات أقدم.
- الظن أن
constexprفي C تعمل تمامًا مثل C++ — نسخة C23 أكثر محدودية (متغيّرات فقط، لا دوالconstexpr). - استخدام
nullptrبدلNULLبدوال قديمة variadic (مثلexecl) دون تحويل صريح عند الحاجة — راجع توثيق الدالة قبل الاعتماد على التحويل التلقائي.
🎯 التالي: خلاصة المسار.