מצביעים, הקצאה דינאמית ומבנים

Slides:



Advertisements
Similar presentations
Singly Linked List BTECH, EE KAZIRANGA UNIVERSITY.
Advertisements

Computer Programming for Engineering Applications ECE 175 Intro to Programming.
Data Structure Lecture-5
Unions The storage referenced by a union variable can hold data of different types subject to the restriction that at any one time, the storage holds data.
C Intro.
Growing Arrays in C By: Victoria Tielebein CS 265- Spring 2011.
Memory allocation CSE 2451 Matt Boggus. sizeof The sizeof unary operator will return the number of bytes reserved for a variable or data type. Determine:
Winter2015 COMP 2130 Intro Computer Systems Computing Science Thompson Rivers University C: Advanced Topics.
1 Day 03 Introduction to C. 2 Memory layout and addresses r s int x = 5, y = 10; float f = 12.5, g = 9.8; char c = ‘r’, d = ‘s’;
Multidimensional Arrays. Example Write a program to keep track of all warmup scores for all students. Need a list of a list of scores Student – score.
Lecture 08 METU Dept. of Computer Eng. Summer 2002 Ceng230 - Section 01 Introduction To C Programming by Ahmet Sacan Mon July 24, 2002.
CSSE 332 Explicit Memory Allocation, Parameter passing, and GDB.
7. Pointers, Dynamic Memory 20 th September IIT Kanpur 1C Course, Programming club, Fall 2008.
CSE 251 Dr. Charles B. Owen Programming in C1 Pointers, Arrays, Multidimensional Arrays Pointers versus arrays – Lots of similarities How to deal with.
Struct 1. Definition: Using struct to define a storage containing different types. For example it can contain int, char, float and array at the same time.
ECE Application Programming
Introduction to Computer Organization & Systems Topics: C arrays C pointers COMP Spring 2014 C Part IV.
ECE Application Programming Instructors: Dr. Michael Geiger & Nasibeh Nasiri Fall 2015 Lecture 31: Structures (cont.) Dynamic memory allocation.
Computer Programming for Engineering Applications ECE 175 Intro to Programming.
Digital Computer Concept and Practice Copyright ©2012 by Jaejin Lee C Language Part 5.
C Tutorial - Pointers CS 537 – Introduction to Operating Systems.
Arrays Name, Index, Address. Arrays – Declaration and Initialization int x; y[0] y[1] y[2]
REEM ALAMER REVISION FOR C LANGUAGE COURSE. OUTPUTS int main (void) { int C1, C2; int *p1, *p2; C1 = 8; p1 = &C1; C2 = *p1 / 2 + 5; p2 = &C2; printf ("C1.
PROGRAMMING II( Dynamic Memory II)
C Structures and Memory Allocation
Pointers verses Variables
Memory allocation & parameter passing
Linked List :: Basic Concepts
C Primer.
Functions and Pointers
Day 03 Introduction to C.
2016.
Course Contents KIIT UNIVERSITY Sr # Major and Detailed Coverage Area
Pointers Department of Computer Science-BGU יום שלישי 31 יולי 2018.
Array 9/8/2018.
Day 03 Introduction to C.
Module 2 Arrays and strings – example programs.
2-D arrays a00 a01 a02 a10 a11 a12 a20 a21 a22 a30 a31 a32
Programming Languages and Paradigms
Arrays & Dynamic Memory Allocation
מצביעים והקצאה דינאמית
Linked List Sudeshna Sarkar.
Programmazione I a.a. 2017/2018.
Some examples.
Functions and Pointers
Quiz 3.
14th September IIT Kanpur
LINKED LISTS.
Dynamic memory allocation and Intraprogram Communication
Introduction to Programming in C
Programming and Data Structures
typedef, struct, מערכים דינמיים דו-ממדיים ומצביעים
Computer Programming תרגול 9 Summer 2016
מ- C++ ל- C קרן כליף.
بنام خدا زبان برنامه نویسی C (21814( Lecture 11 Pointers
תכנות מכוון עצמים ו- C++ יחידה 01 מ- C ל- C++
CSC215 Lecture Memory Management.
CS111 Computer Programming
Pointers Problem Solving & Program Design in C Eighth Edition
EECE.2160 ECE Application Programming
Outline Defining and using Pointers Operations on pointers
Introduction to Problem Solving and Programming
Linked List.
LINKED LIST.
7. Pointers, Dynamic Memory
EECE.2160 ECE Application Programming
Character Arrays char string1[] = “first”;
READING AND PRINTING MATRICES (with functions)
Structures EECS July 2019.
EECE.2160 ECE Application Programming
Presentation transcript:

מצביעים, הקצאה דינאמית ומבנים Engineering Programming A תרגול 9 מצביעים, הקצאה דינאמית ומבנים Introduction to C - Fall 2010 - Amir Menczel

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

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

זיכרון דינמי ישנן שתי שיטות לביצוע הקצאת זיכרון: הקצאת זיכרון סטטית והקצאת זיכרון דינאמית. הקצאת זיכרון סטטית - המהדר קובע את דרישות האחסון על פי הצהרת המשתנים, בזמן הקומפילציה (כך הקצאנו זיכרון עד כה!). בעיה שלעיתים צצה היא שאין אנו יכולים לנחש מראש את כמות הזיכרון שהתוכנית שלנו עלולה לצרוך. הקצאת זיכרון דינאמית - הקצאת מקום נעשית בזמן ריצה על ידי קריאה לפונקציה malloc() (קיצור ל-memory allocation). פונקציה זו מקבלת כפרמטר את מס' הבתים שברצוננו להקצות ומחזירה את הכתובת של הבית הראשון ברצף הבתים שהקצאתה. אם הפונקציה נכשלת מוחזר הערך NULL. שחרור זיכרון דינאמית - מכיוון שהקצאת הזיכרון נעשתה בזמן ריצת התוכנית, יש לדאוג לשחרר את הזיכרון לאחר שנסיים להשתמש בו. שחרור של זיכרון דינאמי נעשה ע"י קריאה לפונקציה free(). הפונקציה מקבלת מצביע לכתובת תחילת קטע הזיכרון שרוצים לשחרר. תוכנית שאינה משחררת זיכרון נחשבת לא תקינה ויכולה לגרום לבעיות ברמת הגורם המפעיל אותה.

הקצאת זיכרון דינאמית שימוש בפונקציות: malloc ,free #include <stdlib.h> //must add this library pointer_variable = (pointer_type) malloc (size_of_memory); דוגמה: int size, *p_list; printf("Enter the number of elements:"); scanf("%d", &size); p_list = (int*)malloc (size * sizeof(int)); …. free(p_list); שחרור הזיכרון בסיום השימוש בו

דוגמא להקצאת ושחרור זיכרון כאשר מקצים זיכרון הערך המוחזר על ידי פונקצית הקצאת זיכרון היא כתובת. יש מקרים שבעבורם הקצאת הזיכרון נכשלת והערך המוחזר על ידי הפונקציה הוא NULL. מכיוון שלא קיבלנו כתובת, לא ניתן להשתמש במצביע כמצביע "חוקי" ונרצה לסיים את התוכנית. לכן, לאחר הקצאת זיכרון תמיד צריך לבדוק האם ההקצאה הצליחה. void main(){ long *l_list; l_list =(long*) malloc (5*sizeof(long)); if (l_list == NULL){ printf ("Failed to allocate memory"); return; } … free(l_list); }

דוגמא נתבונן בתוכנית הבאה: printf("%d",x[0]); #include <stdio.h> #include <stdlib.h> int* foo (); int* foo (){ int arr[3]={1,2,3}; return arr; } void main(){ int* x = foo (); printf("%d",x[0]); התנהגות תוכנית זו אינה מוגדרת מכיוון שהפונקציה מחזירה מצביע למערך לוקאלי. כאשר הפונקציה מסתיימת הזיכרון בו נמצא המערך אינו חוקי עוד לשימוש. נתקן את הפונקציה בצורה הבאה: int *arr = (int*) malloc(sizeof(int)*3); arr[0]=1; arr[1]=2; arr[2]=3; לאחר סיום ריצת הפונקציה ולאחר שסיימנו להשתמש במערך, נדאג לשחרר את הזיכרון ע"י הוספת הפקודה הבאה ל- main או לכל פונקציה אחרת בה זיהינו כי אין עוד צורך בזיכרון שהוקצה. free(x);

תרגיל 1 תוכנית להדגמה של מערך דו-מימדי דינאמי: כשרוצים להגדיר מערך דו מימדי בעל גודל משתנה, עלינו ליצור מערך של מצביעים למערכים. גודל המערך לא ידוע בתחילת התכנית ולכן נגדיר את המערך באמצעות פונקצית malloc(). בתחילה נאתחל מערך של מצביעים, ולאחר מכן נאתחל כל מצביע להיות מערך של ה- type הרצוי. למשל, יצירת מערך של int-ים בגודל המוגדר ע"י המשתמש שבכל איבר בו יש את הערך של מכפלת האינדקסים שלו:

תרגיל 1 #include <stdio.h> #include <stdlib.h> void main(){ int i, j, k, rows, cols; int **array; printf("enter num of rows: "); scanf("%d",&rows); printf("enter num of columns: "); scanf("%d",&cols); if ( !(array=(int **)malloc(rows*sizeof(int *)) )){ printf("Memory allocation failed, quiting… "); return; } . . .

המשך תרגיל 1 for (i=0; i<rows; i++) } /*Fill in the different rows:*/ if ( !(array[i]=(int *)malloc(cols*sizeof(int)) ))} for (k=0; k<i; k++) /*Free all priory allocated memory:*/ free( array[k] ); free(array); printf("Memory allocation failed, quiting… "); return; /*Terminate the program!*/ } for (j=0; j<cols; j++) /*Fill in the different columns:*/ array[i][j] = i*j; for (i=0; i<rows; i++){ /*Print the different rows:*/ for (j=0; j<cols; j++) printf("%d ",array[i][j]); printf("\n");

המשך תרגיל 1 /*Free allocated memory:*/ for(i=0; i<rows; i++) free(array[i]); free(array); }

תרגיל 2 התכנית לא מדפיסה כלום. יש שגיאה בזמן ריצה .(run time error) עיין בקטע הבא וסמן את כל התשובות הנכונות (הנח כי כל הקצאות הזיכרון מצליחות):   #include <stdio.h> #include <stdlib.h> #define MAX 10 void main(){ int *ptr, *arr[MAX]; int i, j; for (i=MAX-1; i>=0; i--) if (arr[i] = (int *) malloc(i * sizeof(int))) for (j=0; j<i; j++) *(*(arr+i)+j) = j*i; //same as arr[i][j]=j*i ptr = *(arr+MAX-1); while (*ptr) printf ("%d ", *ptr--); }    התכנית לא מדפיסה כלום. יש שגיאה בזמן ריצה .(run time error) התכנית תדפיס: 9 18 27 36 45 54 63 72. התכנית תדפיס אינסוף אפסים. התכנית תדפיס 0. התכנית תדפיס ערכים לא ידועים. אף לא אחת מהתשובות לעיל.

פתרון תרגיל 2

פתרון תרגיל 2

תרגיל 3: נתונה הפונקציה הבאה: char *search(char *str1, char *str2){ int i, j, k, length; char *temp, *aux=NULL; for(i=0, length=0; *(str1+i); i++) for(j=0; *(str2+j); j++){ for(k=0; *(str1+i+k) && *(str1+i+k) == *(str2+j+k); k++); if( k>length ){ length=k; aux=str1+i; } //if } //for j if(aux) if( temp = (char *)malloc(length+1) ){ for(i=0; i<length; i++) *(temp+i )= *(aux+i); *(temp+i)='\0'; return temp; return NULL; } תרגיל 3: נתונה הפונקציה הבאה:

תרגיל 3 הסבר בקצרה מה יעודה של הפונקציה הנ"ל. מה הפלט של קטע קוד הבא:   מה הפלט של קטע קוד הבא: char sentence1[]=”It is clever of him to solve the problem”; char sentence2[]=”I am glad to solve your problem”, *sentence3; sentence3 = search(sentence1, sentence2); puts(sentence3);

תרגיל 3 - תשובות הפונקציה מוצאת את המחרוזת המשותפת הארוכה ביותר לשתי המחרוזות הניתנות. הפונקציה מעתיקה את המחרוזת שנמצאה למחרוזת חדשה ומחזירה מצביעה לתחילתה. במקרה שלא קיימת מחרוזת משותפת או שהקצאת הזיכרון נכשלה, הפונקציה מחזירה NULL

structures מבנה (struct) הוא טיפוס אשר יכול להכיל מספר רב של נתונים מטיפוסים שונים. המבנה מורכב ממשתנים, שלכל אחד מהם קוראים שדה. ניתן לומר שמבנה דומה למערך בכך ששניהם יכולים לאגד מספר נתונים, אך מבנה נותן לנו גם חופש בבחירת טיפוסי הנתונים ,כלומר מבנה יכול להכיל גם float, int, double ועוד בעת ובעונה אחת תחת משתנה אחד. התבנית של מבנה: struct nameOfStruct { fields }; בהגדרת המבנה יש לכתוב את המילה השמורה struct ולאחר מכן את שם המבנה שבחרנו. חשוב ששמו של המבנה יהיה משמעותי בדיוק כמו בבחירת שמות למשתנים. בתוך הסוגריים יש לרשום את המשתנים ובסוף נקודה-פסיק.

structures לדוגמא: struct address { char city[20]; char street[20]; int houseNum; }; יצרנו מבנה של "כתובת מגורים" הכולל: שם עיר ורחוב - מחרוזות בעלות 20 תווים, ומספר בית - משתנה מסוג int. כעת נצהיר על משתנה מהטיפוס "כתובת מגורים": struct address addr;

structures אופרטור הנקודה: כשנרצה לגשת לשדות של המשתנה addr ולעדכן אותם, נוכל לעשות זאת על ידי שימוש באופרטור הנקודה (.). לדוגמא, נציב את המספר 5 בשדה של מספר הבית באופן הבא: addr.houseNum = 5; כך ניתן לעדכן ולשנות את השדות. ניתן להצהיר על מספר משתנים מטיפוס מבנה בשורה אחת כמו שהצהרנו על משתנים פשוטים, לדוגמא: struct address addr1, addr2, addr3; כמו כן ניתן לאתחל מבנים כפי שאתחלנו מערך: struct address addr = {"Tel Aviv", "Menachem Begin", 132}; ניתן גם לבצע השמה בין מבנים, בצורה הבאה : addr1=addr2; נסכם ונאמר כי ניתן לבצע את כל הפעולות שביצענו במשתנים פשוטים על מבנה באותו אופן (הצבה כארגומנט בפונקציה, הצהרה וכו').

structures אופרטור <- : האופרטור <- מאפשר גישה לשדה של מבנה כלשהו ממצביע למבנה. לדוגמא, נגדיר מצביע למבנה address, pAddress: struct address addr, *pAddr; pAddr = &addr; נוכל לגשת לשדה houseNum של המבנה ע"י כתיבת: addr.houseNum = 3; או: pAddr->houseNum = 3; (*pAddr).houseNum = 3;

structures מבנה כפרמטר של פונקציה: נראה שני סוגים של העברת מידע האגור במבנים אל פונקציה כפרמטר - האחד העברה לפי כתובת והשני לפי תוכן. תרגיל 1: כתוב תכנית אשר מכילה פונקציה שקולטת לתוך מבנה שם וגיל של אדם, ובאמצעות פונקציה נוספת מדפיסה אותם.

structures #include <stdio.h> // by reference void getDetails (struct person *p) { printf ("Enter name: "); gets (p->name); printf ("Enter age: "); scanf ("%d", &(p->age)); } //by value void printDetails (struct person p) printf("name: %s\n ", p.name); printf("age: %d\n ", p.age); void main() struct person pr; getDetails (&pr); printDetails (pr); הסבר: בפונקצית main() יש מבנה מסוג person בשם pr. כשנפעיל את הפונקציה getDetails, נרצה שהיא תוכל לשנות את המבנה, ולכן נעביר לפונקציה את הכתובת של המשתנה pr. לפיכך, הפרמטר של הפונקציה getDetails צריך להיות כתובת - כלומר מצביע.

structures מבנה כערך המוחזר על ידי פונקציה: כאשר אנו מעוניינים לקלוט נתוני מבנה, לפעמים נעשה זאת בעזרת פונקציה המחזירה מבנה. בהצהרה על הפונקציה יש לרשום: struct nameOfStruct nameOfFunction(arguments);   תרגיל 2: שנה את הפונקציה getDetails מהתכנית הקודמת כך שתחזיר מבנה, ועדכן את ה-main בהתאם.

structures struct person getDetails(){ struct person pers; printf ("Enter name: "); gets (pers.name); printf ("Enter age: "); scanf ("%d", &(pers.age)); return pers; } void main(){ struct person pr; pr=getDetails(); printDetails (pr);

structures מערך של מבנים: ניתן ליצור מערך של מבנים בדיוק כמו בהצהרת מערך רגיל. לדוגמא: struct person pr[3]; יוצר מערך של שלושה משתנים מטיפוס person . כדי לגשת לשדותיו יש לפנות קודם לאיבר במערך שבו אנו מעוניינים - לדוגמא, עבור השדה name באיבר השני של המערך, נרשום: pr[1].name תרגיל 3: שנה את התכנית הקודמת כך שתעבוד עם מערך של מבנים.

structures #include <stdio.h> void getDetails (struct person p[],int size){ int i; for (i=0; i<size; i++){ printf ("Enter name: "); gets (p[i].name); printf ("Enter age: "); scanf ("%d", &(p[i].age)); } void printDetails (struct person p[],int size){ for (i=0; i<size; i++) { printf("name: %s\n ", p[i].name); printf("age: %d\n ", p[i].age); void main(){ struct person pr[3]; getDetails (pr,3); printDetails (pr,3);

Typedef typedef char byte; typedef struct person{ char name[20]; int id; } person;

רשימות משורשרות typedef struct node{ int value; node *next; } node; רשימות מקושרות - הגדרה ותכונות:   רשימה מקושרת – שרשרת של מבנים. כל מבנה מכיל מצביע למבנה הבא או NULL (עבור המבנה האחרון אשר לא מצביע על המבנה הבא - כי אין כזה). לאיבר שנמצא בתחילת הרשימה נהוג לקרוא "עוגן". בניגוד למערך, אין גישה מיידית לכל איבר ברשימה באמצעות סוגריים מרובעות [], אלא באמצעות סריקה של הרשימה עד שמגיעים לאיבר המבוקש. היתרון לעומת מערך – הוספה ומחיקה יעילה של איברים. כלומר – אם נרצה למחוק איבר מאמצע מערך, נהיה חייבים ליצור עותק חדש של המערך כולו. ברשימה כל מה שנצטרך לעשות זה למחוק את האיבר שמיועד להימחק, ולקשר בין שני האיברים שמסביבו. זהו חיסכון משמעותי ביותר מבחינת זמן ריצה. typedef struct node{ int value; node *next; } node;

רשימות משורשרות תרגיל 1: כתוב פונקציה countDifItems(node *list) int המקבלת כארגומנט מצביע לרשימה מקושרת ומחזירה את מספר הערכים השונים (value) שברשימה. לדוגמה, עבור הרשימה באה:   countDifItems(head) שווה 3.

רשימות משורשרות int countDifItems(node *list) { node *temp; int found, count=0; while(list){ found = 0; temp = list; while(temp){ if(temp->value == list->value) found++; temp = temp->next; } if(found==1) count++; list = list->next; return count;

רשימות משורשרות תרגיל 2:

רשימות משורשרות typedef struct node{ int data; struct node *next; }node; node* orderList(node* head){ node* temp,*run,*odd=NULL,*even=NULL; while(head){ temp=head; head=head->next; temp->next=NULL; if(temp->data%2){ temp->next=odd; odd=temp; } else{ if(!even) even=temp; for(run=even;run->next;run=run->next); temp->next=run->next; run->next=temp; temp->value=(temp->value*2); return odd; run->next=odd; return even;