מ 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
© Copyright 2025