Presentation is loading. Please wait.

Presentation is loading. Please wait.

תרגול 2 העברת פרמטרים לתוכניות ב -C קלט ופלט ב -C I/O redirection זיכרון דינמי מצביעים מערכים גישות לא חוקיות לזיכרון.

Similar presentations


Presentation on theme: "תרגול 2 העברת פרמטרים לתוכניות ב -C קלט ופלט ב -C I/O redirection זיכרון דינמי מצביעים מערכים גישות לא חוקיות לזיכרון."— Presentation transcript:

1 תרגול 2 העברת פרמטרים לתוכניות ב -C קלט ופלט ב -C I/O redirection זיכרון דינמי מצביעים מערכים גישות לא חוקיות לזיכרון

2 העברת פרמטרים לתוכניות ב -C שני פרמטריםכאשר main נקראת מועברים אליה שני פרמטרים : – argc מספר הארגומנטים – argc - מספר הארגומנטים בשורת הפקודה ( כולל הפקודה עצמה ) – argv למערך של מחרוזות – argv - מצביע למערך של מחרוזות אשר מכילות את הארגומנטים בשורת הפקודה אשר תדפיס את הארגומנטיםנכתוב תוכנית בשם echo אשר תדפיס את הארגומנטים בשורת הפקודה שלה בשורה אחת עם רווח ביניהם לדוגמא : > echo hello, world hello, world > argv[0] מחזיק את השם של התוכנית הנקראת. לכן argc הוא לפחות 1. אם argc הוא 1 אין ארגומנטים אחרי שם התוכנית. בדוגמא לעיל argc הוא 3 ו argv[0], argv[1] ו argv[2] הם "echo", "hello," ו "world" בהתאמה 2

3 העברת פרמטרים לתוכניות ב -C הארגומט האמיתיהארגומט האמיתי הראשון יהיה ב - argv[1] והאחרון יהיה ב -argv[argc-1]. argv[argc] יהיה NULL. התוכנית echo: #include int main (int argc, char* argv[]){ /* echo command-line arguments */ int i; for (i = 1; i < argc; i++){ printf ("%s ", argv[i]); { printf ("\n"); return 0; } echo\0 hello,\0 World\0 argv argc=3 NULL 3

4 חזרה על קלט פלט ב -C פונקציות קלט / פלטפונקציות קלט / פלט נמצאות בספריה stdio.h –בקובץ שבו משתמשים בהן צריך לבצע #include קלט מהמקלדתפלט למסך לקבציםניתן בעזרתן לבצע קלט מהמקלדת, פלט למסך וקלט / פלט לקבצים אופן העבודה עם קבצים : פותחים 1. פותחים את הקובץ לקריאה או לכתיבה קוראים / כתובים 2. קוראים / כתובים מ -/ אל הקובץ סוגרים 3. סוגרים את הקובץ מצביע ל FILE-כל הפעולות על קבצים נעשות ע " י משתנה מטיפוס מצביע ל FILE- ( הטיפוס FILE מוגדר ב -stdio.h) 4

5 עבודה עם משאבי מערכת משאבי מערכת system resourcesקבצים הם משאבי מערכת (system resources) מערכת הפעלהמערכת הפעלה מנהלת פתיחה / סגירה של הקבצים משאב מוגבל קבצים הם משאב מוגבל – קיימת מגבלה על מספר הקבצים שיכולים להיות פתוחים בו זמנית. חייבים לסגור –לכן חייבים לסגור קבצים אחרי שמסיימים לעבוד איתם וכשלון התוכנית –אי סגירה של קבצים עלולה לגרום למילוי המכסה של הקבצים וכשלון התוכנית 5

6 עבודה עם משאבי מערכת אופן העבודה הכללי עם משאבי מערכת : 1. פותחים / מקצים 1. פותחים / מקצים משאב 2. משתמשים 2. משתמשים במשאב 3. סוגרים / משחררים 3. סוגרים / משחררים משאב לדליפת משאבים resource leakאי ביצוע שחרור משאבים גורם לדליפת משאבים (resource leak) ועלול לגרום לכישלון התוכנה. –טעות תכנות נפוצה זיכרוןדוגמא נוספת למשאב היא זיכרון 6

7 פתיחה של קובץ פתיחת קובץ fopenפתיחת קובץ נעשית ע " י הפונקציה fopen: FILE* fopen(const char* filename, const char* mode); שם הקובץ filename - שם הקובץ לפתיחה המצב mode - המצב בו יש לפתוח את הקובץ : – "r" - פתח לקריאה בלבד  אם הקובץ לא קיים, תקרה שגיאה ( החזרת NULL) – "w" - פתח לכתיבה בלבד  אם הקובץ לא קיים, ייווצר קובץ ריק  אם הקובץ קיים, התוכן שלו יימחק והקובץ יהפוך לריק לכתיבה בסוף – "a" - פתח לכתיבה בסוף הקובץ  אם הקובץ לא קיים, ייווצר קובץ ריק מחזירה מצביע מחזירה NULLהפונקציה מחזירה מצביע ל -FILE שמייצג את הקובץ אם הצליחה לפותחו ואחרת מחזירה NULL. FILE* fd = fopen("hello.c", "r"); 7

8 סגירה של קובץ סגירת קובץ fcloseסגירת קובץ שנפתח ע " י fopen נעשית ע " י הפונקציה fclose: int fclose (FILE* fd); מחזירה 0EOF fclose מחזירה 0 אם הצליחה ו -EOF אם קרתה שגיאה. לדוגמא : FILE* fd; fd = fopen("hello.c", "r"); if(fd == NULL) error(“Failed to open hello.c!\n”);... /* Perform input on file */ if (fclose(fd) == EOF) printf (“Failed to close file!\n"); else printf ("File closed successfuly.\n"); דואגת לסגורבסיום ריצת תוכנית מערכת ההפעלה דואגת לסגור את כל הקבצים בצורה מפורשתלמרות זאת, תכנות נכון הוא לסגור את כל הקבצים בצורה מפורשת גם בסוף main 8

9 בדיקת שגיאות if(fd == NULL) error(“Failed to open hello.c\n”); if(fclose(fd) == EOF) printf(“Failed to close file\n”); חייבים לבדוק ערך חזרהחייבים לבדוק ערך חזרה של כל פונקציה שאנחנו קוראים לה – לוודא שלא קרו שגיאות. כמה שיותר מוקדם כדאי לזהות מצב שבו יש שגיאה כמה שיותר מוקדם שגיאות נגררות –כדי למנוע שגיאות נגררות ונזק נוסף לבעיה הראשונית –כדי שיהיה יותר קל למצוא מה גרם לבעיה הראשונית 9

10 בדיקת שגיאות " פרנואידית "כדאי שהתוכנה תהיה כמה שיותר " פרנואידית " וכל הזמן תבדוק את עצמה ותוודא שאין שגיאות : תקינות הקלט בדיקת תקינות הקלט פרמטרים בדיקת פרמטרים לפונקציה שלנו ערכי חזרה בדיקת ערכי חזרה של פונקציות שאנחנו קוראים להן " תכנות הגנתי " defensive programmingסגנון תכנות כזה נקרא " תכנות הגנתי " (defensive programming) 10

11 פונקציות קלט / פלט char* fgets (char* line, int maxline, FILE *file) קוראת שורהקוראת שורה, כולל '\n’, מקובץ file לתוך line. לכל היותר maxline-1 תווים יקראו מחזירה NULLמחזירה NULL בתנאים הבאים : –בסוף הקובץ אם לא נקרא אף תו – אם קרתה שגיאה אחרת מחזירה line מוסיפה ‘\0’מוסיפה ‘\0’ בסוף המחרוזת int fputs(char* line, FILE *file) כותבתכותבת את line לקובץ מחזירה EOFמחזירה EOF אם קרתה שגיאה, מספר אי שלילי אחרת 11

12 דוגמא : התוכנית copy שני פרמטריםדורשת שני פרמטרים. הקובץ לקובץמעתיקה את הקובץ אשר שמו הנו הפרמטר הראשון, לקובץ אשר שמו הוא הפרמטר השני. 12

13 תוכנית copy – פונקציות עזר #define CHUNK_LENGTH 100 void copy(FILE* input, FILE* output) { char buffer[CHUNK_LENGTH]; while (fgets(buffer, CHUNK_LENGTH, input) != NULL) { fputs(buffer, output); } void error(char *message) { fprintf(stderr, "Error: %s\n", message); exit(1); } 13

14 התוכנית copy #include int main(int argc, char *argv[]) { if (argc!=3) error("Bad number of parameters"); FILE* input = fopen(argv[1], "r"); if (input==NULL) error("Can't open input file"); FILE* output = fopen(argv[2], "w"); if (output == NULL) { fclose(input); error("Can't open output file"); { copy(input,output); fclose(input); fclose(output); return 0; } סגנון תכנות צפוף מדי ! ( מתאים לשקפים, אך אסור לתוכנה אמיתית ) מספר statements בשורה הגוף של if באותה שורה ובלי סוגריים מסולסלים 14

15 ערוצים סטנדרטיים התייחסות אחידה FILEב C- יש התייחסות אחידה לכל סוגי הקלט והפלט. גם הקלט מהמקלדת והפלט מהמסך נעשים ע " י מצביעים ל -FILE שלושה ערוצים סטנדרטייםכאשר תוכנית C מתחילה לרוץ, מערכת ההפעלה פותחת עבורה בצורה אוטומטית שלושה ערוצים סטנדרטיים : stdin – stdin נפתח לקריאה ( בד " כ מהמקלדת ) stdout – stdout נפתח לכתיבה ( בד " כ אל המסך ) stderr – stderr נפתח לכתיבה ( בד " כ אל המסך ) stdin stdoutstderr FILE stdin, stdout ו -stderr הם למעשה שמות משתנים כאשר כולם מטיפוס מצביעים ל -FILE. לדוגמא הפקודה : fclose (stdin); תגרום לכך שלא ניתן יותר לקרוא קלט מהמקלדת הערה : אם תוכנית קוראת נתונים מהמקלדת ורוצים להקליד תו EOF יש להקיש – Ctrl-d ב -Unix – Ctr-z ב -Windows 15

16 פונקציות קלט / פלט char* gets(char* str) קוראת שורהקוראת שורה, לא כולל,'\n’ מהקלט הסטנדרטי לתוך str מוסיפה ‘\0’ בסוף המחרוזת ערך מוחזר – כמו של fgets מקור לבאגים ובעיות security, עדיף להשתמש ב -fgets int puts(char* str) כותבת str אל הפלט הסטנדרטי כותבת ‘\n’ אחרי str ערך מוחזר – כמו של fputs 16

17 דוגמא : התוכנית copy משודרגת שני פרמטריםהקובץ הפרמטר הראשוןלקובץהפרמטר השניבמידה וקיבלה שני פרמטרים, מעתיקה את הקובץ אשר שמו הנו הפרמטר הראשון, לקובץ אשר שמו הוא הפרמטר השני פרמטר יחידהקובץ לערוץ הפלטבמידה וקיבלה פרמטר יחיד, מעתיקה את הקובץ אשר שמו הנו פרמטר זה לערוץ הפלט הסטנדרטי ולא התקבלו פרמטרים בערוץ הקלט לערוץ הפלטבמידה ולא התקבלו פרמטרים כלל, התוכנית מעתיקה את מה שמתקבל בערוץ הקלט הסטנדרטי לערוץ הפלט הסטנדרטי 17

18 התוכנית copy משודרגת #include int main(int argc, char *argv[]) { FILE *input = stdin; FILE *output = stdout; if (argc>3) error("Bad number of parameters"); if (argc>1) input = fopen(argv[1], "r"); if (input == NULL) error("Can't open input file"); if (argc>2) output = fopen(argv[2], "w"); if (output==NULL) { fclose(input); error("Can't open output file"); { copy(input,output); fclose(input); fclose(output); return 0; } 18

19 I/O redirection ערוץ הקלט המקלדתוערוצי הפלט השגיאהבכל תוכנית שרצה תחת Unix ערוץ הקלט הסטנדרטי מחובר אל המקלדת, וערוצי הפלט והודעות השגיאה הסטנדרטיים מחוברים אל המסך כברירת מחדל לכוון מחדש redirectionניתן לכוון מחדש הן את ערוץ הקלט הסטנדרטי והן את ערוצי הפלט והשגיאות הסטנדרטיים אל ומאת קבצים כלשהם. הכיוון מחדש נקרא redirection stdout stdin stderr Program 19

20 Input redirection הקלט הסטנדרטיתגרום ל -program לקבל את הקלט הסטנדרטי שלה מהקובץ filename לדוגמא : % copy < filename תגרום ל - copy הנתונה לקחת את הקלט מהקובץ filename ההפעלה בצורה הנ " ל שקולה ( מבחינת התוצאה ) עבור התוכנית copy להפעלה : % copy filename אך מה ההבדל מבחינת התוכנית ? 20

21 Output redirection > > הפלט הסטנדרטי קיים לא תבוצעתגרום ל -program לכתוב את הפלט הסטנדרטי שלה לקובץ filename. אם קיים כבר קובץ בשם filename לפני ביצוע הפקודה, הפקודה לא תבוצע % cat myfile line 1 line 2 % cat myfile > out % cat out line 1 line 2 % 21

22 Output redirection >> >> הפלט הסטנדרטי לא קיים לא תבוצעמכוונת את הפלט הסטנדרטי של program לקובץ filename אך משרשרת אותו לסוף הקובץ. אם לא קיים קובץ בשם filename לפני ביצוע הפקודה, הפקודה לא תבוצע % cat yourfile line 3 % cat yourfile >> out % cat out line 1 line 2 line 3 % 22

23 Output redirection עלולות להיכשל הקובץ קייםאינו קייםראינו ששתי פקודות כיוון הפלט הקודמות עלולות להיכשל. הראשונה (<) אם הקובץ קיים, והשנייה (<<) אם הקובץ אינו קיים יצליחו בכל מקרהניתן להבטיח שהפקודות הנ " ל יצליחו בכל מקרה ע " י הוספת ! לפקודות הכיוון : >! >! הורסת וכותבת עליוהורסת את הקובץ filename וכותבת עליו את הפלט הסטנדרטי של program בכל מקרה אם הקובץ filename אינו קיים, אזי מתקבלת אותה התנהגות כשל < >>! >>! יוצרת לא קייםיוצרת את הקובץ filename אם הוא לא קיים. אם הקובץ filename קיים, אזי מתקבלת אותה התנהגות כשל << 23

24 Input and output redirection גם את ערוץ הקלט וגם את ערוץ הפלטניתן להפנות גם את ערוץ הקלט הסטנדרטי של התוכנית וגם את ערוץ הפלט לדוגמא הפקודות : % cat out % cat ! out גורמות להעתקת קובץ in לקובץ out ( מה ההבדל ?) % cat > out % cat >! out גורמות להוספת קובץ in לסוף קובץ out ( מה ההבדל ?) 24

25 Multiple redirection לשמור את הודעות השגיאההבעיה : נניח אנחנו רוצים לשמור את הודעות השגיאה של קומפיילר בקובץ. הדבר יכול להיות שימושי כאשר נרצה לראות את השגיאות הראשונות שהקומפיילר מוציא % gcc try_input.c -o try_input > out_err try_input.c: In function `main’: try_input.c:4: parse error before `printf’ *** Error code 1 % more out_err % השגיאות הודפסו למסך ! קובץ הפלט ריק ! 25

26 Multiple redirection גם ערוץ השגיאות הסטנדרטיהפתרון : בנוסף להפניית קלט / פלט סטנדרטיים ניתן להפנות גם את ערוץ השגיאות הסטנדרטי מהמסך אל קבצים >& מפנה הן את הפלט הסטנדרטי והן את ערוץ השגיאות הסטנדרטי לקובץ file % gcc try_input.c -o try_input >& out_err % % more out_err try_input.c: In function `main': try_input.c:4: parse error before `printf' *** Error code 1 26

27 Multiple redirection >&! >&! זהה לפקודה הקודמת אבל עובדת גם אם הקובץ file קיים >>& >>& פועלת כמו במקרה הקודם, אבל כאן המידע החדש משורשר אל סוף הקובץ. ניתן גם להיעזר ב - ! אם רוצים להבטיח כי הפקודה תתבצע >>&! >>&! הפקודה > >& ( > ) >& גורמת לפלט הסטנדרטי להיכתב אל הקובץ f1 ואילו להודעות השגיאה להכתב אל קובץ f2 27

28 Multiple redirection #include int main(){ fprintf(stderr, “This is error\n”); printf(“This is stdout\n”); return 0; } % gcc prog.c –o prog % prog This is error This is stdout % prog > out.txt This is error % cat out.txt This is stdout % prog >& errout.txt % cat errout.txt This is error This is stdout % (prog > out) >& err % cat out This is stdout % cat err This is error 28

29 ניהול זיכרון של תוכניות ב -C משתניםמשתנים ב -C נבדלים בשני אופנים : 1. טיפוס הערכים האפשריים פעולות כמות הזיכרון 1. טיפוס – למשל int, char ו -double. טיפוסים שונים אחד מהשני מבחינת הערכים האפשריים עבור כל אחד, אילו פעולות ניתן לבצע על כל אחד מהם, וכמות הזיכרון שנדרשת עבור כל אחד מהם 2. אופן ההקצאה ונגישות 5 סוגי  מבחינת אופן ההקצאה והנגישות קיימים 5 סוגי משתנים : גלובליים –משתנים גלובליים סטאטיים של קובץ –משתנים סטאטיים של קובץ לוקאליים –משתנים לוקאליים סטאטיים של פונקציה –משתנים סטאטיים של פונקציה דינמיים –משתנים דינמיים 29

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

31 ניהול זיכרון של תוכניות ב -C השימושי ביותרהמשתנים הלוקאליים בשאר כתכנות רעמבין ארבע הקבוצות הללו סוג המשתנים השימושי ביותר הנו המשתנים הלוקאליים. שימוש בשאר סוגי המשתנים נחשב לרוב כתכנות רע - משתמשים בהם רק כשכל הפתרונות האחרים יותר גרועים. הבעיות : –נגישות יתר –נגישות יתר של משתנים – side-effects – side-effects של פונקציות  קשה להבין  קשה יותר להבין מה הפונקציה עושה בלי ההקשר  קשה לבודד בעיות כשמדבגים  קשה לבודד בעיות כשמדבגים – הבאגים יכולים להופיע רק בהקשר מסוים 31

32 משתנים דינמיים יישארו בזיכרון מספר רב של משתנים אינו ידוע בזמן כתיבת התוכניתהבעיה עם משתנים לוקאליים היא שהמשתנים נהרסים כאשר פונקציה נגמרת. לעיתים נרצה כי משתנים מסוימים יישארו בזיכרון גם לאחר שהפונקציה נגמרת. יתר על כן, לעיתים נרצה להקצות בזמן ריצה מספר רב של משתנים אשר מספרם אינו ידוע בזמן כתיבת התוכנית אלא רק בזמן ריצה משתנים דינמיים מוקצים בזמן ריצה ערימה Heapמשתנים דינמיים - מוקצים ע " י התוכנית בזמן ריצה. המתכנת מקצה את המשתנים מאזור בזיכרון המכונה ערימה (Heap) ובאחריות המתכנת לשחרר את המשתנים שהוקצו לאחר שאין בהם צורך 32

33 זיכרון הוא משאב מערכת משאבי מערכתכמו בעבודה עם משאבי מערכת אחרים : 1. מקצים 1. מקצים זיכרון 2. משתמשים 2. משתמשים בזיכרון 3. משחררים 3. משחררים את הזיכרון שהקצנו אחרי השימוש הקצאת mallocהקצאת הזיכרון מתבצעת ע " י פונקציה malloc שחרור freeשחרור הזיכרון מתבצע ע " י פונקציה free לדליפות זיכרון memory leaksאי ביצוע שחרור זיכרון גורם לדליפות זיכרון (memory leaks) והתוכנית עלולה להיכשל –טעות תכנות נפוצה 33

34 מבנה הזיכרון כמערך גדול של בתיםתוכנית המחשב רואה את הזיכרון כמערך גדול של בתים (bytes) כתובת –לכל בית יש כתובת ייחודית לו בבית אחד או יותר –התוכנית יכולה להיעזר בבית אחד או יותר בכדי לשמור משתנים שמות משתנים נדרש בית אחד עבור ערך מסוג char. נדרשים 4 בתים עבור ערך מסוג int. c i ערךכתובת זבל 0x200 'a'0x201 ? 0x202 6 0x203 0 0x204 0 0x205 0 0x206 ? 0x207 …… 34 שמות משתנים

35 מבנה הזיכרון לשני חלקיםזיכרון המחשב מחולק עבור התוכנית לשני חלקים עיקריים באופן אוטומטי הגלובלייםהלוקאליים והסטאטיים –חלק המנוהל באופן אוטומטי ע " י התוכנית שם נשמרים המשתנים הגלובליים, הלוקאליים והסטאטיים ערימההמנוהל המתכנת – heap ( ערימה ) - חלק המנוהל ע " י המתכנת במפורש malloc  ניתן לבקש להקצות מחלק זה שטחי זיכרון בגדלים שונים ע " י הפקודה malloc free  לשחרר זיכרון אשר אין בו צורך יותר ע " י הפקודה free ההקצאההכתובת מצביעפעולת ההקצאה מחזירה את הכתובת של תחילת שטח הזיכרון שהוקצה. בכדי לשמור כתובת זו יש להיעזר במשתנה מסוג מצביע 35

36 מצביעים משתנה מסוג מצביע כתובת בזיכרון –משתנה שערכו הוא כתובת בזיכרון שבתוכה נמצא נתון כלשהוא * * –המשתנה מיועד להצביע על מקום בזיכרון המכיל משתנה מטיפוס int *ptr; מיועד להצביע ptr הוא משתנה אשר מיועד להצביע על מקום בזיכרון מטיפוס int כתובות –נניח שהגדרנו משתנה ע " י int x; הכתובת בזיכרון הביטוי : &x הוא הכתובת בזיכרון בה שמור תוכנו של.x – למשל, אם x הוא שלוש ומאוחסן בכתובת 2000 אזי ערכו של הביטוי &x הוא 2000 36

37 מצביעים שימוש במשתנה מסוג מצביע –גישה למשתנה –גישה למשתנה בעזרת מצביע לכתובתו הפקודה האחרונה שקולה ל : x=5.8. משתנה בעצמושימו לב כי מצביע הוא משתנה בעצמו ולכן גם הוא נשמר בזיכרון. float x; float* ptr; ptr = &x; *ptr=5.8; 37

38 מצביעים ומוצבעים אינה גורמת ליצירת המקוםהגדרת מצביע אינה גורמת ליצירת המקום אשר אליו הוא יצביע int *pi; אינו מצביעיכול להצביע pi אינו מצביע למקום בזכרון המכיל integer אלא רק יכול להצביע למקומות בזכרון המכילים int *pi = 0 ; להעפת התוכנית גישה לא חוקיתהצבת 0 למקום אליו מצביע pi עלולה לגרום להעפת התוכנית כיוון שלא ידוע להיכן בזכרון pi מצביע וגישה למקום אליו הוא " מצביע " עלולה להיות גישה לא חוקית לזכרון יש לוודא לכתובת חוקיתיש לאתחל ניתן לבדוק לכתובת חוקיתלפני גישה למקום בזכירון המוצבע ע " י מצביע יש לוודא כי המצביע מצביע לכתובת חוקית. יש לאתחל מצביעים כך שיכילו את הערך NULL לפני שמוכנסת אליהם כתובת חוקית, בצורה זאת ניתן לבדוק האם הם אינם מצביעים לכתובת חוקית int j ; int *pi = NULL;... pi = &j; *pi = 0; 38

39 מצביעים ומערכים יצירת מערך עם number איברים כאשר ערכי האינדקסים נעים בין 0 ל number-1 [ ] [ ] int x[3]; [ ]גישה למקום במערך ( חד - ממדי ) ע " י : [ ] x[2]; " שם המערך " מצביע לא ניתן לשנות " שם המערך " הנו מצביע אשר לא ניתן לשנות את המקום אליו הוא מצביע. לכן הביטוי x ללא אינדקסים מהווה מצביע אל המקום הראשון במערך ! כלומר, *x=3 שקול ל x[0]=3 הגדרת מערך הקצאה אוטומטית שהגדרת מצביע אינה גורמת להקצאתהגדרת מערך בגודל number גוררת הקצאה אוטומטית number אברים מהטפוס המתאים, בעוד שהגדרת מצביע אינה גורמת להקצאת האיבר המוצבע ניתן להצביע למערך ( או איבר במערך ) גם ע " י מצביע מערכים אוטומטיתמערכים מוקצים ומשוחררים אוטומטית int *p, *q ; p = x ; /* p points to x[0] */ q = x+2 ; /* q points to x[2] */ p = q-1; /* p points to x[1] */ הערות כאלה מתאימות רק לשקפים ! בתוכנה אמיתית הן לא מוסיפות שום מידע לקוד 39

40 מצביעים למצביעים מצביע הנו משתנהיש לו כתובתניתן להציב במשתנהמצביע הנו משתנה, ולכן יש לו כתובת. ניתן להציב את כתובתו במשתנה מטיפוס מצביע למצביע int a, b ; /* a and b are integers */ int *pi = &a ; /* pi is a pointer to integer that points to a */ int** ppi = π /* ppi is a pointer to pointer to integer. It points to pi */ pi = &b ; /* pi points to b */ *pi = 5 ; /* b is set to 5 */ *ppi = &a ; /* pi is set to &a, pi points to a now */ *pi = 6 ; /* a is set to 6 */ *(*ppi) = 7; /* a is set to 7 */ ( נתעלם בדוגמא ממספר הבתים שנדרשים לכל משתנה ( משתנהערךכתובת ppi0x2020x200 'a'0x201 pi0x2050x202 6 0x203 0x204 a 7 0x205 0x206 b50x207 …… 40

41 מצביע ל - void ניתן להגדיר מצביע מטיפוס void*. כתובת של טיפוס כלשהו במצביע מטיפוס זה ניתן לשמור כתובת של טיפוס כלשהו שלקומפיילר לא ידוע castכיוון שלקומפיילר לא ידוע מהו הטיפוס המוצבע ע " י על ידי מצביע מסוג void*, בכדי לקבל את הערך המוצבע נהוג לבצע cast שימיר את המצביע למצביע לטיפוס ששונה מ -void: מצביע ל void פונקציות ניהול הזיכרון הדינמי טיפוס כלשהומצביע ל void הנו שמושי עבור פונקציות ניהול הזיכרון הדינמי אשר מסוגלות להקצות שטח זיכרון עבור טיפוס כלשהו. שמושים נוספים למצביע מסוג זה נראה בהמשך. *ptr; void *ptr; i; int i; d ; double d ;….. (…) { if (…) { ptr = &d ; ptr = &d ; } else ptr = &i; ptr = &i; } *pi; int *pi; j; int j; pi = (*) ptr; pi = (int*) ptr; j = *pi; j = *((*) ptr); j = *((int *) ptr); 41

42 מצביעים והקצאות זיכרון דינמיות void* malloc (unsigned int size) * ההקצאה מוחזר מצביע אחרת NULLאם ההקצאה הצליחה מוחזר מצביע לשטח שהוקצה בזיכרון, אחרת מוחזר NULL לשמירת ערכים מטיפוס כלשהוכיוון שהזיכרון המוקצה יכול לשמש לשמירת ערכים מטיפוס כלשהו, הטיפוס המוחזר ע " י malloc הנו void* ההגדרות של malloc ו -NULL נמצאים ב - stdlib.h. לדוגמא : int *ptr; ptr=(int*)malloc(4); /* allocate 4 bytes */ if (ptr==NULL) { /* check if allocated */ error(“Failed to allocate 4 bytes”); } כעת ptr מצביע למקום בזיכרון שבו ארבעה בתים פנויים ( אם ההקצאה הצליחה ) נהוג castingכיוון ש -ptr הוא מטיפוס int* ו -malloc מחזירה ערך מטפוס void*, נהוג לבצע casting * ההגדרה האמיתית של malloc היא void * malloc (size_t size), size_t מחוץ לחומר של הקורס 42

43 מספרי קסם מה הבעיה בשורה הזאת ? ptr=(int*)malloc(4); /* allocate 4 bytes */ מספר קסם magic number 4 הוא מספר קסם (magic number) –כינוי גנאי –כינוי גנאי לכל מספר בקוד התוכנית, שהוא לא 0 או 1  ( חוץ מהמספרים שמופיעים ב -define) –תכנות רע לא מתעד  המספר לא מתעד את עצמו שינוילעבור על כל המקומות  כל פעם שמתעורר צורך בשינוי המספר, חייבים לעבור על כל המקומות שהמספר מופיע ולתקן אותם  סכנה לבעיות לתקן את כל המקומות  סכנה לבעיות שינבעו מכך שהמתכנת ישכח לתקן את כל המקומות 43

44 מצביעים והקצאות זיכרון דינמיות ptr=(int*)malloc(4); /* allocate 4 bytes */ מה הבעיה עם התיקון הבא ? #define SIZE_OF_INT (4)... ptr=(int*)malloc(SIZE_OF_INT); לא יעבודהקוד הזה לא יעבוד במחשב שבו int הוא בגודל שונה מ -4 non portableקוד כזה נחשב כ -non portable – לא ניתן להעבירו בלי שינוים בין מחשבים שונים 44

45 מצביעים והקצאות זיכרון דינמיות למנוע שינוי תוכניתגודל שונה sizeofכדי למנוע את הצורך בשינוי תוכנית בגלל גודל שונה של טיפוסים, במהדרים / מערכות הפעלה / מחשבים שונים, ניתן להשתמש באופרטור sizeof sizeof( ) מחזיר את מספר הבתים של הטיפוס במערכת הנוכחית sizeof ptr=(int*)malloc(sizeof(int)); *ptr=3; כעת ניתן להשתמש ב *ptr כמשתנה מסוג int ( אם ההקצאה הצליחה ) מה הטעות במקרה הזה ? ptr=(int*)malloc(sizeof(int *)); 45

46 מצביעים והקצאות זיכרון דינמיות free( ) משחרר את בלוק הזיכרון שהוקצה ושהמשתנה מצביע עליו. דוגמא : ptr=(int *)malloc(sizeof(int)); if(ptr == NULL) error(“Not enough memory”);... /* use ptr */ if(ptr != NULL) free if(ptr != NULL) free (ptr); freeאם מעבירים ל -free NULL, לא קורה כלום free- –לכן אין צורך לבדוק ש -ptr לא NULL לפני הקריאה ל free- 46

47 גישות לא חוקיות לזיכרון טעויות נפוצות

48 גישה לא חוקית לזיכרון כתובת שהיאמצביע יכול להכיל כל כתובת שהיא שלא הוקצה –זיכרון שלא הוקצה לשימוש לדברים אחרים –זיכרון שמשמש לדברים אחרים לקרוסהתוכנית עלולה לקרוס Bus error Segmentation Fault /Bus error Segmentation Fault 48 (from The Seattle Times)The Seattle Times

49 גישה לא חוקית לזיכרון לרוץ כרגילהתוכנית עלולה לרוץ כרגיל –אבל... ריצה מוצלחת על קלט אחד לא מבטיחה כלום על קלט אחר כל גישה כתובת חוקית שגיאהכל גישה ( קריאה או כתיבה ) דרך מצביע שאינו מצביע לכתובת חוקית עלולה לגרום לשגיאה 49

50 טעויות נפוצות מחרוזת קצרה מדייהקצאת מחרוזת קצרה מדיי ‘\0’ –לא לשכוח את ‘\0’ – strlen איננה סופרת את ‘\0’ – strlen איננה סופרת את ‘\0’ כאשר היא מחזירה אורך של מחרוזת שימוש שגוי ב -sizeof –האופרטור מקבל טיפוס של משתנה –איזה שורה מבין השורות הבאות נכונה ? 1.ptr = (int*)malloc(sizeof(int *)); 2.ptr = (int*)malloc(sizeof(int)); 3.ptr = (int*)malloc(sizeof(ptr)); 50

51 טעויות נפוצות חריגה מגבולותחריגה מגבולות המערך לא שומר אינו מתריע גישה מחוץ לגבולות –הקומפיילר לא שומר את המידע מהו גודל המערך ולכן אינו מתריע בזמן קומפילציה על גישה מחוץ לגבולות המערך 51

52 טעויות נפוצות הזבל לסל וחסל –אחרי free להשתמש בזיכרון –אחרי free אי אפשר להשתמש בזיכרון, גם לא ממצביע אחר ptr=(int *)malloc(sizeof(int));... ptr2 = ptr;... free (ptr);... *ptr = i; i = *ptr; *ptr2 = i; i = *ptr2; לשחרר פעם שנייה –אסור לשחרר את הזיכרון פעם שנייה אחרי שכבר שוחרר, גם לא דרך מצביע אחר 52

53 טעויות נפוצות משתנים לוקלייםמחוץ לפונקציהשימוש במשתנים לוקליים של פונקציה מחוץ לפונקציה והחזרת הכתובת שלו –יצירת משתנה לוקלי בפונקציה, והחזרת הכתובת שלו לפונקציה הקוראת int* foo() { int i;... return &i; } 53

54 טעויות נפוצות ההבדל בין = ל - == if(ptr == NULL) printf(“Bad pointer!\n”); if(ptr = NULL) printf(“Bad pointer!\n”); 54

55 דוגמא – חריגה מגבולות המערך #include #define TITLE ("\n\tMemory Corruption: How easy it is\n\n") #define BUF_SIZE (8) int main() { int i; char c[BUF_SIZE], buf[BUF_SIZE]; printf("Type string c[]\n"); gets(c); printf("%s\n",c); printf("Type string buf[]\n"); gets(buf); printf("%s\n",buf); for (i=4; i > -10 ; i--){ c[i] = 'a'; printf("i= %2d, buf = %s c= %s\n", i, buf, c); } return 0; } 55

56 0 1 2 3 4 5 6 \0 0 1 2 \0 זבל זבל buf c aa a a a a a a 56


Download ppt "תרגול 2 העברת פרמטרים לתוכניות ב -C קלט ופלט ב -C I/O redirection זיכרון דינמי מצביעים מערכים גישות לא חוקיות לזיכרון."

Similar presentations


Ads by Google