P01NT
0x03 الأساسيات 04/19

أنواع البيانات ومحددات التنسيق

أنواع البيانات وأحجامها الحقيقية على x86/x64، ومحددات التنسيق في printf/scanf — وربطها بأحجام العمليات في الـ assembly.

· 3 دقائق قراءة · 626 كلمة
المحتويات

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

في هذا المقال سنتعرف على أنواع البيانات (Data Types) ومحددات التنسيق (Format Specifiers) الخاصة بها.

ملاحظة: أحياناً يجب حفظ المصطلحات الإنجليزية، لأن أغلب تعاملك مع لغات البرمجة سيكون باللغة الإنجليزية، والترجمة العربية تختلف من شخص لآخر.

ما هي أنواع البيانات؟

هي الشكل الذي تُحفظ به البيانات (كالمتغيرات) التي يمكن تخزينها ومعالجتها. أهم أنواعها:

1. الأعداد الصحيحة (Integers)

  • تستخدم لتخزين الأعداد الصحيحة (1، 55، -72 — أي بدون فواصل عشرية).
  • الكلمة المفتاحية: int.
  • الحجم: 4 بايت في كل من x86 و x64 (انظر ملاحظة الأخطاء الشائعة أدناه).
  • يمكن جعل الأعداد موجبة فقط باستخدام unsigned.

2. الأعداد العشرية (Floating-point)

  • تستخدم لتخزين الأعداد ذات الفواصل العشرية (1.2، 3.7).
  • float: 4 بايت.
  • double: 8 بايت.

3. الحروف (Characters)

  • تخزن محرفاً واحداً (حرف، رقم، أو أي رمز).
  • الكلمة المفتاحية: char.
  • الحجم: 1 بايت.

4. Void (بدون قيمة)

  • يستخدم مع الدوال أو المؤشرات التي لا ترجع قيمة.
  • الكلمة المفتاحية: void.
  • مهم لمجال RE: void* هو مؤشر عام (غير محدد النوع) — سنعود إليه في آخر مقالات الأساسيات لأنه من أهم الأمور في الهندسة العكسية.

جدول أحجام البيانات (على Windows x64)

النوعالحجم (بايت)المدى التقريبي
char1-128 إلى 127
unsigned char10 إلى 255
short2-32,768 إلى 32,767
int4-2.1 مليار إلى 2.1 مليار
unsigned int40 إلى 4.3 مليار
long4 (Windows) / 8 (Linux x64)يعتمد على النظام
long long8أعداد ضخمة
float4فاصلة عشرية
double8فاصلة عشرية بدقة أعلى
pointer4 (x86) / 8 (x64)عنوان في الذاكرة

خطأ شائع يجب تجنبه

حجم int لا يعتمد على المعمارية (x86/x64) — بل على المترجم/الـ ABI. int هي 4 بايت في كل من x86 و x64 على ويندوز ولينكس. أما الفارق الحقيقي بين المعماريات فيظهر في حجم المؤشرات (4 بايت في x86، 8 بايت في x64) وفي long على بعض الأنظمة.

تحقق بنفسك

#include <stdio.h>

int main(void){
    printf("char: %zu bytes\n", sizeof(char));
    printf("int:  %zu bytes\n", sizeof(int));
    printf("long: %zu bytes\n", sizeof(long));
    printf("ptr:  %zu bytes\n", sizeof(void*));
    return 0;
}

لاحظ أن sizeof(void*) سيطبع 8 على نظام 64-bit و 4 على نظام 32-bit — هذه هي الطريقة العملية لمعرفة بنية النظام.

ما فائدة محددات التنسيق؟

تستخدم في دوال مثل printf و scanf لتنسيق إدخال أو إخراج البيانات:

  • printf: دالة الطباعة (إخراج البيانات).
  • scanf: دالة القراءة (إدخال البيانات).

1. محددات الأعداد الصحيحة

المحددالوصف
%dعدد صحيح موقّع (dec)
%uعدد صحيح غير موقّع
%x / %Xعدد سداسي عشري (hex) — صغير/كبير
%oعدد ثماني
%ld / %lldlong / long long موقّع
%zusize_t (المستخدم في sizeof)

2. محددات الأعداد العشرية

المحددالوصف
%fعدد عشري (float/double)
%.1fعدد عشري بخانة واحدة بعد الفاصلة
%eتمثيل علمي
%gتمثيل تلقائي

3. محددات الحروف والسلاسل النصية

المحددالوصف
%cحرف واحد
%sسلسلة نصية (string)
%c مع %dطباعة الحرف كرقم ASCII

4. محددات أخرى مهمة

المحددالوصف
%pمؤشر (عنوان في الذاكرة) — وليس %x
%%طباعة علامة % نفسها

ربط ما تعلمناه بالهندسة العكسية

  1. int دائماً 4 بايت: عندما ترى في الـ disassembly تعليمة تقرأ من [ebp-4] أو [rsp+8] بحجم 4 بايت، فهذا غالباً متغير int. وهذا التطابق بين الحجم في C والأوبكود هو أول مهارة قراءة disassembly.

  2. %d مقابل %u: الأول يطبع القيمة كموقّعة والثاني كغير موقّعة. عند فك تشفير البيانات أو تحليل قيم hex، يهمك جداً هل تُفسَّر القيمة كـ signed أم unsigned — الفرق قد يقلب التحليل.

  3. char مقابل int: التعامل مع 1 بايت مقابل 4 بايتات يفسر لماذا ترى في الـ assembly تعليمات movzx (توسيع صفري) و movsx (توسيع بالإشارة) عند نقل char إلى int. سنشرحها بالتفصيل في سلسلة الـ assembly.

ملاحظة ختامية

لا تقلق إذا واجهت صعوبة في حفظ محددات التنسيق — فبكثرة الاستخدام ستصبح أمراً روتينياً تلقائياً.

ملاحظة: لم نشرح هنا محدد المؤشر %p بالتفصيل لأننا سنتطرق للمؤشرات (Pointers) في آخر مقالات الأساسيات — وهو من أهم الموضوعات المفيدة في مجال الهندسة العكسية، وفقكم الله.