החלפה גנרית
חלק מהיחידה תכנות מונחה עצמים במסלול ה-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 הוא כעת 5double p = 3.14, q = 2.71;
generic_swap(&p, &q, sizeof(double));
// הערך של p הוא כעת 2.71, והערך של q הוא כעת 3.14התבנית הזאת, שמשתמשת ב־void* יחד עם size_t, היא האופן שבו ספריית התקן של C מממשת פונקציות גנריות כמו qsort ו־bsearch.
אתגר
קלבואו נבנה כלי החלפה כללי שיכול להחליף בין הערכים של כל שני משתנים, בלי קשר לסוג שלהם: באמצעות העוצמה של מצביעי void* ושל memcpy.
תארגנו את הקוד בשלושה קבצים:
swap.h: הצהירו על הפונקציהgeneric_swap, שמקבלת שני מצביעיvoid*ופרמטר מסוגsize_tהמציין את גודל הנתונים שיש להחליף. אל תשכחו include guards!swap.c: ממשו את הפונקציהgeneric_swap. השתמשו במאגר זמני וב-memcpyכדי להעביר בתים בין שני מיקומי הזיכרון. זכרו את תבנית ההחלפה בת שלושת השלבים: העתיקו את הערך הראשון למשתנה הזמני, העתיקו את הערך השני לראשון, ואז העתיקו את המשתנה הזמני לשני.main.c: הדגימו את פעולת ההחלפה הכללית עם סוגי נתונים שונים. קראו סימון סוג (iעבור מספרים שלמים,dעבור מספרים מסוג double) ולאחריו שני ערכים. צרו שני משתנים מהסוג המתאים, החליפו ביניהם באמצעות הפונקציהgeneric_swapשלכם, ואז הדפיסו את שני הערכים לאחר ההחלפה.
התוכנית שלכם תקבל שלוש קלטים:
- סימון סוג:
iעבור מספר שלם אוdעבור מספר מסוג double - הערך הראשון
- הערך השני
לאחר ההחלפה, הדפיסו את שני הערכים בשורות נפרדות. עבור מספרים מסוג 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;
}
השיעור הזה כולל חידון קצר. התחילו את השיעור כדי לענות עליו ולעקוב אחרי ההתקדמות.
כל השיעורים ביחידה תכנות מונחה עצמים
4כימוס
המושג של מצביעים אטומיםהגדרת מבנים אטומיםפונקציות Get ו-Setאימות ב-Settersסיכום: הקופסה הסודית2אובייקטים ומתודות
מבנים כאובייקטיםמצביע 'Self'נכונות constמצביע לעומת ערךמתודות עזרחזרה: מנהל נקודות5פרויקט: חשבון בנק פשוט
הגדרת הפרויקטמימוש החשבוןתרגלו בעצמכם: קומפיילר C אונליין