You are on page 1of 42

‫מבוא למחשב בשפת ‪C‬‬

‫הרצאה ‪ :7‬פונקציות‬

‫מבוסס על השקפים שחוברו ע"י שי ארצי‪ ,‬גיתית רוקנשטיין ז"ל‪,‬‬


‫איתן אביאור וסאהר אסמיר עבור הקורס "מבוא למדעי המחשב"‪.‬‬
‫עודכן ע"י דן רביב‪ .‬עדכון אחרון‪ :‬מאי ‪ 2015‬יחיאל קמחי‬

‫נכתב על‪-‬ידי טל כהן‪ ,‬נערך ע"י איתן אביאור‪ © .‬כל הזכויות שמורות לטכניון – מכון טכנולוגי לישראל‬
‫בעיית התוכנית הגדולה‬

‫• תוכנית גדולה המבצעת פעולות רבות‪ ,‬יכולה להפוך לקשה‬


‫מאוד לניהול אם נשתמש רק בכלים שלמדנו עד כה‪:‬‬
‫קשה לכתוב ולנפות את התוכנית‪,‬‬ ‫–‬
‫קשה להבין אותה‪,‬‬ ‫–‬
‫קשה לתחזק ולתקן אותה‪,‬‬ ‫–‬
‫קשה להוסיף לה שיפורים‪.‬‬ ‫–‬

‫• אתם כבר יודעים שקריאת תוכנית קשה מכתיבתה‪.‬‬

‫• בנוסף‪ ,‬תכנות באופן שהצגנו עד כה יסבול מ‪-‬שכפול קוד‪:‬‬


‫– גושים נפרדים של קוד המבצעים את אותה הפעולה )או פעולה‬
‫דומה( החוזרים על עצמם במקומות שונים בתוכנית‪.‬‬
‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪2‬‬
‫פונקציות‬

‫• כדי להקל על ניהול הקוד‪ ,‬מחלקים את התוכנית למספר קטעים‬


‫בלתי‪-‬תלויים )כמעט(‪ ,‬אשר בשפת ‪ C‬מכונים בשם‪ :‬פונקציות‪.‬‬
‫– יש שפות בהן המינוח אחר‪ :‬נהלים‪ ,‬שגרות )‪(Routine, Subroutine‬‬
‫למעשה‪ ,‬כל תוכנית בשפת ‪ C‬בנויה מאוסף של פונקציות‪.‬‬ ‫•‬
‫)בתנאים סטנדרטיים( הפונקציה המופעלת ראשונה נקראת )(‪,main‬‬ ‫•‬
‫ועל כן‪ ,‬כל תוכנית )סטנדרטית( חייבת להכיל פונקציה בשם זה‪.‬‬
‫הפעלת פונקציה מכונה בשם‪ :‬קריאה )‪ (call‬לפונקציה‪.‬‬ ‫•‬
‫פונקציה יכולה לקרוא לכל פונקציה בתכנית )כולל לעצמה(‪.‬‬ ‫•‬
‫חלוקת התוכנית לפונקציות‪:‬‬ ‫•‬
‫– מונעת שכפול קוד‪,‬‬
‫– תורמת לקריאּות התכנית‪,‬‬
‫– ומאפשרת שיתוף חלקי קוד וחבילות תוכנה בין תכניות שונות‪.‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪3‬‬


‫מיתרונות החלוקה לפונקציות‬

‫• בעזרת שימוש בפונקציות‪ ,‬ניתן לפרק בעיות מורכבות לתת‪-‬בעיות‬


‫קטנות ופשוטות )כמו ב‪ :‬הוכחה מתמטית‪ ,‬מתכון בישול וכדומה(‪.‬‬
‫)(‪void spaghetti_bolognese‬‬ ‫• לדוגמה‪ ,‬הכנת ספגטי בולונז‪:‬‬
‫{‬
‫;)(‪prepare_spaghetti‬‬
‫;)(‪prepare_sauce‬‬
‫;‪stir‬‬
‫;‪serve‬‬ ‫)(‪void prepare_spaghetti‬‬
‫}‬ ‫{‬
‫;‪boil_water‬‬
‫;‪add_spaghetti‬‬
‫{ ) ‪while ( not_ready‬‬
‫;‪stir‬‬
‫;)‪wait(2‬‬
‫}‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪4‬‬


‫ של שלושה מספרים‬GCD ‫ מציאת‬:‫דוגמה‬

‫הצהרה‬/‫ הכרזה‬declaration ‫ הגדרה‬definition

#include <stdio.h> int gcd(int n, int m)


{
int gcd(int, int); if (n < 0) n = -n;
if (m < 0) m = -m;
int main() while ( m != 0 ) {
{ int t = m;
int a, b, c, gcd_ab, gcd_abc; m = n % m;
scanf("%d%d%d", &a, &b, &c); n = t;
gcd_ab = gcd(a, b); }
gcd_abc = gcd(gcd_ab, c); return n;
printf("gcd: %d",gcd_abc); }
return 0; 1 ‫קריאה‬
2 ‫קריאה‬
}

7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 5


‫הגדרת פונקציה‬

‫)‪return-type function-name(parameters-list‬‬ ‫כותרת הפונקציה‬


‫{‬
‫‪variable definitions‬‬
‫‪statements‬‬ ‫גוף הפונקציה‬
‫‪return statement‬‬
‫}‬

‫• מילת המפתח ‪ void‬משמשת לציון הדברים הבאים‪:‬‬


‫‪void‬‬ ‫)‪print_result(int‬‬ ‫– פונקציה שאינה מחזירה ערך‪:‬‬
‫– הצהרה על פונקציה בעלת‬
‫‪int‬‬ ‫;)‪getchar(void‬‬ ‫רשימת פרמטרים ריקה‪:‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪6‬‬


‫קריאה לפונקציה‬

‫)‪function-name(arguments-list‬‬

‫מה מתבצע בעת הקריאה לפונקציה?‬ ‫•‬


‫‪ .1‬מוקצים תאי זיכרון עבור המשתנים הנמצאים ברשימת‬
‫הפרמטרים הפורמאליים בהגדרת הפונקציה‪.‬‬
‫‪ .2‬מחושבים הביטויים הנמצאים ברשימת הארגומנטים המופיעים בקריאה‬
‫לפונקציה )שם אחר לארגומנטים‪ :‬פרמטרים אקטואליים(‪.‬‬
‫– שימו לב‪ :‬סדר חישוב ערכי הארגומנטים איננו מוגדר‪.‬‬
‫‪ .3‬ערך כל ביטוי מוכנס למשתנה המתאים‪.‬‬
‫– אופן העברת פרמטרים כזה מכונה בשם‪.call by value :‬‬
‫‪ .4‬מוקצים )ומאותחלים( המשתנים המוגדרים בגוף הפונקציה‪.‬‬
‫‪ .5‬מתבצע קוד הפונקציה‪.‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪7‬‬


‫חזרה מפונקציה‬
‫כיצד מתבצעת החזרה מפונקציה?‬ ‫•‬
‫‪ .1‬ניתן להחזיר ערך לסביבה הקוראת באמצעות פסוק ‪.return‬‬
‫‪ .2‬ישנם שני סוגי פסוק ‪:return‬‬
‫ ;‪ return value‬לפונקציות שיש להן ערך מוחזר‪.‬‬
‫לפונקציות שטיפוס החזרה שלהן הוא ‪.void‬‬ ‫ ;‪return‬‬
‫)ניתן להשמיט פסוק זה אם הוא האחרון בפונקציה(‬
‫‪ .3‬עם ההגעה לפסוק ‪) return‬או לסוף הפונקציה( משוחרר הזיכרון‬
‫שהוקצה עבור משתני הפונקציה )הפרמטרים והמשתנים הפנימיים(‬
‫והשליטה חוזרת לקוד הפונקציה הקוראת‪ ,‬מהמקום בו הופסק‪.‬‬
‫‪ .4‬במידת הצורך מתבצעת המרת טיפוסים אוטומטית‪:‬‬
‫הערך המוחזר בפסוק ה‪return-‬‬
‫מומר אוטומטית לטיפוס החזרה‬
‫המצוין בהגדרת הפונקציה‪.‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪8‬‬


‫קריאה לפונקציה וחזרה ממנה – דוגמה‬

‫‪n‬‬ ‫‪m‬‬
‫)‪int gcd(int n, int m‬‬
‫?‬
‫‪8‬‬
‫‪2‬‬
‫‪1‬‬
‫‪5‬‬ ‫‪2‬‬
‫‪0‬‬
‫?‬
‫{‬
‫‪...‬‬
‫;‪return n‬‬
‫}‬

‫)(‪int foo‬‬
‫{‬ ‫‪d‬‬
‫‪...‬‬ ‫?‬
‫‪2‬‬
‫‪28‬‬
‫‪d = gcd(9-1,‬‬ ‫;)‪2‬‬
‫‪return gcd(5‬‬‫;)‪1 , 2d‬‬
‫}‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪9‬‬


‫הכרזה על פונקציה‬

‫• לפני קריאה לפונקציה חובה שתופיע בקובץ‪ :‬הגדרה )‪ (definition‬שלה‬


‫או הכרזה )‪ (declaration‬עליה‪.‬‬
‫• הכרזה מיידעת את המהדר לגבי מספר הפרמטרים‪ ,‬טיפוסיהם וטיפוס‬
‫החזרה‪ ,‬ומאפשרת לבצע המרות טיפוסים אוטומטיות במידת הצורך‪.‬‬
‫• פסוק ‪ #include‬מאפשר לייבא הכרזות של פונקציות ספריה מקובץ‬
‫הכרזות אשר שמו מכיל סיומת ‪.h‬‬
‫– כך למשל‪ ,‬אנו מייבאים את ההכרזות של פונקציות הקלט‪/‬פלט הסטנדרטיות‬
‫ובכללן ‪ printf‬ו‪ scanf-‬מהקובץ ‪.stdio.h‬‬

‫• דוגמאות להכרזות‪:‬‬
‫;)‪double sqrt(double‬‬
‫;)‪long power(int, int‬‬
‫;)‪long power(int base, int exponent‬‬ ‫מה עדיף?‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪10‬‬


‫הכרזה על פונקציה‬

‫• ניתן להגדיר פונקציות עזר לפני הגדרת הפונקציה ‪ ,main‬אולם הדבר‬


‫נוגד את עקרונות ‪ – top-down-design‬השימוש בהכרזות עדיף‪.‬‬
‫כלומר‪ :‬לב התוכנית הינה ב‪ main -‬ואנו מעדיפים שהקוד שלה יופיע ראשון‪.‬‬

‫• אנו חייבים להשתמש בהכרזה כאשר הפונקציה נמצאת בשרשרת‬


‫קריאות רקורסיביות‪.‬‬
‫כלומר‪ :‬אם הפונקציה קוראת לעצמה או שהיא קוראת לפונקציה אחרת‬
‫שקוראת אליה בחזרה וכן הלאה – מצב היוצר תלות מעגלית‪.‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪11‬‬


‫דוגמה ‪ :1‬ערך מוחלט‬

‫• הפונקציה הבאה תחזיר את הערך המוחלט של הפרמטר שלה‪:‬‬

‫)‪double abs(double x‬‬


‫{‬
‫{ )‪if (x < 0‬‬
‫;‪return -x‬‬
‫}‬
‫;‪return x‬‬
‫}‬

‫האם יש צורך‬
‫ב‪?else-‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪12‬‬


‫ הפונקציה כחלק מתוכנית‬:1 ‫המשך דוגמה‬

:‫• הפונקציה שאנו מגדירים מהווה למעשה חלק מתוכנית‬

#include <stdio.h> double abs(double x)


{
double abs(double x); if (x < 0) {
return -x;
int main() }
{ return x;
double y; }

printf("y = ");
scanf("%lf", &y);
printf("|%f| = %f\n", y, abs(y));
double abs(double x)
return 0; {return x < 0 ? –x : x;}
}

7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 13


‫דוגמה ‪ :2‬פונקציה למציאת מינימום‬

‫• הפונקציה הבאה תחזיר את הערך הנמוך מבין ערכי שני הפרמטרים‬


‫שלה‪:‬‬
‫)‪double min2(double a, double b‬‬
‫{‬
‫;‪if (a < b) return a‬‬
‫;‪return b‬‬
‫}‬ ‫)‪double min2(double a, double b‬‬
‫{‬
‫;‪return a < b ? a : b‬‬
‫}‬

‫• מה תחזיר הפונקציה אם שני הפרמטרים שווים בערכם?‬


‫• כיצד ניתן להגדיר פונקציית ‪?max2‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪14‬‬


‫דוגמה ‪ :3‬מינימום מבין שלושה ערכים‬

‫• הפונקציה הבאה תחזיר את הערך הנמוך מבין ערכי שלושת הפרמטרים‬


‫שלה‪:‬‬
‫)‪double min3(double a, double b, double c‬‬
‫{‬
‫{ )‪if (a <= b && a <= c‬‬
‫;‪return a‬‬
‫}‬
‫{ )‪if (b <= a && b <= c‬‬
‫;‪return b‬‬
‫}‬
‫;‪return c‬‬
‫}‬

‫• הפונקציה הזו מורכבת יותר מקודמתה‪ ...‬האם ניתן לפשט אותה?‬


‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪15‬‬
‫המשך דוגמה ‪ :3‬מינימום מבין שלושה ערכים – קוד‬
‫משופר‬

‫• כאמור‪ ,‬השימוש בפונקציות מפשט תוכניות גדולות‪.‬‬


‫• כאן למשל‪ ,‬ניתן להשתמש בפונקציה שהגדרנו לפני כן )‪ (min2‬כדי‬
‫לממש את הפונקציה החדשה )‪:(min3‬‬

‫)‪double min3(double a, double b, double c‬‬


‫{‬
‫;)‪double minAB = min2(a, b‬‬
‫;)‪return min2(minAB, c‬‬
‫}‬

‫)‪double min3(double a, double b, double c‬‬


‫• ואפילו‪:‬‬
‫{‬
‫;)‪return min2(min2(a,b), c‬‬
‫}‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪16‬‬


‫דוגמה ‪ :4‬עצרת‬

‫• פונקציה יכולה לכלול כל אחד מסוגי הפסוקים שלמדנו עד כה‪.‬‬


‫– למשל‪ ,‬ניתן להשתמש בלולאה בתוך פונקציה‪.‬‬
‫• הפונקציה הבאה מחשבת את העצרת של הפרמטר ‪:n‬‬

‫)‪int factorial(int n‬‬


‫{‬
‫;‪int i, result = 1‬‬
‫{ )‪for (i = 2; i <= n; ++i‬‬
‫;‪result *= i‬‬
‫}‬
‫;‪return result‬‬
‫}‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪17‬‬


‫ארגון קובץ המכיל מספר פונקציות‬

‫‪#include‬‬ ‫>‪<...‬‬

‫;)‪int max(int a, int b‬‬ ‫בראש הקובץ מופיעות הכרזות על‬ ‫•‬
‫;)‪int min(int a, int b‬‬ ‫הפונקציות השונות‪ ,‬ואח"כ הן מוגדרות‪:‬‬
‫)(‪int main‬‬ ‫} ‪{ max(x,y); ...‬‬ ‫– מסודרות למשל‪ ,‬לפי העקרונות של‬
‫תכנון מלמעלה למטה ) ‪top-down‬‬
‫} ‪int max(int a, int b) { ...‬‬
‫‪. (design‬‬
‫} ‪int min(int a, int b) { ...‬‬

‫• לעיתים ניתן לסדר את הפונקציות כך שכל‬


‫‪#include‬‬ ‫>‪<...‬‬
‫פונקציה מוגדרת לפני השימוש בה‪ ,‬לפי‬
‫} ‪int max(int a, int b) { ...‬‬ ‫תכנון מלמטה למעלה ) ‪bottom-up‬‬
‫} ‪int min(int a, int b) { ...‬‬
‫‪.(design‬‬
‫– ניתן לחסוך הכרזות‪.‬‬
‫)(‪int main‬‬ ‫} ‪{ max(x,y); ...‬‬
‫– מומלץ פחות – מקשה על ההבנה‪.‬‬
‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪18‬‬
?‫ איזה מין מספר‬:‫דוגמה‬

‫ ריבוע שלם או‬,‫• נכתוב תוכנית המקבלת מספר ובודקת אם הוא ראשוני‬
:‫לא זה ולא זה‬

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

enum bool {FALSE, TRUE};


typedef enum bool Boolean;

Boolean is_square(unsigned int);


Boolean is_prime(unsigned int);
double my_round(double);
double my_trunc(double);

7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 19


‫ הפונקציה הראשית‬:‫המשך הדוגמה‬
int main()
{
int num;

while (scanf("%d", &num) == 1) {


if (num < 0) {
printf("%d is negative\n", num);
continue;
}
if (is_square(num))
printf("%d is a square\n", num);
else if (is_prime(num))
printf("%d is a prime\n", num);
else
printf("%d is neither a prime nor a square\n", num);
}

return 0;
}
7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 20
‫ פונקציה לבדיקת ראשוניות‬:‫המשך הדוגמה‬

Boolean is_prime(unsigned int n)


{
int i, sqrt_n;

if (n == 2) return TRUE;
if (n % 2 == 0 || n < 2) return FALSE;

sqrt_n = my_round(sqrt(n)); /* is (int)sqrt(n) good too? */

for (i = 3; i <= sqrt_n; i += 2)


if (n % i == 0) return FALSE;

return TRUE;
}

7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 21


‫ עיגול וקטימה‬,‫ בדיקת ריבוע‬:‫המשך הדוגמה‬

Boolean is_square(unsigned int n)


{
int sq = my_round(sqrt(n));
return (sq * sq == n);
}

double my_round(double frac)


{
return my_trunc(frac + (frac < 0 ? -0.5 : 0.5));
}

double my_trunc(double frac)


{
return frac < 0 ? ceil(frac) : floor(frac);
}

7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 22


‫מחסנית הקריאות‬

‫מחסנית )‪ (stack‬הינה מבנה נתונים שבו ניתן לשמור סדרה של ערכים המוכנסים‬ ‫•‬
‫אליה ומּוצאים ממנה לאחר מכן‪.‬‬
‫– סדר ההוצאה הפוך לסדר ההכנסה‪ :‬האחרון שנכנס הוא הראשון לצאת‬
‫)‪.(LIFO – Last In First Out‬‬
‫– כך בנויה‪ ,‬למשל‪ ,‬גם מחסנית של כלי ירייה כגון רובה )לא אקדח תופי(‪.‬‬

‫מחסנית הקריאות של תוכנית הינה אזור מיוחד בזיכרון )הנתמך ע"י המהדר‬ ‫•‬
‫וע"י חומרת‪-‬המחשב(‪ ,‬בה נשמר מידע אודות הפונקציות המתבצעות בכל רגע‪.‬‬
‫כאשר פונקציה נקראת‪ ,‬נדחפת למחסנית רשומת הפעלה הכוללת בין השאר את‪:‬‬ ‫•‬
‫– ערכי הארגומנטים בקריאה לפונקציה )במידת הצורך(‪,‬‬
‫– מקום לערך המוחזר מהפונקציה )במידת הצורך(‪,‬‬
‫– כתובת החזרה מהפונקציה )בסיום פעולתה(‪,‬‬
‫– הקצאת זיכרון למשתנים המקומיים של הפונקציה )במידת הצורך(‪.‬‬
‫בסיום פעולתה של פונקציה‪ ,‬נמחקת רשומת ההפעלה מהמחסנית‪ ,‬והשליטה חוזרת‬ ‫•‬
‫לפונקציה הקוראת‪ ,‬לפי כתובת החזרה‪.‬‬
‫רשומת ההפעלה של ה"פונקציה הנוכחית" הינה תמיד בראש המחסנית‪.‬‬ ‫•‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪23‬‬


‫התפתחות מחסנית הקריאות בדוגמה‬
sqrt() my_round()
frac = 2.8
is_prime() is_prime() is_prime() is_prime()
n = 8 n = 8 n = 8 n = 8
i, sqrt_n i, sqrt_n i, sqrt_n i, sqrt_n
main() main() main() main() main()
num = 8 num = 8 num = 8 num = 8 num = 8
1 2 3 4 5
floor()
my_trunc() my_trunc() my_trunc()
frac = 2.8 frac = 2.8 frac = 2.8
my_round() my_round() my_round() my_round()
frac = 2.8 frac = 2.8 frac = 2.8 frac = 2.8
is_prime() is_prime() is_prime() is_prime() is_prime()
n = 8 n = 8 n = 8 n = 8 n = 8, i
i, sqrt_n i, sqrt_n i, sqrt_n i, sqrt_n sqrt_n = 3
main() main() main() main() main()
num = 8 num = 8 num = 8 num = 8 num = 8
6 7 8 9 10
7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 24
‫תחומי הכרה של מזהים )‪(scope‬‬

‫תוכנית בשפת ‪ C‬מורכבת מבלוקים‪ :‬פונקציות ובהן פסוקים מורכבים }…{‪.‬‬ ‫•‬
‫בתחילת כל בלוק ניתן להגדיר משתנים מקומיים‪.‬‬ ‫•‬
‫ניתן לפנות אל משתנה מקומי באמצעות המזהה שלו רק בבלוק בו הוא‬ ‫•‬
‫מוגדר‪ ,‬כולל בבלוקים המוכלים בו‪ ,‬החל ממקום ההגדרה ועד סוף הבלוק‪.‬‬
‫מזהה מבלוק חיצוני תקף בתוך בלוק פנימי‪ ,‬אלא אם הוגדר מחדש בבלוק‬ ‫•‬
‫הפנימי‪.‬‬
‫– במילים אחרות‪ :‬מזהה המוגדר בבלוק פנימי "מסתיר" מזהה בעל שם זהה‬
‫בבלוק חיצוני‪.‬‬
‫• כשקוראים לפונקציה‪ ,‬משתני הפונקציה )הבלוק( הקוראת אומנם שמורים‬
‫במחסנית‪ ,‬אך המזהים שלהם אינם מוכרים בפונקציה הנקראת‪ ,‬ועל כן לא‬
‫ניתן לפנות אליהם באמצעות המזהים שלהם מהפונקציה הנקראת‪.‬‬
‫• משתנה המוגדר מחוץ לכל בלוק נקרא משתנה חיצוני או משתנה גלובלי‪.‬‬
‫• משתנה חיצוני מוכר בכל הקוד החל ממקום הגדרתו ועד סוף הקובץ‪,‬‬
‫ומשתנה גלובלי עשוי להיות מוכר גם בקבצים אחרים של התוכנית‪.‬‬
‫– מלבד בבלוקים שבהם הוגדרו מחדש‪ ,‬ועל כן הוסתרו‪.‬‬
‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪25‬‬
#include <stdio.h>
int a = 1, b = 2, c = 3, d = 4;
void v() ‫דוגמאות‬
{
int a = 101, b = 102, e = 105;
printf("%d %d %d %d %d\n", a, b, c, d, e); 101 102 3 4 105
{
int a = 201, c = 203, d = 204;
printf("%d %d %d %d %d\n", a, b, c, d, e);
} 201 102 203 204 105
{
printf("%d %d %d %d %d\n", a, b, c, d, e);
} 101 102 3 4 105
printf("%d %d %d %d %d\n", a, b, c, d, e);
}
int main() 101 102 3 4 105
{
int a = 11, b = 12, d = 14, m = 17;
printf("%d %d %d %d %d\n", a, b, c, d, m); 11 12 3 14 17
{
printf("%d %d %d %d %d\n", a, b, c, d, m);
{ 11 12 3 14 17
int a = 21;
printf("%d %d %d %d %d\n", a, b, c, d, m);
{ 21 12 3 14 17
printf("%d %d %d %d %d\n", a, b, c, d, m);
} 21 12 3 14 17
}
}
v();
printf("%d %d %d %d %d\n", a, b, c, d, m); 11 12 3 14 17
return 0;
}
7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 26
‫מערך חד מימדי ופונקציות‬

‫ראשית‪ ,‬ניזכר כיצד עובר ארגומנט לפונקציה )‪:(call by value‬‬

‫)(‪int main‬‬
‫{‬ ‫)‪change_a(int a‬‬
‫;‪int a = 7‬‬ ‫{‬
‫;)‪change_a (a‬‬ ‫;‪a = 3‬‬
‫;)‪printf(“%d\n”,a‬‬ ‫;‪return‬‬
‫}‬
‫;‪return 0‬‬
‫}‬

‫על המסך יודפס ‪ .7‬הפונקציה מקבלת עותק זמני‪ ,‬המייצג את הערך )‪(value‬‬
‫של המשתנה ומשנה אותו‪ .‬בתוכנית הראשית הערך של ‪ a‬לא משתנה !‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪27‬‬


‫מערך חד מימדי ופונקציות‬

‫לעומת זאת‪ ,‬בהעברת מערך לפונקציה‬


‫)(‪int main‬‬
‫{‬
‫;}‪int a[1] = {7‬‬ ‫)][‪void change_a(int a‬‬
‫;)‪change_a (a‬‬ ‫{‬
‫;)]‪printf(“%d\n”, a[0‬‬ ‫;‪a[0] = 3‬‬
‫;‪return‬‬
‫;‪return 0‬‬ ‫}‬ ‫גודל המערך מיותר‬
‫}‬ ‫)נלמד בהמשך(‬

‫על המסך יודפס ‪ .3‬הפונקציה לא מקבלת עותק זמני של המערך‪.‬‬


‫למעשה המערך עצמו עובר לפונקציה ושינוי בתוך הפונקציה ישפיע‬
‫על המערך מחוץ לפונקציה‪) .‬אנו נלמד בהמשך מדוע הדבר קורה(‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪28‬‬


‫מערך חד מימדי ופונקציות‬

‫כאשר אנו שולחים לפונקציה מערך‪ .‬כיצד נדע כמה אברים יש בו ?‬

‫למשל‪ :‬נכתוב פונקציה המקבלת מערך של שלמים ומחזירה את סכום אבריו‬

‫)(‪int main‬‬ ‫)][‪int sum (int a‬‬


‫{‬ ‫{‬
‫;}‪int a[6] = {7,4,7,4,2,1‬‬ ‫;‪int i, val = 0‬‬
‫;))‪printf(“%d\n”, sum(a‬‬ ‫{ )‪for (i = 0; i < ? ; i++‬‬
‫;‪return 0‬‬ ‫;]‪val += a[i‬‬
‫}‬ ‫}‬
‫;‪return val‬‬
‫}‬

‫איך נדע בתוך הפונקציה כמה אברים יש במערך?‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪29‬‬


‫מערך חד מימדי ופונקציות‬

‫איך נדע בתוך הפונקציה כמה אברים יש במערך ?‬


‫– נשלח את מספרם לפונקציה‬

‫)(‪int main‬‬ ‫)‪int sum (int a[], int n‬‬


‫{‬ ‫{‬
‫;}‪int a[6] = {7,4,7,4,2,1‬‬ ‫;‪int i, val = 0‬‬
‫;))‪printf(“%d\n”, sum(a,6‬‬ ‫{ )‪for (i = 0; i < n; i++‬‬
‫;‪return 0‬‬ ‫;]‪val += a[i‬‬
‫}‬ ‫}‬
‫;‪return val‬‬
‫}‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪30‬‬


‫גודל מערכים ופונקציות‬

‫באופן כלל ישנם ‪ 3‬שיטות להעביר את גודל המערך‪:‬‬


‫‪ (1‬נשלח את גודלו )כמו שראינו קודם(‬
‫‪ (2‬נסמן את האבר האחרון‬
‫)בערך המוסכם על התוכנית השולחת והפונקציה בשימוש(‬
‫‪ (3‬נגדיר את גודל המערך בשלב קומפילציה )ע"י ‪(#define‬‬

‫בקורס זה נשתמש לרוב באופציה ‪ 1‬עבור מערכים חד ממדיים‪,‬‬


‫אופציה ‪ 2‬עבור מחרוזות )נראה בהמשך( ואופציה ‪ 3‬עבור‬
‫אורך השורות במערכים דו‪-‬ממדיים‪.‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪31‬‬


‫מערך דו מימדי ופונקציות‬

‫)(‪int main‬‬
‫{‬
‫‪int a[1][1] = {7}; /* {{7}} */‬‬
‫;)‪change_a (a‬‬
‫;)]‪printf(“%d\n”, a[0][0‬‬
‫;‪return 0‬‬
‫}‬

‫)]‪void change_a(int a[][1‬‬


‫{‬
‫;‪a[0][0] = 3‬‬
‫‪/* a[0] = 3; is an Error */‬‬
‫;‪return‬‬
‫}‬

‫כמו במערך חד מימדי‪ ,‬המערך בתוך הפונקציה הוא זה שמחוץ לפונקציה‪.‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪32‬‬


‫מערך דו מימדי ופונקציות‬

#define N 3 ‫סכימת אברי מערך דו מימדי‬


#define M 2

int main()
{
int a[N][M] = {{2,3}, {3,4}, {5,6}};
printf(“%d\n”, sum(a));
return 0;
N ‫מיותר )ומטעה( לשים‬
}

int sum (int a[N][M])


{
int i, j, val = 0
for (i = 0; i < N; i++)
for (j = 0; j < M; j++)
val += a[i][j];
return val;
}

7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 33


(‫מערך דו מימדי ופונקציות )הדרך הנכונה‬

#define N 3 ‫סכימת אברי מערך דו מימדי‬


#define M 2

int main()
{
int a[N][M] = {{2,3}, {3,4}, {5,6}};
int b[M][M] = {{9,8}, {7,6}};
printf(“%d\n”, sum(a, N));
printf(“%d\n”, sum(b, M)); ‫מספר השורות עשוי להשתנות‬
return 0;
} int sum (int a[][M], int rows)
{
int i, j, val = 0
for (i = 0; i < rows; i++)
for (j = 0; j < M; j++)
val += a[i][j];
return val;
}

7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 34


‫ מציאת הגדולים מהממוצע‬:‫דוגמה‬
.‫ סדרת משכורות‬:‫קלט‬
#include <stdio.h> ‫ ממוצע איברי הסדרה‬:‫פלט‬
#define EMPLOYEES_NUM 10
.‫ומספר האיברים שמעליו‬
int main()
{ double salaries[EMPLOYEES_NUM], sum = 0.0, average;
int i, above_average;
for (i = 0; i < EMPLOYEES_NUM; ++i) {
scanf("%lf", &salaries[i]); /* if input fails? */
}
for (i = 0; i < EMPLOYEES_NUM; ++i) { ‫לכל קטע קוד‬
sum += salaries[i]; ‫משימה אחת ברורה‬
}
average = sum / EMPLOYEES_NUM;
for (i = 0, above_average = 0; i < EMPLOYEES_NUM; ++i) {
above_average += (salaries[i] > average); /* add 0 or 1 */
}
printf("The average is: %.2f\n", average);
printf("There are %d salaries above the average\n",
above_average);
return 0;
}
7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 35
‫ הגדולים מהממוצע – עם פונקציות‬:‫דוגמה‬
#include <stdio.h>
#define NUM 10
‫מיותר ומטעה‬

void ArrayRead (double a[NUM], int len); ‫ללא שמות‬


double ArraySum (double [], int);
int ArrayCountAbove(double a[], int len, double val);

int main(void) ‫המועדף‬ .‫ סדרת משכורות‬:‫קלט‬ •


{ double salaries[NUM], average; ‫ ממוצע איברי הסדרה‬:‫פלט‬ •
int above_average ; .‫ומספר המשכורות שמעליו‬
ArrayRead(salaries, NUM); // Input is tested inside!
average = ArraySum(salaries, NUM) / NUM;
above_average = ArrayCountAbove(salaries, NUM, average);
printf("The average is: %f\n", average);
printf("%d salaries are above average\n", above_average );
return 0;
}

7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 36


(‫)המשך‬ ‫ הגדולים מהממוצע‬:‫דוגמה‬
void ArrayRead(double a[], int len)
{ int i;
for (i = 0; i < len; ++i)
if (scanf("%lf", &a[i]) < 1) exit(1);
return;
}
double ArraySum(double a[], int n)
{ double sum = 0.0;
int i;
for (i = 0; i < n; ++i)
sum += a[i];
return sum;
}
int ArrayCountAbove(double a[], int n, double val)
{ int above = 0, i;
for (i = 0; i < n; ++i)
above += (a[i] > val);
return above;
}
7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 37
‫מחלקות זיכרון‬

‫• הזיכרון להקצאת המשתנים מקורו בארבעה סוגים‪:‬‬


‫– זיכרון סטטי – קטע בזיכרון התוכנית המוקצה לאותם משתנים למשך כל‬
‫חיי התוכנית‪.‬‬
‫– זיכרון אוטומטי – קטע ברשומת הפעלה שבתוך מחסנית הקריאות‬
‫המוקצה לאותם משתנים )או פרמטרים( למשך חיי הקריאה לפונקציה‪.‬‬
‫– אוגרים – יחידות זיכרון קטנות בתוך המעבד עצמו אשר לצורך הקצאת‬
‫המשתנים ניתן לראותן כ"הרחבה" של רשומת ההפעלה של פונקציה‪.‬‬
‫– זיכרון דינאמי – קטע בזיכרון התוכנית המוקצה ע"פ בקשה מפורשת עד‬
‫שהוא משוחרר ע"פ בקשה מפורשת או עד לסיום התוכנית‪.‬‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪38‬‬


‫הסוגים העיקריים של משתנים בתוכנית‬

‫משתנים סטטיים‬
‫משתנה אוטומטי‬
‫משתנה גלובלי‬ ‫משתנה חיצוני‬ ‫משתנה מקומי‬

‫;‪int p = 1‬‬ ‫;‪static int p = 1‬‬ ‫;‪static int p = 1‬‬ ‫;‪int p = 1‬‬ ‫תחביר הגדרה‬

‫מחוץ לבלוק‬ ‫מחוץ לבלוק‬ ‫בתוך בלוק‬ ‫בתוך בלוק‬ ‫מקום ההגדרה‬

‫כל התוכנית‬ ‫כל הקובץ‬ ‫הבלוק‬ ‫הבלוק‬ ‫תחום הכרה‬

‫כל חיי התוכנית‬ ‫כל חיי התוכנית‬ ‫כל חיי התוכנית‬ ‫חיי הבלוק‬ ‫זמן חיים‬

‫ביטוי קבוע‬ ‫ביטוי קבוע‬ ‫ביטוי קבוע‬ ‫ביטוי כלשהו‬ ‫סוג אתחול‬

‫‪0‬‬ ‫‪0‬‬ ‫‪0‬‬ ‫לא‪-‬מוגדר )זבל(‬ ‫ערך מחדל‬

‫לפני תח' התוכנית‬ ‫לפני תחילת התוכנית‬ ‫לפני תחילת התוכנית‬ ‫כניסה לבלוק‬ ‫זמן אתחול‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪39‬‬


‫הערות נוספות‬
‫• משתנה אוטומטי‪:‬‬
‫– שאינו מאותחל במפורש מכיל ערך לא‪-‬מוגדר )או "זבל"( כל עוד לא הושם בו‬
‫במפורש ערך מוגדר‪.‬‬
‫– המאותחל במפורש – ערכו מאותחל בכל כניסה לבלוק מחדש‪.‬‬
‫– איננו שומר את ערכו בין קריאה לקריאה‪.‬‬
‫• משתנה סטטי מקומי‪:‬‬
‫– שאינו מאותחל במפורש – מאותחל ל‪.0-‬‬
‫– שומר את ערכו בין קריאה לקריאה‪.‬‬
‫• מותר להגדיר‪/‬להצהיר משתנים סטטיים חיצוניים וגלובליים כמה פעמים‪:‬‬
‫– כל ההגדרות חייבות להיות תואמות מבחינת הטיפוס והאתחול‪.‬‬
‫– מותר לרשום את ערך האתחול רק בחלק מההגדרות‪.‬‬
‫– אם אף הגדרה איננה מכילה אתחול – המשתנה מאותחל ל‪.0-‬‬
‫• רצוי‪:‬‬
‫– למקם משתנים בתוך הבלוקים אליהם הם "שייכים" באופן טבעי‪,‬‬
‫– להעביר ערכם של משתנים מיד ליד באמצעות פרמטרים וערך מוחזר‪,‬‬
‫– למעט ככל הניתן בשימוש במשתנים חיצוניים וגלובליים‪.‬‬
‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪40‬‬
‫דוגמה לשימוש במשתנה סטטי מקומי‬

‫• תכנית השולטת על מערכת חומרה שבה‬


‫ישנה נורה שניתן להחליף את מצבה‪.‬‬
‫• הפונקציה המחליפה את מצב הנורה‪:‬‬

‫)(‪void toggle_light‬‬
‫{‬
‫;‪static int light_status = OFF‬‬

‫{ )‪if (light_status == OFF‬‬


‫;)(‪turn_light_on‬‬
‫;‪light_status = ON‬‬
‫{ ‪} else‬‬
‫;)(‪turn_light_off‬‬
‫;‪light_status = OFF‬‬
‫}‬
‫}‬

‫הרצאה ‪7‬‬ ‫מבוא למחשב בשפת ‪ .C‬כל הזכויות שמורות ©‬ ‫‪41‬‬


‫דוגמה לשימוש במשתנה סטטי גלובלי‬
:‫• תכנית השולטת על מערכת חומרה שבה ישנה נורה שניתן‬
static int light_status = OFF; ,‫– להדליק‬
void turn_light_on()
{ if (light_status == ON) return;
,‫– לכבות‬
light_status = ON; .‫– להחליף מצב‬
. . .
}
void turn_light_off()
{ if (light_status == OFF) return;
light_status = OFF; ‫בקורס זה נעדיף‬
. . . ‫לא להשתמש‬
}
void toggle_light()
.‫במשתנים מסוג זה‬
{ if (light_status == OFF) {
turn_light_on();
} else {
turn_light_off();
}
}

7 ‫הרצאה‬ © ‫ כל הזכויות שמורות‬.C ‫מבוא למחשב בשפת‬ 42

You might also like