Menu
Coddy logo textTech

החלפה גנרית

חלק מהיחידה תכנות מונחה עצמים במסלול ה-C של Coddy. שיעור 51 מתוך 61.

פעולה נפוצה בתכנות היא החלפה בין שני ערכים. בדרך כלל, היית כותב פונקציית החלפה נפרדת לכל טיפוס: אחת עבור int, אחרת עבור double, וכן הלאה. אבל בעזרת void* ו-memcpy, אפשר לכתוב פונקציה אחת שמחליפה בין כל שני משתנים.

התובנה המרכזית היא שהחלפה היא פשוט הזזה של בתים ממקום למקום. אם ידוע לך גודל הנתונים, אפשר להעתיק בתים בלי לדעת מהו הטיפוס בפועל. חתימת הפונקציה נראית כך:

void generic_swap(void* a, void* b, size_t size);

הפרמטר size מציין לפונקציה כמה בתים להעביר. בתוך הפונקציה, אנחנו משתמשים במאגר זמני וב־memcpy מתוך <string.h> כדי להחליף בין הבתים:

void generic_swap(void* a, void* b, size_t size) {
    char temp[size];      // מאגר זמני
    memcpy(temp, a, size);  // temp = a
    memcpy(a, b, size);     // a = b
    memcpy(b, temp, size);  // b = temp
}

כעת הפונקציה היחידה הזו פועלת עבור כל סוג:

int x = 5, y = 10;
generic_swap(&x, &y, sizeof(int));
// x הוא כעת 10, y הוא כעת 5
double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// הערך של p הוא כעת 2.71, והערך של q הוא כעת 3.14

התבנית הזאת, שמשתמשת ב־void* יחד עם size_t, היא האופן שבו ספריית התקן של C מממשת פונקציות גנריות כמו qsort ו־bsearch.

challenge icon

אתגר

קל

בואו נבנה כלי החלפה כללי שיכול להחליף בין הערכים של כל שני משתנים, בלי קשר לסוג שלהם: באמצעות העוצמה של מצביעי void* ושל memcpy.

תארגנו את הקוד בשלושה קבצים:

  • swap.h: הצהירו על הפונקציה generic_swap, שמקבלת שני מצביעי void* ופרמטר מסוג size_t המציין את גודל הנתונים שיש להחליף. אל תשכחו include guards!
  • swap.c: ממשו את הפונקציה generic_swap. השתמשו במאגר זמני וב-memcpy כדי להעביר בתים בין שני מיקומי הזיכרון. זכרו את תבנית ההחלפה בת שלושת השלבים: העתיקו את הערך הראשון למשתנה הזמני, העתיקו את הערך השני לראשון, ואז העתיקו את המשתנה הזמני לשני.
  • main.c: הדגימו את פעולת ההחלפה הכללית עם סוגי נתונים שונים. קראו סימון סוג (i עבור מספרים שלמים, d עבור מספרים מסוג double) ולאחריו שני ערכים. צרו שני משתנים מהסוג המתאים, החליפו ביניהם באמצעות הפונקציה generic_swap שלכם, ואז הדפיסו את שני הערכים לאחר ההחלפה.

התוכנית שלכם תקבל שלוש קלטים:

  1. סימון סוג: i עבור מספר שלם או d עבור מספר מסוג double
  2. הערך הראשון
  3. הערך השני

לאחר ההחלפה, הדפיסו את שני הערכים בשורות נפרדות. עבור מספרים מסוג double, השתמשו בשתי ספרות אחרי הנקודה העשרונית.

דוגמת פלט כאשר הקלטים הם i, 5 ו-10:

10
5

דוגמת פלט כאשר הקלטים הם d, 3.14 ו-2.71:

2.71
3.14

דוגמת פלט כאשר הקלטים הם i, -7 ו-42:

42
-7

דוגמת פלט כאשר הקלטים הם d, 99.99 ו-0.01:

0.01
99.99

היתרון של הגישה הזאת הוא שלפונקציה generic_swap שלכם אין מושג אילו סוגים היא מחליפה. היא רק מעבירה בתים. אותה פונקציה עובדת עם מספרים שלמים, מספרים מסוג double, מבנים או כל סוג נתונים אחר. תצטרכו לכלול את <string.h> עבור memcpy ואת <stddef.h> או <stdlib.h> עבור size_t.

נסו בעצמכם

#include <stdio.h>
#include "swap.h"

int main() {
    char type;
    scanf("%c", &type);
    
    if (type == 'i') {
        int a, b;
        scanf("%d", &a);
        scanf("%d", &b);
        
        // TODO: קראו ל-generic_swap כדי להחליף בין a ל-b
        // רמז: השתמשו ב-&a, ב-&b וב-sizeof(int)
        
        printf("%d\n", a);
        printf("%d\n", b);
    } else if (type == 'd') {
        double a, b;
        scanf("%lf", &a);
        scanf("%lf", &b);
        
        // TODO: קראו ל-generic_swap כדי להחליף בין a ל-b
        // רמז: השתמשו ב-&a, ב-&b וב-sizeof(double)
        
        printf("%.2f\n", a);
        printf("%.2f\n", b);
    }
    
    return 0;
}
quiz iconבחנו את עצמכם

השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.

כל השיעורים ביחידה תכנות מונחה עצמים

תרגלו בעצמכם: קומפיילר C אונליין