C Academy · درس

مخاوف سلامة الأنواع

مخاطر الدوال متغيرة المعاملات

الدرس 3 من 413 خطوة

مخاوف سلامة الأنواع درس مجاني في C Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C Academy 4 دروس في المجموع.

الوسائط المتغيرة تتجاوز فحص الأنواع

تتحقق دوال C العادية من تطابق الوسائط مع أنواع المعاملات. أما الوسائط المتغيرة فتقوم بتجاوز هذا الفحص تمامًا.

لا يستطيع المصرّف التحقق مما تمرره بعد ...، مما يجعل الأخطاء سهلة الوقوع وخطيرة.

#include <stdio.h>
#include <stdarg.h>

int add(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", add(2, 3, 4));
    return 0;
}

نوع خاطئ في va_arg

إذا قرأت وسيطًا باستخدام النوع الخاطئ، فستكون النتيجة سلوكًا غير محدد.

تمرير قيمة int وقراءتها باعتبارها double يعيد تفسير بايتات غير مرتبطة وينتج عنه بيانات عشوائية أو انهيار.

#include <stdio.h>
#include <stdarg.h>

int read_int(int n, ...) {
    va_list a; va_start(a, n);
    int v = va_arg(a, int);
    va_end(a);
    return v;
}

int main(void) {
    printf("%d\n", read_int(1, 100));
    return 0;
}

عدم تطابق سلسلة التنسيق

في الدوال ذات أسلوب printf، يُعد استخدام محدد خاطئ خطأً شائعًا. يقرأ printf("%d", 3.14) قيمة double كما لو كانت قيمة int.

تحذّر المصرّفات الحديثة من عدم التطابق في السلاسل الحرفية، لكنها لا تستطيع اكتشاف سلاسل التنسيق المُنشأة أثناء التشغيل.

#include <stdio.h>

int main(void) {
    double pi = 3.14;
    printf("%f\n", pi);
    return 0;
}

مفاجآت ترقية الأعداد الصحيحة

تُرقّى الأنواع الصغيرة إلى int في جزء الوسائط المتغيرة. لذلك، حتى إذا مررت قيمة char، يجب أن تقرأها مجددًا باعتبارها int.

قراءتها باعتبارها char باستخدام va_arg(a, char) سلوك غير محدد.

#include <stdio.h>
#include <stdarg.h>

void show(int n, ...) {
    va_list a; va_start(a, n);
    int c = va_arg(a, int);
    printf("char is %c\n", c);
    va_end(a);
}

int main(void) {
    show(1, 'A');
    return 0;
}

تحول float إلى double

تُرقّى الوسيطة من النوع float دائمًا إلى double في الوسائط المتغيرة. يجب قراءتها باعتبارها double، وليس float مطلقًا.

هذا أحد أكثر أخطاء الوسائط المتغيرة شيوعًا.

#include <stdio.h>
#include <stdarg.h>

void show(int n, ...) {
    va_list a; va_start(a, n);
    double d = va_arg(a, double);
    printf("%.2f\n", d);
    va_end(a);
}

int main(void) {
    float f = 1.5f;
    show(1, f);
    return 0;
}

قراءة عدد كبير جدًا من الوسائط

إذا استدعت شيفرتك va_arg مرات أكثر من عدد الوسائط التي قدمها المستدعي، فستقرأ بيانات عشوائية من المكدس.

لهذا السبب يُعد وجود عدد دقيق أو قيمة حارسة أمرًا ضروريًا.

#include <stdio.h>
#include <stdarg.h>

int sum(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", sum(3, 1, 2, 3));
    return 0;
}

تحتاج القيم الحارسة لمؤشرات NULL إلى الانتباه

استخدام NULL كقيمة حارسة محفوف بالمخاطر: ففي بعض المنصات تكون NULL هي 0 من النوع int، ويختلف حجم ذلك عن حجم المؤشر.

حوّلها صراحةً، كما في (char *)NULL، لضمان دفع النوع الصحيح.

#include <stdio.h>
#include <stdarg.h>

void print_all(const char *first, ...) {
    va_list a; va_start(a, first);
    const char *s = first;
    while (s) { printf("%s\n", s); s = va_arg(a, const char *); }
    va_end(a);
}

int main(void) {
    print_all("a", "b", (char *)NULL);
    return 0;
}

سمات التنسيق في المصرّف

يوفر GCC وClang السمة __attribute__((format(printf, 1, 2))) لإخبار المصرّف بأن دالتك تستخدم تنسيقًا بأسلوب printf.

بعد ذلك يفحص المصرّف سلاسل التنسيق أثناء الترجمة، فيكتشف العديد من الأخطاء.

#include <stdio.h>
#include <stdarg.h>

void logf(const char *fmt, ...)
    __attribute__((format(printf, 1, 2)));

void logf(const char *fmt, ...) {
    va_list a; va_start(a, fmt);
    vprintf(fmt, a);
    va_end(a);
}

int main(void) {
    logf("%d items\n", 3);
    return 0;
}

فضّل البدائل الأكثر أمانًا

نظرًا إلى أن الوسائط المتغيرة عرضة للأخطاء، ففضّل التصاميم الآمنة من ناحية الأنواع متى أمكن: مرّر مصفوفة صريحة أو بنية أو معاملات ثابتة.

احصر استخدام الوسائط المتغيرة في الاحتياجات الحقيقية الشبيهة بـ printf.

#include <stdio.h>

int sum_array(const int *a, int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += a[i];
    return s;
}

int main(void) {
    int nums[] = {1, 2, 3, 4};
    printf("%d\n", sum_array(nums, 4));
    return 0;
}

وثّق العقد

بما أن المصرّف لا يستطيع فرض أنواع الوسائط المتغيرة، فإن التوثيق الواضح هو وسيلة الأمان لديك.

اذكر بدقة الأنواع وعدد الوسائط التي يجب على المستدعي توفيرها، وكيف تنتهي القائمة.

#include <stdio.h>
#include <stdarg.h>

/* sum_ints(n, ...): pass exactly n int values */
int sum_ints(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", sum_ints(2, 5, 5));
    return 0;
}

الاختبار الشامل

بما أن الأخطاء قد تختفي دون ظهور واضح، اختبر الدوال ذات الوسائط المتغيرة باستخدام أعداد وأنواع عديدة من الوسائط.

غالبًا ما تكشف الحالات الحدية، مثل عدم وجود وسائط أو استخدام الحد الأقصى للعدد، الأخطاء.

#include <stdio.h>
#include <stdarg.h>

int sum(int n, ...) {
    va_list a; va_start(a, n);
    int s = 0;
    for (int i = 0; i < n; i++) s += va_arg(a, int);
    va_end(a);
    return s;
}

int main(void) {
    printf("%d\n", sum(0));
    printf("%d\n", sum(1, 9));
    return 0;
}

اختبار سريع

اختبر مدى فهمك لمخاطر أنواع الوسائط المتغيرة.

مراجعة

لقد تعلمت مخاطر الدوال ذات الوسائط المتغيرة:

  • لا يجري المصرّف أي فحص لأنواع الوسائط المتغيرة.
  • تؤدي الأنواع الخاطئة والأعداد الخاطئة والقيم الحارسة غير الصحيحة إلى سلوك غير محدد.
  • تذكّر الترقيات: الأعداد الصحيحة الصغيرة إلى int، وfloat إلى double.
  • استخدم سمات التنسيق الخاصة بالمصرّف، ووثّق العقد، وفضّل البدائل الآمنة من ناحية الأنواع متى أمكن.
البدء مجانًا

تعلم C مع معلم ذكاء اصطناعي — مجانًا

اكتب وقم بتشغيل أكوادك الفعلية في المتصفح، واحصل على مساعدة فورية من معلم ذكاء اصطناعي متاح 24/7، واستمر من حيث توقفت على الويب أو في التطبيق.

الدورات
39
الدروس
144

الأسئلة الشائعة

هل درس «مخاوف سلامة الأنواع» مجاني؟

نعم — نص درس «مخاوف سلامة الأنواع» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C Academy 4 دروس في المجموع.

ماذا ستتعلم في «مخاوف سلامة الأنواع»؟

مخاطر الدوال متغيرة المعاملات تتمرن على C Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ C Academy؟

لا تُشترط خبرة سابقة. C Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «مخاوف سلامة الأنواع»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس C Academy هذا؟

نعم. كل درس في C Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. وحدات الماكرو stdarg
  2. كتابة دوال شبيهة بـprintf
  3. مخاوف سلامة الأنواع
  4. أمثلة عملية
← العودة إلى C Academy