C: المكتبة القياسية

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

1. العمليات الحسابية — math.h

استخدام الدوال الحسابية يتطلب تضمين الملف الرأسي وربط مكتبة الرياضيات:

C
#include <math.h>

الترجمة بعلامة -lm: gcc program.c -lm.

(1) الدوال الحسابية الشائعة

الدالة الغرض مثال
fabs(x) القيمة المطلقة fabs(-3.5) ← 3.5
sqrt(x) الجذر التربيعي sqrt(16.0) ← 4.0
pow(x, y) x مرفوعة للقوة y pow(2.0, 10.0) ← 1024.0
ceil(x) تقريب لأعلى ceil(3.2) ← 4.0
floor(x) تقريب لأدنى floor(3.8) ← 3.0
round(x) تقريب لأقرب round(3.5) ← 4.0
fmod(x, y) باقِ قسمة عشري fmod(7.5, 2.5) ← 0.0
log(x) لوغاريتم طبيعي log(2.718) ← 1.0
log10(x) لوغاريتم عشري log10(100.0) ← 2.0
sin(x) جيب sin(3.14/2) ← 1.0
cos(x) جيب التمام cos(0.0) ← 1.0
tan(x) ظل tan(0.0) ← 0.0
⚠️ ملاحظة: الدوال المثلثية تأخذ راديان، لا درجات! لتحويل الدرجات إلى راديان: rad = deg * 3.14159265 / 180.0.

▶ مثال

حساب المسافة بين نقطتين:

C
#include <stdio.h>
#include <math.h>

typedef struct {
    double x;
    double y;
} Point;

double distance(Point a, Point b) {
    double dx = a.x - b.x;
    double dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
}

int main(void) {
    Point p1 = {3.0, 4.0};
    Point p2 = {0.0, 0.0};
    printf("Distance: %.2f\n", distance(p1, p2));
    return 0;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Distance: 5.00

2. الأدوات العامة — stdlib.h

(1) الأعداد العشوائية

C
int rand(void);
void srand(unsigned int seed);

تُرجع rand() عددًا صحيحًا عشوائيًا زائفًا بين 0 وRAND_MAX. دون استدعاء srand، كل تشغيل للبرنامج يُنتج نفس تسلسل الأعداد العشوائية.

C
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    srand((unsigned int)time(NULL));

    for (int i = 0; i < 5; i++) {
        printf("%d ", rand());
    }
    printf("\n");

    for (int i = 0; i < 5; i++) {
        printf("%d ", rand() % 100);
    }
    printf("\n");

    return 0;
}
TEXT 📖 للعرض فقط
1804289383 846930886 1681692777 1714636915 1957747793
83 86 77 15 93
⚠️ ملاحظة: rand() % N لا يوفر عشوائية جيدة في معظم التطبيقات — البتات الدنيا قد تتبع نمطًا. للسيناريوهات التي تتطلب عشوائية عالية الجودة، استخدم مولّد أعداد عشوائية أكثر تقدمًا.

(2) تحويل الأنواع

الدالة الغرض
atoi(str) سلسلة إلى int
atol(str) سلسلة إلى long
atof(str) سلسلة إلى double
strtol(str, &end, base) سلسلة إلى long (بأساس)
strtod(str, &end) سلسلة إلى double (مع كشف الأخطاء)
C
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int a = atoi("42");
    double b = atof("3.14");
    long c = strtol("0xFF", NULL, 16);

    printf("a = %d\n", a);
    printf("b = %.2f\n", b);
    printf("c = %ld\n", c);

    char *end;
    long d = strtol("123abc", &end, 10);
    printf("d = %ld, Unconverted part: %s\n", d, end);

    return 0;
}
TEXT 📖 للعرض فقط
a = 42
b = 3.14
c = 255
d = 123, Unconverted part: abc
💡 نصيحة: atoi لا يمكنها كشف الأخطاء — الإدخال غير الصالح يُرجع 0، والذي لا يمكن تمييزه عن 0 شرعي. فضّل strtol/strtod، اللتين تتيحان تحديد نجاح التحويل عبر مؤشر end.


3. إدارة الذاكرة الديناميكية

C
void *malloc(size_t size);
void *calloc(size_t count, size_t size);
void *realloc(void *ptr, size_t size);
void free(void *ptr);
C
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    int *arr = calloc(5, sizeof(int));
    if (arr == NULL) {
        return 1;
    }

    for (int i = 0; i < 5; i++) {
        arr[i] = i * 10;
    }

    int *new_arr = realloc(arr, 10 * sizeof(int));
    if (new_arr == NULL) {
        free(arr);
        return 1;
    }
    arr = new_arr;

    for (int i = 5; i < 10; i++) {
        arr[i] = i * 10;
    }

    for (int i = 0; i < 10; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");

    free(arr);
    return 0;
}
TEXT 📖 للعرض فقط
0 10 20 30 40 50 60 70 80 90
⚠️ ملاحظة: عندما تفشل realloc، تُرجع NULL لكن الذاكرة الأصلية لا تُحرر! لذا يجب استخدام متغير مؤقت لاستقبال القيمة المُرجعة من realloc — عند الفشل، يمكنك تحرير الذاكرة الأصلية.

(1) الترتيب والبحث

qsort

C
void qsort(void *base, size_t count, size_t size,
           int (*compare)(const void *, const void *));

قواعد دالة المقارنة: تُرجع سالبًا إذا a < b، و0 إذا تساوى، وموجبًا إذا a > b.

C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int cmp_int(const void *a, const void *b) {
    return *(const int *)a - *(const int *)b;
}

int cmp_str(const void *a, const void *b) {
    return strcmp(*(const char **)a, *(const char **)b);
}

int main(void) {
    int nums[] = {42, 17, 8, 95, 3, 61};
    int n = sizeof(nums) / sizeof(nums[0]);

    qsort(nums, n, sizeof(int), cmp_int);
    for (int i = 0; i < n; i++) {
        printf("%d ", nums[i]);
    }
    printf("\n");

    const char *names[] = {"Alice", "Bob", "Charlie", "Diana"};
    int m = sizeof(names) / sizeof(names[0]);

    qsort(names, m, sizeof(char *), cmp_str);
    for (int i = 0; i < m; i++) {
        printf("%s ", names[i]);
    }
    printf("\n");

    return 0;
}
TEXT 📖 للعرض فقط
3 8 17 42 61 95
Alice Bob Charlie Diana

bsearch

C
void *bsearch(const void *key, const void *base, size_t count,
              size_t size, int (*compare)(const void *, const void *));

تُجري بحثًا ثنائيًا على مصفوفة مرتبة. تُرجع مؤشرًا للعنصر الموجود، أو NULL إذا لم يُوجد.

C
#include <stdio.h>
#include <stdlib.h>

int cmp_int(const void *a, const void *b) {
    return *(const int *)a - *(const int *)b;
}

int main(void) {
    int nums[] = {3, 8, 17, 42, 61, 95};
    int n = sizeof(nums) / sizeof(nums[0]);

    int key = 42;
    int *result = bsearch(&key, nums, n, sizeof(int), cmp_int);

    if (result != NULL) {
        printf("Found %d at index %ld\n", key, result - nums);
    } else {
        printf("%d not found\n", key);
    }

    return 0;
}
TEXT 📖 للعرض فقط
Found 42 at index 3

4. معالجة الوقت — time.h

(1) دوال الوقت

الدالة/النوع الغرض
time_t نوع الوقت (عادةً ثوانٍ منذ 1970-01-01)
time(&t) الحصول على الوقت الحالي
clock() الحصول على دورات ساعة المعالج المستخدمة
localtime() التحويل إلى بنية الوقت المحلي
gmtime() التحويل إلى بنية الوقت UTC
strftime() تنسيق الوقت كسلسلة
difftime() حساب الفرق بين وقتين (ثوانٍ)

(2) البنية tm

C
struct tm {
    int tm_sec;
    int tm_min;
    int tm_hour;
    int tm_mday;
    int tm_mon;
    int tm_year;
    int tm_wday;
    int tm_yday;
    int tm_isdst;
};

tm_mon يتراوح من 0-11 (0 = يناير)، وtm_year هو عدد السنوات منذ 1900، وtm_wday يستخدم 0 للأحد.

▶ مثال

C
#include <stdio.h>
#include <time.h>

int main(void) {
    time_t now = time(NULL);
    struct tm *local = localtime(&now);

    char buf[64];
    strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", local);
    printf("Current time: %s\n", buf);

    printf("Today is day %d of the week (0=Sun)\n", local->tm_wday);

    return 0;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Current time: 2025-03-15 14:30:22
Today is day 6 of the week (0=Sun)

(3) توقيت البرنامج

C
#include <stdio.h>
#include <time.h>

int main(void) {
    clock_t start = clock();

    volatile long sum = 0;
    for (long i = 0; i < 100000000L; i++) {
        sum += i;
    }

    clock_t end = clock();
    double elapsed = (double)(end - start) / CLOCKS_PER_SEC;
    printf("Elapsed: %.3f seconds\n", elapsed);

    return 0;
}
TEXT 📖 للعرض فقط
Elapsed: 0.235 seconds
⚠️ ملاحظة: clock() يقيس وقت المعالج، لا الوقت الفعلي. إذا كان البرنامج نائمًا أو ينتظر إدخال/إخراج، لا يُحسب ذلك الوقت. استخدم difftime لقياس الوقت الفعلي المنقضي.


5. معالجة الأحرف — ctype.h

الدالة شرط الفحص
isalpha(c) حرف
isdigit(c) رقم
isalnum(c) حرف أو رقم
isupper(c) حرف كبير
islower(c) حرف صغير
isspace(c) مسافة بيضاء (مسافة، جدول، سطر جديد، إلخ)
ispunct(c) علامة ترقيم
isprint(c) حرف قابل للطباعة
toupper(c) تحويل إلى كبير
tolower(c) تحويل إلى صغير

هذه الدوال تتطلب قيمة unsigned char أو EOF كمعامل. تمرير قيمة char سالبة سلوك غير معرّف.

▶ مثال

عدّ الأحرف والأرقام والأحرف الأخرى في سلسلة:

C
#include <stdio.h>
#include <ctype.h>

int main(void) {
    char str[] = "Hello, World! 123";
    int letters = 0, digits = 0, others = 0;

    for (int i = 0; str[i] != '\0'; i++) {
        if (isalpha((unsigned char)str[i])) {
            letters++;
        } else if (isdigit((unsigned char)str[i])) {
            digits++;
        } else {
            others++;
        }
    }

    printf("Letters: %d, Digits: %d, Others: %d\n", letters, digits, others);
    return 0;
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Letters: 10, Digits: 3, Others: 6

6. التأكيدات — assert.h

C
void assert(int expression);

عندما يكون التعبير خاطئًا، ينتهي البرنامج ويطبع رسالة خطأ (اسم الملف، رقم السطر، التعبير). تعريف الماكرو NDEBUG قبل #include <assert.h> يعطّل جميع التأكيدات.

C
#include <stdio.h>
#include <assert.h>

double safe_divide(double a, double b) {
    assert(b != 0 && "Divisor cannot be zero");
    return a / b;
}

int main(void) {
    printf("10 / 2 = %.1f\n", safe_divide(10.0, 2.0));
    printf("10 / 0 = %.1f\n", safe_divide(10.0, 0.0));
    return 0;
}
TEXT 📖 للعرض فقط
10 / 2 = 5.0
Assertion failed: b != 0 && "Divisor cannot be zero", file main.c, line 5
💡 نصيحة: السلسلة الحرفية في assert تُطبع، لذا كتابة "Divisor cannot be zero" أكثر فعالية من تعليق — عند فشل التأكيد، يمكنك رؤية السبب مباشرة.


❓ أسئلة شائعة

س لماذا يُنتج rand() نفس التسلسل في كل تشغيل؟
ج لم يتم استدعاء srand لضبط البذرة العشوائية. البذرة الافتراضية هي 1، لذا التسلسل ثابت. استخدم srand(time(NULL)) للبذر بالوقت الحالي.
س لماذا تستخدم دالة مقارنة qsort مؤشرات void؟
ج qsort دالة عامة لا تعرف نوع العناصر التي ترتبها. مؤشرات void يمكن أن تشير إلى أي نوع، ودالة المقارنة تحوّلها داخليًا إلى النوع المحدد.
س لماذا يجب تحويل معاملات دوال ctype.h إلى unsigned char؟
ج إذا كان char موقّعًا، فقد تكون الأحرف ذات البت الأعلى المضبوط قيمًا سالبة، وهو سلوك غير معرّف عند تمريرها لدوال ctype. التحويل إلى unsigned char يضمن أن القيمة في النطاق 0-255.
س ما الفرق بين assert وفحص if؟
ج assert يفحص أشياء يجب ألا تحدث أبدًا (أخطاء منطقية) أثناء التطوير، ويختفي عند تعريف NDEBUG. فحوصات if تتعامل مع أخطاء وقت التشغيل المحتملة وهي موجودة دائمًا.

📖 ملخص

📝 تمارين

  1. اكتب برنامجًا يُولّد 100 عدد صحيح عشوائي من 1 إلى 1000، يرتبها بـ qsort، ويُخرج القيمتين القصوى والدنيا
  2. اكتب برنامجًا يستخدم clock() لمقارنة فارق الوقت بين الترتيب بالفقاعات وqsort على 10000 عدد صحيح
  3. اكتب دالة تستخدم دوال ctype.h لتنفيذ تحويل حالة السلسلة (إدخال سلسلة، وإخراج نسخة بأحرف كبيرة وأخرى بأحرف صغيرة)
Web-Tutorial.com

فريق Web-Tutorial التقني

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

100%