מבוא למחשב בשפת C : מערכים חד ודו-ממדיים מבוסס על השקפים שחוברו ע"י שי ארצי, גיתית רוקשטיין, איתן אביאור וסאהר אסמיר עבור הקורס "מבוא למדעי המחשב". עודכן ע"י דן רביב נכתב על-ידי טל כהן, נערך ע"י איתן אביאור. כל הזכויות שמורות לטכניון מכון טכנולוגי לישראל
מערכים: מוטיבציה הקלט: סדרה של 800 מספרים המייצגים את גובה המשכורות של 800 עובדים. )3( החציון. המשימות: קליטת הנתונים, חישוב: )1( הממוצע, )2( מספר המשכורות שמעל הממוצע, אבחנות: לא את כל הפעולות ניתן לבצע תוך כדי קליטת הנתונים, על כן צריך לשמור את כל הערכים בזיכרון. כאן לא מתאים להשתמש במשתנים בדידים כגון: double sal0, sal1,, sal799; אי אפשר לעבור על רשימת המשתנים באמצעות לולאה, מה נעשה כשמשימה תשתנה ל- 8,000 או 80,000 מספרים? 2
מערך מערך הינו מבנה נתונים אשר בו ניתן לאחסן סדרה של ערכים מאותו טיפוס, כך שניתן לפנות אליהם באמצעות אינדקס. מערכים בשפת C:.]ANSI-C[ איברי המערך מאוחסנים בתאים עוקבים בזיכרון. האינדקס של האיבר הראשון במערך הוא 0. גודל המערך עליו מכריזים חייב להיות קבוע )לא משתנה!( הגדרת מערך: salaries[800]; double salaries[0] salaries[799] התייחסות לאיבר במערך: salaries[i] salaries[2*i+1] 3
אתחול מערך for ( i = 0; i < 800; i++ ) scanf("%lf", &salaries[i]); for ( i = 0; i < 800; i++ ) salaries[i] = hours[i] * rate[i]; double prices[3] = {7.49, 9.99, 1.49}; int grades[5] = {100, 97, 79, 0, 0}; int grades[5] = {100, 97, 79}; באמצעות לולאה: בזמן ההגדרה: שקולים קביעת גודל המערך ע"י אתחולו: שקולים int grades[5] = {100, 97, 79, 0, 0}; int grades[] = {100, 97, 79, 0, 0}; 4
חריגה מגבולות מערך במערך שמימד מסוים שלו הוא בגודל K, האינדקסים המותרים בגישה למימד זה הם בתחום 0 עד 1 K. קומפיילרים בשפת C לא בודקים באופן אוטומטי בזמן הריצה האם בגישה מסוימת נוצרת חריגה מגבולות המערך. על כן, אם )בשל שגיאה בתוכנית( נוצרת חריגה, היא עלולה להתבטא בכל מיני אופנים בלתי צפויים: התוכנית תעוף מיידית ERROR(,)RUN-TIME 1) התוכנית תמשיך לרוץ, ותעוף בשלב מאוחר יותר, 2) התוכנית תסתיים אבל עם תוצאות שגויות, 3) ואפילו: תרוץ כראוי, תיתן תוצאות נכונות אבל תעוף במחשב אחר! 4) 5
דוגמה: מציאת ממוצע משכורות #include <stdio.h> #define EMPLOYEES_NUM 10 int main() { double salaries[employees_num], sum = 0.0, average; int i, above_average = 0; for ( i = 0; i < EMPLOYEES_NUM; i++ ) { } scanf("%lf", &salaries[i]); sum += salaries[i]; קלט: סדרת משכורות. פלט: ממוצע איברי הסדרה ומספר האיברים שמעליו. } average = sum / EMPLOYEES_NUM; for ( i = 0; i < EMPLOYEES_NUM; i++ ) above_average += (salaries[i] > average); printf("the average is: %.2f\n", average); printf("there are %d salaries above the average\n", above_average); return 0; 6
מבוא לסטטיסטיקה: חציון (Median) x 1 מוגדר כמספר המרכזי של המספרים x n חציון של סדרת ערכים לאחר המיון של הסדרה. )אם מספר האיברים זוגי אזי נהוג לבחור את הממוצע של שניים המרכזיים( באופן אינטואיטיבי הוא המספר אשר עבורו מתקיים כי: מספר האיברים בסדרה הגדולים או שווים אליו זהה למספר האיברים בסדרה הקטנים או שווים לו. לחציון יש מספר תכונות חשובות: אולם החשובה מכולן היא אי רגישות לערכי קיצון. לדוגמא: בכיתה יש 100 ילדים. 99 קבלו 0 במבחן ואחד קיבל 10000 )הוא פתר את חידת הבונוס...( מה הממוצע של המבחן? מה החציון של המבחן? 8 הרצאה 7
מציאת חציון כשהנתונים תחומים כיצד נחשב חציון? דרך א: אין מידע נוסף על המספרים. מיין את מערך המספרים )נלמד בהמשך. "קשה" חשב ממוצע שני המספרים האמצעיים. למחשב.( דרך ב: ידוע לנו הנחות נוספות על המספרים. )למשל שהם מספרים שלמים מ- 0 עד 100( האם אפשר לכתוב תוכנית טובה )יעילה יותר(? אנו נראה את דרך ב' בשקפים הבאים ואת דרך א' בהרצאות הבאות. 8
מציאת חציון כשהנתונים תחומים אם ידוע שהנתונים נמצאים בתוך תחום מוגדר, ניתן למצוא את החציון ע"י בניית היסטוגרמה )גרף שכיחויות( ומעבר סדרתי על ערכי הגרף עד למציאת ערך המחלק את הנתונים כך שחציים מתחתיו וחציים מעליו. דוגמה: נתונים 11 )או 13 או 501( ציונים בתחום 0 10: 0 0 0 0 1 2 5 6 3 7 9 4 5 5 5 10 9 7 6 5 5 7 9 3 4 5 6 7 9 10 0 1 2 3 4 5 6 7 8 9 10 9
דוגמה: מציאת חציון ציונים #include<stdio.h>.)0-100( #define GRADES_NUM 11 #define MAX_GRADE 100 #define MIN_GRADE 0 int main() { int histogram[max_grade - MIN_GRADE + 1] = {0}; int i, current, median = -1, count=0; for (i = 0; i < GRADES_NUM; ++i) { scanf("%d", ¤t); ++histogram[current MIN_GRADE]; } while (count <= GRADES_NUM/2) { ++median; count += histogram[median]; } printf("the median is: %d", median + MIN_GRADE);... קלט: סדרת ציונים הנחה: מספר אי-זוגי של ערכים. פלט: הציון החציון. 5 הרצאה מבוא למדעי המחשב. כל הזכויות שמורות 10
מיון ראשון Bucket sort 8 הרצאה 12
bucket sort מיון ראשון 1( תוכנית חישוב החציון כללה שלב ראשון שבו "דחסנו" את המידע לתוך מערך עזר של היסטוגרמה. למה "דחסנו"? אם ישנם 101 ציונים שונים אבל 10000 תלמידים, אזי היינו צריכים רק 101 תאים בזיכרון לשמור את הציונים. 2( האם נוכל להשתמש בהיסטוגרמה על מנת אותם בסדר עולה, למשל(? האם יש דרך מהירה יותר למיין? כן. bucket sort למיין את הציונים )להדפיס 3( הקוד הוא דוגמה ללואלות מקוננות. 8 הרצאה 13
דוגמה: מיון דליים.)100 0( #include <stdio.h> #define GRADES_NUM 5001 #define MAX_GRADE 100 #define MIN_GRADE 0 #define SLOTS_NUM (MAX_GRADE - MIN_GRADE + 1) int main() { int histogram[slots_num] = {0}; int i, j, grade, median, quantity; for ( i = 0; i < GRADES_NUM; i++ ) { scanf("%d", &grade); histogram[grade - MIN_GRADE] ++; } קלט: סדרת ציונים פלט: הדפס מספרים ממוינים.... for ( i = MIN_GRADE; i <= MAX_GRADE ; i++ )} for ( j=0 ; j < histogram[i MIN_GRADE] ; j++)} printf("%d ", i); { { 14
מערכים דו-ממדיים 8 הרצאה 15
שמירת טבלאות מידע לעיתים ברצוננו לשמור מידע בטבלה, לדוגמה: מספר האנשים בכל קבוצת גיל שיש בכל פקולטה: 25 ומעלה 21 עד 18 25 עד 21 עד 18 5 1 4 1 ספרות 6 4 2 2 אנגלית ספ' היסטוריה גיאוגרפיה בלשנות ארכיאולוגיה משפטים מדעי הדשא 5 7 0 6 2 9 7 3 3 9 2 3 6 0 3 8 9 8 172 7 125 4 2 0 16
אפשרויות מימוש ניתן לממש את הטבלה בעזרת קבוצת מערכים: 1) מערך אחד לכל פקולטה, ואיבריו לפי קבוצת הגיל; 2) או, מערך אחד לכל קבוצת גיל, ואיבריו לפי פקולטה. במקרה הראשון, קל לקבל מידע כולל על פקולטה נתונה, אבל קשה לקבל מידע על קבוצת גיל נתונה, ובמקרה השני להיפך. הפתרון: מערך דו-מימדי, "מערך של מערכים". 17
מערכים דו-מימדיים int data[8][4]; 0 1 2 3 5 1 4 1 6 4 2 2 5 7 0 6 2 9 7 3 3 9 2 3 6 0 3 8 9 8 172 7 125 4 2 0 18 ספרות ספ' אנגלית היסטוריה גיאוגרפיה בלשנות ארכיאולוגיה משפטים מדעי הדשא 0 1 2 3 4 5 6 7 25 ומעלה 21 עד 18 25 עד 21 עד 18
גישה למערך דו-מימדי גישה לאיבר במערך דו-ממדי נעשית באמצעות שני אינדקסים: מספר השורה, 1) ואחריו מספר העמודה. 2) לדוגמה: הביטוי data[4][2] מתייחס לאיבר אשר החמישית, ובעמודה השלישית )יש לזכור: בשורה בשפת C מתחילים לספור מאפס(. כעת ניתן לעבור על כל הנתונים לגבי פקולטה מסוימת ע"י סריקת שורה, ועל כל הנתונים לגבי מחלקה מסוימת ע"י סריקת עמודה. לדוגמה: מציאת סך כל הסטודנטים למדעי הדשא )פקולטה 7(: int j; int num = 0; for ( j = 0; j < 4; j++ ) } { num += data[7][j]; 19
מחרוזות 20
מערכים של תווים ומחרוזות כמו כל טיפוס אחר המוכר בשפה, ניתן לבנות גם מערכים של איברים מטיפוס,char )וכן signed char או.)unsigned char מערך שכזה יכול להכיל: סדרה של מספרים קטנים שעליה יש לבצע חישובים, סדרה של מספרים שהם קודים של תווים. הגדרה: מערך המכיל סדרת קודים של תווים נקרא מחרוזת. במחרוזות, מקובל שהמחרוזת דווקא כן נושאת עימה מידע לגבי אורכה..0 זה נעשה ע"י הוספת תו נוסף בסופה, שערכו המספרי הוא בשפת C 22
אתחול מחרוזות ניתן להגדיר מחרוזת ולאתחל אותה באופן הבא: char string[22] = {'C', 'o', 'u', 'r', 's', 'e', ' ', '2', '3', '4', '1', '1', '2', ' ', 'i', 's', ' ', 'f', 'u', 'n', '!', '\0' }; אפשר לוותר על ציון מפורש של אורך המערך: char string[] = {'C', 'o', 'u', 'r', 's', 'e', ' ', '2', '3', '4', '1', '1', '2', ' ', 'i', 's', ' ', 'f', 'u', 'n', '!', '\0' }; ניתן לאתחל מחרוזות ע"י קבועי מחרוזת: char string[] = "Course 234112 is fun!"; printf("%s", string); מחרוזות מדפיסים ע"י פורמט s%: בניגוד למערכים רגילים, ניתן להשתמש בקבועי מחרוזת גם בתוך הקוד: printf("%s", "Course 234112 is much fun!"); 23
המחרוזת הריקה קבוע מחרוזת הוא סדרת תווים או קודים הנתונה בתוך גרשיים כפולות( )מירכאות :" " קבוע מחרוזת מכיל בסופו באופן אוטומטי את התו.\0 על כן, המקום בזיכרון שתופס קבוע מחרוזת גדול ב- 1 מאורכה.."" צמד של גרשיים )מירכאות כפולות( מהווים את המחרוזת הריקה: המחרוזת הריקה איננה מכילה תווים, אולם קיים בה התו 0\ המסיים, כך ש sizeof("") == 1,strlen("") == 0 25
ספירת תווים במילה int main() { int i = 0; char str][ = Ubuntu ; while (str[i]!= 0) { i++; } printf) %d\n, i(; return 0; } ספירת תווים במילה 26
int main() { int i = 0, count = 1; char str][ = Ubuntu is an open OS ; while (str[i]!= 0) { if )str]i[ == ( { count++; } i++; } printf) %d\n, count(; return 0; } ספירת מילים במחרוזת ספירת מילים במחרוזת הנחות: מחרוזת לא מתחילה ולא מסתיימת ברווחים. 1. בין המילים יש רווח יחיד. 2. יש לפחות מילה אחת. 3. למה כל הנחה הכרחית בקוד המוצג? 1. הרחיבו את הפתרון כך שניתן יהיה לוותר 2. על כל ההנחות. 27
רשימות של מילים )מערך מחרוזות( אם ברצוננו לשמור רשימה של מילים )כלומר, מחרוזות( נוכל להקצות מערך דו-ממדי (למעשה, מערך של מערכים של תווים): char names [][8] = { "Avraham", "Sara", "Izhak", "Hagar", "Ishmael" }; A v r a h a m \0 S a r a \0 I z h a k \0 H a g a r \0 I s h m a e l \0 כאשר ברשימה יש מילים )מחרוזות( ארוכות מאוד, )מחרוזות( קצרות מאוד, נוצר בזבוז מקום. וגם מילים על מנת לחסוך במקום, משתמשים בד"כ במערך של מצביעים למחרוזות. 28