תוכנה 1 - חזרה שולי לב יהודי shulyl@tau.ac.il. 2 Arrays and Strings מערך -...

Preview:

Citation preview

- חזרה1תוכנה

שולי לב יהודי

shulyl@tau.ac.il

2

Arrays and Strings

מערך - אוסף משתנים בעלי שם משותף. הפנייה לכל •איבר נעשית ע”י אינדקס.

מערך מוגדר בזיכרון רציף. האיבר הראשון נמצא Cב-• מוגדר החל מכתובת aבכתובת הנמוכה. לדוגמא, אם

1000:char a[6];

Element a[0] a[1] a[2] a[3] a[4] a[5]Address 1007 1008 1009 1010 1011 1012

3

Single-Dimension Arrays

type var_name[size]; double balance[100];

. לדוגמא:0האינדקס של האיבר הראשון הוא •char p[10];

p[0] אלמנטים 10 עם charactersמגדיר מערך של .p[9]עד

השטח שמוגדר עבור מערך:•total bytes = sizeof(base type) * size of

array

4

Single-Dimension Arrays (cont.)

אין בדיקה של גבולות המערך!ניתן לכתוב מעבר לגבולות ללא שגיאת קומפילציה:int count[10], i;

/* this causes count to be overrun */for (i=0; i < 100; i++) count[i] = i;

5

Generating a Pointer to an Array

שם המערך ללא ציון אינדקס הוא מצביע למערך:•int sample[10];int *p;p = sample;

•p האיבר הראשון של כתובת יקבל את sample.

6

Passing Single-Dimension Arrays to Functions

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

void main(void){

int i[10];func1(i);

}

3בפונקציה, ניתן להגדיר את הפרמטר באחד מ-• אופנים:

7

Passing Single-Dimension Arrays to Functions

• void func1(int *x) /* pointer */{...}

• void func1(int x[10]) /* sized array */{...}

• void func1(int x[]) /* unsized array */{...}

איברים:32גם זה יעבוד - יעבור מצביע - לא יוצר •• void func1(int x[32]) /* sized array */

8

Return an Array from a Function

int main(void){

char *mystr;

mystr = getstr();printf("%s\n", mystr);

return 0;}

char *getstr() {

char str[20]; str[0] = 'a';

str[1] = 'b';str[2] = '\0';

return(str);}

יודפס main משוחרר ולכן ב-str ביציאה מהפונקציה השטח של •"זבל".

= char *str דינמית ע"י: str צריך להקצות את •malloc(20);

9

Strings

•String הוא מערך של characters-המסתיים ב null 1`. לכן גודל המערך צריך להיות גדול ב-\0שמצוין ע”י `

:10מאורך המחרוזת. למשל למחרוזת באורך char str[11];

helloניתן להגדיר קבוע מחרוזת ע”י גרשיים: ”•there”

.nullבקבוע אין צורך להוסיף

10

String Manipulation Functions

Name Functionsstrcpy(s1,s2) Copies s2 into s1strcat(s1,s2) Concatenates s2 onto the end of s1strlen(s1) Returns the length of s1strcmp(s1,s2) Returns 0 if s1 and s2 are the same;

less than 0 if s1<s2; greater than 0 if s1>s2

•strcmp מחזירה false:אם המחרוזות שוות. לכן כדי לבדוק שוויון if (!strcmp(s1,s2)) printf(“equal strings\n”);

includeכדי להשתמש בפונקציות: <•<string.h #

11

Arrays of Strings

מוגדר ע”י מערך דו-מימדי:•

char str_array[30][80]; תווים.79 מחרוזות, כל אחת באורך של עד 30מגדיר מערך עם

כדי לגשת למחרוזת אחת:•

gets(str_array[2]);שקול ל:

gets(&str_array[2][0]);אך צורת הכתיבה הראשונה מקובלת יותר.

12

Indexing Pointers

שם מערך ללא אינדקס הוא מצביע לאיבר הראשון במערך.•

:trueלכן, הביטוי הבא הוא •p == &p[0]

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

int *p, i[10];p = i;p[5] = 100; /* assign using index */*(p+5) = 100; /* assign using pointer arithmetic */

.i באיבר השישי של 100שתי הפקודות מציבות

13

Array Initialization

• int i[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

•[0]i [9 והלאה עד ]1 יקבל את הערךi שיקבל את הערך 10.

:charactersאיתחול מערך של •char str[15] = “I like the sea”;

זה שקול ל:•

char str[15] = {‘I’, ‘ ‘, ‘l’, ‘i’, ‘k’, ‘e’, ‘ ‘, ‘t’, ‘h’, ‘e’, ‘ ‘,

‘s’, ‘e’, ‘a’, ‘\0’};

14

Pointers

?Pointerמה זה •משתנה המכיל כתובת של

משתנה אחר10031000

1001

1002

1003

1004

1005

Variable in memory

Memory address

15

Pointer Variable

משתנה שצריך להכיל ערך של מצביע צריך להיות •מוגדר כך:

type *name;

•type-מציין את סוג המשתנים שה pointer יכול להצביע עליהם.

כל מצביע יכול להצביע לכל משתנה מסוג כלשהו, אולם•pointer arithmetic-נעשה לפי ה base type.

16

The Pointer Operators

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

m = &count;-שם בm את הכתובת של count אין כל קשר לערך של .

!countהמשתנה

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

q = *m; m. כלומר, את הערך ש-q בתוך countישים את הערך של

מצביע עליו.

17

The Pointer Operators(cont.)

ועם *א להתבלבל עם כפל - ל bitwise AND& - .

קודמים לכל )של מצביעיםו-& ( *האופרטורים שהוא -)5(האופרטורים האריתמטיים, למעט מינוס אונרי

בעל קדימות זהה.

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

. intכתובת שהוא יכיל היא כתובת של ערך מסוג

18

The Pointer Operators(cont.)

void main(void) { double x, y; int *p;

/* The next statement causes p (which is an integer pointer) to point to a double. */

p = &x;

/* The next statement does not operate as expected */ y = *p;}

בתים 4 , רקint מוגדר כמצביע ל-p. כיוון ש-y ל-xלא תהייה השמה של ערך •.floating point בתים שמכילים מספר 8ולא yיועברו ל-

(שגיאת קומפילציה).castingאי אפשר לבצע השמה כזו ללא C++ב- •

19

Pointer Expressions

Pointer Assignmentsvoid main(void){

int x;int *p1, *p2;

p1 = &x;p2 = p1;

printf(“ %p”, p2); /* print the address of x, not x ’s value */

}

20

Pointer Expressions (cont.)

Pointer Arithmetic פעולות אפשריות: חיבור וחיסור.2•

בתים.4 הוא באורך int. נניח ש-2000 וערכו int מצביע ל-p1דוגמא: •.2001 ולא 2004 יכיל p1 , p1++אחרי הפעולה

הבא.integer מקודם, הוא יצביע ל-p1בכל פעם ש-

p1 = p1+12ניתן לחבר ערך כלשהו, למשל: ;•p1-מהסוג של 12 יצביע לאלמנט ה) p1.אחרי זה שהוא מצביע כעת (

ניתן להחסיר ערך של מצביע מערך של מצביע אחר כדי למצוא את • שלהם) הנמצאים ביניהם.base typeמספר האלמנטים (מה-

:לכפול, לחלק, לחבר שני מצביעים, לבצע פעולות לוגיותלא ניתן )bitwise לחבר או לחסר ערכי ,(float או double .

21

Pointer Expressions (cont.) Pointer Arithmetic - example:

ch

ch +1

ch+2

ch+3

ch+4

ch+5

3000

3001

3002

3003

3004

3005

char *ch = 3000;

short *i = 3000; i

i + 1

i + 2

כל פעולות האריתמטיקה על

מצביע נעשות baseבהתאם ל-

type שלו

22

Pointer Expressions (cont.) Pointer Comparisons• Example: if (p < q) printf(“p points to lower memory than q\n”);

בד”כ שימושי כאשר מספר מצביעים מצביעים לאובייקטים משותפים, כמו •במערך.

:stack דוגמא - מחסנית• רשימת איברים שהגישה אליהם היא בשיטתLast-in , First-out

(LIFO).2 :פעולות

()pushמכניסה ערך למחסנית - ()popמוציאה ערך מהמחסנית -

0בדוגמא, הערכים שהמשתמש מקליד,מוכנסים למחסנית. אם הוקלד ,-.1). התכנית מסתיימת כשמתקבל ערך popמוציאים ערך מהמחסנית (

23

Pointer Expressions (cont.)

Stack example:#include <stdio.h>#include <stdlib.h>

#define SIZE 50

void push(int i);void pop(void);

int *tos, *p1, stack[SIZE];

void main(void){ int value; tos = stack; /* tos points to the top of the stack */ p1 = stack; /* initialize p1 */

do {printf(“Enter value: “);scanf(“%d”, &value);if (value != 0) push(value);else printf(“value on top is%d\n”, pop());} while (value != -1);

}void push(int i){

p1++;if (p1 == (tos + SIZE)) { printf(“Stack overflow”); exit(1);}*p1 = i;

}

24

Pointer Expressions (cont.)

Stack example (cont.):

pop(void){

if (p1 == tos) { printf(“Stack underflow”); exit(1);}p1--;return *(p1 + 1);

}

:הערה•

הסוגריים returnבפקודת ה-הכרחיים. בלעדיהם היה:

return *p1 + 1;הערך שהיה חוזר הוא הערך

, ולא 1 ועוד p1שבכתובת (שהיא p1+1הערך בכתובת

. )p1 בתים אחרי 4

25

Arrays of Pointers

int:int *x[10] מצביעים ל-10מערך של •

לאיבר השלישי במערך: var הכנסת כתובת המשתנה•

x[2] = &var;

:varלמציאת הערך של •

*x[2]

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

תראה כך:xמערך

26

Arrays of Pointers (cont.)

void display_array(int *q[ ]){ int t;

for (t=0; t < 10; t++) printf (“%d “, *q[t]);}

•q-אינו מצביע ל integers אלא מצביע למערך ,.integersשל מצביעים ל-

27

Arrays of Pointers (cont.)

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

לדוגמא, פונקציה המקבלת קוד שגיאה ומדפיסה הודעה מתאימה:•void syntax_error(int num){ static char *err[] = {

“Cannot open file\n”, “Read error\n”, “Write error\n”, “Media Failure\n”

}; printf (“%s”, err[num]);

}

28

Using Structures Pointers

לפני שם המשתנה: & ע”י structureמציאת כתובת ה-•struct bal { float balance; char name[80];} person;

struct bal *p; /* declare a structure pointer */

p = &person; ואז.p במצביע person שם את כתובת

-p : -> נעשית ע”י האופרטורstructureגישה לשדה ב-•>balance

משמש כדי לגשת לשדה כאשר פועלים ישירות על )dot (.האופרטור • (ולא ע”י מצביע).structureה-

העברת כתובת של שדה תעשה כך:•func(&person.x); func2(person.name); func3(&person.name[2])

29

The Operator -> An Example: display a timer

struct my_time {int hours;int minutes;int seconds;

};void main(void) {

struct my_time systime;systime.hours = 0;systime.minutes = 0;systime.seconds = 0;for ( ; ; ) {

update(&systime); display(&systime);}

}

void update(struct my_time *t){

t->seconds++;if (t->seconds == 60) { t->seconds = 0; t->minutes++;}

...}void display(struct my_time *t){

printf(“%02d:”, t->hours);printf(“%02d:”, t->minutes);printf(“%02d:”, t->seconds);

}

30

Multiple Indirection(Pointers to Pointers)

address value

address address value

Pointer Variable

Pointer Pointer Variable

Single Indirection

Multiple Indirection

31

Multiple Indirection (cont.)float **newbalance ; הגדרת מצביע למצביע: •newbalance אינו מצביע למספר floating-point אלא

. floatמצביע למצביע ל- פעמיים:*כדי לגשת לערך עצמו יש להפעיל את אופרטור ה-•

int x, *p, **q;

x = 10; p = &x; q = &p;

printf(“%d”, **q); /* print the value of x */

32

Initializing Pointers

אחרי שמצביע הוגדר אך לפני שהושם לו ערך, הוא מכיל •ערך לא ידוע (“זבל”).

,”אם ננסה להשתמש במצביע לפני שיש לו ערך “חוקיסביר שהתכנית תעוף (וכנראה גם מערכת ההפעלה)!

מוסכמה: מצביע שכרגע אינו מצביע למיקום בר-תוקף • null). מצביע שערכו 0 (שהוא nullבזיכרון, מאותחל ל-אינו מצביע לכלום.

כדי לציין למשל, סוף של מערך של nullנשתמש בערך •מצביעים.

33

Initializing Pointers (cont.)

Search (char *p[], char *name) { register int i; for (i=0; p[i]; ++i) if (!strcmp(p[i], name)) return; return -1;}

מציין את סוף המערך. הלולאה רצה כל nullכאן, ערך •.null ולא הגענו למצביע שערכו nameעוד לא נמצא

34

Initializing Pointers (cont.)

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

char *p = “hello world”; string table שומר את כל קבועי המחרוזות ב-compiler ה-•

את הכתובת של הקבועpולכן הפקודה תכניס ל-“hello world”-כפי שהוא מאוחסן ב string table.

35

Pointers to Functions

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

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

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

דוגמא:•

36

Pointers to Functions (cont.)

void check (char *a, char *b, int (*cmp)(const char *, const char *));

void main(void){

char s1[80], s2[80];int (*p)(const char *, const char *);

p = strcmp;gets(s1);gets(s2);check(s1, s2, p);

}

void check (char *a, char *b, int (*cmp)(const char *, const char *))

{ printf(“testing for equality\

n”);if (!(*cmp)(a,b)) printf(“equal”);else printf(“not equal”);

}.strcmp*) קורא ל-cmp)(a,bהביטוי (•

גם כך:checkניתן לקרוא ל-•

check(s1, s2, strcmp);

37

Dynamic Allocation Functions

קצאה דינמית - הקצאת שטח בזמן ריצה.ה•

- שטח הנמצא בין התכנית heapזיכרון דינמי מוקצה על ה-•.stack לבין ה-),data(והשטח הקבוע שלה

- משחררת.free - מקצה שטח, mallocפונקציות עיקריות: •# include stdlib.h צריך את השורה: •

prototype: void *malloc(size_t number_of_bytes);

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

38

Dynamic Allocation Functions (cont.)

הראשון בשטח שהוקצה. byteבהצלחה - מוחזר מצביע ל-•.nullבכשלון - כאשר אין מספיק מקום - מוחזר

char *p ; לדוגמא: •

p = malloc(1000);

-אין צורך בcast-הערך מוסב אוטומטית לפי ה ,type בצד לא נעשית הסבה אוטומטית!).C++שמאל (ב-

:portability נחוץ ל-sizeof. ה-integers 50הקצאת •int *p;p = malloc(50 * sizeof(int));

39

Dynamic Allocation Functions (cont.)

יש לבדוק האם הערך החוזר אינוnull!

•free.מחזירה למערכת שטח שהוקצה קודם prototype: void free(void *p);

•p.הוא מצביע לשטח שהוקצה קודם -חשוב לא לקרוא לfree!עם ארגומנט לא נכון

40

Problems with Pointers

שימוש בערך לא טוב של מצביע יכול לגרום לכתיבה לשטח •אחר.

טעויות נפוצות:• uninitialized pointer:

/* this program is wrong */void main(void){

int x, *p;x = 10;*p = x;

}

p לא אותחל ולכן נכתב 10הערך

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

משמעותית בתכנית גדולה.

41

Problems with Pointers (cont.)

Misunderstanding of how to use a pointer:/* this program is wrong */void main(void){

int x, *p;x = 10;p = x;printf(“%d”, *p);

}

printf לאתדפיס את הערך

אלא ערך 10כלשהו אחר.

תיקון יש לכתוב:ל

p = &x;

42

Problems with Pointers (cont.)

Incorrect assumptions about variables locations:אי אפשר לדעת היכן המשתנים ממוקמים בזיכרון. לכן אם •

ננסה להשוות בין מצביעים שאינם מצביעים לאובייקט משותף, נקבל תוצאה לא צפויה:

char s[80], y[80];char *p1, *p2;p1 = s;p2 = y;if (p1 < p2) . . .

לא נכון להניח מוקצים y ו-sש-

ברצף.

43

Problems with Pointers (cont.)

second ו-firstבאותו אופן לא נכון לאתחל את המערכים • זה compilers (למרות שבחלק מה-19 עד 0במספרים

יעבוד):

int first[10], second[10];int *p, i;p = first;for (i=0; i < 20; i++) *p++ = i;

44

argc and argv – Arguments to main()

command line ע"י ה-()mainמעבירים ערכים ל-•arguments:

program_name command_line_arguments •argc-ו argv מוגדרים built-in ובהם מתקבלים

הארגומנטים.•argc מכיל את מספר הארגומנטים והוא int, ערכו תמיד

כיוון ששם התכנית הוא הארגומנט הראשון.1לפחות •argv -הוא מצביע למערך של מצביעים לcharacter כל ,

איבר במערך מצביע לארגומנט. – התכנית צריכה להמיר stringsכל הארגומנטים הם •

המתאים.formatמספרים ל-

45

argc, argv

/* The name of the program is “name” */void main(int argc, char *argv[ ]){ if (argc != 2) { printf (“You forgot to type your name.\n”);

exit(1);}printf(“Hello %s”, argv[1]);

}

> name Tom> Hello Tom

46

argc, argv

ברוב סביבות העבודה הארגומנטים מופרדים ע"י רווח או •tab:פסיק, נקודה-פסיק אינם נחשבים מפרידים. לדוגמא .

run Spot, run מחרוזות: 3 אלו הןHerb,Rick,Fred וזוהי מחרוזת אחת:

” Alon Ronit“ וגם זו:

47

argc, argv - Example/* countdown – the program counts down from a starting value (which is specified on the command line) and beeps when it reachs 0. */#include <stdio.h>#include <stdlib.h>#include <ctype.h>#include <string.h>

void main(int argc, char *argv[ ]){ int disp, count;

if (argc < 2) { printf(“You must enter the length of the count.\n”); exit(1);}

48

argc, argv – Example (cont.)

/* if the string “display” is the second argument, the countdown will also be displayed on the screen. */ if (argc == 3 && !strcmp(argv[2], “display”)) disp = 1; else disp = 0;

for (count=atoi(argv[1]); count; --count) if (disp) printf(“%d\n”, count);

putchar(‘\a’); /* this will ring the bell in most computers */

printf (“Done\n”);}

49

What Does main() Return?

לתהליך שקורא לה שהוא int מחזירה ()mainהפונקציה •בד"כ מערכת ההפעלה.

עם אותו ()exit שקולה לקריאה ל-()mainהחזרת ערך מ-•ערך

אם לא מוחזר ערך הערך שחוזר לתהליך הקורא אינו מוגדר •, אך לא תמיד).0 יחזירו compilers(רוב ה-

אם איננה מחזירה ערך, void כ-()mainניתן להגדיר את •אולם אם פונקציה איננה מחזירה ערך וגם לא מוגדרת

.warning יתקבל voidכ-

Recommended