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

Post on 17-Jan-2016

82 views 0 download

description

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

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

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

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

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

תוכנייה

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

3

מצביעים

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

4

מאותחל לא

67

10-4

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

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

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

#1000

כתובת התא

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

יש כתובת

#1004

#1008

תא

בתא השמור ערך

#1012

הזיכרון

5

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

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

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

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

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

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

6

מצביעים

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

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

int* iptr;

double *dptr;

char* cptr, c;

char *cptr, c;

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

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

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

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

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

מצביע

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

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מערכות) ביט

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מטיפוס

10

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

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

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

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

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

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

void swap(int* x, int* y)

11

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

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

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

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

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

הפונקציה

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

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

של : 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);}

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

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

עולה בסדר

14

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

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);}

void*

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

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

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

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

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

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

מפורשת.16

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);

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 = …

void*-וmemcpy

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

19

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הוספת בבית המצביע את

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

בהמשך.

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

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

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

21

char *ptr;

*ptr=‘A’;

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

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

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

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

השימוש בו:

22

if (p) {

*p = …

}

else {

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

}

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

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

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

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

23

int *p = NULL;

*p = 3;

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

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

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

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

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

24

25

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

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

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

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

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;}

}

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

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

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

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

27

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

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

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

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

• . של מערך לדוגמא בזיכרון תאים של ברצף יישמר מערך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

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

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

החיבוריים:

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

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

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

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

29

+ - += -= ++ --

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

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

• , הוספת בזיכרון מהאברים לאחד מצביע אליו 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

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

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

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

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

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

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

31

> < >= <= == !=

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

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

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

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

32

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

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

printf("%p", speeds);

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

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

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

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

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

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

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

33

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

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

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

השלישי האיבר

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

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

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עבור

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

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

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

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

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

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

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

int a[5], b[8];

a = b;שגיאת

קומפילציה

int a, b, *ptr;

ptr = &a;

ptr = &b;

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

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

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

חיצוני.int *ptr;

ptr[0] = 100;

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

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

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

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

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

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

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

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

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

p = a;

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

20 4

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

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

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

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

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

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

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

יהיה • המקרים בשני הראשון הפרמטר של !*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);

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

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

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

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

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

לפונקציה.

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

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

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);

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

פרמטר

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

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

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

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

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

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

לפונקציה

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

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

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

43

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

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;}

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

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;}

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

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

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

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

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

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

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

double matrix[N][M];

matrix[][]

x x x x x x

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

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

לאיבר • מצביע נחשב ? נבצע 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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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; } }}

54

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

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

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

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

int number;

printf(“Enter a number: “);

scanf(“%d”, &number);

return &number;

}

int x, *p;

p = getNumber();

x = *p; // Bug!

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

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

זיכרון דינמי

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

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

זיכרון דינמי

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

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

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

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

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

void* malloc(size_t size);

זיכרון דינמי

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

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

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

void free(void *ptr);

זיכרון דינמי

#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ה-הדרוש, אך נעשה זאת כדי לשפר את

קריאות הקוד

זיכרון דינמי

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

זיכרון

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

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