Transcript Document

‫פונקציות‬
‫קרן כליף‬
‫ביחידה זו נלמד‪:‬‬
‫‪ ‬מהי פונקציה‪ ,‬פרמטרים וערך מוחזר‬
‫‪ ‬כיצד נראה הזיכרון‬
‫‪ ‬ספריות של פונקציות‬
‫‪ ‬מערכים כפרמטר לפונקציה‬
‫‪ ‬יצירת מספרים אקראיים‬
‫‪ ‬פונקציות מ‪math.h -‬‬
‫‪ ‬תהליך הפיכת תוכנית ב‪ C++ -‬לשפת מכונה‬
‫‪ ‬סוגי משתנים וטווח הכרתם‪ :‬לוקאליים‪ ,‬גלובליים‪ ,‬סטטיים‬
‫‪2‬‬
‫‪© Keren Kalif‬‬
‫מהי פונקציה‬
‫‪‬‬
‫פונקציה היא אוסף הוראות לביצוע שמממשות רעיון‬
‫משותף‬
‫‪‬‬
‫למשל ‪ sizeof‬היא פונקציה‬
‫‪‬‬
‫גם ה‪ main -‬שאנחנו כותבים הוא פונקציה‬
‫‪ ‬עבורנו פונקציות אלו הן "קופסא שחורה" שקיבלנו‬
‫מספריה מסוימת‪ ,‬ואנו רק יודעים מה הן עושות‪ ,‬אבל לא‬
‫יודעים איך‪ ,‬כלומר מהו אוסף הפעולות המבוצעות‬
‫‪3‬‬
‫‪© Keren Kalif‬‬
‫בשיעור זה נלמד לכתוב פונקציות בעצמנו!‬
‫דוגמא לשימוש בפונקציה‬
#include <iostream>
using namespace std;
void main()
{
int num, size;
‫הערך המוחזר מהפונקציה‬
‫שם הפונקציה‬
‫ כל נתון נקרא ארגומנט‬.‫הנתונים המועברים‬
size = sizeof(num);
cout << "the variable 'num' uses “ << size << “ bytes\n”;
}
4
© Keren Kalif
log ‫ חישוב חלוקת‬:‫דוגמא לשכפול קוד‬
const int BASIS =10;
void main()
{
int num1=100, num2=1000, res;
int log1, log2;
‫ רק‬,‫רצף הפעולות לחישוב לוג זהה‬
‫כל פעם ביצענו את הפעולות על‬
!‫ שכפול קוד‬ ‫ערכים שונים‬
‫ צריך‬main -‫מי שקורא את ה‬
...‫להבין מה תפקידה של כל לולאה‬
res = BASIS;
for (log1=1 ; res < num1 ; log1++)
log10100 ‫חישוב‬
res *= BASIS;
cout << "log(" << num1 << ") = " << log1 << endl;
res = BASIS;
for (log2=1 ; res < num2 ; log2++)
log101000 ‫חישוב‬
res *= BASIS;
cout << "log(" << num2 << ") = " << log2 << endl;
cout << "log(" << num1 << "/" << num2 << ") = "
<< log1-log2 << endl;
{
5
© Keren Kalif
‫הדוגמא ללא שכפול קוד‪ ,‬קריאות‬
‫טובה ומודלריות‬
‫>‪#include <iostream‬‬
‫שם הפונקציה‪.‬‬
‫יעיד מה היא עושה ;‪using namespace std‬‬
‫טיפוס המידע‬
‫שהפונקציה מחזירה‬
‫;‪const int BASIS=10‬‬
‫)‪int log(int num‬‬
‫הנתון שהפונקציה צריכה לקבל‬
‫}‬
‫;‪int res = BASIS, i‬‬
‫עם השימוש בפונקציה הקוד קצר‬
‫)‪for (i=1 ; res < num ; i++‬‬
‫יותר וקריא יותר!‬
‫;‪res *= BASIS‬‬
‫ה‪ main -‬כתוב ב"ראשי פרקים"‬
‫איך הפונקציה‬
‫;‪return i‬‬
‫וניתן להבין בקלות מה הוא מבצע‬
‫מבצעת את העבודה‬
‫{‬
‫הערך שהפונקציה מחזירה‬
‫שימוש בערך שהפונקציה מחזירה‬
‫)(‪void main‬‬
‫}‬
‫קריאה לפונקציה שיודעת לחשב ‪,log‬‬
‫;‪int num1=100, num2=1000, res‬‬
‫פעם עם ‪ num1‬ופעם עם ‪num2‬‬
‫;)‪res = log(num1) - log(num2‬‬
‫;‪cout << "log(“ << num1 << “/” << num2 << “) = “ << res << endl‬‬
‫‪6‬‬
‫{‬
‫‪© Keren Kalif‬‬
‫פונקציות ‪ -‬מוטיבציה‬
‫‪ ‬מודולריות‪ :‬חלוקת התוכנית לקטעי קוד יותר‬
‫קטנים‪ ,‬כאשר כל קטע עושה משהו נקודתי‬
‫‪ ‬יתרונות‪:‬‬
‫‪ ‬קוד קריא יותר‬
‫‪ ‬יותר קל לבדוק את התוכנית‬
‫‪ ‬חלוקת עבודה‬
‫‪ ‬שימוש חוזר בקוד‬
‫‪ ‬חיסכון בבדיקות‬
‫‪ ‬פחות שגיאות‬
‫‪7‬‬
‫‪© Keren Kalif‬‬
!‫תזכרו‬
http://www.thedistractionnetwork.com/images/programming-fail-007.jpg
8
© Keren Kalif
‫החלקים בשילוב פונקציה שלנו בקוד‬
‫‪.1‬‬
‫הצהרה על הפונקציה‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪.2‬‬
‫‪.3‬‬
‫‪9‬‬
‫‪© Keren Kalif‬‬
‫כאשר כותבים פונקציה ראשית צריך להחליט מה הפונקציה‬
‫עושה‬
‫לאחר שהחלטנו על ה"מה"‪ ,‬יש להחליט אילו נתונים הפונקציה‬
‫צריכה לקבל כדי לבצע את העבודה‬
‫לבסוף נגדיר מה הטיפוס שהפונקציה מחזירה לנו‬
‫שימוש בפונקציה‬
‫מימוש הפונקציה‬
‫איך כותבים הצהרה של פונקציה‬
‫‪ ‬דוגמא‪ :‬פונקציה המחשבת חזקה‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫מה הפונקציה עושה‪ :‬הפונקציה מחשבת חזקה של שני מספרים‬
‫מה הפונקציה מקבלת‪ 2 :‬מספרים‪ ,‬הראשון בסיס והשני מעריך‬
‫מה הפונקציה מחזירה‪ :‬את תוצאת חישוב הבסיס במעריך‬
‫לבחור שם משמעותי לפונקציה‬
‫‪int base‬‬
‫‪int exponent‬‬
‫‪‬‬
‫תחביר‪:‬‬
‫‪‬‬
‫ובאופן כללי‪:‬‬
‫‪baseexponent‬‬
‫‪int‬‬
‫)‪int power(int base, int exponent‬‬
‫‪10‬‬
‫‪© Keren Kalif‬‬
‫)>‪<function_name> (<parameters list‬‬
‫>‪<returned value type‬‬
‫בחירת שם לפונקציה‬
‫‪‬‬
‫שם הפונקציה צריך להעיד מה היא עושה‬
‫‪‬‬
‫‪‬‬
‫למשל‪power, printf :‬‬
‫מבחינת סגנון כתיבה‪ ,‬ההמלצה היא‪:‬‬
‫‪‬‬
‫‪‬‬
‫שם הפונקציה יתחיל באות קטנה‬
‫אם יש יותר ממילה אחת בשם הפונקציה כל מילה נוספת‬
‫תתחיל באות גדולה‬
‫‪‬‬
‫‪‬‬
‫‪11‬‬
‫‪© Keren Kalif‬‬
‫למשל‪calcRectangleArea :‬‬
‫ניתן לאמץ סגנון אחר‪ ,‬אבל הקפידו להיות עקביים בסגנון לאורך‬
‫כל התוכנית‪ .‬כנ"ל גם לגבי שמות משתנים‬
‫הצהרת פונקציה ‪ -‬הערך המוחזר‬
‫(‪)1‬‬
‫‪ ‬הערך המוחזר יכול להיות כל אחד מהטיפוסים שאנחנו‬
‫מכירים‬
‫‪12‬‬
‫‪© Keren Kalif‬‬
‫‪‬‬
‫למשל‪ ,‬פונקציה המקבלת אורך צלע ריבוע ומחזירה את שטחו‬
‫;)‪int calcSquareArea(int length‬‬
‫‪‬‬
‫למשל‪ ,‬פונקציה המקבלת מספר‪ ,‬ומחזירה את השורש שלו‬
‫;)‪double sqrt(int num‬‬
‫הצהרת פונקציה ‪ -‬הערך המוחזר ‪void‬‬
‫‪ ‬אם הפונקציה אינה מחזירה ערך‪ ,‬נרשום כערך המוחזר‬
‫‪void‬‬
‫‪‬‬
‫החזרה = ניתן לאכסן ערך במשתנה כלשהו‬
‫שימו לב‪ :‬הדפסה למסך אינה החזרה של ערך!‬
‫‪‬‬
‫למשל‪ ,‬פונקציה המדפיסה למסך‬
‫)‪void printRectangle(int base‬‬
‫‪‬‬
‫‪13‬‬
‫‪© Keren Kalif‬‬
‫הצהרת פונקציה –‬
‫‪‬‬
‫הפרמטרים המועברים‬
‫רשימת הנתונים אשר הפונקציה מקבלת מופרדת ע"י‬
‫פסיקים‬
‫‪ ‬דוגמא‪ :‬פונקציה המקבלת אורך ורוחב של מלבן ומחזירה את‬
‫שטחו‬
‫;)‪int calcRectangleArea(int height, int width‬‬
‫‪ ‬למשל‪ ,‬פונקציה המקבלת מספר ‪ n‬ומחזירה !‪n‬‬
‫;)‪int factorial(int num‬‬
‫‪‬‬
‫אם פונקציה אינה מקבלת נתונים‪ ,‬רשימת הפרמטרים‬
‫תהייה ריקה‬
‫‪‬‬
‫‪14‬‬
‫‪© Keren Kalif‬‬
‫למשל‪ ,‬פונקציה הקוראת מהמשתמש את גילו ומחזירה אותו‬
‫;)(‪int readAge‬‬
‫מימוש פונקציה‬
‫‪‬‬
‫דוגמא למימוש פונקציה המקבלת בסיס ומעריך ‪,‬ומחזירה‬
‫את החזקה‬
‫)‪int power(int a, int b‬‬
‫{‬
‫;‪int result=1‬‬
‫)‪for (int i=0 ; i < b ; i++‬‬
‫;‪result *= a‬‬
‫;‪return result‬‬
‫הפקודה ‪ return‬מציינת הפסקת ביצוע‬
‫הפקודות בפונקציה והחזרת ערך המשתנה‬
‫}‬
‫‪15‬‬
‫‪© Keren Kalif‬‬
‫פונקציה שהערך המוחזר שלה הוא ‪void‬‬
‫אינה חייבת להכיל את הפקודה ‪.return‬‬
‫באם מופיעה הפקודה ‪ return‬בפונקציה‬
‫המחזירה ‪ ,void‬רצף הפעולות יפסק מיד‪.‬‬
!‫אבל זהירות‬
http://funnypictures4u.files.wordpress.com/2013/06/people_v
s_programmers.jpg
16
© Keren Kalif
‫תוכנית שלמה עם פונקציה‬
‫הפונקציה מוכרת ממקום הגדרתה ומטה‪,‬‬
‫ולכן הצהרת הפונקציה תופיע לפני השימוש!‬
‫הצהרה‪+‬מימוש‬
‫יש להעביר לפונקציה משתנים‬
‫כמספר הפרמטרים שהיא דורשת‪,‬‬
‫מאותו הסוג ובאותו הסדר!‬
‫נשים לב כי בקריאה לפונקציה מעבירים רק‬
‫את שמות המשתנים‪ ,‬ולא את הטיפוסים‪,‬‬
‫בניגוד להצהרת ומימוש הפונקציה!‬
‫איחסון הערך המוחזר מהפונקציה‬
‫) ‪int power(int a , int b‬‬
‫{‬
‫;‪int i, result=1‬‬
‫)‪for (i=0 ; i < b ; i++‬‬
‫;‪result *= a‬‬
‫;‪return result‬‬
‫}‬
‫)(‪void main‬‬
‫{‬
‫;‪int base, exponent, result‬‬
‫;“ ‪cout << "Please enter base and exponent:‬‬
‫;‪cin >> base >> exponent‬‬
‫שימוש‬
‫;) ‪result = power( base , exponent‬‬
‫;‪cout << base << “^” << exponent << “=“ << result << endl‬‬
‫}‬
‫‪17‬‬
‫‪© Keren Kalif‬‬
max ‫ תוכנית שלמה עם הפונקציה‬:‫דוגמא‬
int max (int x, int y)
{
if (x > y)
return x;
else
return y;
}
void main()
{
int num1, num2, maximum;
‫בשורה זו מוערך הביטוי מימין וערכו נכנס‬
cout << "Enter 2 numbers: ";
. maximum ‫לתוך המשתנה‬
cin >> num1 >> num2;
!‫לא חייבים לאחסן את הערך המוחזר‬
maximum = max(num1, num2);
cout << "The max is “ << maximum << endl;
}
18
© Keren Kalif
..‫ואפשר גם כך‬
int max (int x, int y)
{
if (x > y)
return x;
return y; // witout the ‘else’…
}
void main()
{
int num1, num2, maximum;
cout << "Enter 2 numbers: “;
cin >> num1 >> num2;
maximum = max(num1, num2);
cout << "The max is “ << maximum << endl;
}
19
© Keren Kalif
‫מחסנית הקריאות‬
‫‪ ‬כאשר קוראים לפונקציה‪ ,‬למעשה "קופצים" לאוסף‬
‫הפקודות שלה‪ ,‬ושומרים את מקום החזרה‬
‫‪‬‬
‫כאשר נתקלים בפקודה ‪ return‬בתוך גוף הפונקציה‪,‬‬
‫חוזרים לביצוע הפעולות מהמקום בו קראנו לה‬
‫‪‬‬
‫בפונקציה שהטיפוס המוחזר שלה הוא ‪ void‬יתכן ולא תהייה‬
‫הפקודה ‪return‬‬
‫‪‬‬
‫‪20‬‬
‫‪© Keren Kalif‬‬
‫במקרה זה‪ ,‬לאחר סיום הפונקציה‪ ,‬חוזרים לביצוע הפעולות מהמקום בו‬
‫קראנו לפונקציה‬
‫ הרצה‬- ‫תוכנית שלמה עם פונקציה‬
#include <iostream>
using namespace std;
int power(int a, int b)
{
int i, result=1;
for (i=0 ; i < b ; i++)
result *= a;
return result;
}
void main()
{
int base, exponent, result;
cout << "Please enter base and exponent: “;
cin >> base >> exponent;
}
power
main
( line 4)
‫מחסנית הקריאות‬
result = power(base, exponent);
cout << base << “^” << exponent << “=“ << result << endl;
21
© Keren Kalif
‫סדר ביצוע פעולות‬
#include <iostream>
using namespace std;
int power(int a, int b)
{
cout << "debuging.. in 'power'\n”;
int i, result=1;
for (i=0 ; i < b ; i++)
result *= a;
return result;
}
void main()
‫קודם תבוצע הפונקציה‬
{
cout << "2^3=“ << power(2, 3) << endl;
}
22
© Keren Kalif
)2( ‫סדר ביצוע פעולות‬
#include <iostream>
using namespace std;
int power(int a, int b)
{
cout << "debuging.. in 'power': a=“ << a << “ b=“ << b < endl;
int i, result=1;
for (i=0 ; i < b ; i++)
result *= a;
return result;
}
void main()
‫ביצוע הפונקציות מתבצע מימין לשמאל‬
{
cout << "2^4=" << power(2, 4)
<< " 2^5=" << power(2, 5) << endl;
23
© Keren Kalif
}
‫כיצד נראה הזיכרון‬
‫‪‬‬
‫ראינו את הזיכרון של ה‪main -‬‬
‫‪‬‬
‫תזכורת‪ :‬ה‪ main -‬הוא פונקציה‬
‫‪ ‬לכל פונקציה יש שטח נפרד בזיכרון ה‪ stack -‬בו מוגדרים‬
‫משתניה‬
‫‪ ‬פונקציה יכולה לגשת לשטח הזיכרון שלה בלבד‪ ,‬ולכן אינה‬
‫יכולה לגשת למשתנים שהוגדרו בפונקציות אחרות‬
‫‪‬‬
‫‪‬‬
‫הפרמטרים המועברים לפונקציה הם גם משתנים של הפונקציה‬
‫הפרמטרים המועברים לפונקציה הם העתקים של המשתנים‬
‫שנשלחו‪ ,‬ולכן העברת פרמטרים כזו נקראת ‪by value‬‬
‫‪ ‬כאשר יוצאים מפונקציה‪ ,‬התאים במחסנית שבהם נשמרו‬
‫ערכיה נמחקים‪ ,‬ולכן קריאה נוספת לפונקציה תייצר אותם‬
‫מחדש‬
‫‪24‬‬
‫‪© Keren Kalif‬‬
‫ הזיכרון‬- ‫תוכנית שלמה עם פונקציה‬
#include <iostream>
using namespace std;
int power(int a, int b)
{
int i, result=1;
for (i=0 ; i < b ; i++)
result *= a;
return result;
}
void main()
{
int base, exponent, result;
int:base
???
2
1000
int: exponent
???
3
1004
int: result
???
8
1008
main -‫הזיכרון של ה‬
‫ וזה‬main -‫ שב‬result
‫המשתנה‬
2
2000
,‫שבפונקציה אינם אותו משתנה‬
int: b
2004
!‫יהיו עם שם זהה‬3‫הכרח שהם‬
‫ואין‬
int: i
???
…
2008
int: a
int: result
cout << "Please enter base and exponent: “;
cin >> base >> exponent;
}
???
1
8
2012
power ‫הזיכרון של‬
power
main
( line 4)
‫מחסנית הקריאות‬
result = power(base, exponent);
cout << base << “^” << exponent << “=“ << result << endl;
25
© Keren Kalif
‫הפרדה בין הצהרות למימוש‬
#include <iostream>
using namespace std;
// prototypes
int power(int,
power(int base,
int); int exponent);
‫בשורת ההצהרה לא חייבים‬
‫לציין את שמות המשתנים‬
void main()
{
int base, exponent, result;
cout << "Please enter base and exponent: “;
cin >> base >> exponent;
result = power(base, exponent);
; ‫ ובסיום כל הצהרה‬,main -‫• את ההצהרות נרשום לפני ה‬
cout << base << “^” << exponent << “=“ << result << endl;
)‫טיפוס‬-‫ (אב‬prototype ‫• חלק זה נקרא‬
}
main -‫• את המימושים נכתוב מתחת ל‬
int power(int base, int exponent)
‫• ההפרדה היא מאחר ומעניין אותנו איזה פונקציות יש‬
{
‫ ופחות איך הן מבצעות את העבודה‬,‫בתוכנית‬
int i, result=1;
‫• חתימת הפונקציה בהגדרה בראש הקובץ ובמימוש חייבות‬
for (i=0 ; i < exponent ; i++)
result *= base;
‫להיות זהות‬
return result;
26
© Keren Kalif
}
‫תכנון ‪TOP-DOWN‬‬
‫‪ ‬כאשר ניגשים לכתוב תוכנית‪ ,‬יש לחשוב מהי הבעיה‬
‫הגדולה שעלינו לפתור ונחלק אותה לחלקים‬
‫‪ ‬לכל חלק "קשה" נתייחס כאל "קופסא שחורה" (שתמומש‬
‫בהמשך)‬
‫‪ ‬כאשר נממש כל חלק "קשה" אנו מתמודדים עם בעיה‬
‫יותר קטנה שננסה גם אותה לפרק לבעיות יותר קטנות‪,‬‬
‫עד אשר נגדיר בעיות מספיק קטנות‬
‫‪27‬‬
‫‪© Keren Kalif‬‬
TOP-DOWN ‫תכנות‬
‫ ציור משולש‬:‫דוגמא‬
#include <iostream>
using namespace std;
// prototypes
void printLine(int, char);
void printTriangle(int, char);
int:
void main()
{
int base;
char ch;
length
char: ch
int: i
2
1
3
2000
‘$’
2004
???
1
2
3
4
2005
printLine ‫הזיכרון של‬
cout << "Enter base and char: “;
cin >> base >> ch;
printTriangle(base, ch);
}
int:
int:
base
char: ch
base
???
3
3000
char: ch
???
‘$’
3004
int: i
main -‫הזיכרון של ה‬
void printLine(int length, char c)
{
int i;
for ( i=1 ; i<=length ; i++ )
cout << c;
cout << endl;
}
void printTriangle(int base, char ch)
{
int i;
print Line
for( i=1 ; i<=base ; i++ )
printLine(i, ch); print Triangle
3}
1000
‘$’
1004
???
1
2
3
4
1005
printTriangle ‫הזיכרון של‬
(line 3)
main
( line 5)
‫מחסנית הקריאות‬
28
© Keren Kalif
‫דוגמאת סיכום‪ :‬חישוב מרחק בין ‪2‬‬
‫נקודות‬
‫‪‬‬
‫הנוסחא לחישוב מרחק בין ‪ 2‬הנקודות )‪ (x1,y1‬ו‪:(x2,y2) -‬‬
‫‪2‬‬
‫‪‬‬
‫נרצה לכתוב פונקציה המקבלת ‪ 4‬קואורדינטות המייצגות ‪ 2‬נקודות‪,‬‬
‫ומחזירה את המרחק בניהן‪:‬‬
‫‪x1‬‬
‫‪y1‬‬
‫‪x2‬‬
‫‪y2‬‬
‫‪‬‬
‫)‪d  (x 2  x 1)  (y 2  y1‬‬
‫‪2‬‬
‫‪int‬‬
‫‪int‬‬
‫‪int‬‬
‫‪int‬‬
‫‪distance‬‬
‫‪float dist‬‬
‫לכן ההצהרה של הפונקציה תראה כך‪:‬‬
‫;)‪float distance(int x1, int y1, int x2, int y2‬‬
‫‪29‬‬
‫‪© Keren Kalif‬‬
‫דוגמא‪ :‬חישוב מרחק בין ‪ 2‬נקודות‬
‫‪‬‬
‫(‪)2‬‬
‫הנוסחא לחישוב מרחק בין ‪ 2‬הנקודות )‪ (x1,y1‬ו‪:(x2,y2) -‬‬
‫‪d  (x 2  x 1) 2  (y 2  y1) 2‬‬
‫‪‬‬
‫ניתן לראות שכדי לחשב מרחק בין שתי נקודות צריך לדעת לחשב‬
‫חזקה ושורש‪ ,‬לכן נגדיר ונשתמש ב‪ 2 -‬פונקציות עזר‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪30‬‬
‫‪© Keren Kalif‬‬
‫‪ sqrt‬המקבלת מספר ‪ float‬ומחזירה את השורש שלו‪ .‬פונקציה זו כבר‬
‫קיימת בספריה ‪math.h‬‬
‫‪ power‬שאותה נכתוב בעצמנו לצורך התרגול (למרות שהיא גם קיימת ב‪-‬‬
‫‪)math.h‬‬
float distance(int x1, int y1, int x2, int y2)
#include <iostream>
‫ לספריה‬include
‫{ שנשתמש הצהרות‬
using namespace std; ‫בפונקציהעלממנה‬
‫פונקציות שנממש‬
float xDiffPower, yDiffPower;
#include <math.h>
float result;
int power(int base, int exponent);
xDiffPower = power(x2-x1, 2);
float distance(int x1, int y1, int x2, int y2);
yDiffPower = power(y2-y1, 2);
void main()
result = sqrt(xDiffPower+yDiffPower);
{
return result;
int x1, y1, x2, y2;
???
0
int: x1
1000
}
float dist;
int: y1
???
0
1004
cout << "Please enter 2 points (x1, y1, x2, y2): “;
‫ יש מרחב‬power -‫גם ל‬
cin >> x1 >> y1 >> x2 >> y2;
‫ אך לא נראה אותו פה‬,‫זיכרון‬
‫מימוש הפונקציות שלנו‬
)+
;
‫מקום‬
‫קריאהחוסר‬
‫מפאת‬
‫לפונקציות‬
dist = distance(x1, y1, x2, y2);
}
cout << "The distance is “ << dist << endl;
int power(int base, int exponent)
{
int i, result=1;
for (i=0 ; i < exponent ; i++)
result *= base;
return result;
}
31
© Keren Kalif
power
sqrt
distance
(line 5)
3)
4)
main
( line 5)
‫מחסנית הקריאות‬
int: x2
???
4
1008
int: y2
???
4
1012
float: dist
5.6
???
1016
1016
main -‫הזיכרון של ה‬
int: x1
0
2000
int: y1
0
2004
int: x2
4
2008
int: y2
4
2012
float: xDiffPower
16
???
2016
float: yDiffPower
16
???
2024
float: result
5.6
???
2032
distance ‫הזיכרון של‬
‫פונקציות ספריה‬
‫‪ ‬כל תוכנית בשפת תכנות עילית‪ ,‬ובפרט שפת ‪,C++‬‬
‫מורכבת מאוסף של פונקציות‬
‫‪ ‬קיימות פונקציות ספריה בשפה אותן ניתן להכליל בתוכנית‬
‫ולקרוא להן בכל מקום בו נרצה ולהשתמש בהן כמה‬
‫פעמים שנרצה‬
‫‪‬‬
‫למשל כאשר עושים‪:‬‬
‫>‪#include <iostream‬‬
‫ניתן להשתמש בפונקציות המוגדרות בספריה זו בכל מיני‬
‫מקומות בקוד (למשל ‪)cin, cout‬‬
‫‪‬‬
‫גם אנו יכולים לכתוב פונקציות או ספריות של פונקציות‪,‬‬
‫שאותן ניתן להכליל ולהשתמש בהן כרצוננו‬
‫‪32‬‬
‫‪© Keren Kalif‬‬
‫יצירת קובץ ספריה‬
‫‪‬‬
‫‪‬‬
‫עד כה השתמשנו בפונקציות שקיבלנו משפת ‪C‬‬
‫כל פונקציה כזו נכתבה בספריה שאליה ביצענו ‪include‬‬
‫‪‬‬
‫‪‬‬
‫למשל כדי להשתמש בפונקציות של מחרוזות‪ ,‬עשינו‬
‫>‪#include <string.h‬‬
‫כדי לייצר ספריה משלנו נייצר ‪ 2‬קבצים חדשים‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪ <file_name>.h‬הוא קובץ אשר יכלול את ה‪-‬‬
‫‪ prototypes‬של הפונקציות‪ ,‬ואליו אח"כ נעשה ‪include‬‬
‫מהקובץ שירצה להשתמש בפונקציות המוגדרות בו‬
‫‪ <file_name>.cpp‬הוא קובץ שיכיל ‪ include‬לקובץ ה‪-‬‬
‫‪ header‬התואם ויממש את הפונקציות המוגדרות בו‬
‫‪ include ‬לספריה שכתבנו יהיו בתוך "" (גרשיים) ולא‬
‫בתוך <>‬
‫‪33‬‬
‫‪© Keren Kalif‬‬
)1( ‫דוגמא – ספריה המטפלת בתווים‬
character.h ‫הקובץ‬
// prototypes
int isSmallLetter(char);
int isCapitalLetter(char);
int isAlpha(char);
int isDigit(char);
34
© Keren Kalif
)2( ‫דוגמא – ספריה המטפלת בתווים‬
#include "charachter.h"
character.cpp ‫הקובץ‬
int isSmallLetter(char ch)
{
return (ch >= 'a' && ch <= 'z');
}
int isCapitalLetter(char ch)
{
return (ch >= 'A' && ch <= 'Z');
}
int isAlpha(char ch)
{
return (isSmallLetter(ch) || isCapitalLetter(ch));
}
int isDigit(char ch)
{
return (ch >= '0' && ch <= '9');
}
35
© Keren Kalif
)3( ‫דוגמא – ספריה המטפלת בתווים‬
#include <iostream>
using namespace std;
#include "charachter.h"
main.cpp ‫הקובץ‬
void main()
{
char ch=0;
printf("Please enter a charachter: ");
ch = getchar();
printf("Is '%c' a digit? %d\n", ch, isDigit(ch));
printf("Is '%c' a small letter? %d\n", ch, isSmallLetter(ch));
printf("Is '%c' a capital letter? %d\n", ch, isCapitalLetter(ch));
printf("Is '%c' a letter? %d\n", ch, isAlpha(ch));
36
}
© Keren Kalif
‫ספריות שלנו ‪ -‬סיכום‬
‫‪ ‬ניתן לכתוב את כל הקוד בקובץ אחד‪ ,‬אבל אם אנחנו‬
‫כותבים קוד כללי‪ ,‬שיתכן ויהיה שימושי גם במקומות‬
‫אחרים‪ ,‬נעדיף לחלק את הקוד לקובץ ספריה נפרד‬
‫‪ ‬מי שירצה להשתמש בספריה שלנו‪ ,‬יתעניין מה יש לה‬
‫להציע‪ ,‬ולא איך היא מבצעת את הפעולות‪ ,‬וכך הוא יוכל‬
‫להסתכל בקובץ ‪ header‬בלבד בו יש ריכוז של כל‬
‫הפונקציות‬
‫‪ ‬מכירת הספריה ללקוח תסתכם בקובץ ה‪ h -‬ובקובץ‬
‫בינארי שהוא תוצר של הקומפילציה‪ ,‬וכך לא נחשוף את‬
‫ה"איך"‬
‫‪37‬‬
‫‪© Keren Kalif‬‬
‫דוגמא איך לא כותבים פונקציה‬
#include <iostream>
using namespace std;
void power()
{
int base, exponent, i, result=1;
cout << "Please enter base and exponent: ";
cin >> base >> exponent;
for (i=0 ; i < exponent ; i++)
result *= base;
cout << "The result is “ << result << endl;
}
void main()
{
power();
}
38
© Keren Kalif
‫מדוע לא לכתוב קוד כמו הדוגמא הקודמת?‬
‫‪‬‬
‫ניתן לעקוף חלקית את הרעיון של העברת ארגומנטים והחזרת ערך‬
‫מפונקציות ע"י כך שפונקציה תבקש מהמשתמש את הנתונים‬
‫ותדפיס בסוף את התוצאה‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫לא נעשה זאת!‬
‫נמנע מלקלוט קלט בתוך פונקציה‪ ,‬אלא אם זה יעוד הפונקציה‬
‫הסיבה‪ :‬יתכן ופונקציה אחרת צריכה להשתמש בנתון המתקבל או‬
‫להעביר נתון‪ ,‬ולא ניתן לצפות מי ישתמש בפונקציה שלנו‬
‫‪‬‬
‫‪‬‬
‫למשל‪ ,‬כמו בדוגמא הקודמת‪ ,‬הפונקציה המחשבת חזקה לא קיבלה‬
‫פרמטרים ולא החזירה את התוצאה‬
‫דוגמא‪ :‬כמו בפונקציה ‪ distance‬הקוראת ל‪power -‬‬
‫כדי לבדוק פונקציה‪ ,‬נקלוט את הנתונים ב‪ main -‬ונעבירם‬
‫לפונקציה‪ .‬את התוצאה נדפיס ב‪.main -‬‬
‫‪39‬‬
‫‪© Keren Kalif‬‬
‫העברת פרמטר לפונקציה – ‪by reference‬‬
‫‪‬‬
‫ראינו‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫כאשר מעבירים משתנה לפונקציה עותק שלו מועבר למחסנית‬
‫של הפונקציה (העברה ‪)by value‬‬
‫אם בפונקציה משנים את הפרמטר זה לא משפיע על המשתנה‬
‫המקורי‬
‫מה יקרה כאשר נרצה שהפונקציה תשנה דווקא את‬
‫הפרמטרים שקיבלה?‬
‫‪‬‬
‫‪40‬‬
‫‪© Keren Kalif‬‬
‫נעביר הפניה למשתנה המקורי‪...‬‬
1.
2.
3.
4.
void changeTo4byVal(int x)
{
x = 4;
{
int: x
5.
6.
7.
8.
9.
by ref ‫שליחת פרמטר‬
by val ‫לעומת‬
void changeTo4byRef(int& x)
}
x = 4;
}
10.
void main()
12. }
13.
int num = 10;
11.
10
4
2000
chagneTo4ByVal -‫הזכרון של ה‬
int: x
???
chagneTo4ByRef -‫הזכרון של ה‬
int: num
10
4
1000
main -‫הזכרון של ה‬
14.
cout << "orig num = " << num << endl;
15.
16.
changeTo4byVal(num);
cout << "after changeTo4byVal: num = " << num << endl;
17.
18.
19.
changeTo4byRef(num);
cout << "after changeTo4byRef: num = " << num << endl;
20.
21.
22.
{
41
© Keren Kalif
‫העברת פרמטר לפונקציה – ‪by reference‬‬
‫‪‬‬
‫בהגדרת הפרמטרים שהשיטה מקבלת מציינים ליד‬
‫הפרמטר &‬
‫‪‬‬
‫‪‬‬
‫זהו למעשה מתן שם נוסף לארגומנט המקורי שנשלח הנגיש‬
‫מתוך הפונקציה‬
‫אין הבדל סינטקטי בשליחת המשתנה בעבור משתנה‬
‫שעובר ‪ by value‬או ‪by reference‬‬
‫‪42‬‬
‫‪© Keren Kalif‬‬
swap :‫ – דוגמא‬by value ‫העברה‬
‫ מספרים ומחליפה בינהם‬2 ‫ פונקציה המקבלת‬:‫המטרה‬
void swap(int a, int b)
{
cout << "In function, before swap: a=“ << a << “ b=“ << b << endl;
int temp = b;
b = a;
a = temp;
cout << "In function, after swap: a=“ << a << “ b=“ << b << endl;
}
3
2
int: a
2000
...‫הפונקציה לא באמת החליפה בין הערכים‬
void main()
{
int num1=2, num2=3;
}
int: b
int: temp
2
3
2004
3
???
2008
swap ‫הזיכרון של‬
cout << "In main, before swap: num1=“ << num1 << “, num2=“ << num2;
swap(num1, num2);
cout << "In main, after swap: num1=“ << num1 << “, num2=“ << num2;
int: num1
2
???
1000
3
???
1004
main -‫הזיכרון של ה‬
int: num2
43
© Keren Kalif
swap ‫הדוגמא‬
void swap(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
{
void main()
}
int x=3, y=5;
int: a
???
int: b
???
int: temp
???
3
2000
swap -‫הזכרון של ה‬
int: x
???
3
5
1000
int: y
???
5
3
1004
main -‫הזכרון של ה‬
cout << "Before swap: x = " << x << ", y = " << y << "\n";
swap(x, y);
cout << "After swap: x = " << x << ", y = " << y << "\n";
{
44
© Keren Kalif
‫החזרת יותר מערך יחיד מפונקציה‬
‫‪‬‬
‫‪‬‬
‫למשל נרצה לכתוב פונקציה המקבלת מערך‪ ,‬וצריכה‬
‫להחזיר מהו המספר המקסימאלי ומה המינימאלי‬
‫הפתרון‪ :‬העברה לפונקציה פרמטר שעתיד להחזיק את‬
‫התוצאה הסופית‬
‫‪45‬‬
‫‪© Keren Kalif‬‬
‫מציאת מינימום ומקסימום‬
void minMax(int arr[], int size, int& min, int& max)
{
int i;
min = max = arr[0];
for ) i=1 ; i < size ; i++ (
}
if (min > arr[i])
min = arr[i];
if (max < arr[i])
max = arr[i];
}
}
‫ כאשר מעבירים‬:‫תזכורת‬
‫מערך לפונקציה מתייחסים‬
!‫ ולא לעותק שלו‬,‫למערך המקורי‬
void main()
{
int arr[] = {5,2,8};
int minimum, maximum;
}
int: size
3
2000
int: min
int: max
int:i
???
1
2
3
2004
minMax ‫הזיכרון של‬
int[]: arr
int:minimum
5
1000
2
1004
8
1008
5
2
???
1012
5
???
8
1016
main -‫הזיכרון של ה‬
int:maximum
minMax(arr, sizeof(arr)/sizeof(arr[0]), minimum, maximum);
cout << "max is “ << maximum << “ and min is “ << minimum << endl;
46
© Keren Kalif
‫הארות‬
‫‪‬‬
‫ניתן היה להעביר לפונקציה רק את ‪ min‬או רק את ‪max‬‬
‫ואת הערך השני להחזיר ע"י ‪return‬‬
‫‪‬‬
‫‪‬‬
‫אבל‪ :‬כאשר הפונקציה מחזירה יותר מערך אחד והם כולם בעלי‬
‫אותה תפקיד‪ ,‬נעדיף שכולם יוחזרו ‪(by ref‬אחידות בסגנון)‬
‫אם מעבירים לפונקציה פרמטר ‪ by ref‬שהפונקציה‬
‫מתבססת על ערכו‪ ,‬חובה לאתחלו בפונקציה‪ ,‬ולא‬
‫להתבסס על אתחול (שאולי) בוצע בפונקציה שקראה‬
‫‪47‬‬
‫‪© Keren Kalif‬‬
by ref ‫אתחול פרמטר המועבר‬
void countPositive(int arr[], int size, int& count)
{
int i;
count = 0; // it is our responsibility to
// initialize the value!
for (i=0 ; i < size ; i++ )
{
if (arr[i] > 0)
count++;
}
}
int: size
3
2000
0
1
2
???
3
2008
2004
int: count
int: i
countPositive ‫הזיכרון של‬
int[]: arr
-4
1000
2
1004
-8
1008
void main()
1
0
? 1012
int: numOfPositive
{
main -‫הזיכרון של ה‬
int arr[] = {-4,2,-8};
int numOfPositive; // we can’t assume that who wrote
// the main initialized that variable!!
}
countPositive(arr, sizeof(arr)/sizeof(arr[0]), numOfPositive);
cout << "There are “ << numOfPositive << “ positive numbers in the array\n”;
48
© Keren Kalif
by value ‫תזכורת למשמעות של העברה‬
void incNumber(int x)
{
cout << "In function: number before: “ << x << endl;
x++;
cout << "In function: number after: “ << x << endl;
{
int:
void main()
}
int num = 3;
3
4
x
2000
incNumber ‫הזיכרון של‬
int:
num
??
3
1000
main ‫הזיכרון של‬
cout << "In main: number before function: “ << num << endl;
incNumber(num);
cout << "In main: number after function: “ << num << endl;
{
49
© Keren Kalif
‫ דוגמא‬- ‫מערכים כפרמטר לפונקציה‬
void incArray(int arr[], int size)
{
for (int i=0 ; i < size ; i++)
arr[i]++;
}
void printArray(int arr[], int size)
{
for (int i=0 ; i < size ; i++)
cout << arr[i] << “ “;
cout << endl;
}
void main()
{
int arr[] = {4,3,8};
int size = sizeof(arr)/sizeof(arr[0]);
cout << "Orig array: “;
printArray(arr, size);
incArray(arr, size);
}
cout << "Array after increment: “;
printArray(arr, size);
50
© Keren Kalif
‫מערכים כפרמטר לפונקציה‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫ראינו כי מערך מתנהג באופן שונה מאשר משתנה מטיפוס‬
‫בסיסי כאשר מעבירים אותו לפונקציה‬
‫כאשר מעבירים מערך לפונקציה‪ ,‬לא מועבר עותק של‬
‫המערך )‪ ,(by value‬אלא מועברת רק כתובת ההתחלה‬
‫של המערך‬
‫לכן כאשר מעבירים מערך לפונקציה ומשנים אותו‪ ,‬השינוי‬
‫משפיע על המערך המקורי‪ ,‬ולא על עותק – בניגוד לכל‬
‫משתנה אחר שאנחנו מכירים!‬
‫נסביר לעומק כאשר נלמד את השיעור על מצביעים‬
‫‪51‬‬
‫‪© Keren Kalif‬‬
‫העברת מספר האיברים במערך‬
‫כפרמטר לפונקציה‬
‫‪ ‬ראינו כי כאשר שלחנו מערך לפונקציה העברנו גם‬
‫את מספר האיברים שבו‪ ,‬ולא התבססנו על ערך‬
‫קבוע‬
‫‪ ‬זאת כדי שהפונקציה תהיה מספיק כללית על‪-‬מנת‬
‫שתבצע את העבודה על מערכים בגדלים שונים‬
‫‪52‬‬
‫‪© Keren Kalif‬‬
‫דוגמא איך פונקציה המקבלת מערך‬
‫לא צריכה להיות‬
‫שימו לב‪ :‬הקבוע מוגדר באותיות גדולות‪ .‬דגש בהמשך‪...‬‬
‫;‪const int SIZE = 3‬‬
‫הפונקציה יודעת לטפל אך ורק במערכים בגודל הקבוע‬
‫)][‪void printArray(int arr‬‬
‫‪ ,SIZE‬ולא תעשה את המתבקש עבור מערכים בגודל שונה‬
‫{‬
‫)‪for (int i=0 ; i < SIZE ; i++‬‬
‫;" " << ]‪cout << arr[i‬‬
‫;‪cout << endl‬‬
‫}‬
‫)(‪void main‬‬
‫{‬
‫;}‪int arr1[SIZE] = {1,2,3}, arr2[5]={10,20,30,40,50‬‬
‫;" ‪cout << "arr1:‬‬
‫;)‪printArray(arr1‬‬
‫;" ‪cout << "arr2:‬‬
‫;)‪printArray(arr2‬‬
‫‪53‬‬
‫‪© Keren Kalif‬‬
‫}‬
‫דוגמא איך פונקציה המקבלת מערך‬
‫כן צריכה להיות‬
const int SIZE = 3;
void printArray(int arr[], int size)
{
for (int i=0 ; i < size ; i++)
cout << arr[i] << " ";
cout << endl;
}
‫ שונים‬SIZE ‫ והקבוע‬size ‫ הפרמטר‬:‫שימו לב‬
.)case sensitive ‫(הקומפיילר הוא‬
‫הפונקציה מקבלת כפרמטר נוסף את כמות האיברים‬
‫ ולכן יודעת לטפל במערך בכל גודל‬,‫שעליה להדפיס‬
void main()
{
int arr1[SIZE] = {1,2,3}, arr2[5]={10,20,30,40,50};
cout << "arr1: ";
printArray(arr1, SIZE);
cout << "arr2: ";
printArray(arr2, 5);
}
54
© Keren Kalif
– ‫העברת מטריצה לפונקציה‬
‫הפונקציה מספיק כללית כדי לבצע את‬
‫דוגמא‬
const int COLS = 5; ‫העבודה עבור מטריצה עם כל מספר שורות‬
void setMatrix(int mat[][COLS], int rows)
}
for (int i=0 ; i < rows ; i++)
}
for (int j=0 ; j < COLS ; j++)
mat[i][j] = i*10+j;
{
{
void printMatrix(int mat[][COLS], int rows)
}
for (int i=0 ; i < rows ; i++)
}
for (int j=0 ; j < COLS ; j++)
cout << mat[i][j] << “\t“;
cout << endl;
{
{
void main()
}
int mat1[3][COLS];
int mat2[4][COLS];
setMatrix(mat1, 3);
setMatrix(mat2, 4);
cout << "Matrix 1:\n";
printMatrix(mat1, 3);
cout << "Matrix 2:\n”;
printMatrix(mat2, 4);
{
55
© Keren Kalif
‫העברת מטריצה לפונקציה –‬
‫דוגמא (פלט)‬
‫‪56‬‬
‫‪© Keren Kalif‬‬
‫העברת מטריצה לפונקציה‬
‫‪ ‬גם כאשר מעבירים מטריצה לפונקציה‪ ,‬עוברת כתובת‬
‫ההתחלה בלבד‪ ,‬ולכן שינוי המטריצה בפונקציה משנה את‬
‫המטריצה המקורית‬
‫‪ ‬כאשר מעבירים מטריצה לפונקציה‪ ,‬ניתן לציין רק את‬
‫כמות העמודות ולהשאיר את הסוגריים של השורות ריקים‬
‫(במקום להעביר כפרמטר את מספר השורות)‬
‫‪‬‬
‫‪‬‬
‫‪57‬‬
‫‪© Keren Kalif‬‬
‫נרצה להעביר את כמות השורות כדי שהפונקציה תהיה מספיק‬
‫כללית לכל מטריצה עם אותו מספר עמודות‬
‫בהמשך נראה שאפשר גם להעביר מטריצה שמספר העמודות‬
‫בה שונה‬
‫יצירת מספרים אקראיים‬
‫ אשר מגרילה מספר‬rand() ‫ בשפה קיימת הפונקציה‬
MAX_RAND ‫ לקבוע‬0 ‫ טווח הערכים שיוגרלו הינו בין‬
stdlib.h ‫ קבוע זה מוגדר בספריה‬
#include <iostream>
using namespace std;
#include <stdlib.h> // for ‘RAND_MAX’
void main()
{
cout <<"rand gives value between 0-" << RAND_MAX << ":\n";
for (int i=0 ; i < 5 ; i++)
cout << rand() << " ";
cout << endl;
}
58
© Keren Kalif
‫יצירת מספרים אקראיים (‪)2‬‬
‫‪ rand ‬פועלת עפ"י אלגוריתם קבוע המתבסס על מספר‬
‫התחלתי כלשהו (נקרא ‪ ,)seed number‬ולכן תמיד‬
‫בכל הרצה תחזיר ערכים זהים‬
‫‪ ‬כדי ש‪ rand -‬באמת תחזיר מספרים אקראיים‪ ,‬כלומר‬
‫תתבסס על מספר התחלתי שונה בכל פעם‪ ,‬יש להפעיל‬
‫את הפונקציה ‪ srand‬עם ערך התחלתי המייצג את‬
‫הזמן הנוכחי (והוא אכן יחודי בכל הרצה)‬
‫‪59‬‬
‫‪© Keren Kalif‬‬
)3( ‫יצירת מספרים אקראיים‬
#include <iostream>
using namespace std;
#include <stdlib.h> // for ‘RAND_MAX’
#include <time.h> // for 'time'
‫ מחזירה מספר המייצג את הזמן הנוכחי‬time(NULL)
-)1.1.1970 -‫(מספר השניות שעברו מאז ה‬
void main()
{
srand ( time(NULL) ); // initialize random seed
cout <<"rand gives value between 0-" << RAND_MAX << ":\n";
for (int i=0 ; i < 5 ; i++)
cout << rand() << " ";
cout << endl;
}
60
© Keren Kalif
‫יצירת מספרים אקראיים בטווח מסוים‬
#include <iostream>
using namespace std;
#include <stdlib.h> // for ‘RAND_MAX’
#include <time.h> // for 'time'
void main()
{
srand ( time(NULL) ); // initialize random seed
cout <<"rand gives value between 1-6 \n";
for (int i=0 ; i < 5 ; i++)
cout << rand()%6+1 << " ";
‫ ומאחר‬,0-5 ‫ תחזיר לנו ערכים בין‬%6 ‫פעולת‬
..1 ‫ הוספנו‬1-6 ‫ואנחנו רוצים בטווח בין‬
cout << endl;
}
61
© Keren Kalif
)2( ‫מסוים‬
‫יצירת מספרים אקראיים בטווח‬
#include <iostream>
using namespace std;
#include <stdlib.h> // for ‘RAND_MAX’
#include <time.h> // for 'time'
void main()
{
srand ( time(NULL) ); // initialize random seed
cout <<"rand gives value between -10 to 10 \n";
for (int i=0 ; i < 5 ; i++)
‫ תחזיר לנו ערכים בין‬%21 ‫פעולת‬
cout << rand()%21-10 << " ";
‫ ומאחר ואנחנו רוצים בטווח‬,0-20
10 ‫ החסרנו‬10 -‫ ל‬-10 ‫בין‬
cout << endl;
62
}
© Keren Kalif
)-; ‫ טובה‬rand ‫ זו לא פונקציית‬,‫אגב‬
https://sslimgs.xkcd.com/comics/random_number.png
63
© Keren Kalif
‫פונקציות נפוצות שיש בספריה ‪math.h‬‬
‫‪ :cos, sin ‬מחשבות סינוס וקוסינוס בהתאמה (לשים לב‬
‫שמקבלות זוית ברדיאנים‪ ,‬ולא במעלות)‬
‫‪ :ceil ‬מעגלת מספר כלפי מעלה‪ ,‬מחזירה ‪float‬‬
‫‪ :floor ‬מעגלת מספר כלפי מטה‪ ,‬מחזירה ‪float‬‬
‫‪ :abs ‬נותנת ערך מוחלט של מספר ועושה ‪ floor‬לשלם‬
‫‪ :pow ‬מחשבת חזקה‬
‫‪ :sqrt ‬מחשבת שורש‬
‫‪64‬‬
‫‪© Keren Kalif‬‬
‫דוגמאות חישוב‬
#include <iostream>
using namespace std;
#include <math.h>
void main()
{
cout << "cos(90)=" << cos(90.0) << endl;
cout << "sin(90)=" << sin(90.0) << endl;
cout << "ceil(4.2)=" << ceil(4.2) << endl;
cout << "floor(4.2)=" << floor(4.2) << endl;
cout << "abs(-5.8)=" << abs(-5.8) << endl;
cout << "abs(5)=" << abs(5) << endl;
cout << "pow(2,3)=" << pow(2.0, 3) << endl;
cout << "pow(8,0.5)=" << pow(8.0, 0.5) << endl;
cout << "sqrt(16)=" << sqrt(16.0) << endl;
cout << "sqrt(18)=" << sqrt(18.0) << endl;
}
65
© Keren Kalif
‫תהליך הפיכת תוכנית ‪ cpp‬לשפת מכונה‬
‫‪Disk‬‬
‫‪Editor‬‬
‫‪Disk‬‬
‫‪Preprocessor‬‬
‫‪Disk‬‬
‫‪Disk‬‬
‫‪Compiler‬‬
‫‪Linker‬‬
‫התוכנית נכתבת בעורך טקסטואלי ונכתבת לדיסק‬
‫קדם המעבד עובר על הקוד ומבצע החלפות נחוצות‬
‫(כל הפקודות המתחילות ב‪ ,# -‬כמו ‪include‬‬
‫עבור כל קובץ ‪ ,cpp‬הקומפילר יוצר קובץ ‪ obj‬בשפת מכונה ושומר‬
‫אותו על הדיסק‪ .‬בשלב זה רק נבדקת תקינות הסינטקס בפונקציות‪.‬‬
‫ה‪ linker -‬קושר את קובץ ה‪ obj -‬עם הספריות‪ ,‬ויוצר קובץ ‪ exe‬המוכן‬
‫להרצה ונשמר בדיסק‪ .‬כלומר‪ ,‬מקשר בין קריאה לפונקציה‪ ,‬למימוש‬
‫שלה‪.‬‬
‫‪Primary Memory‬‬
‫‪Loader‬‬
‫‪..‬‬
‫‪..‬‬
‫‪..‬‬
‫‪Disk‬‬
‫‪Primary Memory‬‬
‫‪CPU‬‬
‫‪..‬‬
‫‪..‬‬
‫‪..‬‬
‫בעת ההרצה‪ ,‬טוענים את התוכנית מהדיסק לזיכרון הראשי‬
‫עם הרצת התוכנית‪ ,‬ה‪ cpu -‬עובר על כל פקודה ומריץ אותה‬
‫‪66‬‬
‫‪© Keren Kalif‬‬
‫דוגמא לשגיאת קומפילציה‬
#include <iostream>
using namespace std;
void foo(int x)
}
cout >> “x=“ >> x >> endl;
{
void main()
:‫נקבל את שגיאת הקומפילציה הבאה‬
{
error C2065: 'i' : undeclared identifier
for (i=0 ; i < 5 ; i++)
}
:goo ‫הקומפיילר רק נותן אזהרה שהוא לא מוצא את‬
cout << i;
warning C4013: 'goo' undefined; assuming
goo(i);
extern returning int
{
}
‫ הקומפיילר אינו ממשיך לתהליך לינקר‬,‫במידה ויש שגיאות קומפילציה‬
67
© Keren Kalif
‫דוגמא לשגיאת לינקר‬
#include <iostream>
using namespace std;
void foo(int x)
}
cout >> “x=“ >> x >> endl;
{
void main()
{
for (int i=0 ; i < 5 ; i++)
}
cout << i;
goo(i);
{
}
‫תוכנית זו מתקמפלת (אין שגיאות סינטקטיות בתוך‬
,‫הפונקציות) ולכן הקומפיילר עובר לתהליך הלינקר‬
:‫ומוציא את השגיאה הבאה‬
error LNK2019: unresolved external symbol
_goo referenced in function _main
68
© Keren Kalif
!‫נכון שזה נכון?? אז די‬
http://programmingpalace.files.wordpress.com/2011/12/smokingwarningforsoftwareengineers.j
pg
69
© Keren Kalif
‫סוגי משתנים‬
‫‪ ‬משתנים מקומיים‬
‫‪ ‬משתנים סטטיים‬
‫‪ ‬משתנים גלובליים‬
‫‪70‬‬
‫‪© Keren Kalif‬‬
‫משתנים מקומיים‬
‫‪ ‬עד כה ראינו שכל הקוד שלנו מורכב מפונקציות‬
‫‪ ‬המשתנים שבתוך כל פונקציה (גם אלו שהועברו‬
‫כפרמטרים) נקראים משתנים מקומיים (לוקאליים) וטווח‬
‫ההכרה שלהם הוא רק בפונקציה בה הם מוגדרים‬
‫‪ ‬ערכו של משתנה לוקאלי נשמר כל עוד אנחנו בפונקציה‪,‬‬
‫והוא נמחק ביציאה ממנה‬
‫‪ ‬משתנה לוקאלי מאוחסן במחסנית של הפונקציה‬
‫‪ ‬בכל קריאה חדשה לפונקציה המשתנים מאותחלים מחדש‬
‫ונמחקים בסיום ביצוע הפונקציה‬
‫‪ ‬ערכו זבל במידה ולא אותחל‬
‫‪71‬‬
‫‪© Keren Kalif‬‬
‫משתנה סטטי הוא משתנה שערכו‬
‫נשמר בין הקריאות השונות לפונקציה‬
#include <iostream>
using namespace std;
‫משתנים סטטיים‬
‫כדי להגדיר משתנה סטטי‬
‫נשתמש במילת המפתח‬
‫ לפני טיפוס המשתנה‬static
int counter()
‫משתנה סטטי מאותחל רק‬
{
‫בקריאה הראשונה לפונקציה‬
counter ‫הזיכרון של‬
static int count = 0;
count++;
return count;
3
2
1
0
counter::count = ?
}
main -‫הזיכרון של ה‬
data segment -‫זיכרון ה‬
void main()
{
cout << "'counter' was called “ << counter() << “ times\n” ;
cout << "'counter' was called “ << counter() << “ times\n” ;
counter
cout << "'counter' was called “ << counter() << “ times\n” ;
main
}
( line 3)
1)
2)
‫מחסנית הקריאות‬
72
© Keren Kalif
‫משתנים סטטיים‬
‫‪‬‬
‫בדומה למשתנים מקומיים‪ ,‬המשתנים הסטטיים מוכרים‬
‫אך ורק בתוך הפונקציה אשר בה הם הוגדרו‬
‫‪‬‬
‫משך חייהם מרגע תחילת הריצה של התוכנית ועד סיום‬
‫ריצת התוכנית‬
‫‪ ‬מאחר ושטח הזיכרון של כל פונקציה נמחק עם היציאה‬
‫ממנה‪ ,‬משתנים סטטיים נשמרים באזור זיכרון אחר‬
‫הנקרא ‪ ,data-segment‬הקיים לאורך כל חיי התוכנית‪,‬‬
‫והם משתחררים רק עם היציאה מהתוכנית‬
‫‪73‬‬
‫‪© Keren Kalif‬‬
‫משתנה גלובלי מוגדר בראש התוכנית‬
)‫(אינו משויך לאף פונקציה‬
‫כל הפונקציות שכתובות‬
‫בקובץ בו הוגדר יכולות‬
‫לגשת אליו ולשנות את ערכו‬
‫משתנים גלובליים‬
int global = 3;
void incGlobal()
{
global++;
cout << "In function: global=“ << global << endl;
}
data -‫זיכרון ה‬
11
3
4
incGlobal ‫הזיכרון של‬
global = 10
segment
void main()
{
cout << "At first, global=“ << global << endl;
main -‫הזיכרון של ה‬
incGlobal();
cout <<"After function (1), global=“ << global << endl;
global = 10;
incGlobal
cout << "In main after change global, global=“ << global << endl;
incGlobal();
main
cout << "After function (2), global=“ << global << endl;
‫מחסנית הקריאות‬
}
74
© Keren Kalif
‫משתנים גלובליים‬
‫‪ ‬במידה ולא אותחל ערכו ‪ ,0‬ולא זבל‬
‫‪‬‬
‫משתנה גלובלי קיים לאורך כל חיי התוכנית‬
‫‪‬‬
‫השימוש בו הוא בעייתי ולכן נשמר למצבים מיוחדים בלבד‬
‫‪‬‬
‫כל אחד יכול לשנות אותו זה פוגע ברעיון של הסתרת המידע‬
‫ושכל פונקציה מסתמכת רק על נתונים שהם פנימיים לה‬
‫‪ ‬מאחר ואינו משויך לאף פונקציה‪ ,‬הוא אינו נמצא על‬
‫המחסנית‪ ,‬אלא על חלקת הזיכרון המשותפת לכל‬
‫הפונקציות ה‪data-segment -‬‬
‫‪75‬‬
‫‪© Keren Kalif‬‬
‫השוואה בין סוגי המשתנים השונים‬
‫משתנה מקומי (רגיל) משתנה גלובלי‬
‫משתנה סטטי‬
‫היכן מוגדר‬
‫בתוך הפונקציה‬
‫מחוץ לפונקציות‬
‫בתוך הפונקציה‬
‫טווח‬
‫ההכרה‬
‫בפונקציה בה הוא‬
‫מוגדר‬
‫מנקודת הגדרתו ומטה‬
‫בפונקציה בה הוא‬
‫מוגדר‬
‫מי יכול‬
‫לגשת אליו‬
‫רק הפונקציה בה הוא‬
‫מוגדר‬
‫כל מקום בקוד הנמצא מתחת רק הפונקציה בה‬
‫הוא מוגדר‬
‫להגדרתו‬
‫מתי‬
‫מאותחל‬
‫בכל פעם כשנכנסים‬
‫לפונקציה‬
‫‪76‬‬
‫‪© Keren Kalif‬‬
‫בתחילת ריצת התוכנית‬
‫רק בפעם הראשונה‬
‫שמגיעים אליו‬
‫השוואה בין סוגי המשתנים השונים‬
‫משתנה מקומי (רגיל) משתנה גלובלי‬
‫משתנה סטטי‬
‫היכן מוגדר‬
‫בתוך הפונקציה‬
‫מחוץ לפונקציות‬
‫בתוך הפונקציה‬
‫טווח‬
‫ההכרה‬
‫בפונקציה בה הוא‬
‫מוגדר‬
‫מנקודת הגדרתו ומטה‬
‫בפונקציה בה הוא‬
‫מוגדר‬
‫מי יכול‬
‫לגשת אליו‬
‫רק הפונקציה בה הוא‬
‫מוגדר‬
‫כל מקום בקוד הנמצא מתחת רק הפונקציה בה‬
‫הוא מוגדר‬
‫להגדרתו‬
‫מתי‬
‫מאותחל‬
‫בכל פעם כשנכנסים‬
‫לפונקציה‬
‫‪77‬‬
‫‪© Keren Kalif‬‬
‫בתחילת ריצת התוכנית‬
‫רק בפעם הראשונה‬
‫שמגיעים אליו‬
‫ביחידה זו למדנו‪:‬‬
‫‪ ‬מהי פונקציה‪ ,‬פרמטרים וערך מוחזר‬
‫‪ ‬כיצד נראה הזיכרון‬
‫‪ ‬ספריות של פונקציות‬
‫‪ ‬מערכים כפרמטר לפונקציה‬
‫‪ ‬יצירת מספרים אקראיים‬
‫‪ ‬פונקציות מ‪math.h -‬‬
‫‪ ‬תהליך הפיכת תוכנית ב‪ C++ -‬לשפת מכונה‬
‫‪ ‬סוגי משתנים וטווח הכרתם‪ :‬לוקאליים‪ ,‬גלובליים‪ ,‬סטטיים‬
‫‪78‬‬
‫‪© Keren Kalif‬‬
‫תרגיל ‪:1‬‬
‫‪ ‬כתוב פונקציה המקבלת ‪ 2‬מספרים‬
‫‪ ‬במידה ושני המספרים בעלי אורך זהה‪ ,‬היא תחזיר בכמה‬
‫מיקומים זהים במספרים יש ספרה זהה‬
‫‪ ‬דוגמאות‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫עבור ‪ 1594‬ו‪ 7599 -‬יוחזר ‪ 2‬מאחר וספרת העשרות וספרת‬
‫המאות זהה בשני המספרים‬
‫עבור ‪ 9287‬ו‪ 9487 -‬יוחזר ‪ ,3‬כי גם ספרות האחדות‪ ,‬עשרות‬
‫ואלפים זהות בשני המספרים‬
‫במידה והמספרים שונים באורכם הפונקציה תחזיר ‪-1‬‬
‫‪79‬‬
‫‪© Keren Kalif‬‬
‫תרגיל ‪:2‬‬
‫‪ ‬כתוב פונקציה המקבלת ‪ 2‬מחרוזות‪ .‬הפוקנציה תחזיר ‪1‬‬
‫האם המחרוזת השניה היא סופה של המחרוזת הראשונה‬
‫ו‪ 0 -‬אחרת‬
‫‪ ‬דוגמאות‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪80‬‬
‫‪© Keren Kalif‬‬
‫עבור ‪ abcde‬ו‪ cde -‬יוחזר ‪1‬‬
‫עבור ‪ abcde‬ו‪ cfe -‬יוחזר ‪0‬‬
‫עבור ‪ abcde‬ו‪ zabcde -‬יוחזר ‪0‬‬
‫תרגיל ‪:3‬‬
‫‪‬‬
‫‪‬‬
‫כתוב פונקציה המקבלת מערך של מספרים‪ ,‬גודלו וספרה‬
‫הפונקציה תחזיר כמה ערכים במערך מכילים את הספרה‬
‫‪ ‬דוגמאות‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪81‬‬
‫‪© Keren Kalif‬‬
‫עבור המערך ‪ 123, 456, 817, 991‬והספרה ‪ 1‬יוחזר ‪,3‬‬
‫מאחר והספרה ‪ 1‬מופיעה ב‪ 3 -‬איברים במערך‬
‫עבור המערך ‪ 9988, 458, 751, 654, 888‬והספרה ‪ 8‬יוחזר‬
‫‪ 3‬מאחר והספרה ‪ 8‬מופיעה ב‪ 3 -‬מספרים‪ .‬שימו לב שאין לספור‬
‫את הספרה פעמיים עבור מספר מסויים!‬
‫תרגיל ‪:4‬‬
‫‪‬‬
‫כתוב פונקציה המקבלת מטריצה של מספרים ואת כמות‬
‫השורות במטריצה‪ .‬הפונקציה תחזיר ‪ 1‬אם ערכי מסגרת‬
‫המטריצה שווים ל‪ 1 -‬ו‪ 0 -‬אחרת‪ .‬אין חשיבות לערכם של‬
‫האיברים שאינם על המסגרת‬
‫‪ ‬דוגמא‪:‬‬
‫‪82‬‬
‫‪© Keren Kalif‬‬