P01NT
0x06 الأساسيات 07/19

الحلقات التكرارية

while وdo-while وfor والحلقات المتداخلة — والبصمة التي تعرف بها الحلقة في الـ assembly: القفزة العكسية.

· 4 دقائق قراءة · 764 كلمة
المحتويات

بسم الله الرحمن الرحيم.

في هذا المقال سنتعرف على الحلقات التكرارية: while loop, do-while loop, for loop والحلقات المتداخلة (Nested Loops)، وسنتعلم كيفية التحكم فيها والفرق بين استخداماتها.

الحلقات التكرارية: تُستخدم لتنفيذ كتلة من التعليمات بشكل متكرر بناءً على شرط معين. هناك أنواع متعددة، وكل واحد يناسب حالة استخدام مختلفة.

1. حلقة while

تُستخدم عندما يكون التكرار معتمداً على شرط معين — وقد لا تُنفذ الحلقة على الإطلاق إذا لم يتحقق الشرط.

while (condition) {
    // كود يُنفذ طالما الشرط صحيح
}

مثال

#include <stdio.h>

int main() {
    int i = 1;

    while (i <= 5) {
        printf("Iteration %d\n", i);
        i++; // زيادة i
    }

    return 0;
}

في while، يُفحص الشرط قبل كل دورة — إن لم يتحقق من البداية، لا تنفذ الحلقة إطلاقاً.

2. حلقة do-while

تشبه while، لكنها تنفذ الكود مرة واحدة على الأقل حتى لو كان الشرط غير محقق في البداية — يُفحص الشرط بعد تنفيذ الكود.

do {
    // كود يُنفذ مرة واحدة على الأقل
} while (condition);

مثال

#include <stdio.h>

int main() {
    int i = 1;

    do {
        printf("Iteration %d\n", i);
        i++;
    } while (i <= 5);

    return 0;
}

ملاحظة: حتى لو كان i = 6 في البداية، سيُنفذ الكود مرة واحدة ثم يفحص الشرط.

3. حلقة for

تُستخدم عندما يكون عدد التكرارات معلوماً مسبقاً. تحتوي على 3 أجزاء:

  1. Initialization — التعريف الأولي للمتغير.
  2. Condition — شرط التوقف.
  3. Update — تعديل المتغير بعد كل تكرار.
for (initialization; condition; update) {
    // كود يُنفذ أثناء كل تكرار
}

مثال

#include <stdio.h>

int main() {
    for (int i = 1; i <= 5; i++) {
        printf("Iteration %d\n", i);
    }

    return 0;
}

4. الحلقات المتداخلة (Nested Loops)

حلقة تكرار خارجية (outer-loop) تحيط بحلقة داخلية (inner-loop). الحلقة الداخلية جزء من جسم الحلقة الخارجية، وتدور دورة كاملة لكل دورة واحدة من الخارجية.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 3; i++) {
        for (int j = 1; j <= 2; j++) {
            printf("Outer: %d, Inner: %d\n", i, j);
        }
    }

    return 0;
}

الناتج: 3 دورات خارجية × 2 دورات داخلية = 6 أسطر.

التحكم في الحلقات

break

الخروج من الحلقة فوراً.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 10; i++) {
        if (i == 5) {
            break; // الخروج من الحلقة
        }
        printf("%d\n", i);
    }
    return 0;
}

الناتج: 1 2 3 4 — الحلقة توقفت عند 5.

continue

تخطي التعليمات المتبقية في التكرار الحالي والانتقال إلى التكرار التالي.

#include <stdio.h>

int main() {
    for (int i = 1; i <= 5; i++) {
        if (i == 3) {
            continue; // تخطي التكرار
        }
        printf("%d\n", i);
    }
    return 0;
}

الناتج: 1 2 4 5 — الرقم 3 تخطى الطباعة فقط.

جدول المقارنة بين الحلقات

الحلقةمتى تستخدمالفحصعدد التنفيذ
whileالتكرار بشرط متغيرقبل كل دورةصفر أو أكثر
do-whileيجب تنفيذ الجسم مرة على الأقلبعد كل دورةواحد أو أكثر
forعدد تكرارات معروفقبل كل دورةصفر أو أكثر

كيف ستبدو الحلقات في الـ Assembly؟

استكمالاً للمقال السابق — الحلقات هي القفزات العكسية (Backward Jumps) في الـ disassembly:

البنية النموذجية

أي حلقة تتحول إلى:

loop_start:
    ; جسم الحلقة
    inc [i]        ; التحديث (i++)
    cmp [i], 5     ; الشرط
    jle loop_start ; قفزة للخلف طالما الشرط صحيح

القفزة للخلف (loop target) هي البصمة الواضحة التي تعرف بها الحلقة في الـ disassembly فوراً.

while مقابل do-while في الـ assembly

  • while: الشرط يُفحص قبل الجسم — القفزة الشرطية في بداية البنية.
  • do-while: الشرط يُفحص بعد الجسم — القفزة في النهاية.

معلومة تحسين: بنية do-while أسرع للمعالج لأنها قفزة واحدة. لذلك عند التحسين (O2) قد يحوّل المترجم حلقة while إلى do-while (loop rotation) — أي أن الشكل النهائي في الـ assembly لا يطابق دائماً ما كتبته.

break و continue في الـ assembly

  • break → قفزة للخروج النهائي من الحلقة.
  • continue → قفزة إلى الخطوة التالية (التحديث/الشرط) مباشرة.

مثال من تحليل malware

في عينات حقيقية ستصادف حلقات تفحص حرفاً حرفاً (كما في نصوص strlen أو خوارزميات فك التشفير) — وتبدو هكذا:

decrypt_loop:
    mov al, [esi]   ; قراءة بايت
    xor al, 0x1F    ; فك التشفير
    mov [edi], al
    inc esi         ; التقدم
    inc edi
    cmp al, 0       ; نهاية النص؟
    jne decrypt_loop

هذه الصورة الذهنية (قراءة ← معالجة ← تقدم ← فحص ← قفزة للخلف) ستخدمك كثيراً في تحليل الـ binaries.

تمرين عملي

  1. أعد كتابة مثال for بصيغة while والعكس، ثم قارن الـ assembly الناتج بـ gcc -S — ستلاحظ أن المترجم يبني كليهما بنفس البنية.
  2. جرب gcc -O2 -S على حلقة while — ولاحظ كيف قد تتحول إلى بنية do-while.
  3. اكتب حلقة تفك تشفير بايتاً بايتاً (مثل المثال أعلاه) وافحصها في IDA/Ghidra — ستطابق البنية في قسم “كيف ستبدو”.