Download presentation
Presentation is loading. Please wait.
1
שיעור שישי: מערכים ופונקציות
מבוא כללי למדעי המחשב שיעור שישי: מערכים ופונקציות 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9
2
שאלה רוצים לקלוט 30 מספרים שלמים, ולאחר מכן להדפיס אותם בסדר הפוך מהסדר שקלטנו אותם (כלומר מהסוף להתחלה). כיצד נוכל לעשות זאת? לדוגמא, אם היה מדובר על קלט של 3 מספרים: 200 100 70 הפלט היה צריך להיות:
3
זו תהיה תוכנית מאוד מסורבלת (ומה נעשה אם יהיו 1000 מספרים?)
פתרון אפשרי נשתמש ב-30 משתנים, משתנה לכל ערך מהקלט: a1,…,a30. נקלוט את כל אחד מ-30 המספרים לתוך משתנה (הראשון ל- a1, השני ל- a2, וכן הלאה) נדפיס את ערכי שלושים המשתנים בסדר הפוך מסדר הקלט: קודם a30, אחר-כך a29, וכך הלאה עד a1. אז בתוכנית יהיו 30 משתנים, 30 פקודות קלט, 30 פקודות הדפסה,... זו תהיה תוכנית מאוד מסורבלת (ומה נעשה אם יהיו 1000 מספרים?)
4
מה היינו רוצים יש פה הרבה פעולות זהות על משתנים מאותו סוג – היינו רוצים לכתוב אותן פעם אחת בלבד: להגדיר בבת-אחת 30 משתנים מאותו סוג עבור הקלט. לכתוב פעם אחת את פקודת הקלט, ושהמחשב יבצע אותה 30 פעם, כל פעם למשתנה המתאים. לכתוב פעם אחת את הדפסת הפלט, ושזה יבוצע עבור כל אחד מהמשתנים בסדר המתאים.
5
הפתרון: מערך שפת C מאפשרת להגדיר בבת-אחת אוסף של משתנים מאותו סוג, ולגשת לכל אחד מהם בעזרת אינדקס (מספר סידורי). אוסף כזה נקרא "מערך". זה מאפשר לעשות לולאה על המספר הסידורי של המשתנה, ולבצע אותו דבר על כל אחד מהמשתנים ("התאים") במערך. . . . . . . . . 99 95 A[0] A[29]
6
איך ניראה הפיתרון התוכנית קולטת 30 מספרים ומדפיסה אותם בסדר הפוך
#include<stdio.h> int main() { int i, input[30]; printf(“Enter the 30 integers:\n”); for (i=0; i<=29; i++) scanf(“%d”, &input[i]); for (i=29; i>=0; i--) printf(“%d”, input[i]); } מגדירים מערך של 30 משתנים מסוג int עוברים בלולאה על המשתנים האלה קלט לתאי המערך, מתא 0 עד תא 29 הדפסה בסדר הפוך, מתא 29 עד תא 0
7
מערכים - נושאים מה זה מערך ולמה הוא משמש הגדרה ואיתחול גישה לתאים
8
שימוש במערכים עד עכשיו הגדרנו כל משתנה בנפרד, ויכולנו לעבוד כך רק עם מספר קטן של משתנים. במקרים רבים צריך לשמור בזיכרון הרבה נתונים מאותו סוג ולעשות על כל אחד מהם אותן פעולות (למשל שכר של כל אחד מהעובדים בחברה, ציונים של כל התלמידים, הפז"מ של כל חייל וכו'). שפת C מאפשרת להגדיר קבוצה של כמה משתנים מאותו סוג בבת אחת. קבוצה כזאת נקראת "מערך". כפי שראינו בדוגמא, בעבודה עם מערכים אפשר לעשות לולאה על המס' הסידורי של המשתנה במערך, וזה מאפשר לכתוב פעם אחת את פעולות הקלט/פלט והחישובים ולבצע אותן באמצעות הלולאה על כל אחד מהמשתנים.
9
מערכים – משמעות והגדרה כאמור, מערך הוא אוסף של משתנים מאותו טיפוס (הטיפוס יכול להיות int, char, float, double או כל טיפוס אחר שנכיר). בהגדרת המערך רושמים את טיפוס המשתנים ואת מספרם. למשל כך נגדיר מערך של 100 משתנים מטיפוס double: double prices[100]; הגדרת מערך מקצה רצף של מקומות בזיכרון שיכילו את ערכי תאי (משתני) המערך. למשל: . . . . . . . . 5 9 A[0] A[9]
10
הכנסת ערך לתא במערך לכל אחד מהתאים במערך יש מספר סידורי (אינדקס), החל מ-0. ההתייחסות אל כל תא היא על-ידי שם המערך והמספר הסידורי של התא, בתוך סוגריים מרובעים. לדוגמא, נגדיר מערך של 10 משתנים מסוג int, ונשים 51 בתא הראשון ו-45 בתא העשירי: int A[10]; A[0]=51; A[9]=45; . . . . . . . . 51 45 A[0] A[9]
11
קריאת ערך מתא במערך כמו שאפשר לשים ערך בתא מסויים, אפשר גם לקבל את הערך שנמצא בתא מסויים. למשל: printf(“%d”, A[0]); i=A[5]; נשים לב: כדי להתייחס לתא במערך צריך תמיד לציין את המספר הסידורי שלו בתוך סוגריים מרובעים. שם המערך ללא סוגריים מרובעים מתייחס לכתובת ההתחלה של המערך בזיכרון, ולא לתא במערך.
12
הדוגמא שראינו התוכנית קולטת 30 מספרים ומדפיסה אותם בסדר הפוך
#include<stdio.h> int main() { int i, input[30]; printf(“Enter the 30 integers:\n”); for (i=0; i<=29; i++) scanf(“%d”, &input[i]); for (i=29; i>=0; i--) printf(“%d”, input[i]); } מגדירים מערך של 30 משתנים מסוג int עוברים בלולאה על המשתנים האלה קלט לתאי המערך, מתא 0 עד תא 29 הדפסה בסדר הפוך, מתא 29 עד תא 0
13
איתחול מערכים בשורת ההגדרה
כמו טיפוסי משתנים אחרים, ניתן לאתחל מערך כשמגדירים אותו. זה נעשה באופן הבא: int A[5]={7,43,15,17,235}; ערכי האיתחול נכנסים לתאי-המערך לפי הסדר (משמאל לימין). אם ניתן ערכים רק לחלק מהתאים, שאר התאים יאותחלו עם הערך 0. int A[5]={9,42}; אז אפשר למשל לאתחל מערך באפסים על-ידי int A[5]={0}; אם לא נציין את מספר תאי המערך, הוא יהיה כמספר ערכי האיתחול (בדוגמא הבאה יהיו לו 6 תאים): int A[]={16,2,19,256,5,1432}; לא ניתן להכניס ערכים למערך באופן הזה אחרי ההגדרה שלו.
14
אין גישה לכל המערך בבת-אחת
אחרי הגדרת המערך אפשר רק להתייחס לכל אחד מהתאים שלו בנפרד. אי-אפשר לשים ערכים בתאי המערך בבת-אחת: למשל אי-אפשר לאפס בבת-אחת את כל התאים, וצריך לעשות זאת ע"י לולאה: for(i=0; i<10; i++) A=0 A[i]=0; אי-אפשר להעתיק מערך למערך אחר ע"י פעולת = גם זה דורש לולאה: for(i=0; i<10; i++) A=B A[i]=B[i]; כך גם לגבי פעולות השוואה, הדפסה, קלט, וכו' (דרושה לולאה).
15
חריגה מגבולות המערך אם נכתוב לתא במערך מעבר לגודל שהוגדר, בסבירות גבוהה יפגעו נתונים של תוכנות אחרות, והתוכנית "תעוף". למשל: int a[10]; a[10]=1; a[-1]=5;
16
נקודה תשומת-לב בהגדרת מערך הגודל שלו חייב להיות מספר קבוע, ולא יכול להיות ערך של משתנה. למשל: int a[10]; int my_size=10; ולא: int a[my_size]; גודל מערך יכול להיות קבוע שמוגדר ב- #define (כי זה מספר שכתוב בתוכנית ולא יכול להשתנות במהלך הריצה). #define SIZE 10 int a[SIZE];
17
מערכים - דוגמא נוספת התוכנית הזו קולטת מספר שלם חיובי ומדפיסה כמה פעמים הופיעה בו כל סיפרה #include<stdio.h> int main() { int i, num, digits[10]={0}; printf(“Enter a positive integer:\n”); scanf(“%d”, &num); while (num!=0) digits[num%10]++; num=num/10; } for (i=0; i<10; i++) printf(“Digit %d appeared %d times\n”, i, digits[i]); i במערך יכיל את מספר ההופעות של הספרה i תא כל תאי המערך מאותחלים לאפס 1 2 1 (מניחים כאן שהקלט תקין) נניח לדוגמא 557 5 55 לולאה שנמשכת כל עוד המספר לא מתאפס מגדילים ב-1 את התא שמתאים לספרת האחדות של המספר, ומחלקים אותו ב-10 מדפיסים כמה פעמים כל ספרה הופיעה
18
נקודה לתשומת-לב: מערך קבוע
כמו כל משתנה, אפשר להגדיר מערך כקבוע (const). const int M[12]={31,28,31,30,31,30,31,31,30,31,30,31}; ערכי המערך הוגדרו כקבועים, ולכן לא ניתן להעביר אותו
19
מערכים רב-מימדיים לפעמים נירצה לייצג מטריצות, או מערכים ממימדים גבוהים יותר. נוכל להגדיר מערכים כאלה למשל על-ידי: int a[10][100]; המשמעות היא שהגדרנו 10 מערכים בגודל 100 של מספרים שלמים. ואז נוכל לגשת לכל תא ע"י שני אינדקסים, למשל: a[5][8]=4;
20
מערכים רב-מימדיים - איתחול
ניתן לתת ערכים התחלתיים גם כשמגדירים מערך רב-מימדי. מתייחסים אליו כאל מערך של מערכים חד-מימדיים, ומעבירים ערכי איתחול לכל אחד מהם. למשל: int my_matrix[3][2]={ {1,0}, {0,1}, {1,1} }; איתחול נפוץ יותר הוא בעזרת לולאות מקוננות (אם יש הרבה תאים לא נירצה לכתוב את ערכי כולם).
21
דוגמא: איפוס מערך דו-מימדי בגודל 100100x
אין צורך לרשום את המימד הראשון בסוגריים המרובעים (כמו במערכים רגילים), אבל חייבים לרשום גודל בסוגריים השניים. int main() { int a[100][100] int i ,j; for(i=0; i<100; i++) for(j=0; j<100; j++) a[ i ][ j ] = 0; return 0; }
22
דוגמא: מה עושה התוכנית הבאה?
#include<stdio.h> int main() { int i ,j, a[11][11]; for(i=1; i<=10; i++) for(j=1; j<=10; j++) a[ i ][ j ] = i*j; printf(“%4d”, a[ i ][ j ] ); printf(“\n”); } מדפיסה את לוח הכפל יודפס: . כך יוקצו 4 תווים להדפסת כל מספר (ואם יש פחות ספרות יהיו משמאל רווחים)
23
מבוא כללי למדעי המחשב פונקציות
24
שאלה כיצד נוכל לחשב את הסכום הבא: = ?
25
פתרון אפשרי #include <stdio.h> int main() { int i; double sum=0, result=1; for(i=1; i<=20; i++) result=result*2; sum +=result; result=1; for(i=1; i<=15; i++) result=result*3; for(i=1; i<=17; i++) result=result*5; printf(“2^20 + 3^15 + 5^17= %g”, sum); return 0; } אם היו יותר מחוברים - היינו צריכים לחזור פעמים נוספות על אותן שורות עם מספרים אחרים זה מאריך את התוכנית פי כמה וכמה, מסרבל אותה, ויוצר פתח לבאגים
26
מה היינו רוצים? היינו רוצים להגדיר פעם אחת פקודה חדשה, שתבצע חישוב של חזקה, ואז להשתמש בפקודה החדשה הזאת כמה פעמים, כל פעם עבור מספרים אחרים. למשל היינו רוצים שנוכל לחשב את התוצאה של על-ידי: sum = power (2, 20) + power (3,15) + power (5,17); שפת C אכן מאפשרת הגדרת פקודות חדשות, כפי שניראה היום. פקודות חדשות כאלה נקראות ב-C "פונקציות".
27
איך זה ניראה ב-C הגדרת הפונקציה מופיעה לפני התוכנית (לפני ה- (main
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; הגדרת הפונקציה מופיעה לפני התוכנית (לפני ה- (main
28
איך זה ניראה ב-C ההגדרה כוללת את שם הפונקציה #include <stdio.h>
double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; ההגדרה כוללת את שם הפונקציה
29
איך זה ניראה ב-C ההגדרה כוללת את שם הפונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; ההגדרה כוללת את שם הפונקציה היא כוללת גם את המשתנים שעליהם הפונקציה מוגדרת (בדומה לפונקציות במתמטיקה)
30
איך זה ניראה ב-C ההגדרה כוללת את שם הפונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; ההגדרה כוללת את שם הפונקציה היא כוללת גם את המשתנים שעליהם הפונקציה מוגדרת (בדומה לפונקציות במתמטיקה) ואת סוג הערך שהפונקציה מחשבת ("מחזירה")
31
איך זה ניראה ב-C #include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; השורה הראשונה נקראת "כותרת הפונקציה" (header, prototype) גוף הפונקציה (מה שרוצים שיתבצע כשמשתמשים בה) מופיע אחרי הכותרת בתוך סוגריים מסולסלים. פקודת return אומרת מה ערך הפונקציה ("מה היא מחזירה")
32
איך זה ניראה ב-C #include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; השורה הראשונה נקראת "כותרת הפונקציה" (header, prototype) כאן גוף הפונקציה מחשב כמה זה base בחזקת exponent ושם את התוצאה במשתנה בשם result הפונקציה הזו מחזירה את הערך שנמצא במשתנה בשם result
33
איך זה ניראה ב-C #include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; כשמשתמשים בפונקציה, רושמים בסוגריים עבור איזה ערכים מעוניינים לחשב אותה ריצת התוכנית תמיד תתחיל כאן: בזמן הריצה, כשמגיעים לשימוש בפונקציה, המחשב יעבור לחשב את ערך הפונקציה, ורק לאחר מכן ימשיך בתוכנית
34
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0;
35
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; solution
36
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; solution
37
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; base 2 exponent 10 solution
38
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; base 2 exponent 10 i solution
39
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; base 2 exponent 10 i 1 result solution
40
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; base 2 exponent 10 i 1 1 result solution
41
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; base 2 exponent 10 i 1 2 result solution
42
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; base 2 exponent 10 i 2 2 result solution
43
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; base 2 exponent 10 i 2 4 result solution
44
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; base 2 exponent 10 i 11 אחרי 10 איטרציות 1024 result solution
45
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; base 2 exponent 10 i 11 1024 result solution
46
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; 1024 solution
47
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; 1024 solution 1024
48
הדגמת הריצה בשימוש פונקציה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,10); printf(“2^10 = %g”, solution); return 0; 1024 solution 1024
49
פונקציות – נושאים איך יוצרים פקודה חדשה: הגדרת פונקציות
שימוש בפונקציות: קריאה לפונקציה והערך המוחזר ממנה משתנים בפונקציות והעברת ערכים לפונקציה הכרזות על פונקציות וספריות של פונקציות העברת מערכים לפונקציות ...בונוס: מחרוזות
50
שימוש בפונקציות בתכנות קורה לעיתים קרובות שרוצים להגדיר פקודות חדשות.
הסיבה הנפוצה ביותר לכך היא כשרוצים להשתמש באותו קטע תוכנית כמה פעמים בתוכנית (יתכן שעם נתונים שונים). שפת C מאפשרת כאמור להגדיר פקודות חדשות (שנקראות "פונקציות"), ולאחר מכן להשתמש בהן. הגדרות פונקציות יופיעו לפני התוכנית הראשית (לפני ה- main). נתאר מה הגדרה כזאת כוללת (כותרת הפונקציה וגוף הפונקציה).
51
מה כוללת הגדרת פונקציה (פקודה חדשה)?
1. השם שבחרנו לפונקציה (למשל power עבור חזקה). - כללי השמות המותרים הם כמו הכללים לשמות-משתנים. - אסור לתת לפונקציה שם שכבר נתנו למשתנה. 2. איזה ערכים דרושים לביצוע הפונקציה ("המשתנים שעליהם היא פועלת"). - למשל לחישוב חזקה דרושים שני ערכים (בסיס ומעריך). שניהם אמורים להיות מספרים ממשיים. 3. סוג הערך שהפונקציה מחשבת ("מחזירה"). למשל התוצאה של חזקה היא מספר ממשי. - כל אלה נקראים "כותרת הפונקציה" (prototype, header).
52
דוגמא: כותרת להגדרת פונקציה שמחשבת חזקה
double power (double base, double exponent) הערכים שהפונקציה מקבלת שם סוג הפונקציה הערך המוחזר (מוגדרים עבורם משתנים, שיש להם שמות, כך שניתן להשתמש בהם לתיאור גוף הפונקציה)
53
דוגמא: אם מגדירים חזקה רק למעריכים שלמים
double power (double base, int exponent) הערכים שהפונקציה מקבלת שם סוג הפונקציה הערך המוחזר (מוגדרים עבורם משתנים, שיש להם שמות, כך שניתן להשתמש בהם לתיאור גוף הפונקציה)
54
מה עוד כוללת הגדרת פונקציה (פקודה חדשה)?
4. צריך כמובן לרשום את קטע-התוכנית שמבצע את הפקודה החדשה (מה שאנחנו רוצים שיקרה כשמשתמשים בה). יופיע בתוך סוגריים מסולסלים אחרי כותרת הפונקציה. מותר להגדיר בתחילתו משתנים נוספים. 5. צריך להגדיר מה הערך שהפונקציה מחזירה. - נעשה בעזרת פקודת return.
55
דוגמא: הגדרת פונקציה לחישוב חזקה
double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } חישוב החזקה (הערך שנמצא במשתנה base בחזקת הערך שנמצא במשתנה exponent) הערך שהפונקציה מחשבת (מחזירה)
56
הגדרת פונקציות לסיכום, הגדרת פונקציה מבוצעת באופן הבא: למשל:
(הגדרת משתנים לערכים שמועברים לפונקציה) שם הפונקציה טיפוס הערך המוחזר { פקודות הפונקציה return הערך המוחזר למשל: double power(double base, int exponent) int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } בתוך פונקציה אפשר לעשות כל מה שאפשר לעשות ב- main: להגדיר משתנים ולבצע פקודות (כולל להפעיל פונקציות).
57
שימוש בפונקציה ("קריאה לפונקציה")
בשימוש בפונקציה רושמים את שמה, ורושמים בסוגריים עבור איזה ערכים ספציפיים רוצים להשתמש בה. למשל: result=power(5,10); a=32*(power(b,5)+17); הערכים יכולים להיות גם ערכים של משתנים או תוצאה של חישוב (צריכים להתאים לטיפוסים שנקבעו בהגדרת הפונקציה). כשקוראים לפונקציה, המחשב עובר לבצע את קטע-התוכנית של הפונקציה, מחשב מה הערך שלה (כלומר מה הערך שהיא מחזירה), ולאחר מכן ממשיך בביצוע התוכנית. ניתן לשמור את הערך המוחזר במשתנה או לבצע איתו חישובים. התחלת הריצה היא תמיד ב- main.
58
דוגמא: שימוש בפונקציית חזקה
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; ריצת התוכנית תמיד תתחיל כאן: הפונקציה נקראת (מחושבת) כל פעם עבור הערכים המתאימים, והתוצאות מוחזרות
59
voidהגדרת פונקציות: אם הפונקציה לא מקבלת ערכים, אפשר להשאיר את הסוגריים ריקים או לכתוב בתוכם void. למשל: int print_hello(void) אם הפונקציה לא מחזירה ערכים, צריך לכתוב void בתור טיפוס הערך המוחזר (אם לא רושמים שם כלום זה מתפרש כ- int). למשל: void print_hello(void)
60
פונקציות – סיום הפונקציה
אם הפונקציה אמורה להחזיר ערך, צריך לסיים אותה על-ידי פקודת return, שבה מוחזר ערך מטיפוס מתאים (ריצת הפונקציה נפסקת ע"י פקודת return). יכולות להיות לפונקציה כמה אפשרויות סיום, שהבחירה ביניהן תתבצע לפי תנאים. למשל: int greater(int a, int b) { if (a>b) return 1; else return 0; } עם סיום ריצת הפונקציה, חוזרים למקום שממנו הפונקציה נקראה. כל המשתנים שלה מתבטלים ולא מוגדרים יותר.
61
פונקציות - דוגמא #include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; הפונקציה נקראת (מחושבת) כל פעם עבור הערכים המתאימים, והתוצאות מוחזרות
62
mainפונקציית כל תוכנית C מכילה פונקציה בשם main, שבה התוכנית מתחילה.
הערך שמוחזר ממנה מועבר למערכת ההפעלה. טיפוסו צריך להיות int. אם ערכו 0 המשמעות היא שהתוכנית הסתיימה באופן תקין. פונקציית main יכולה גם לקבל משתנים ממערכת ההפעלה (ערכים שנכתבים בשורת הפקודה כשמריצים את התוכנית), אבל יכולה לפעול גם בלי לקבל משתנים. int main() { printf(“Hello World!\n”); return 0; }
63
סיבות נוספות לשימוש בפונקציות
אמרנו שהסיבה הנפוצה ביותר לכך היא כשרוצים להשתמש באותו קטע תוכנית כמה פעמים בתוכנית (יתכן שעם נתונים שונים). סיבה נוספת היא כדי לחלק את התוכנית לחלקים, שכל אחד מהם מבצע חלק לוגי מסוים (פונקציה אחת תטפל בכל נושא הקלט, פונקציה אחרת תעשה את כל העיבוד, פונקציה שלישית תטפל בפלט, וכד'). קשה לכתוב ולתחזק תוכנית אחת ארוכה שהכל נעשה בתוכה. מקובל לחלק לפונקציות בהתאם לחלקים הלוגיים ולכתוב פונקציית main קצרה שבעיקר קוראת לפונקציות אחרות.
64
סיבות נוספות לשימוש בפונקציות
כדי להשתמש בחלק מתוכנית שכתבנו בתוכניות אחרות שנכתוב. נוכל להכין ספריות של פונקציות שימושיות שכתבנו ולהשתמש בהן בתוכניות אחרות שנכתוב. יש כבר ספריות מוכנות כאלה ב- C, כמו stdio.h.
65
המשתנים בפונקציה משתנים שמוגדרים בתוך פונקציה הם מקומיים (לוקאליים) רק לפונקציה הזאת. הם לא מוגדרים בפונקציות אחרות (אפשר להגדיר בפונקציות אחרות משתנים אחרים באותו שם). זה כולל גם את המשתנים שאליהם מועברים ערכים עבור הפונקציה (גם הם מקומיים). חשוב להדגיש: בסיום ריצת פונקציה משתניה לא מוגדרים יותר, וערכיהם לא נשמרים.
66
משתנים בפונקציה -דוגמא
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double solution; solution=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, solution); return 0; base, exponent, i, result מוגדרים רק בתוך הפונקציה power solutionהמשתנה main מוגדר רק בתוך הפונקציה
67
משתנים בפונקציה - דוגמא
#include <stdio.h> double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } int main() double i; i=power(2,20)+power(3,15)+power(5,17); printf(“2^20 + 3^15 + 5^17= %g”, i); return 0; אפשר להגדיר בפונקציות שונות משתנים באותו שם, והם אינם קשורים זה לזה
68
פונקציות – העברת ערכים נשים לב: לפונקציה מועברים ערכים – לא משתנים. שינויים במשתנה שאליו הוכנס הערך לא משפיעים על המשתנה המקורי. למשל בדוגמא הבאה: #include<stdio.h> int square (int num) { num=num*num; return num; } int main() int num=16; printf(“%d is the square of %d”, square(num), num); return 0; הפלט: 256 is the square of 16
69
פונקציות – העברת ערכים נשים לב: לפונקציה מועברים ערכים – לא משתנים. שינויים במשתנה שאליו הוכנס הערך לא משפיעים על המשתנה המקורי. למשל בדוגמא הבאה: #include<stdio.h> int square (int num) { num=num*num; return num; } int main() int num=16; printf(“%d is the square of %d”, square(num), num); return 0; הפלט: 256 is the square of 16 כאן מועבר הערך 16 המשתנה num שב- main לא מושפע מהשינויים במשתנה שאליו הועבר ערכו
70
משתנים גלובליים אפשר, אבל לא מומלץ, להגדיר משתנים בתחילת הקובץ, לא בתוך אף פונקציה. במקרה כזה הם יהיו מוגדרים בכל הפונקציות שמופיעות בקובץ. זה נקרא "משתנים גלובליים". זה לא מומלץ כי זה עלול ליצור באגים (עושים שינוי בפונקציה אחת ולא שמים לב שזה משפיע על פונקציה אחרת), ופוגע בהפרדה הלוגית בין הפונקציות.
71
קבועים ניתן להגדיר משתנה שערכו ישאר קבוע על-ידי תוספת המילה const:
const double pi=3.1415; יתרון כאשר הקבוע צריך להיות בשימוש רק בתוך פונקציה אחת אם רוצים להגדיר ערכים קבועים שיהיו בשימוש כל חלקי התוכנית, אין צורך במשתנה גלובלי, וניתן להגדיר קבוע על-ידי: #define pi זו פקודה ל- preprocessor שמחליפה את המילה pi במספר בכל התוכנית לפני תחילת התרגום לשפת מכונה.
72
הכרזה על פונקציות אמרנו שכדי להשתמש בפונקציה, צריך שהיא תהיה מוגדרת.
לכן רשמנו תמיד את הפונקציה לפני ה- main, שבו השתמשנו בה. אופציה נוספת היא לרשום הכרזה (declaration) של הפונקציה בתחילת הקובץ, ולרשום את המימוש שלה (definition) בסופו, או בקובץ נפרד.
73
הכרזה על פונקציות דוגמא להכרזה ((declaration, prototype:
double power(double base, int exponent); ומותר גם בלי שמות המשתנים: double power(double, int); כשרק בסוף הקובץ נרשום את ההגדרה המלאה (definition): double power(double base, int exponent) { int i; double result=1; for(i=1; i<=exponent; i++) result=result*base; return result; } שימו לב שבהגדרת פונקציה אין ; בכותרת (רק בהכרזה)
74
הכרזה על פונקציות – למה זה טוב
לפעמים נוח לראות בתחילת הקובץ את הפונקציות העיקריות ולהשאיר את פונקציות העזר בסוף. הרבה פעמים התוכנית מחולקת לכמה קבצים שאומרים לקומפיילר לצרף יחד, ואז ההגדרה (המימוש) של הפונקציה צריכה להופיע רק באחד מהם, ובאחרים מופיעה רק ההכרזה. בפרט, זה המצב כשאנחנו משתמשים בספריות של פונקציות, כמו stdio.h.
75
הכרזה על פונקציות – ספריות
השורה #include<stdio.h> פשוט אומרת לקומפיילר לצרף לתחילת הקובץ שלנו את הקובץ stdio.h מתוך קבצי ההכרזות של C. הקובץ הזה מכיל הכרזות של פונקציות קלט/פלט שימושיות (כמו printf,scanf). כך אנחנו יכולים להשתמש בהן בתוכנית שלנו בלי להכיר את המימוש שלהן, ובזמן הקומפילציה הלינקר מצרף את המימוש מתוך הספריות הקיימות של C.
76
ספריות נוספות ב- C יש ספריות נוספות שנוכל להשתמש בהן. למשל ב- math.h נוכל למצוא פונקציות כמו sin,cos,tan,abs,fabs,log,sqrt,pow ב- ctype.h יש פונקציות שעוסקות בסוג של תווים, כמו islower, toupper, ועוד. תכירו בהמשך ספריות נוספות.
77
העברת מערך לפונקציה כיצד נכתוב פונקציה שמקבל כערך מערך ומבצעת חישוב על איבריו? נניח שנרצה לכתוב פונקציה שמקבלת מערך של מספרים ומחזירה את סכום איברי המערך. העברת מערך לפונקציה מתבצעת למעשה ע"י העברת כתובת ההתחלה של המערך. int sum(int a[ ], int size); prototype
78
העברת מערך לפונקציה aזהו פשוט כתובת ההתחלה של מערך של int.
הפרמטר השני הוא גודל המערך. הפרמטר הראשון לפונקציה הוא כתובת ההתחלה של המערך ואינו נותן מידע לגבי גודל המערך. לכן, נעביר את המידע הזה כפרמטר נוסף לפונקציה.
79
דוגמא: פונקציה שסוכמת מערך של שלמים
#include <stdio.h> int calc_sum(int arr[ ], int size) { int i, sum = 0; for(i=0; i<size; i++) sum=sum+arr[i]; return sum; } int main() { int input[10], i; for(i=0; i<10; i++) scanf("%d", &input[i]); printf("The sum is %d\n", calc_sum(input, 10)); return 0; פונקציית הסכום (גודל המערך מועבר במשתנהsize ) עוברים על תאי המערך בלולאה, סוכמים אותם, ומחזירים את הסכום. קליטת ערכים למערך קריאה לפונקציה והדפסת התוצאה
80
העברת מערך לפונקציה ההגדרות הבאות שקולות: int f(float arr[]);
81
מחרוזות מחרוזות ב C הן פשוט מערכים שאיבריהם הם תווים.
המוסכמה בנוגע למחרוזות היא שמחרוזות מסתימות ב תו מיוחד אשר מסמן את סוף המחרוזת. תו זה הוא ‘\0’ שקוד ה ascii שלו הוא 0.
82
מחרוזות העובדה הזו מאפשרת לנו לכתוב פונקציות עבור מחרוזות אשר מקבלות את כתובת ההתחלה של המחרוזת ומטיילות על המערך עד שמגיעים לתו הסיום של המחרוזת. כלומר, אין צורך לדעת מראש את גודל המחרוזת (בניגוד למערכים רגילים).
83
מחרוזות int strlen(char s[ ]) { int cnt=0; while ( s[cnt] != ’\0’ )
return cnt; } מה עושה הפונקציה הזו? איפה משתמשים בעובדה שמחרוזת מסתיימת בתו מסוים?
84
סימון סוף המחרוזת: התו המיוחד '0\'
כל הפעולות שנכיר על מחרוזות דואגות בעצמן לשמירת המוסכמה הזו של תו הסיום. למשל פעולת האיתחול הזו: char word[ ]=“HELLO”; שקולה לפעולת האיתחול הזו: char word[ ]={‘H’,’E’,’L’,’L’,’O’,’\0’}; (כלומר מחרוזת באורך 5 דורשת מערך של 6 תווים). ‘H’ ‘E’ ‘L’ ‘L’ ‘O’ ‘\0’ word[0] word[5]
85
הוספת תו-הסיום '0\' word[5]=‘\0’;
אם ניצור מערך של תווים בלי תו-הסיום, ונירצה להפעיל עליו פעולות על מחרוזות (שנכיר בהמשך), אז נצטרך להוסיף את התו '0\' אחרי התווים ששמנו בו. למשל אם נירצה להשתמש בפעולות על מחרוזות עבור המערך: אז נצטרך להוסיף את התו '0\' אחרי האות 'O' על-ידי : word[5]=‘\0’; ‘H’ ‘E’ ‘L’ ‘L’ ‘O’ word[0] word[6] ‘H’ ‘E’ ‘L’ ‘L’ ‘O’ ‘\0’ word[0] word[6]
86
הספריה string.h – פונקציות לדוגמא
פונקציה למציאת אורך מחרוזת (לפי מקום התו '0\'): int strlen(const char str[ ]); האורך לא כולל את התו '0\'. - השימוש ניראה כך: len = strlen(my_string); (למשל עבור מחרוזת שהוגדרה char my_string[6]=“Hello”; יוחזר 5) פונקציה להשוואה בין מחרוזות: int strcmp(const char str1[ ], const char str2[ ]); מחזירה 0 אם המחרוזות זהות, כלומר שוות בכל תו עד לתו ‘\0’. אחרת מוחזר מס' שונה מ-0: חיובי אם המחרוזת הראשונה גדולה יותר לקסיקוגרפית (כלומר לפי סדר מילוני, סדר טבלת-האסקי), ושלילי אם היא קטנה יותר.
87
- דוגמאות נוספותstring.h
העתקת המחרוזת שבמשתנה source למחרוזת target תתבצע על-ידי: strcpy(target, source); שירשור מחרוזות (מעתיק את str2 לסוף str1): strcat(str1, str2); חיפוש ההופעה הראשונה של התו במשתנה c מסוג char במחרוזת str: strchr(str, c); חיפוש ההופעה הראשונה של מחרוזת str2 במחרוזת str1: strstr(str1, str2); בעתיד נפרט יותר על השימוש בפעולות האלה ועל פעולות נוספות בספריה הזאת.
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.