60
בבבב בבבבב בבבבב בבבבבב8 :בבבבבבב בבבבבב בבבבבבב1 בבבבבב- ' בבבב בבבבב בבבבב ב2

תירגול 8:מצביעים והקצאה דינאמית

  • Upload
    leiko

  • View
    81

  • Download
    0

Embed Size (px)

DESCRIPTION

תירגול 8:מצביעים והקצאה דינאמית. תוכנייה. מצביעים מצביעים ומערכים הקצאה דינמית. מצביעים. תזכורת- כתובות זיכרון. הזיכרון. הזיכרון במחשב מחולק לתאים, כל אחד בגודל בית אחד. תאי הזיכרון ממוספרים. מס' התא נקרא גם הכתובת של התא. תא. כתובת התא. לכל תא בזיכרון יש כתובת. 10. ערך השמור בתא. - PowerPoint PPT Presentation

Citation preview

Page 1: תירגול  8:מצביעים  והקצאה דינאמית

המחשב למדעי מבואדינאמית: 8תירגול והקצאה מצביעים

2מבוא למדעי המחשב מ' - תירגול 1

Page 2: תירגול  8:מצביעים  והקצאה דינאמית

מבוא למדעי המחשב מ' - תירגול 3 2

תוכנייה

מצביעים•ומערכים • מצביעיםדינמית • הקצאה

Page 3: תירגול  8:מצביעים  והקצאה דינאמית

3

מצביעים

2מבוא למדעי המחשב מ' - תירגול

Page 4: תירגול  8:מצביעים  והקצאה דינאמית

4

מאותחל לא

67

10-4

תזכורת- כתובות זיכרון

• , בית בגודל אחד כל לתאים מחולק במחשב הזיכרוןאחד.

• ' . גם נקרא התא מס ממוספרים הזיכרון הכתובתתאי. התא של

#1000

כתובת התא

תא לכלבזיכרון

יש כתובת

#1004

#1008

תא

בתא השמור ערך

#1012

הזיכרון

Page 5: תירגול  8:מצביעים  והקצאה דינאמית

5

תזכורת- כתובות זיכרון

לאכסן • יכולה היא בו בזיכרון איזור מוקצה תוכנית לכל . נקרא זה כתובות אוסף נתוניה הכתובות את של מרחב

התוכנית• . רצופים זיכרון תאי יותר או באחד מאוחסן משתנה כל

המשתנה שמכיל כתובת הראשון התא של הכתובת היאאותו.

הכתובת • את לציין התוכנית על מהזיכרון לקרוא כדי ' ( ' הבתים מס לקרוא שיש הבתים מס ואת הרצויה

) תופס שהמשתנה

Page 6: תירגול  8:מצביעים  והקצאה דינאמית

6

מצביעים

של • זיכרון כתובות המחזיקים משתנים הם מצביעים. אחרים משתנים

•: מצביע על הצהרה

int* iptr;

double *dptr;

char* cptr, c;

char *cptr, c;

ל מצביע על intהצהרה

מצביע על הצהרהdoubleל

משנה לא הכוכבית מיקוםלהגדרה

המשתנה של הטיפוס של? cptrמה הטיפוס ?cמה

את נכתוב בלבול למנוע מנת על * , שהוא המשתנה ליד עם כך השורה

מצביע

Page 7: תירגול  8:מצביעים  והקצאה דינאמית

7

&אופרטור

•: משתנה של כתובתו את מחזיר & אופרטור

דוגמא:•&var_name

int* iptr;

double* dptr;

int a = 3;

double d = 6.4;

iptr = &a;

dptr = &d;

2000

2004

2008

2012

2000

2004

6.4

3

d

a

dptr

iptr

Page 8: תירגול  8:מצביעים  והקצאה דינאמית

8

מצביעים

• , ! הם ולכן דבר לכל משתנים הם מצביעים לב שימו. אחר משתנה כל כמו במחסנית מאוחסנים

זיכרון • כתובת לאחסון הדרושה הבתים כמותהמצביעים היא סוגי לכל תלות זהה ללא

מצביעים עליו בטיפוס

int* iptr;

double* dptr;

int a = 3;

double d = 6.4;

iptr = &a;

dptr = &d;

2000

2004

2008

2012

2000

2004

6.4

3

d

a

dptr

iptr

זיכרון כתובת לאחסון הדרושה הבתים כמותרצה עליו המחשב של בארכיטקטורה תלויה

התוכנית.תופס מצביע כיום המחשבים בתים 8ברוב

(.64מערכות) ביט

Page 9: תירגול  8:מצביעים  והקצאה דינאמית

9

אופרטור *

שעליו * • המשתנה את מחזיר אופרטור מצביע בהינתן: מצביע הוא

מטיפוס: • למשתנה מצביע intדוגמא*ptr_name

int a = 3, b = 5;int* ptr;

ptr = &a;*ptr = 8;b = *ptr + 3;

???2008ptr

b

a

5

38

11

של *intהוא ptrהטיפוס

* ptr כמו מתנהגמשתנה

intמטיפוס

Page 10: תירגול  8:מצביעים  והקצאה דינאמית

10

מתי נשתמש במצביעים?

•? לפונקציה כפרמטר מצביעים נעביר מתינרצה – כאשר לפונקציה משתנה של כתובת של נעביר ערכו את לשנות

המשתנה- המשתנה לזיכרון ישירה גישה לפונקציה מקנים אנו כך) בהרצאה: ) שראינו דוגמא

. Cב-– נרצה אם אחד החזרה ערך פונקציה שמחזירה לכל פונקציהאחד החזרה מערך . יותר מצביעים, באמצעות זאת נממש

: החתימה: עם פונקציה דוגמאלהחזיר .2יכולה ערכים

המשתנה של כערכו יוחזר מהם אחד של .resערכוint foo(int x, int* res)

void swap(int* x, int* y)

Page 11: תירגול  8:מצביעים  והקצאה דינאמית

11

מתי נשתמש במצביעים? )המשך(

למשל ) Cבשפת – רב זיכרון התופסים טיפוסים להגדיר ניתןלפונקציה(. העברתם זה במקרה כלומר ) by valueמערכים

. ) את" לחסוך ניתן מאוד כבדה פעולה תהיה העתקה י עידי על ההעתקה משתנים עבודת של הכתובת העברת

כאלה .מטיפוסים התוכן, את במקום לפונקציה

! תמיד נעשית לפונקציה מצביע העברת לב byשימוvalue ,של פנימי למשתנה מועתק המצביע ערך כלומר

הפונקציה

Page 12: תירגול  8:מצביעים  והקצאה דינאמית

12

Swap)תזכורת(

שני • של התוכן בין המחליפה פונקציה ראינו בהרצאהמטיפוס :intמספרים

•: כך בפונקציה נשתמש

void swap(int* p, int* q)

{

int tmp = *p;

*p = *q;

*q = tmp;

}

float a = 3, b = 7;

swap(&a, &b);

printf("a=%d, b=%d", a, b);

קורה היה מההיינו לא אםמשתמשים במצביעים?

a=7, b=3

Page 13: תירגול  8:מצביעים  והקצאה דינאמית

מצביעים ופונקציות

של : 1תרגיל • רדיוס אורך כקלט המקבלת פונקציה כתבו ) והיקפו ) המעגל שטח את ומחזירה שלם בהכרח לא מעגל

13

#include <stdio.h>#define PI 3.14

void calc_perimeter_and_area(double radius, double* p_perim, double* p_area)

{ *p_perim = 2 * PI * radius; *p_area = PI * (radius * radius);}

Page 14: תירגול  8:מצביעים  והקצאה דינאמית

מצביעים ופונקציות

המקבלת :2תרגיל • פונקציה -a, bמצביעים 3כתבו , cו, , מסודרים שיהיו כך תוכנם את ומחליפה שלמים למספרים

עולה בסדר

14

Page 15: תירגול  8:מצביעים  והקצאה דינאמית

מצביעים ופונקציות

15

#include <stdio.h>

void swap_int(int* p, int* q){ int tmp = *p; *p = *q; *q = tmp;}

void sort3(int* a, int* b, int* c){ if (*a > *b) swap_int(a, b); if (*a > *c) swap_int(a, c); if (*b > *c) swap_int(b, c);}

Page 16: תירגול  8:מצביעים  והקצאה דינאמית

void*

הוא טיפוס לכל דבר, וניתן להגדיר ממנו *voidהטיפוס •משתנים.

ייצג כתובת זיכרון "טהורה", טיפוס זה משמש כאשר אנחנו רוצים ל•ללא כל מידע לגבי הטיפוס שמאוחסן שם.

. *void להמיר כל מצביע למצביע מטיפוס Cניתן בשפת •למעשה, אנו נפטרים כך מהמידע לגבי הטיפוס שמאוחסן בזיכרון,

ונותרים עם כתובת הזיכרון בלבד.

.*void על משתנה מטיפוס *לא ניתן להפעיל אופרטור •

, *voidאם ידוע לנו הטיפוס האמיתי אליו מצביע משתנה מסוג •ניתן להמיר אותו חזרה לסוג המצביע האמיתי באמצעות המרה

מפורשת.16

Page 17: תירגול  8:מצביעים  והקצאה דינאמית

void*דוגמא לשימוש -

הפונקציה • את נרחיבswap )( במגוון שתתמוך כך

טיפוסים.נוסף • פרמטר על נסתמך אנו

הטיפוס את לדעת בכדישל :q ו-pהאמיתי

17

#define INT 0

#define SHORT 1

#define DOUBLE 2

.

.

.

void swap(void* p, void* q, int type) {

switch (type) {

case (INT): {

int temp = *((int*)p);

*((int*)p) = *((int*)q);

*((int*)q) = temp;

break;

}

case (DOUBLE): {

double temp = …

int a, b;

swap(&a, &b, INT);

Page 18: תירגול  8:מצביעים  והקצאה דינאמית

void*דוגמא לשימוש -

משתנה • חייבים tempאנחנו , אבל ההחלפה את לבצע כדי

הטיפוס את לדעת יכולים איננו , יוקצה הוא ולכן מראש שלו

- ה בתוך .caseרק המתאים •- , ה בתוך רק כן ניתן caseכמו

את -pלהמיר לטיפוס qו . שלהם האמיתי

18

#define INT 0

#define SHORT 1

#define DOUBLE 2

.

.

.

void swap(void* p, void* q, int type) {

switch (type) {

case (INT): {

int temp = *((int*)p);

*((int*)p) = *((int*)q);

*((int*)q) = temp;

break;

}

case (DOUBLE): {

double temp = …

Page 19: תירגול  8:מצביעים  והקצאה דינאמית

void*-וmemcpy

המעתיקה :3תרגיל • פונקציה מקור nכתבו מכתובת בתים - . כ נתונות יהיו הכתובות שתי יעד *.voidלכתובת

19

Page 20: תירגול  8:מצביעים  והקצאה דינאמית

void*-וmemcpy

המעתיקה :3תרגיל • פונקציה מקור nכתבו מכתובת בתים - . כ נתונות יהיו הכתובות שתי יעד *.voidלכתובת

20

#include <stdio.h>

void memcopy(void* dest, void* src, int n){ int i; char *src_p, *dst_p;

src_p = (char*)src; dst_p = (char*)dest;

for (i = 0; i < n; i++) { *dst_p = *src_p; dst_p = dst_p + 1; src_p = src_p + 1; }}

הטיפוס את נמירכדי* charל-

בעובדה להשתמשבית charש תופס

בזיכרון אחד

מקדמת 1הוספת בבית המצביע את

. בזיכרון אחדכך על נרחיב

בהמשך.

Page 21: תירגול  8:מצביעים  והקצאה דינאמית

NULL והקבוע 0כתובת

•. זבל מכילים הם מאוחלים אינם רגילים משתני כאשר•? מאותחלים לא לפוינטרים ניגשים כאשר קורה מה

למקום • מצביע מאותחל לא בזיכרון שרירותי מצביע

21

char *ptr;

*ptr=‘A’;

Page 22: תירגול  8:מצביעים  והקצאה דינאמית

NULL והקבוע 0כתובת

, 0הכתובת • חוקית כתובת שאינה מיוחדת כתובת הינהשהוא, לציין רוצים כאשר מצביע לאתחול ומשמשת בזיכרון

. כלשהו חוקי לזיכרון מאותחל אינוהערך • בקבוע, 0במקום להשתמש – זהו NULLיש

#define.שמוגדר אוטומטית כאפס מנגנון זה מאפשר לבדוק את חוקיותו של כל מצביע לפני •

השימוש בו:

22

if (p) {

*p = …

}

else {

printf("p is unallocated!\n");

}

- ל לאתחל שאינו NULLיש מצביע כל , לשים וכן חוקי לזיכרון NULLמאותחל

הוא שאליו והזיכרון במידה במצביעמוקצה להיות מפסיק מצביע

Page 23: תירגול  8:מצביעים  והקצאה דינאמית

NULL והקבוע 0כתובת

מצביע • של לתוכן לגשת שגיאת NULLניסיון מיידית גורר: ריצה זמן

23

int *p = NULL;

*p = 3;

Page 24: תירגול  8:מצביעים  והקצאה דינאמית

מצביעים- סיכום ביניים

משתנה • של לזיכרון ישירה גישה מאפשרים מצביעיםשימושים:•

פונקציה – בתוך משתנה של הערך שינוימפונקציה – החזרה ערכי ריבויהעתקה – לבצע בלי לפונקציה גדולים משתנים העברת

עליו • מצביעים שהם המידע סוג פי על למצביעים שונים טיפוסים–int*, char*, float*, etc.–void *

•- ל מצביעים לכתובת( 0כתובת ) NULLאתחול מצביעים אינם אםחוקית

24

Page 25: תירגול  8:מצביעים  והקצאה דינאמית

25

מערכים ומצביעיםאריתמטיקה של מצביעים

2מבוא למדעי המחשב מ' - תירגול

Page 26: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כפרמטר לפונקציה

חידה:•? הבא הקוד קטע ידפיס מה

26

#define N 3int salaries[N] = {3000, 8500, 5500};

printf(“Old salaries:\n”);for (i=0; i<N; ++i) {printf(“%d\n", salary_array[i]);

}

cutSalaries(salaries, N);

printf(“New salaries:\n”);for (i=0; i<N; ++i) {printf(“%d\n", salary_array[i]);

}

void cutSalaries (int salary_array[], int len){for (i=0; i<len; ++i) {

salary_array[i] /= 2;}

}

Page 27: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כפרמטר לפונקציה

•! המערך תוכן את שינתה הפונקציהמשתנים Cב-• שהמערך, by valueמעבירים ייתכן איך אז

התבצע בה לפונקציה מחוץ מופיע החדש והערך שונההשינוי?

• , המערך אורך את להעביר צריכים היינו למה כן כמולפונקציה?

27

הקלעים מאחורי קורה מה נסביר הבאים בשקפים . נציג כך לשם לפונקציה מערך מעבירים כאשר

. מצביעים של אריתמטיקה

Page 28: תירגול  8:מצביעים  והקצאה דינאמית

מערכים בזיכרון

• , נרצה פונקציה בתוך מערכים משנים איך שנסביר לפני: בזיכרון מערכים שמורים איך התחלה בתור לדעת

• . של מערך לדוגמא בזיכרון תאים של ברצף יישמר מערךint:

• , ברצף כן גם בזיכרון שמורים מימדי דו מערך או מטריצה . של מימדי דו במערך לדוגמא שורה אחרי :charשורה

28

1 -3 5 7 8 4#1000 #1004 #1008 #1012 #1016 #1020

התא' מסint a = {1,-3,5,7,8,4}

char b = {{‘a’, ‘b’, ‘c’}, {‘d’, ‘e’, ‘f’}, {‘g’, ‘h’, ‘i’}};

a b c d e f#100 #101 #102 #103 #104 #105

g h i#106 #107 #108

Page 29: תירגול  8:מצביעים  והקצאה דינאמית

אריתמטיקה של מצביעים

• , לחבר וכן מצביעים לקדם מאפשרת מצביעים אריתמטיקת . האופרטורים בכל שימוש לעשות ניתן שלמים מספרים אליהם

החיבוריים:

• / : של הפחתה תוספת כל כך מוגדרת את 1האריתמטיקה משנהבתים במספר במצביע שרשומה אליו הכתובת הטיפוס כגודל

.מצביעים• , הם אם רק מזה זה מצביעים שני לחסר טיפוס ניתן . מאותו

שלם ) למספר מומרת ) המחזיק את המרחק intהתוצאהביניהם בזיכרון.

אסור לחבר/להכפיל/לחלק שני מצביעים או •להכפיל/לחלק מצביע בקבוע.

29

+ - += -= ++ --

Page 30: תירגול  8:מצביעים  והקצאה דינאמית

אריתמטיקה של מצביעים

בזיכרון • לנו יש כאשר מצביעים באריתמטיקת משתמשים.) במערך ) למשל הטיפוס מאותו רצופים איברים מספר

• , הוספת בזיכרון מהאברים לאחד מצביע אליו 1בהינתןאותנו בזיכרון תקדם הבא הפחתת, לאיבר תחזיר 1ואילו

. הקודם לאיבר אותנו

30

long *lptr; short *sptr;

sptr sptr+1 sptr+2 sptr+3 sptr+4 sptr+5 sptr+6 sptr+7

--- long --- --- long --- --- long --- --- long ---

short short short short short short short short

lptr lptr+1 lptr+2 lptr+3

sptr sptr+1 sptr+2 sptr+3 sptr+4 sptr+5 sptr+6 sptr+7

--- long --- --- long --- --- long --- --- long ---

short short short short short short short short

Page 31: תירגול  8:מצביעים  והקצאה דינאמית

השוואה בין מצביעים

השוואת, Cבשפת • ידי על מצביעים שני בין להשוות ניתן . מצביע מכילים שהם ממצביע קטן הוא ptr1הכתובות

ptr2 אם הוא מכיל כתובת מוקדמת יותר בזיכרון.

השוואה בין מצביעים מועילה, למשל, כאשר יש •לנו שני מצביעים לאותו המערך, ואנו רוצים לדעת איזה מהם מצביע למקום קודם במערך.

במקרה זה, אם מצביע אחד קטן מהשני – סימן שהוא מצביע לתא מוקדם יותר במערך.

ניתן להשתמש בכל אחד מן האופרטורים הבאים •להשוואת מצביעים:

31

> < >= <= == !=

Page 32: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כמצביעים

האופרטור, Cבשפת • ללא כלשהו מערך של שמו כתיבתמצביע לאיבר הראשון מחזירה אוטומטית []

. במערך, אזי T[N]אם נניח שטיפוס המערך הוא •

.*Tטיפוס המצביע המוחזר יהיה

32

int speeds[] = { 25, 50, 80, 90, 110 };

printf("%p", &speeds[0]);

printf("%p", speeds);

מצביע להדפסת דגל

האלה הביטוייםשקולים

שקולים הביטויים

Page 33: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כמצביעים

•- ה: לאיבר נפנה איך ?speedsבמערך 3חידה

בצורה: • בזיכרון נמצאים המערך איברי .רציפהתזכורת• , באמצעות ניתן המערך לתחילת מצביע לנו שיש כיוון

מצביעים של :אריתמטיקה כך השלישי לאיבר להגיע

33

int speeds[] = { 25, 50, 80, 90, 110 };

printf("%p", speeds+3);

*(speeds+3) += 10;כתובת את נדפיס

השלישי האיבר

של תוכנו את נשנההשלישי האיבר

Page 34: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כמצביעים

25 50 80 90 110

speeds

*(speeds+1)speeds + 2&speeds[0]

speeds[1]&speeds[2]

speeds[]מצביעים כתיב מערכים כתיב

&a[0]

&a[i]

a

a[i]

a+i

)*a+i(

בשם מערך aעבור

Page 35: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כמצביעים

ומצביע :4תרגיל • שלמים מערך המקבלת פונקציה כתבו. אורכו את ומחזירה שבו האחרון לאיבר

int get_array_length(int a[], int *p_last){ return p_last - a + 1;}

Page 36: תירגול  8:מצביעים  והקצאה דינאמית

מצביעים לעומת מערכים- השוואה

•? למערך מצביע בין מהותיים הבדלים אילו

הוא 1. מערך של לשנות, קבועשמו ניתן לא כלומר. מצביע הוא שאליה הכתובת את

' שונות כתובות מס להכיל יכול מצביע זאת לעומת: התוכנית ריצת לאורך

int a[5], b[8];

a = b;שגיאת

קומפילציה

int a, b, *ptr;

ptr = &a;

ptr = &b;

Page 37: תירגול  8:מצביעים  והקצאה דינאמית

מצביעים לעומת מערכים- השוואה

•? למערך מצביע בין מהותיים הבדלים אילו

.2 . לעומת המערך לאברי זיכרון מקצה מערך יצירתגורם ידי על שמוקצה לזיכרון זקוק מצביע זאת

חיצוני.int *ptr;

ptr[0] = 100;

יתכן. warningשגיאת כן גםבהנחת ריצה זמן שגיאת

שמכיל הזבל ptrשכתובתחוקית אינה

Page 38: תירגול  8:מצביעים  והקצאה דינאמית

מצביעים לעומת מערכים- השוואה

•? למערך מצביע בין מהותיים הבדלים אילו

משתנה – 3. כל כמו מערך שהוא משתנה עבור , תופס – הוא זיכרון כמה יודעת התוכנית אחר

, . את לקבל ניתן לכן שלו בטיפוס מוגדר שזה כיווןהאופרטור באמצעות בזיכרון המערך של גודלו

sizeof לעומת זאת, מצביע איננו .יודע על איזה אורך מערך הוא מצביע,

עליו פשוט מחזירה את sizeofוהפעלת כמות הזיכרון הדרושה לאחסון המצביע

נקבל:Code::blocksעצמו. למשל, ב-int a[5], *p;

p = a;

printf("%d %d", sizeof(a), sizeof(p));

20 4

Page 39: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כפרמטר לפונקציה

לשנות • יכולה שהפונקציה ראינו הפרק בתחילת בחידה " " , מחוץ גם שורדים אלה שערכים מערך בתוך ערכים

לפונקציהבשפת • לפונקציה Cכידוע עוברים by valueפרמטרים•: הסתירה את ליישב הכלים את בידינו יש עכשיו

Page 40: תירגול  8:מצביעים  והקצאה דינאמית

החתימה של פונקציה המקבלת מערך

•: הבאה החתימה למעשה

לחלוטין • :שקולה , הבאה, לחתימה שהיא בחינה מכל

•: הבאה בקריאה כמו לפונקציה לקרוא יש המקרים בשני

יהיה • המקרים בשני הראשון הפרמטר של !*intוהטיפוס

מאפשרת Cצורת הכתיבה הראשונה היא פשוט צורת כתיבה ש-•להשתמש בה כשרוצים להדגיש כי הכוונה שהפונקציה תקבל

בודד. ניתן להשתמש intמערך כפרמטר, ולא סתם מצביע ל-! לא רק עבור פרמטרים של פונקציותבצורת כתיבה זו

ניתן להשתמש בה בהצהרות על משתנים, למשל.

double average(int grades[], int n);

double average(int* grades, int n);

int grades[3]={90,98,65};

double res = average(grades,3);

Page 41: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כפרמטר לפונקציה

בשפת • שלמעשה היא יכול Cהמשמעות אינו מערךלפונקציה פרמטר להיות

ידי • על היא לפונקציה מערך להעביר היחידה הדרךשלו הכתובת בשם. העברת שימוש באמצעות זאת עושים

. בו הראשון לאיבר כמצביע גם המתפקד המערך•! יותר, טובה זו העברה צורת מעשית

, להמנע ועדיף רב זיכרון לתפוש עשוי שמערך כיוון זאתכפרמטר אותו שמעבירים פעם בכל תוכנו משכפול

לפונקציה.

Page 42: תירגול  8:מצביעים  והקצאה דינאמית

מערך כפרמטר לפונקציה

•: כך, תראה מערך המקבלת פונקציה לסיכום

void print(int *array, int len)

{

for (int i=0; i<len; ++i)

printf("%d\n", array[i]);

}

int my_array[8];

print(my_array, 8);

תקבל הפונקציהבתור מצביע

פרמטר

נציין לפונקציה בקריאהשיהפוך ) המערך שם את

למצביע אוטומטית) המערך לתחילת

נעבוד הפונקציה בתוךשהיינו כמו המצביע עם

מערך עם עובדים

מצביע שקיבלנו כיוון ) יודעים) איננו מערך ולא

המערך של אורכו מהאותו נעביר ולכןנפרד כפרמטר

לפונקציה

Page 43: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כפרמטר לפונקציה

- :5תרגיל • ל מצביעים של מערך המקבלת פונקציה כתבוint. בשימוש שאינם המצביעים מספר את ומחזירה

פונקציית • בצורה mainכתבו שלכם בפונקציה המשתמשתתקינה

43

Page 44: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כפרמטר לפונקציה

44

#include <stdio.h>

int get_non_used_ptrs(int* a[], int len){ int i, count = 0;

for (i = 0; i < len; i++) if (a[i] == NULL) count++;

return count;}

Page 45: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כפרמטר לפונקציה

45

int main(){ int x = 3, y = 6; int* arr[] = {&x, NULL, &y, NULL, NULL};

printf("Non-used pointers in array: %d\n", get_non_used_ptrs(arr, 5));

return 0;}

Page 46: תירגול  8:מצביעים  והקצאה דינאמית

מערכים כפרמטר לפונקציה

לפונקציה • המערך תוכן את העברנו שלמעשה לב byנשיםreference , של. זמני עותק קיבלה לא הפונקציה כלומר

העברה ) שזו לה(, by valueהמערך מספקים אנו אלאשלנו למערך ישירה במערך. גישה תבצע שהיא שינוי כל

. עצמו המקורי במערך יתרחשבאמצעות, • ציונים מעדכנת הבאה הפונקציה למשל

: חיבורי void factor(int *grades, int n)פקטור{ for (int i=0; i<n; ++i) { grades[i] += 5; }}

Page 47: תירגול  8:מצביעים  והקצאה דינאמית

העברת מערך דו מימדי לפונקציה

•: מימדי דו מערך נגדירשורה • אחר שורה ברצף בזיכרון פרוש זה :מערך

double matrix[N][M];

matrix[][]

x x x x x x

matrix[1][0..M-1]matrix[0][0..M-1]

Page 48: תירגול  8:מצביעים  והקצאה דינאמית

העברת מערך דו מימדי לפונקציה

לאיבר • מצביע נחשב ? נבצע matrix[i][j]כיצדזאת בשלבים:

כתובת תחילת המערך הדו-ממדי היא:•כעת נמצא את כתובתו של האיבר הראשון בשורה •

:matrix[i][0]הראשון, כלומר מצביע ל- iה- i+1נשים לב ששורה זו היא למעשה השורה ה-

שורות לפניה. כיוון iבמטריצה, ולכן יש איברים, יוצא שבסה"כ ישנם Mשבכל שורה יש

M*i איברים בזיכרון לפני האיבר matrix[i], ובחשבון מצביעים נקבל:[0]

&matrix[0][0]

&matrix[i][0] == &matrix[0][0] + M*i

Page 49: תירגול  8:מצביעים  והקצאה דינאמית

העברת מערך דו מימדי לפונקציה

לאיבר • , נותר לנו רק matrix[i][0]משהגענו. לשם כך matrix[i][j]להתקדם בשורה לאיבר איברים בזיכרון, jעלינו לדלג על עוד

matrix[i][j] == &matrix[0][0] + M*i + j&ובסיכומו של דבר אנו מקבלים:

, : מסקנה - ממדי דו במערך כלשהו איבר לאתר מנת הכרחיעלבמערך – הטורים מספר את דהיינו שלו השורה אורך את Mלדעת

(שימו לב שאת מספר השורות הכולל דווקא אין הכרח לדעת).

זו הסיבה, למשל, מדוע כאשר מאתחלים מערך דו-ממדי חייבים לציין את אורך השורה שלו, גם כאשר

מציינים מפורשות רשימת אתחול.

Page 50: תירגול  8:מצביעים  והקצאה דינאמית

העברת מערך דו מימדי לפונקציה

•? לפונקציה כפרמטר מימדי דו מערך נעביר כיצד כן אם• , נעביר מימדי החד במקרה למערך כמו את מצביע ולא

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

שורה אורך בעל מימדי דו מערך המקבלת פונקציה נציג4.

: כך תראה הפונקציה חתימתint funky(int matrix[][4]);

Page 51: תירגול  8:מצביעים  והקצאה דינאמית

העברת מערך דו מימדי לפונקציה

לקבל • יכולה עתה זה שהגדרנו שהפונקציה לב לשים חשובהוא שלהם השורה שאורך מערכים ורק אך כל! 4כפרמטריגרור אחר שורה אורך בעל מערך לה להעביר ניסיון

. קומפילציה שגיאת

int funky(int matrix[][4]);

Page 52: תירגול  8:מצביעים  והקצאה דינאמית

דוגמא- חישוב מינימום ומקסימום

כפרמטר • ממדי דו מערך המקבלת פונקציה נכתוב. בו והמקסימאלי המינימלי האיבר את ומחזירה

•? הפונקציה של החתימה תהיה מה

השורה • אורך את לציין חייבים אנו זו שבחתימה לב שימו ( - זהו שלנו במקרה ממדי הדו המערך , המוגדר Mשל

). עם זאת, איננו מניחים דבר define#כ-בנוגע למספר השורות במערך, ולכן ערך זה

של הפונקציה.nמועבר כפרמטר

void extremes(int a[][M], int n, int *min, int *max)

Page 53: תירגול  8:מצביעים  והקצאה דינאמית

דוגמא- חישוב מינימום ומקסימום

void extremes(int a[][M], int n, int *min, int *max){ int i, j; *min = *max = a[0][0];

for (i = 0; i < n; ++i) { for (j = 0; j < M; ++j) { *min = (a[i][j] < *min) ? a[i][j] : *min; *max = (a[i][j] > *max) ? a[i][j] : *max; } }}

Page 54: תירגול  8:מצביעים  והקצאה דינאמית

54

הקצאה דינאמית

2מבוא למדעי המחשב מ' - תירגול

Page 55: תירגול  8:מצביעים  והקצאה דינאמית

מצביעים ואורך חיי המשתנה

• , שהמשתנה אומרת אינה למשתנה מצביע שבידנו העובדה! בזיכרון } ()int* getNumberחי

int number;

printf(“Enter a number: “);

scanf(“%d”, &number);

return &number;

}

int x, *p;

p = getNumber();

x = *p; // Bug!

p מוקצה שאינו למשתנה מצביעבזיכרון!

בזיכרון כעת נמצא מה לדעת ניתן לא.pש- אליו מצביע

Page 56: תירגול  8:מצביעים  והקצאה דינאמית

זיכרון דינמי

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

int* getNumbers(int n) { int numbers[n]; //C99 style printf(″Please enter %d numbers: ″,n); for (int i = 0; i < n; i++) { scanf(″ %d″, &numbers[i]); } return numbers;}

Warning: function returns address of local variable

Page 57: תירגול  8:מצביעים  והקצאה דינאמית

זיכרון דינמי

דינמי • " זיכרון ממנו " להזמין שניתן מהזיכרון חלק הינו. בקשה לפי זכרון כמות

• " פקודת י ע נעשות בספריה ) mallocהבקשות שמוגדרתstdlib.h)

בפרמטר • מקבלת שרוצים sizeהפקודה הבתים מספר אתומחפשת .רצופיםבתים sizeלהקצות בזיכרון פנויים

הזיכרון • את להקצות הצליחה ההפעלה מערכת אםכתובת מוחזרת , הדרוש ברצף הראשון אחרת- הבית

הכתובת (.Null )0מוחזרת

void* malloc(size_t size);

Page 58: תירגול  8:מצביעים  והקצאה דינאמית

זיכרון דינמי

באופן • יודעת התוכנית בה רגילה משתנים להקצאת בניגוד , המתכנת כאן מהבלוק ביציאה הזיכרון את לפנות אוטומטי

, הסתיים מתי יודע הוא ורק הזיכרון את שהזמין הוא . הזיכרון את לשחרר האחריות עליו מוטלת לכן בו השימוש

הפקודה באמצעות מפורש :freeבאופן

void free(void *ptr);

Page 59: תירגול  8:מצביעים  והקצאה דינאמית

זיכרון דינמי

#include <stdlib.h>

int* getNumbers(int n) { int* numbers = (int*)malloc(n*sizeof(int)); if (!numbers) { return NULL; } printf(″Please enter %d numbers: ″,n); for (int i = 0; i < n; i++) { scanf(″ %d″, &numbers[i]); } return numbers;}

עשוי טיפוס שגודל כיווןמחשבים בין להשתנות

להשתמש, מומלץ שונים על מנת sizeofב-

לחשב את הגודל

באופן להמיר חובה לאתוצאת את מפורש

לטיפוס mallocה-הדרוש, אך נעשה זאת כדי לשפר את

קריאות הקוד

Page 60: תירגול  8:מצביעים  והקצאה דינאמית

זיכרון דינמי

לשחרר • לזכור זיכרון חייביםמדליפות להמנע כדי דינמי

זיכרון

int main() { int *numbers = getNumbers(10); if (!numbers) { printf(″Memory allocation failed!″); return 1; } … /* Do things with the numbers array */ … free(numbers); return 0;}

מפונקציה שנצא לפני , את נשחרר תוכנית או. אחר לשימוש הזיכרון