Document

‫מ‪ JAVA -‬ל‪C -‬‬
‫קרן כליף‬
‫‪© Keren Kalif‬‬
‫ביחידה זו נלמד‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪2‬‬
‫רקע לשפת ‪C‬‬
‫תוכנית ראשונה בשפת ‪C‬‬
‫הגדרת משתנים בשפת ‪C‬‬
‫קלט ופלט בשפת ‪C‬‬
‫פונקציות‬
‫מערכים‬
‫סוגי משתנים‬
‫‪© Keren Kalif‬‬
‫רקע כללי לשפת ‪C‬‬
‫‪‬‬
‫‪‬‬
‫שפת ‪ C‬תוכננה לראשונה בשנים ‪ 1973-1969‬על ידי דניס ריצ'י‬
‫ממעבדות בל על מערכות ‪UNIX‬‬
‫הצורך בשפה עלה לשם כתיבת מערכת הפעלה‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪3‬‬
‫רוב הגרעין )‪ )Kernel‬של מערכת ההפעלה לינוקס כתוב ב‪ ,C -‬כמו גם הרבה‬
‫תוכנות אחרות המהוות את מערכת ההפעלה ‪GNU/Linux‬‬
‫באותה עת הייתה קיימת שפת ‪ B‬ובאמצעותה נכתבה שפת ‪C‬‬
‫בתחילה‬
‫‪ ‬מקור השם של השפה הוא באותה שפת ‪ B‬תוך התקדמות אות‬
‫אחת באלפבית‬
‫עם התפתחות שפת ‪ ,C‬הקומפיילר בו מקמפלים את השפה נכתב אף‬
‫הוא בשפת ‪C‬‬
‫‪© Keren Kalif‬‬
‫עקרון מנחה של שפת ‪ - C‬יעילות‬
‫‪‬‬
‫כל פעולה בשפה מתבטאת בפעולה בודדת או במספר מועט של‬
‫פעולות בשפת סף‬
‫‪‬‬
‫לכן אין בשפה‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫מסיבה זו היא גם כוללת לא מעט אפשרויות לשגיאות בזמן ריצה‪,‬‬
‫אשר אינן מאותרות ע"י הקומפיילר בזמן קומפילציה‪ ,‬למשל‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪4‬‬
‫סוגי מבנים מורכבים כגון מחרוזות‬
‫בדיקות סמויות בגישה למערכים‬
‫מערכת הטיפוסים שלה אינה בטוחה‬
‫פנייה למשתנה שלא הוקצה‬
‫פנייה לערך לא מאותחל בזיכרון (ה‪ JAVA -‬מתקבלת שגיאת קומפילציה)‬
‫הנחת העבודה של מתכנני השפה (והתקנים המאוחרים יותר) היא‬
‫כי "המתכנת מבין מה הוא עושה"‬
‫‪© Keren Kalif‬‬
‫שפת ‪ C‬כשפה פרוצדורלית‬
‫‪‬‬
‫שפה פרוצדורלית‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪5‬‬
‫הרעיון שהקוד מחולק לפרוצדורות (פונקציות) שכל אחת אחראית על‬
‫חלק מסויים בתוכנית‬
‫בכל פרוצדורה יש את המשתנים המוגדרים בה ומוכרים אך ורק לה‬
‫(נקראים גם משתנים לוקאליים או מקומיים)‬
‫ישנם גם משתנים גלובליים אשר משותפים לכל הפרוצדורות‬
‫בניגוד לשפה מונחת‪-‬עצמים‪ ,‬ישנם רכיבים בשפה שאינם חלק‬
‫מאובייקטים (למשל ה‪ main -‬אינו נמצא בתוך מחלקה)‬
‫האובייקטים רק מכילים מידע ואינם יודעים לבצע פעולות‬
‫לצורך השוואה‪ JAVA ,‬היא שפה מכוונת עצמים‬
‫טיפוסים וקלט ופלט בשפת ‪C‬‬
‫הפקודות ‪ printf‬ו‪ ,scanf -‬פורמטי הדפסה שונים‬
‫‪© Keren Kalif‬‬
‫כתיבת תוכנית ראשונה ב‪C -‬‬
‫‪‬‬
‫כל תוכנית ב‪ C -‬תראה כך‪:‬‬
‫ספריה המכילה פקודות‬
‫קלט ופלט‬
‫>‪#include <stdio.h‬‬
‫נשים לב לסגנון‪ :‬הסוגרים המסולסלים‬
‫הפותחים בלוק נמצאים בשורה חדשה‬
‫‪‬‬
‫‪ main‬היא פונקציה המורצת עם הרצת התוכנית‬
‫‪‬‬
‫‪7‬‬
‫בכל פרוייקט תהייה פונקצית ‪ main‬אחת בדיוק‬
‫)(‪void main‬‬
‫{‬
‫…‬
‫}‬
© Keren Kalif
!‫דגש כדי שאני לא אתעצבן‬
http://qph.is.quoracdn.net/main-qimg-e0c9dafb319150b6c6d9816047ed9eae?convert_to_webp=true
8
© Keren Kalif
C -‫טיפוסי נתונים ב‬
Definition
Size
Range
char
1 byte
-27…27-1
4 bytes
-231…231-1
)‘a’, ‘A’, ‘6’,’!’( ‫תו‬
int
(-128..127)
)-3,7,8234( ‫מספר שלם‬
float
4 bytes
)5.2 ,-89 ,3.6( ‫מספר ממשי‬
double
8 bytes
)5.2 ,-89 ,3.6( ‫מספר ממשי‬
boolean ‫אין‬
9
© Keren Kalif
)2( C -‫טיפוסי נתונים ב‬
Definition
Size
Range
short
2 bytes
-215… 215-1
(-32,768…32,767)
8 bytes
-263…263-1
4 bytes
0…232-1
2 bytes
0…216-1
(0….65535)
8 bytes
0…264-1
1 byte
0…28-1
‫מספר שלם‬
long
‫מספר שלם‬
unsigned int
‫מספר שלם חיובי‬
unsigned short
‫מספר שלם חיובי‬
unsigned long
‫מספר שלם חיובי‬
unsigned char
‫תו חיובי בלבד‬
(0…255(
10
‫‪© Keren Kalif‬‬
‫הגדרת משתנים בתוכנית‬
‫‪ char‬מורכב מ‪byte1 -‬‬
‫‪ int‬מורכב מ‪byte 4 -‬‬
‫‪1000‬‬
‫‪‬‬
‫{‬
‫‪1004‬‬
‫‪5.2‬‬
‫‪1012‬‬
‫’‪‘f‬‬
‫‪char: ch‬‬
‫‪1013‬‬
‫‪7‬‬
‫‪short: n3‬‬
‫‪1015‬‬
‫‪77‬‬
‫‪short: n4‬‬
‫;‪short n3 = 7, n4 = 77‬‬
‫‪1017‬‬
‫‪234234‬‬
‫‪uint: n5‬‬
‫;‪unsigned int n5 = 234234‬‬
‫‪1021‬‬
‫‪-11‬‬
‫‪int: n6‬‬
‫‪int‬‬
‫‪double: n2‬‬
‫;‪n1 = 4‬‬
‫;‪n2 = 5.2‬‬
‫‪int‬‬
‫‪double‬‬
‫;’‪ch = ‘f‬‬
‫;‪n6 = -11‬‬
‫‪char‬‬
‫}‬
‫למה ההפרש בין הכתובות שונה?‬
‫‪‬‬
‫‪11‬‬
‫‪4‬‬
‫‪int: n1‬‬
‫)(‪void main‬‬
‫כי כל סוג משתנה תופס כמות שונה של בייטים‬
‫בשפת ‪ C‬ערכו של משתנה שאינו מאותחל הוא זבל‪,‬‬
‫וניתן לקרוא את ערכו‪ ,‬אך יתכן ונעוף בזמן ריצה (בניגוד‬
‫לשפת ‪ JAVA‬שתתריע על כך בז מן קומפילציה)‬
© Keren Kalif
‫הדפסה למסך‬
:‫ היא הדפסה למסך‬stdio.h ‫אחת הפקודות הנמצאות בספריה‬

#include <stdio.h>
void main()
{
printf(“Hello World!”);
}
12
‫‪© Keren Kalif‬‬
‫הדפסה למסך (‪)2‬‬
‫>‪#include <stdio.h‬‬
‫הרצה של תוכנית זו תדפיס‬
‫למסך את המחרוזת‬
‫‪6 is a number‬‬
‫)(‪void main‬‬
‫{‬
‫;)”‪printf(“6 is a number‬‬
‫}‬
‫דרך נוספת לכתוב תוכנית זו‪:‬‬
‫>‪#include <stdio.h‬‬
‫מציין שבחלק זה‬
‫במחרוזת יהיה מספר‬
‫דצימלי‪ ,‬במקרה זה ‪6‬‬
‫)(‪void main‬‬
‫{‬
‫;)‪printf(“%d is a number”, 6‬‬
‫}‬
‫‪13‬‬
‫‪© Keren Kalif‬‬
‫קבלת קלט מהמשתמש‬
‫‪‬‬
‫פקודה נוספת הנמצאת בספריה ‪ stdio.h‬היא קליטת נתון מהמשתמש‪:‬‬
‫פירוש הפקודה ‪:scanf‬‬
‫אחסן את הערך שיתקבל מהמשתמש‬
‫במשתנה הנקרא ‪x‬‬
‫‪‬‬
‫‪‬‬
‫‪ x‬הוא השם של המקום שבו נאכסן את הערך המתקבל מהמשתמש‬
‫& מציין לנו איפה נמצא מקום זה‬
‫מה זה ‪?%d‬‬
‫‪‬‬
‫‪14‬‬
‫)(‪void main‬‬
‫{‬
‫;‪int x‬‬
‫;)‪scanf(“%d”, &x‬‬
‫}‬
‫מה זה ‪?x‬‬
‫‪‬‬
‫‪‬‬
‫>‪#include <stdio.h‬‬
‫ציון שהנתון אותו אנו קוראים הוא מספר דצימלי‬
© Keren Kalif
‫פורמט קליטה והדפסה לטיפוסים השונים‬
Data Type
Format
Explaination
int
%d
Decimal
short
%d
Decimal
long
%ld
Long Decimal
char
%c
Character
float
%f
Float
double
%lf
Long Float
unsigned
%u
Unsigned
15
‫‪© Keren Kalif‬‬
‫בעיה‪ :‬התוכנית "מדלגת" על פקודת הקלט‬
‫)(‪void main‬‬
‫}‬
‫;‪int n1‬‬
‫;‪char c1‬‬
‫הקלדתי ‪ 5‬ואז ‪.ENTER‬‬
‫לתוך ‪ n1‬נכנס ‪ ,5‬ולתוך ‪ ch1‬נכנס האנטר‪.‬‬
‫‪ ENTER‬הוא גם תו‪ ,‬ומאחר והוא‬
‫היה ב‪ buffer -‬הוא נקלט לתוך ‪ch1‬‬
‫;)" ‪printf("Please enter a number:‬‬
‫;)‪scanf("%d", &n1‬‬
‫;)" ‪printf("Please enter a char:‬‬
‫;)‪scanf("%c", &c1‬‬
‫;)‪printf("n1= |%d| c1=|%c|\n", n1, c1‬‬
‫כאשר אנחנו קולטים נתונים בפקודות נפרדות‪,‬‬
‫נרצה לנקות את ה‪ buffer -‬בין הפעולות השונות‪.‬‬
‫‪16‬‬
‫{‬
© Keren Kalif
flushall ‫ הפקודה‬:‫הפתרון‬
void main()
}
int
n1;
char c1;
printf("Please enter a number: ");
scanf("%d", &n1);
printf("Please enter a char: ");
flushall();
scanf("%c", &c1);
printf("n1= |%d| c1=|%c|\n", n1, c1);
{
char ‫כאשר קוראים לתוך משתנה מטיפוס‬
‫ נשים את‬,‫לאחר שכבר בוצעה קליטה כלשהי‬
.‫ בין הקריאות השונות‬flushall ‫הפקודה‬
.f -‫ ו‬ENTER ,7 ‫הקלדתי‬
‫ ואז ניקיתי את‬,7 ‫ נכנס‬n1 ‫לתוך‬
buffer -‫ ה‬ENTER -‫ה‬
.flushall ‫באמצעות הפקודה‬
.ch1 ‫ נכנס לתוך‬f ‫התו‬
17
© Keren Kalif
‫הדפסת וקליטת משתנים מטיפוסים שונים‬
#include <stdio.h>
void main()
{
int
n1;
double n2;
char
ch;
}
printf(“Please enter an int, double and char: “);
scanf(“%d %lf %c”, &n1, &n2, &ch);
printf(“values are: %d %lf %c \n”, n1, n2, ch);
18
‫‪© Keren Kalif‬‬
‫פורמט הדפסה‪ :‬ישור הטקסט‬
‫‪‬‬
‫כאשר משתמשם בפקודה ‪ printf‬לצורך הדפסה ניתן לשלוט על‬
‫כמות התווים שכל חלק יתפוס‪ ,‬וכן על כיוון היישור שלו‪:‬‬
‫‪‬‬
‫לאחר ה‪ % -‬נכתוב מספר המציין כמה מקומות ערך המשתנה יתפוס‬
‫בתצוגה‬
‫‪‬‬
‫‪‬‬
‫ברירת המחדל היא הצמדת הטקסט לימין‬
‫כדי להצמיד לשמאל נוסיף מינוס‬
‫יישור הטקסט לימין‬
‫יישור הטקסט לשמאל‬
‫‪19‬‬
‫)(‪void main‬‬
‫{‬
‫;‪int n‬‬
‫;)" ‪printf("Please enter a number:‬‬
‫;)‪scanf("%d", &n‬‬
‫;)‪printf("The number is: |%d|\n", n‬‬
‫;)‪printf("The number is: |%5d|\n", n‬‬
‫;)‪printf("The number is: |%-5d|\n", n‬‬
‫{‬
© Keren Kalif
1.
2.
3.
void main()
}
int rows, cols, i, j, res;
‫ לוח הכפל‬:‫דוגמא‬
4.
5.
6.
7.
8.
9.
10.
11.
printf("Enter rows and cols for multiplication-board: ");
scanf("%d %d", &rows, &cols);
12.
13.
14.
for (i=1 ; i <= (cols+1)*5 ; i++)
printf("-");
printf("\n");
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
for (i=1 ; i <= rows ; i++)
}
// print the left column
printf("%4d|", i);
for (j=1 ; j <= cols ; j++)
printf("%4d|", i*j);
printf("\n");
{
printf("\n");
// print first line
printf("\n%4c|", ' ');
for (i=1 ; i <= cols ; i++)
printf("%4d|", i);
printf("\n");
{
20
© Keren Kalif
‫יישור בגודל משתנה‬
‫ניתן גם שכמות התווים שכל חלק יתפוס בהדפסה תלקח‬
‫ממשתנה‬
‫ וברשימת הפרמטרים נשים עבורה את הערך‬,* ‫ נשים‬% -‫לאחר ה‬
‫המבוקש‬


void main()
}
int n, space;
}
printf("Please enter a number and number of spaces: ");
scanf("%d %d", &n, &space);
printf("The number is: |%d|\n", n);
printf("The number is: |%*d|\n", space, n);
printf("The number is: |%-*d|\n", space, n);
21
© Keren Kalif
‫ מוביל‬0 ‫הדפסת‬
‫ מוביל‬0 ‫ניתן להדפיס נתונים עם הדפסת‬
% -‫ לאחר ה‬0 ‫נוסיף‬


‫ ספרות‬9 ‫ תמיד עם‬.‫ז‬.‫למשל לצורך הדפסת ת‬

void main()
}
int id;
printf("Please enter your id: ");
scanf("%d", &id);
printf("Your id is %09d\n", id);
{
22
‫‪© Keren Kalif‬‬
‫הגבלת כמות התווים הנכנסים לתוך משתנה‬
‫‪‬‬
‫ניתן להגביל את כמות התווים הנקלטים לתוך משתנה‬
‫‪‬‬
‫‪‬‬
‫בפקודת ה‪ scanf -‬לאחר ה‪ % -‬נגביל את כמות התווים‬
‫למשל‪ :‬קליטת מספר קטן מ‪1000 -‬‬
‫)(‪void main‬‬
‫{‬
‫;‪int num‬‬
‫;)" ‪printf("Please enter a number less than 1000:‬‬
‫;)‪scanf("%3d", &num‬‬
‫;)‪printf("The number is %d\n", num‬‬
‫{‬
‫‪23‬‬
© Keren Kalif
‫קליטת נתונים תוך הפרדתם‬
‫ניתן לקלוט מהמשתמש ערך (בד"כ מספר או מחרוזת) ובפועל‬
‫לקלוט את הערך ליותר ממשתנה אחד‬

void main()
}
int departmentId, innerCourseId;
}
printf("Please enter a 5 digits course code: ");
scanf("%2d%3d", &departmentId, &innerCourseId);
printf("Department: %d Course: %d\n", departmentId, innerCourseId);
24
‫‪© Keren Kalif‬‬
‫הגדרת משתנים בתוכנית‬
‫שגיאת קומפילציה‪ :‬המשתנים חייבים‬
‫להיות מוגדרים בתחילת בלוק!‬
‫‪25‬‬
‫‪© Keren Kalif‬‬
‫‪#define‬‬
‫‪‬‬
‫אפשר להגדיר קבוע באופן הבא‪:‬‬
‫שורה זו אינה מסתיימת ב‪; -‬‬
‫‪26‬‬
‫‪#define TAXES 0.17‬‬
‫‪‬‬
‫את הפקודה ‪ #define‬רצוי לכתוב בתחילת התוכנית‪ ,‬לפני‬
‫הפונקציה ‪ main‬ובכך לקבוע לערך תחום הכרה ציבורי‬
‫‪‬‬
‫אפשר לכתוב את הפקודה בכל מקום בתוכנית‪ .‬הקבוע יהיה‬
‫מוכר מאותו מקום ועד לסוף התוכנית‬
‫‪‬‬
‫בכל פעם שהקומפיילר יפגוש את השם )‪ TAXES‬בדוגמא)‬
‫הוא יחליפו בערך )‪(0.17‬‬
© Keren Kalif
define ‫הגדרת קבועים ע"י‬
#include <stdio.h>
#define TAXES 0.17
‫ אינו משתנה‬define ‫קבוע שהוגדר ע"י‬
.‫ולכן אינו תופס מקום בזכרון‬
.‫הסבר מפורט בהמשך‬
float: price
void main()
{
float price;
float totalPrice;
1000
float: totalPrice
1004
1008
printf("Please enter the product’s price: ");
scanf("%f", &price);
% ‫ כדי להציג את התו‬%%
totalPrice = price + (price*TAXES);
printf("Total price including %.2f%% taxes is %.2f\n",
TAXES*100, totalPrice);
‫ ספרות‬2 ‫ציון להצגת רק‬
}
‫אחרי הנקודה העשרונית‬
27
‫פונקציות‬
‫הפרדה בין הצהרה למימוש‪ ,‬יצירת קובץ ספריה‪ ,‬תהליך הקומפילציה‪ ,‬שגיאות‬
‫קומפילציה לעומת שגיאות לינקר‪ ,‬יצירת מספרים אקראיים‬
© Keren Kalif
C ‫תוכנית שלמה עם פונקציה – איך זה בשפת‬
#include <stdio.h>
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;
printf("Please enter base and exponent: ");
scanf("%d %d", &base, &exponent);
int:base
???
2
1000
int: exponent
???
3
1004
int: result
???
8
1008
main -‫הזיכרון של ה‬
int: a
2
2000
int: b
3
2004
int: i
???
…
2008
int: result
???
1
8
2012
result = power(base, exponent);
printf("%d^%d=%d\n", base, exponent, result);
}
power ‫הזיכרון של‬
power
main
( line 4)
‫מחסנית הקריאות‬
29
© Keren Kalif
‫הפרדה בין הצהרות למימוש‬
#include <stdio.h>
// prototypes
power(int,base,
int); int exponent);
int power(int
void main()
{
int base, exponent, result;
‫בשורת ההצהרה לא חייבים‬
‫לציין את שמות המשתנים‬
printf("Please enter base and exponent: ");
scanf("%d %d", &base, &exponent);
}
result = power(base, exponent);
printf("%d^%d=%d\n", base, exponent, result);
; ‫ ובסיום כל הצהרה‬,main -‫• את ההצהרות נרשום לפני ה‬
int power(int base, int exponent)
)‫טיפוס‬-‫ (אב‬prototype ‫• חלק זה נקרא‬
{
int i, result=1;
main -‫את המימושים נכתוב מתחת ל‬
for (i=0 ; i < exponent ; i++)
‫ההפרדה היא מאחר ומעניין אותנו איזה פונקציות יש‬
result *= base;
‫ ופחות איך הן מבצעות את העבודה‬,‫בתוכנית‬
return result;
}
‫חתימת הפונקציה בהגדרה בראש הקובץ ובמימוש חייבות‬
•
•
•
‫להיות זהות‬
30
‫‪© Keren Kalif‬‬
‫פונקציות ספריה‬
‫‪‬‬
‫‪‬‬
‫כל תוכנית בשפת תכנות עילית‪ ,‬ובפרט שפת ‪ ,C‬מורכבת‬
‫מאוסף של פונקציות‬
‫קיימות פונקציות ספריה בשפה אותן ניתן להכליל בתוכנית‬
‫ולקרוא להן בכל מקום בו נרצה ולהשתמש בהן כמה פעמים‬
‫שנרצה‬
‫‪‬‬
‫למשל כאשר עושים‪:‬‬
‫>‪#include <stdio.h‬‬
‫ניתן להשתמש בפונקציות המוגדרות בספריה זו בכל מיני מקומות‬
‫בקוד (למשל ‪)printf, scanf‬‬
‫‪‬‬
‫‪31‬‬
‫גם אנו יכולים לכתוב פונקציות או ספריות של פונקציות‪ ,‬שאותן‬
‫ניתן להכליל ולהשתמש בהן כרצוננו‬
‫‪© Keren Kalif‬‬
‫יצירת קובץ ספריה‬
‫‪‬‬
‫‪‬‬
‫עד כה השתמשנו בפונקציות שקיבלנו משפת ‪C‬‬
‫כל פונקציה כזו נכתבה בספריה שאליה ביצענו ‪include‬‬
‫‪‬‬
‫‪‬‬
‫כדי לייצר ספריה משלנו נייצר ‪ 2‬קבצים חדשים‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪32‬‬
‫למשל כדי להשתמש בפונקציות של מחרוזות נכלול את‬
‫>‪#include <string.h‬‬
‫‪ <file_name>.h‬הוא קובץ אשר יכלול את ה‪ prototypes -‬של‬
‫הפונקציות‪ ,‬ואליו אח"כ נעשה ‪ include‬מהקובץ שירצה להשתמש‬
‫בפונקציות המוגדרות בו‬
‫‪ <file_name>.c‬הוא קובץ שיכיל ‪ include‬לקובץ ה‪header -‬‬
‫התואם ויממש את הפונקציות המוגדרות בו‬
‫‪ include‬לספריה שכתבנו יהיו בתוך "" (גרשיים) ולא בתוך <>‬
‫‪© Keren Kalif‬‬
‫דוגמא – ספריה המטפלת בתווים‬
‫‪33‬‬
‫‪© Keren Kalif‬‬
‫דוגמא – ספריה המטפלת בתווים (‪)2‬‬
‫‪34‬‬
‫‪© Keren Kalif‬‬
‫ספריות שלנו ‪ -‬סיכום‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪35‬‬
‫ניתן לכתוב את כל הקוד בקובץ אחד‪ ,‬אבל אם אנחנו כותבים‬
‫קוד כללי‪ ,‬שיתכן ויהיה שימושי גם במקומות אחרים‪ ,‬נעדיף‬
‫לחלק את הקוד לקובץ ספריה נפרד‬
‫מי שירצה להשתמש בספריה שלנו‪ ,‬יתעניין מה יש לה להציע‪,‬‬
‫ולא איך היא מבצעת את הפעולות‪ ,‬וכך הוא יוכל להסתכל‬
‫בקובץ ‪ header‬בלבד בו יש ריכוז של כל הפונקציות‬
‫מכירת הספריה ללקוח תסתכם בקובץ ה‪ h -‬ובקובץ בינארי‬
‫שהוא תוצר של הקומפילציה‪ ,‬וכך לא נחשוף את ה"איך"‬
‫‪© Keren Kalif‬‬
‫תהליך הפיכת תוכנית ‪ C‬לשפת מכונה‬
‫‪Disk‬‬
‫‪Editor‬‬
‫‪Disk‬‬
‫‪Preprocessor‬‬
‫‪Disk‬‬
‫‪Compiler‬‬
‫‪Disk‬‬
‫‪Linker‬‬
‫התוכנית נכתבת בעורך טקסטואלי ונכתבת לדיסק‬
‫קדם המעבד עובר על הקוד ומבצע החלפות נחוצות‬
‫(כל הפקודות המתחילות ב‪ ,# -‬כמו ‪ define‬ו‪include -‬‬
‫עבור כל קובץ ‪ ,c‬הקומפילר יוצר קובץ ‪ obj‬בשפת מכונה ושומר אותו‬
‫על הדיסק‪ .‬בשלב זה רק נבדקת תקינות הסינטקס בפונקציות‪.‬‬
‫ה‪ linker -‬קושר את קובץ ה‪ obj -‬עם הספריות‪ ,‬ויוצר קובץ ‪ exe‬המוכן‬
‫להרצה ונשמר בדיסק‪ .‬כלומר‪ ,‬מקשר בין קריאה לפונקציה‪ ,‬למימוש‬
‫שלה‪.‬‬
‫‪Primary Memory‬‬
‫‪Loader‬‬
‫‪..‬‬
‫‪..‬‬
‫‪..‬‬
‫‪Disk‬‬
‫‪Primary Memory‬‬
‫‪CPU‬‬
‫‪..‬‬
‫‪..‬‬
‫‪..‬‬
‫‪36‬‬
‫בעת ההרצה‪ ,‬טוענים את התוכנית מהדיסק לזיכרון הראשי‬
‫עם הרצת התוכנית‪ ,‬ה‪ cpu -‬עובר על כל פקודה ומריץ אותה‬
© Keren Kalif
‫דוגמא לשגיאת קומפילציה‬
#include <stdio.h>
void foo(int x)
}
printf("the number is %d\n");
{
void main()
{
for (i=0 ; i < 5 ; i++)
}
printf("%d ", i);
goo(i);
{
}
:‫נקבל את שגיאת הקומפילציה הבאה‬
error C2065: 'i' : undeclared identifier
:goo ‫הקומפיילר רק נותן אזהרה שהוא לא מוצא את‬
warning C4013: 'goo' undefined; assuming
extern returning int
‫ הקומפיילר אינו ממשיך לתהליך לינקר‬,‫במידה ויש שגיאות קומפילציה‬
37
© Keren Kalif
‫דוגמא לשגיאת לינקר‬
#include <stdio.h>
void foo(int x)
}
printf("the number is %d\n");
{
void main()
{
int i;
}
for (i=0 ; i < 5 ; i++)
}
printf("%d ", i);
goo(i);
{
‫תוכנית זו מתקמפלת (אין שגיאות סינטקטיות בתוך‬
,‫הפונקציות) ולכן הקומפיילר עובר לתהליך הלינקר‬
:‫ומוציא את השגיאה הבאה‬
error LNK2019: unresolved external symbol
_goo referenced in function _main
38
© Keren Kalif
‫יצירת מספרים אקראיים‬
‫ אשר מגרילה מספר‬rand() ‫ קיימת הפונקציה‬C ‫בשפת‬
MAX_RAND ‫ לקבוע‬0 ‫טווח הערכים שיוגרלו הינו בין‬
stdlib.h ‫ קבוע זה מוגדר בספריה‬


#include <stdio.h>
#include <stdlib.h> // for ‘RAND_MAX’
void main()
}
int i;
printf("rand gives value between 0-%d\n", RAND_MAX);
for (i=0 ; i < 5 ; i++)
printf("%d ", rand());
}
printf("\n");
39
‫‪© Keren Kalif‬‬
‫יצירת מספרים אקראיים (‪)2‬‬
‫‪40‬‬
‫‪‬‬
‫‪ rand‬פועלת עפ"י אלגוריתם קבוע המתבסס על מספר‬
‫התחלתי כלשהו (נקרא ‪ ,)seed number‬ולכן תמיד בכל‬
‫הרצה תחזיר ערכים זהים‬
‫‪‬‬
‫כדי ש‪ rand -‬באמת תחזיר מספרים אקראיים‪ ,‬כלומר תתבסס‬
‫על מספר התחלתי שונה בכל פעם‪ ,‬יש להפעיל את הפונקציה‬
‫‪ srand‬עם ערך התחלתי המייצג את הזמן הנוכחי (והוא אכן‬
‫יחודי בכל הרצה)‬
© Keren Kalif
)3( ‫יצירת מספרים אקראיים‬
#include <stdio.h>
#include <stdlib.h> // for 'RAND_MAX'
#include <time.h> // for 'time'
void main()
}
int i;
‫ מחזירה מספר המייצג את הזמן הנוכחי‬time(NULL)
)1.1.1970 -‫(מספר השניות שעברו מאז ה‬
srand ( time(NULL) ); // initialize random seed
printf("rand gives value between 0-%d\n", RAND_MAX);
for (i=0 ; i < 5 ; i++)
printf("%d ", rand());
printf("\n");
{
41
© Keren Kalif
‫ יצירת מספרים אקראיים בטווח מסוים‬:‫תזכורת‬
#include <stdio.h>'
#include <time.h> // for 'time'
void main()
}
int i;
srand ( time(NULL) ); // initialize random seed
printf("rand gives value between 0-6\n");
for (i=0 ; i < 5 ; i++)
printf("%d ", rand()%6+1);
printf("\n");
{
‫ ומאחר‬,0-5 ‫ תחזיר לנו ערכים בין‬%6 ‫פעולת‬
..1 ‫ הוספנו‬1-6 ‫ואנחנו רוצים בטווח בין‬
42
‫מערכים‬
‫הגדרת מערכים‪ ,‬מערכים בזכרון‪ ,‬גודל של מערך‪ ,‬אתחול מערך‪ ,‬השמת‬
‫מערכים‪ ,‬מערך רב‪-‬מימדי‪ ,‬העברת מערך לפונקציה‬
‫‪© Keren Kalif‬‬
‫מערכים‬
‫‪‬‬
‫בשפת ‪ C‬מערך הוא אוסף של משתנים מאותו טיפוס המוגדרים‬
‫על ה‪stack -‬‬
‫‪‬‬
‫‪‬‬
‫בניגוד לשפת ‪ JAVA‬בה מערך הוא אובייקט המוקצה ונמצא על ה‪-‬‬
‫‪heap‬‬
‫לכן יש להגדיר את גודל המערך עם יצירתו‬
‫‪‬‬
‫הגודל צריך להיות ידוע בזמן קומפילציה (לכן אינו יכול להיות משתנה)‬
‫הסוגריים [ ] יהיו צמודים‬
‫לשם המשתנה‬
‫‪44‬‬
‫)(‪void main‬‬
‫{‬
‫;]‪int arr1[3‬‬
‫}‬
© Keren Kalif
‫דוגמא למערך בזיכרון‬
int: arr1[]
#define SIZE 3
void main()
{
int arr1[SIZE], x=4, arr2[SIZE];
}
int: x
int: arr2[]
???
1000
???
1004
???
1008
4
1012
???
1016
???
1020
???
1024
‫ כמו כל משתנה שלא אותחל‬,‫ערכם של איברי המערך הוא זבל‬
SIZE*sizeof(type) :‫גודל המערך בזיכרון‬
3*sizeof(int) = 3*4 = 12 :‫ובדוגמא זו‬
45
© Keren Kalif
‫גודל של מערך‬
‫גודל המערך יוחזק במשתנה נפרד‬
‫ בה המערך "יודע" את גודלו‬JAVA ‫בניגוד לשפת‬


#define SIZE 5
void main()
{
int arr[SIZE], i;
printf("Please enter %d numbers: ", SIZE);
for (i=0 ; i < SIZE ; i++)
scanf("%d", &arr[i]);
printf("The numbers are: ");
for (i=0 ; i < SIZE ; i++)
printf("%d ", arr[i]);
printf("\n");
}
46
‫‪© Keren Kalif‬‬
‫אתחול מערך‬
‫‪‬‬
‫‪‬‬
‫כאשר מגדירים מערך ערכי איבריו הוא זבל‬
‫ניתן לאתחל את איברי המערך באחת מהדרכים הבאות‪:‬‬
‫‪//arr1[0]=5, arr1[1]=3, arr1[2]=1‬‬
‫‪‬‬
‫‪47‬‬
‫;}‪int arr1[3] = {5, 3, 1‬‬
‫‪//arr2[0]=5, arr2[1]=3, arr2[2]=1‬‬
‫!‪and the size of the array is 3‬‬
‫;}‪int arr2[] = {5, 3, 1‬‬
‫‪//arr3[0]=5, arr3[1]=0, arr3[2]=0‬‬
‫;}‪int arr3[3] = {5‬‬
‫‪//arr4[0]=0, arr4[1]=0, arr4[2]=0‬‬
‫;}‪int arr4[3] = {0‬‬
‫נשים לב כי רק בעת האיתחול ניתן לתת ערך לכמה איברים‬
‫יחד! כל נתינת ערך בהמשך הינה השמה‪ ,‬ולא איתחול‪ ,‬ולכן‬
‫יבוצע על כל איבר בנפרד‬
© Keren Kalif
‫ הגדרת הגודל והערכים‬:‫אתחול מערך‬
:‫ עבור המערכים הבאים‬
int numbers[3] = {5, 3, 1};
char letters[3] = {‘m’, ‘A’, ‘k’};
int[]: numbers
5
3
1
char[]: letters
‘m’
‘A’
‘k’
:‫ הזכרון יראה כך‬
1000
1004
1008
1012
1013
1014
48
‫‪© Keren Kalif‬‬
‫אתחול מערך‪ :‬הגדרת הערכים בלבד‬
‫‪‬‬
‫עבור המערך הבא‪:‬‬
‫‪‬‬
‫הזכרון יראה כך‪:‬‬
‫‪‬‬
‫נשים לב שאין צורך בהגדרת גודל המערך‪ ,‬הקומפיילר יודע‬
‫זאת לבד לפי כמות הערכים שאותחלו‬
‫;}‪double numbers[] = {5, 3.2, 1.1‬‬
‫‪1000‬‬
‫‪1008‬‬
‫‪1016‬‬
‫‪49‬‬
‫‪5.0‬‬
‫‪double[]: numbers‬‬
‫‪3.2‬‬
‫‪1.1‬‬
‫‪© Keren Kalif‬‬
‫אתחול מערך‪ :‬הגדרת גודל וחלק מהערכים‬
‫‪‬‬
‫כאשר נגדיר מערך באופן הבא‪:‬‬
‫;}‪int numbers[3] = {5‬‬
‫‪‬‬
‫הזכרון יראה כך‪:‬‬
‫‪‬‬
‫כאשר מאתחלים את איברי המערך באופן חלקי‪ ,‬שאר האיברים‬
‫מקבלים ערך ‪( 0‬בניגוד לזבל שהיה אם לא היינו מאתחלים‬
‫כלל)‬
‫‪1000‬‬
‫‪1004‬‬
‫‪1008‬‬
‫‪50‬‬
‫‪5‬‬
‫‪int[]: numbers‬‬
‫‪0‬‬
‫‪0‬‬
‫‪© Keren Kalif‬‬
‫אתחול מערך‪ :‬איפוס כל איברי המערך‬
‫‪‬‬
‫כאשר נגדיר מערך באופן הבא‪:‬‬
‫;}‪int numbers[3] = {0‬‬
‫‪ ‬הזכרון יראה כך‪:‬‬
‫‪1000‬‬
‫‪1004‬‬
‫‪1008‬‬
‫‪0‬‬
‫‪int[]:numbers‬‬
‫‪0‬‬
‫‪0‬‬
‫‪ ‬זהו מקרה פרטי של צורת האתחול הקודמת‬
‫‪51‬‬
© Keren Kalif
sizeof ‫הפונקציה‬
‫ היא פונקציה המקבלת משתנה או טיפוס ומחזירה את‬sizeof
‫מספר הבתים שהוא תופס בזיכרון‬

void main()
{
int
num;
double d;
char
ch;
printf("sizeof(int)=%d,\t sizeof(num)=%d\n",
sizeof(int), sizeof(num));
printf("sizeof(double)=%d,\t sizeof(d)=%d\n",
sizeof(double), sizeof(d));
printf("sizeof(char)=%d,\t sizeof(ch)=%d\n",
sizeof(char), sizeof(ch));
}
52
© Keren Kalif
‫חישוב גודל המערך‬
SIZE*sizeof(type) ‫גודל המערך בזיכרון הוא‬
‫יהיו מקרים בהם נרצה לדעת בזמן ריצה כמה איברים יש‬
:‫ למשל‬,‫ ולא תמיד הגודל מוגדר לנו‬,‫במערך‬


void main()
{
int i, arr[] = {4, 3, 2, 7};
int size = sizeof(arr) / sizeof(int);
printf("There are %d numbers in the array: ", size);
for (i=0 ; i < size ; i++)
printf("%d ", arr[i]);
printf("\n");
}
53
‫‪© Keren Kalif‬‬
‫חישוב גודל המערך (‪)2‬‬
‫‪‬‬
‫ניתן לחשב את כמות האיברים במערך גם באופן הבא‪:‬‬
‫)(‪void main‬‬
‫{‬
‫;}‪int arr[] = {4, 3, 2, 7‬‬
‫)]‪sizeof(arr[0‬‬
‫;)‪int size = sizeof(arr) / sizeof(int‬‬
‫…‬
‫}‬
‫‪‬‬
‫‪54‬‬
‫דרך זו עדיפה‪ ,‬שכן אם נשנה את טיפוס איברי המערך לא‬
‫נצטרך לתקן את השורה המחשבת את ה‪size -‬‬
‫‪© Keren Kalif‬‬
‫השמת מערכים‬
‫‪‬‬
‫כדי לבצע השמה בין משתנים מאותו הסוג אנו משתמשים‬
‫באופרטור =‬
‫;‪int x, y=5‬‬
‫;‪x = y‬‬
‫‪‬‬
‫עבור מערכים לא ניתן לבצע זאת‪:‬‬
‫‪‬‬
‫השמה בין מערכים תבוצע בעזרת לולאה‪ ,‬בה נעתיק איבר‪-‬‬
‫איבר‬
‫;]‪int arr1[]={1,2,3}, arr2[3‬‬
‫;‪arr2 = arr1‬‬
‫‪55‬‬
‫וזה בניגוד לשפת ‪ JAVA‬בה השמת מערכים משנה את‬
‫ההפניה (מאחר ומערך ב‪ JAVA -‬הוא אובייקט)‬
© Keren Kalif
‫ דוגמא‬- ‫השמת מערכים‬
void main()
{
int arr1[] = {1,2,3}, arr2[3], i;
// arr2 = arr1; // DOESN'T COMPILE!!
printf("Elements in arr2 before: ");
for (i=0 ; i < 3 ; i++)
printf("%d ", arr2[i]);
for (i=0 ; i < 3 ; i++)
arr2[i] = arr1[i];
printf("\nElements in arr2 after: ");
for (i=0 ; i < 3 ; i++)
printf("%d ", arr2[i]);
printf("\n");
}
56
‫‪© Keren Kalif‬‬
‫מערך דו‪-‬מימדי‬
‫‪‬‬
‫בהגדרת מערך חד‪-‬מימדי מגדירים את כמות התאים בו (מספר‬
‫העמודות)‪:‬‬
‫]‪arr[3‬‬
‫‪‬‬
‫]‪arr[2‬‬
‫]‪arr[1‬‬
‫בהגדרת מערך דו‪-‬מימדי נגדיר את כמות התאים בו ע"י ציון‬
‫מספר השורות ומספר העמודות‪:‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫]‪[0][3‬‬
‫]‪[1][3‬‬
‫‪‬‬
‫‪57‬‬
‫]‪arr[0‬‬
‫;]‪int arr[4‬‬
‫]‪[0][2‬‬
‫]‪[1][2‬‬
‫]‪[0][1‬‬
‫]‪[1][1‬‬
‫]‪[0][0‬‬
‫;]‪int arr[2][4‬‬
‫]‪[1][0‬‬
‫מערך דו‪-‬מימדי הוא למעשה מטריצה‪ ,‬או ניתן להסתכל עליו‬
‫כמערך של מערכים‬
‫מערך דו‪-‬מימדי – דוגמא‪ :‬קליטת ציונים לכמה‬
‫כיתות והדפסתם ‪ -‬פלט‬
‫‪© Keren Kalif‬‬
‫‪58‬‬
‫ קליטת ציונים לכמה‬:‫מימדי – דוגמא‬-‫מערך דו‬
‫כיתות והדפסתם‬
© Keren Kalif
#define NUM_CLASSES
3
#define STUDENTS_IN_CLASS 5
void main()
{
int grades[NUM_CLASSES][STUDENTS_IN_CLASS];
int i, j;
printf("Please enter grades for students in %d classes:\n", NUM_CLASSES);
for (i=0 ; i < NUM_CLASSES ; i++)
{
printf("Please enter grades for %d students in class
#%d:",
STUDENTS_IN_CLASS, i+1);
for (j=0 ; j < STUDENTS_IN_CLASS ; j++)
scanf("%d", &grades[i][j]);
}
printf("The grades in all classes:\n");
for (i=0 ; i < NUM_CLASSES ; i++)
{
printf("Class #%d: ", i+1);
for (j=0 ; j < STUDENTS_IN_CLASS ; j++)
printf("%d ", grades[i][j]);
printf("\n");
}
59
‫‪© Keren Kalif‬‬
‫מערך דו‪-‬מימדי – ייצוגו בזיכרון‬
‫‪‬‬
‫כמו מערך חד‪-‬מימדי‪ ,‬גם מערך דו‪-‬מימדי נשמר בזיכרון ברצף‪,‬‬
‫כאשר איברי השורה הראשונה נשמרים קודם‪ ,‬ומיד אח"כ איברי‬
‫השורה השניה וכו'‬
‫;]‪int arr[2][4‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫]‪[0][3‬‬
‫]‪[0][2‬‬
‫]‪[0][1‬‬
‫]‪[0][0‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫]‪[1][3‬‬
‫‪60‬‬
‫‪1000‬‬
‫???‬
‫‪arr[0][0] ‬‬
‫‪1004‬‬
‫???‬
‫‪arr[0][1] ‬‬
‫‪1008‬‬
‫???‬
‫‪arr[0][2] ‬‬
‫‪1012‬‬
‫???‬
‫‪arr[0][3] ‬‬
‫‪1016‬‬
‫???‬
‫‪arr[1][0] ‬‬
‫‪1020‬‬
‫???‬
‫‪arr[1][1] ‬‬
‫‪1024‬‬
‫???‬
‫‪arr[1][2] ‬‬
‫‪1028‬‬
‫???‬
‫‪arr[1][3] ‬‬
‫]‪[1][2‬‬
‫]‪[1][1‬‬
‫]‪[1][0‬‬
‫‪int[2][4]:‬‬
‫‪‬‬
‫ההתאמה בין המקום במערך‬
‫הדו ‪-‬מימדי למערך החד‪-‬‬
‫מימדי היא‬
‫‪COLUMNS * i + j‬‬
‫‪© Keren Kalif‬‬
‫מערך דו‪-‬מימדי – ייצוגו בזיכרון‬
‫‪‬‬
‫כמו מערך חד‪-‬מימדי‪ ,‬גם מערך דו‪-‬מימדי נשמר בזיכרון ברצף‪,‬‬
‫כאשר איברי השורה הראשונה נשמרים קודם‪ ,‬ומיד אח"כ איברי‬
‫השורה השניה וכו'‬
‫;]‪int arr[2][4‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫]‪[0][3‬‬
‫]‪[0][2‬‬
‫]‪[0][1‬‬
‫]‪[0][0‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫]‪[1][3‬‬
‫‪61‬‬
‫‪1000‬‬
‫???‬
‫‪arr[0][0] ‬‬
‫‪1004‬‬
‫???‬
‫‪arr[0][1] ‬‬
‫‪1008‬‬
‫???‬
‫‪arr[0][2] ‬‬
‫‪1012‬‬
‫???‬
‫‪arr[0][3] ‬‬
‫‪1016‬‬
‫???‬
‫‪arr[1][0] ‬‬
‫‪1020‬‬
‫???‬
‫‪arr[1][1] ‬‬
‫‪1024‬‬
‫???‬
‫‪arr[1][2] ‬‬
‫‪1028‬‬
‫???‬
‫‪arr[1][3] ‬‬
‫]‪[1][2‬‬
‫]‪[1][0‬‬
‫]‪[1][1‬‬
‫‪int[2][4]:‬‬
‫‪‬‬
‫ההתאמה בין המקום במערך‬
‫הדו ‪-‬מימדי למערך החד‪-‬‬
‫מימדי היא‬
‫‪COLUMNS * i + j‬‬
‫‪‬‬
‫למשל‪ arr[0][3] :‬נמצא‬
‫במקום ‪4*0+3 = 3‬‬
‫‪© Keren Kalif‬‬
‫מערך דו‪-‬מימדי – ייצוגו בזיכרון‬
‫‪‬‬
‫כמו מערך חד‪-‬מימדי‪ ,‬גם מערך דו‪-‬מימדי נשמר בזיכרון ברצף‪,‬‬
‫כאשר איברי השורה הראשונה נשמרים קודם‪ ,‬ומיד אח"כ איברי‬
‫השורה השניה וכו'‬
‫;]‪int arr[2][4‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫]‪[0][3‬‬
‫]‪[0][2‬‬
‫]‪[0][1‬‬
‫]‪[0][0‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫‪arr‬‬
‫]‪[1][3‬‬
‫‪62‬‬
‫‪1000‬‬
‫???‬
‫‪arr[0][0] ‬‬
‫‪1004‬‬
‫???‬
‫‪arr[0][1] ‬‬
‫‪1008‬‬
‫???‬
‫‪arr[0][2] ‬‬
‫‪1012‬‬
‫???‬
‫‪arr[0][3] ‬‬
‫‪1016‬‬
‫???‬
‫‪arr[1][0] ‬‬
‫‪1020‬‬
‫???‬
‫‪arr[1][1] ‬‬
‫‪1024‬‬
‫???‬
‫‪arr[1][2] ‬‬
‫‪1028‬‬
‫???‬
‫‪arr[1][3] ‬‬
‫]‪[1][2‬‬
‫‪int[2][4]:‬‬
‫]‪[1][1‬‬
‫‪‬‬
‫]‪[1][0‬‬
‫ההתאמה בין המקום במערך‬
‫הדו ‪-‬מימדי למערך החד‪-‬‬
‫מימדי היא‬
‫‪COLUMNS * i + j‬‬
‫‪‬‬
‫‪‬‬
‫למשל‪ arr[0][3] :‬נמצא‬
‫במקום ‪4*0+3 = 3‬‬
‫למשל‪ arr[1][2] :‬נמצא‬
‫במקום ‪4*1+2 = 6‬‬
‫‪© Keren Kalif‬‬
‫מערך דו‪-‬מימדי ‪ -‬איתחול‬
‫‪‬‬
‫ניתן לאתחל מערך דו‪-‬מימדי באחת מהדרכים הבאות‪:‬‬
‫;} }‪int arr1[2][3] = { {1,2,3}, {4,5,6‬‬
‫;} }‪int arr2[][3] = { {1,2,3}, {4,5,6‬‬
‫ניתן לאתחל בלי ציון מספר השורות‪ ,‬אבל תמיד חייבים לציין את מספר העמודות‬
‫;} }‪int arr3[2][3] = { {5,5‬‬
‫;}‪int arr4[2][3] = {0‬‬
‫‪63‬‬
© Keren Kalif
‫מימדי – חישוב מספר השורות‬-‫מערך דו‬
#include <stdio.h>
#define NUM_OF_COLS 3
void main()
{
int arr[][NUM_OF_COLS ] = { {1,2,3}, {4,5,6} };
int i, j;
int numOfRows = sizeof(arr)/sizeof(int)/NUM_OF_COLS;
for (i=0 ; i < numOfRows ; i++)
{
for (j=0 ; j < NUM_OF_COLS ; j++)
printf("%d ", arr[i][j]);
printf("\n");
}
}
64
‫‪© Keren Kalif‬‬
‫מערך רב‪-‬מימדי‬
‫‪‬‬
‫‪‬‬
‫עד כה ראינו מערכים חד‪-‬מימדיים ומערכים דו‪-‬מימדיים‬
‫ניתן להרחיב את ההגדרה לכל מספר סופי של מימדים‬
‫‪‬‬
‫למשל‪ :‬מערך תלת –מימדי‬
‫;]‪int matrix[LENGTH][HEIGHT][DEPTH‬‬
‫‪‬‬
‫דוגמא לשימוש‪ :‬נרצה לשמור ממוצע ציונים עבור ‪ 5‬בתי‪-‬ספר‪ ,‬כאשר‬
‫בכל בית‪-‬ספר יש ‪ 10‬כיתות‪ ,‬ובכל כיתה ‪ 30‬סטודנטים‪:‬‬
‫;]‪double average[5][10][30‬‬
‫‪‬‬
‫‪65‬‬
‫במקרה זה נשתמש בלולאה‪ ,‬בתוך לולאה‪ ,‬בתוך לולאה‪..‬‬
© Keren Kalif
– ‫מימדי‬-‫מערך רב‬
#define SCHOOLS
3
#define CLASSES
2
‫ הספר‬-‫דוגמאת נתוני בתי‬
#define STUDENTS 4
void main()
{
float grades[SCHOOLS][CLASSES][STUDENTS] = {
{ {90, 100, 95, 88}, {87, 70, 90, 98} },
{ {88, 75, 80, 60}, {55, 87, 90, 82} },
};
{ {60, 91, 40, 95}, {77, 66, 88, 99} }
int i, j, k;
for (i=0 ; i < SCHOOLS ; i++)
{
printf("Classes in school #%d:\n", i+1);
for (j=0 ; j < CLASSES ; j++)
{
printf(" Grades in class #%d: ", j+1);
for (k=0 ; k < STUDENTS ; k++)
printf("%.2f ", grades[i][j][k]);
printf("\n");
}
printf("\n");
}
}
66
© Keren Kalif
by value ‫תזכורת למשמעות של העברה‬
void incNumber(int x)
{
printf("In function: number before: %d\n", x);
x++;
printf("In function: number after: %d\n", x);
{
void main()
}
int num = 3;
4
3 2000
incNumber ‫הזיכרון של‬
int:
x
int:
num
??
3
1000
main ‫הזיכרון של‬
printf("In main: number before function: %d\n", num);
incNumber(num);
printf("In main: number after function: %d\n", num);
{
67
© Keren Kalif
‫ דוגמא‬- ‫מערכים כפרמטר לפונקציה‬
void incArray(int arr[], int size)
{
int i;
for (i=0 ; i < size ; i++)
arr[i]++;
}
void printArray(int arr[], int size)
{
int i;
for (i=0 ; i < size ; i++)
printf("%d ", arr[i]);
printf("\n");
}
void main()
{
int arr[] = {4,3,8};
int size = sizeof(arr)/sizeof(arr[0]);
printf("Orig array: ");
printArray(arr, size);
incArray(arr, size);
}
printf("Array after increment: ");
printArray(arr, size);
68
‫‪© Keren Kalif‬‬
‫מערכים כפרמטר לפונקציה‬
‫‪69‬‬
‫‪‬‬
‫ראינו כי מערך מתנהג באופן שונה מאשר משתנה מטיפוס בסיסי‬
‫כאשר מעבירים אותו לפונקציה‬
‫‪‬‬
‫כאשר מעבירים מערך לפונקציה‪ ,‬לא מועבר עותק של המערך‬
‫)‪ ,(by value‬אלא מועברת רק כתובת ההתחלה של המערך‬
‫‪‬‬
‫לכן כאשר מעבירים מערך לפונקציה ומשנים אותו‪ ,‬השינוי‬
‫משפיע על המערך המקורי (ולא על ההעתק)‬
‫‪‬‬
‫נסביר לעומק כאשר נלמד את השיעור על מצביעים‬
‫‪© Keren Kalif‬‬
‫העברת מספר האיברים במערך כפרמטר לפונקציה‬
‫‪70‬‬
‫‪‬‬
‫ראינו כי כאשר שלחנו מערך לפונקציה העברנו גם את‬
‫מספר האיברים שבו‪ ,‬ולא התבססנו על ערך קבוע‬
‫‪‬‬
‫זאת כדי שהפונקציה תהיה מספיק כללית על‪-‬מנת שתבצע‬
‫את העבודה על מערכים בגדלים שונים‬
‫‪© Keren Kalif‬‬
‫דוגמא איך פונקציה המקבלת מערך לא צריכה להיות‬
‫שימו לב‪ :‬הקבוע מוגדר באותיות גדולות‪ .‬דגש בהמשך‪...‬‬
‫‪#define SIZE 3‬‬
‫)][‪void printArray(int arr‬‬
‫הפונקציה יודעת לטפל אך ורק במערכים בגודל הקבוע‬
‫{‬
‫‪ ,SIZE‬ולא תעשה את המתבקש עבור מערכים בגודל שונה‬
‫;‪int i‬‬
‫)‪for (i=0 ; i < SIZE ; i++‬‬
‫;)]‪printf("%d ", arr[i‬‬
‫;)"‪printf("\n‬‬
‫{‬
‫)(‪void main‬‬
‫}‬
‫;}‪int arr1[SIZE] = {1,2,3}, arr2[5]={10,20,30,40,50‬‬
‫;)" ‪printf("arr1:‬‬
‫;)‪printArray(arr1‬‬
‫‪71‬‬
‫;)" ‪printf("arr2:‬‬
‫;)‪printArray(arr2‬‬
‫{‬
© Keren Kalif
‫דוגמא איך פונקציה המקבלת מערך כן צריכה להיות‬
#define SIZE 3
SIZE ‫ והקבוע‬size ‫ הפרמטר‬:‫שימו לב‬
.)case sensitive ‫שונים (הקומפיילר הוא‬
...‫דגש בהמשך‬
void printArray(int arr[], int size)
{
int i;
‫הפונקציה מקבלת כפרמטר נוסף את כמות האיברים‬
for (i=0 ; i < size; i++)
‫ ולכן יודעת לטפל במערך בכל גודל‬,‫שעליה להדפיס‬
printf("%d ", arr[i]);
printf("\n");
{
void main()
}
int arr1[SIZE] = {1,2,3}, arr2[5]={10,20,30,40,50};
printf("arr1: ");
printArray(arr1, SIZE);
printf("arr2: ");
printArray(arr2, 5);
{
72
‫‪© Keren Kalif‬‬
‫שימו לב‪ :‬שגיאה נפוצה!‬
‫‪‬‬
‫מי שלא מקפיד על הגדרת קבועים באותיות גדולות עלול‬
‫להיתקל בשגיאת הקומפילציה הבאה‪:‬‬
‫בעקבות פקודת ה‪ :define -‬בכל מקום‬
‫שהקומפיילר רואה ‪ size‬הוא מחליף אותו‬
‫בערך ‪3‬‬
‫)‪void printArray(int arr[], int size‬‬
‫ולכן מה שהקומפיילר רואה בתור‬
‫}‬
‫הפרמטר השני‪int 3 :‬‬
‫;‪int i‬‬
‫ו‪ 3 -‬אינו שם תקף למשתנה‬
‫)‪for (i=0 ; i < size ; i++‬‬
‫;)]‪printf("%d ", arr[i‬‬
‫;)"‪printf("\n‬‬
‫{‬
‫{…} )(‪void main‬‬
‫‪#define size 3‬‬
‫‪73‬‬
© Keren Kalif
– ‫העברת מטריצה לפונקציה‬
‫דוגמא‬
‫הפונקציה מספיק כללית כדי‬
‫לבצע את העבודה עבור‬
‫מטריצה עם כל מספר שורות‬
#define COLS 5
void setMatrix(int mat[][COLS], int rows)
}
int i, j;
for (i=0 ; i < rows ; i++)
}
for (j=0 ; j < COLS ; j++)
mat[i][j] = i*10+j;
{
{
void printMatrix(int mat[][COLS], int rows)
}
int i, j;
for (i=0 ; i < rows ; i++)
}
for (j=0 ; j < COLS ; j++)
printf("%4d", mat[i][j]);
printf("\n");
{
{
void main()
}
int mat1[3][COLS];
int mat2[4][COLS];
setMatrix(mat1, 3);
setMatrix(mat2, 4);
printf("Matrix 1:\n");
printMatrix(mat1, 3);
printf("Matrix 2:\n");
printMatrix(mat2, 4);
{
74
‫‪© Keren Kalif‬‬
‫העברת מטריצה לפונקציה –‬
‫דוגמא (פלט)‬
‫‪75‬‬
‫‪© Keren Kalif‬‬
‫העברת מטריצה לפונקציה‬
‫‪‬‬
‫‪‬‬
‫גם כאשר מעבירים מטריצה לפונקציה‪ ,‬עוברת כתובת‬
‫ההתחלה בלבד‪ ,‬ולכן שינוי המטריצה בפונקציה משנה את‬
‫המטריצה המקורית‬
‫כאשר מעבירים מטריצה לפונקציה‪ ,‬ניתן לציין רק את כמות‬
‫העמודות ולהשאיר את הסוגריים של השורות ריקים (ולכן‬
‫במקום יש להעביר כפרמטר את מספר השורות)‬
‫‪‬‬
‫‪‬‬
‫‪76‬‬
‫נרצה להעביר את כמות השורות כדי שהפונקציה תהיה מספיק‬
‫כללית לכל מטריצה עם אותו מספר עמודות‬
‫בהמשך נראה שאפשר גם להעביר מטריצות שמספר העמודות בהן‬
‫שונה‬
‫סוגי משתנים‬
‫לוקאלים‪ ,‬גלובלים‪ ,‬סטטים‬
‫‪© Keren Kalif‬‬
‫משתנים מקומיים‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪78‬‬
‫עד כה ראינו שכל הקוד שלנו מורכב מפונקציות‬
‫המשתנים שבתוך כל פונקציה (גם אלו שהועברו כפרמטרים)‬
‫נקראים משתנים מקומיים (לוקאליים) וטווח ההכרה שלהם‬
‫הוא רק בפונקציה בה הם מוגדרים‬
‫ערכו של משתנה לוקאלי נשמר כל עוד אנחנו בפונקציה‪ ,‬והוא‬
‫נמחק ביציאה ממנה‬
‫משתנה לוקאלי מאוחסן במחסנית של הפונקציה‬
‫בכל קריאה חדשה לפונקציה המשתנים מאותחלים מחדש‬
‫ונמחקים בסיום ביצוע הפונקציה‬
‫ערכו זבל במידה ולא אותחל‬
© Keren Kalif
‫משתנה סטטי הוא משתנה שערכו‬
‫נשמר בין הקריאות השונות לפונקציה‬
#include <stdio.h>
‫כדי להגדיר משתנה סטטי‬
int counter()
‫נשתמש במילת המפתח‬
{
‫ לפני טיפוס המשתנה‬static
static int count = 0;
‫משתנה סטטי מאותחל רק‬
count++;
‫בקריאה הראשונה לפונקציה‬
return count;
}
‫משתנים סטטיים‬
counter ‫הזיכרון של‬
main -‫הזיכרון של ה‬
3
2
1
0
counter::count = ?
void main()
data segment -‫זיכרון ה‬
{
printf("'counter' was called %d times\n",
printf("'counter' was called %d times\n",
printf("'counter' was called %d times\n",
}
counter() );
counter() );
counter() );
counter
main
( line 3)
1)
2)
‫מחסנית הקריאות‬
79
‫‪© Keren Kalif‬‬
‫משתנים סטטיים‬
‫‪80‬‬
‫‪‬‬
‫בדומה למשתנים מקומיים‪ ,‬המשתנים הסטטיים מוכרים אך‬
‫ורק בתוך הפונקציה אשר בה הם הוגדרו‬
‫‪‬‬
‫משך חייהם מרגע תחילת הריצה של התוכנית ועד סיום ריצת‬
‫התוכנית‬
‫‪‬‬
‫מאחר ושטח הזיכרון של כל פונקציה נמחק עם היציאה ממנה‪,‬‬
‫משתנים סטטיים נשמרים באזור זיכרון אחר הנקרא ‪data-‬‬
‫‪ ,segment‬הקיים לאורך כל חיי התוכנית‪ ,‬והם משתחררים רק‬
‫עם היציאה מהתוכנית‬
© Keren Kalif
‫משתנה גלובלי מוגדר בראש התוכנית‬
)‫(אינו משויך לאף פונקציה‬
‫משתנים גלובליים‬
‫כל הפונקציות שכתובות‬
‫בקובץ בו הוגדר יכולות‬
‫לגשת אליו ולשנות את ערכו‬
int global = 3;
void incGlobal()
11
3
4
global = 10
{
data segment -‫זיכרון ה‬
global++;
printf("In function: global=%d\n", global);
}
incGlobal ‫הזיכרון של‬
void main()
{
printf("At first, global=%d\n", global);
main -‫הזיכרון של ה‬
incGlobal();
printf("After function (1), global=%d\n", global);
incGlobal
global = 10;
printf("In main after change global, global=%d\n", global);
main
incGlobal();
printf("After function (2), global=%d\n", global);
‫מחסנית הקריאות‬
}
81
‫‪© Keren Kalif‬‬
‫משתנים גלובליים‬
‫‪‬‬
‫במידה ולא אותחל ערכו ‪ ,0‬ולא זבל‬
‫‪‬‬
‫משתנה גלובלי קיים לאורך כל חיי התוכנית‬
‫‪‬‬
‫השימוש בו הוא בעייתי ולכן נשמר למצבים מיוחדים בלבד‬
‫‪‬‬
‫‪‬‬
‫‪82‬‬
‫כל אחד יכול לשנות אותו‪ ,‬מה שפוגע ברעיון של הסתרת המידע‬
‫ושכל פונקציה מסתמכת רק על נתונים שהם פנימיים לה‬
‫מאחר ואינו משויך לאף פונקציה‪ ,‬הוא אינו נמצא על המחסנית‪,‬‬
‫אלא על חלקת הזיכרון המשותפת לכל הפונקציות ה‪data- -‬‬
‫‪segment‬‬
‫‪© Keren Kalif‬‬
‫השוואה בין סוגי המשתנים השונים‬
‫משתנה מקומי (רגיל) משתנה גלובלי‬
‫‪83‬‬
‫משתנה סטטי‬
‫היכן מוגדר‬
‫בתוך הפונקציה‬
‫מחוץ לפונקציות‬
‫בתוך הפונקציה‬
‫טווח‬
‫ההכרה‬
‫בפונקציה בה הוא‬
‫מוגדר‬
‫מנקודת הגדרתו ומטה‬
‫בפונקציה בה הוא‬
‫מוגדר‬
‫מי יכול‬
‫לגשת אליו‬
‫רק הפונקציה בה הוא‬
‫מוגדר‬
‫כל מקום בקוד הנמצא מתחת רק הפונקציה בה‬
‫הוא מוגדר‬
‫להגדרתו‬
‫מתי‬
‫מאותחל‬
‫בכל פעם כשנכנסים‬
‫לפונקציה‬
‫בתחילת ריצת התוכנית‬
‫רק בפעם הראשונה‬
‫שמגיעים אליו‬
‫‪© Keren Kalif‬‬
‫השוואה בין סוגי המשתנים השונים‬
‫משתנה מקומי (רגיל) משתנה גלובלי‬
‫‪84‬‬
‫משתנה סטטי‬
‫היכן מוגדר‬
‫בתוך הפונקציה‬
‫מחוץ לפונקציות‬
‫בתוך הפונקציה‬
‫טווח‬
‫ההכרה‬
‫בפונקציה בה הוא‬
‫מוגדר‬
‫מנקודת הגדרתו ומטה‬
‫בפונקציה בה הוא‬
‫מוגדר‬
‫מי יכול‬
‫לגשת אליו‬
‫רק הפונקציה בה הוא‬
‫מוגדר‬
‫כל מקום בקוד הנמצא מתחת רק הפונקציה בה‬
‫הוא מוגדר‬
‫להגדרתו‬
‫מתי‬
‫מאותחל‬
‫בכל פעם כשנכנסים‬
‫לפונקציה‬
‫בתחילת ריצת התוכנית‬
‫רק בפעם הראשונה‬
‫שמגיעים אליו‬
‫‪© Keren Kalif‬‬
‫ביחידה זו למדנו‪:‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪85‬‬
‫רקע לשפת ‪C‬‬
‫תוכנית ראשונה בשפת ‪C‬‬
‫הגדרת משתנים בשפת ‪C‬‬
‫קלט ופלט בשפת ‪C‬‬
‫פונקציות‬
‫מערכים‬
‫סוגי משתנים‬
© Keren Kalif
‫תרגול‬
86