|
|||||||||||||||||||||||||||||
קורס:"שפת C"
מתוך הספר: C - מדריך מקצועי
תכנית ראשונה ב- Cנכתוב תכנית בסיסית בשפת C המדפיסה למסך שורה בודדת. קוד התכנית: file: hello.c #include <stdio.h> void main ( ) { printf("hello, Israel!\n"); } פלט התכנית: hello, Israel! הסבר התכניתהתכנית כתובה בקובץ בשם hello.c. בהמשך נראה כיצד ניתן להדר את התכנית ולהריץ אותה. החלק הראשון:
#include <stdio.h>
מודיע למהדר שאנו עומדים להשתמש בספריית הקלט/פלט התקנית stdio.h (Standard I/O). החלק השני : void main () { } הוא הגדרת הפונקציה הראשית של התכנית:
החלק הנמצא בין הסוגריים המסולסלות { printf("hello, Israel!\n"); } הוא גוף הפונקציה הראשית, והוא כולל הוראת הדפסה ע"י קריאה לפונקצית הספרייה printf: הפונקציה מדפיסה את המחרוזת הנתונה לה בין הסוגריים, "hello, Israel!\n", כאשר סיומת המחרוזת "\n" היא צירוף תווים מיוחד המורה על הדפסת תו שורה חדשה (new line). הפונקציה printf לא מדפיסה תו שורה חדשה אלא אם כן הוכנס צירוף התווים המיוחד במחרוזת להדפסה. לכן ניתן לכתוב את התוכנית גם כך: #include <stdio.h> void main( ) { printf("hello,"); printf("Israel!"); printf("\n"); } כתיבה, הידור והרצת התכניתלצורך יצירת והרצת התכנית יש צורך בסביבת פיתוח. קיימות סביבות פיתוח חינמיות רבות ליצירת תכניות C/C++, כגון "Eclipse", "GCC - GNU C/C++", "Borland C++". ליצירת התכנית ולהרצתה דרושים מספר שלבים :
תרגולקרא/י סעיף זה בספר ובצע/י את התרגיל שבעמ' 33. הגדרת משתנים, קלט ופלטבתכנית הדוגמא הבאה נכיר מרכיבים בסיסיים בשפת C:
חישוב והדפסת הממוצע של 3 מספריםהתכנית הבאה מממשת את האלגוריתם שראינו בפרק 1, "מבוא לתכנות", לחישוב והדפסת הממוצע של 3 מספרים שלמים:
תרשים האלגוריתם:
קוד התכניתקוד התכנית בשפת C נכתב בקובץ average.c ונראה כך: /* file: average.c */ #include <stdio.h> /* calculate the average of 3 numbers */ void main() { int num1, num2, num3; float avg; printf("Enter 3 integer numbers: "); scanf("%d %d %d", &num1, &num2, &num3); avg = num1 + num2 + num3; avg = avg / 3; printf("The average is: %f", avg); } הרצת התכניתבהרצת התכנית הוכנסו 3 מספרים Enter 3 numbers: 23 45 11 והתקבל הפלט: The average is: 26.333334 הסבר התכניתהקטע ההצהרתי
#include <stdio.h>
/* calculate the average of 3 numbers */ void main ( ) { ... } כמו בתכנית הקודמת, קוד התכנית נכתב בתוך פונקציה בשם main. על משמעות המילה void נלמד בהמשך. כמו כן נראה שניתן להגדיר את הפונקציה בדרכים שונות.
int num1, num2, num3;
float avg;
טיפוסים נוספים הקיימים בשפה:
נרחיב על טיפוסי משתנים בפרק הבא. הקטע הביצועיהקטע הביצועי של התכנית מתחיל עם ההוראה הראשונה שאינה הגדרת משתנה. לאחר תחילת הקטע הביצועי לא ניתן להגדיר משתנים נוספים. ההוראה הביצועית הראשונה היא הוראת הפלט printf("Enter 3 integer numbers: "); בהוראה זו מתבקש המשתמש להקליד 3 מספרים שלמים. בשלב הבא נקראים המספרים ע"י ההוראה scanf : scanf("%d %d %d", &num1, &num2, &num3); scanf היא פונקצית קלט הקוראת מהקלט לתוך רשימת פרמטרים עפ"י פורמט הנתון לה במחרוזת בקרה. לצורך ביצוע הקלט אנו מספקים ל- scanf את מחרוזת הבקרה
"%d %d %d"
ולאחריה את רשימת כתובות המשתנים שמבקשים לקלוט, מופרדים ע"י פסיק: &num1, &num2, &num3 הפונקציה scanf קוראת נתונים מהקלט לתוך כתובות שניתנות לה כפרמטרים. האופרטור "&" מציין "הכתובת של": הצבתו לפני שם המשתנה מציינת התייחסות לכתובתו - נעסוק במצביעים בהרחבה בפרק 8, "מצביעים". במקרה זה ציינו שאנו מעונינים לקרוא 3 מספרים שלמים: %d במחרוזת הבקרה הוא מציין טיפוס המציין עבור המהדר טיפוס שלם של פרמטר מתאים ברשימת הפרמטרים. לכל פרמטר מותאם מציין טיפוס, עפ"י הסדר:
קיימים מצייני טיפוס גם לטיפוסים האחרים, לדוגמא:
scanf מתעלמת מהתווים ה"לבנים" - רווח, טאב ותו שורה חדשה - אלא אם כן סוג הנתון הנקרא הוא תווי. לכן ניתן להקליד את המספרים עם רווחים ושורות ריקות ביניהם, והם עדיין ייקראו נכונה. לאחר קליטת המספרים, מחושב סכומם ומוצב ל- avg : avg = num1 + num2 + num3; הסימן "=" ב- C מציין פעולת הצבה (Assignment). תוצאת הביטוי שמימין לסימן מוצבת לתא בזיכרון שמשמאל לו. פעולה זו נקראת גם "השמה". שאלה: איך מסמנים שוויון בביטוי לוגי? תשובה: ע"י שני תווים רצופים כנ"ל : "==". חישוב הממוצע מתבצע ע"י חילוק avg ב- 3 :
avg = avg / 3;
avg / 3 מבצע פעולת חילוק וזו מוצבת ל- avg. אין כל בעיה בהימצאותו של avg משני צידי הוראת ההצבה - הצד הימני מחושב קודם, ולאחר מכן מוצבת התוצאה למשתנה שבצד השמאלי. ניתן לקצר את התכנית ע"י ביצוע פעולות החיבור והחלוקה בהוראה יחידה: avg = (num1 + num2 + num3) / 3; כאן ביצענו חיבור בתוך סוגריים מכיוון שלאופרטור החילוק עדיפות על אופרטור החיבור. הסוגריים מציינים את סדר הפעולות הנדרש. בסיום מודפס ערכו של avg ע"י הפונקציה printf : printf("The average is: %f", avg); בדומה לפונקציה scanf, גם printf כוללת מחרוזת בקרה ולאחריה רשימת פרמטרים. בשונה ממנה, הפרמטרים ל- printf אינם כתובות המשתנים אלא המשתנים עצמם. במקרה זה ציינו שאנו מעונינים להדפיס את המחרוזת The average is: ולאחריה את ערכו של avg. כפי שראינו, פלט התכנית שהורצה לעיל הוא The average is: 26.333334 משפטי תנאי ולולאותלעיתים תכופות מהלך התכנית נקבע באופן דינמי עפ"י הקלטים או חישובים מספריים. בנקודות מסוימות בתכנית נרצה לבצע קטע קוד כתלות בתנאי מסוים. בנקודות אחרות נרצה לבצע קטע קוד מספר פעמים. שפת C כוללת הוראות בקרה לקביעת מהלך התכנית:
משפט if-elseמשפט if-else מבצע הוראה או מספר הוראות כתלות בתנאי מסוים. לדוגמא, אם נרצה להדפיס את ההפרש שבין 2 מספרים, num1 ו- num2, בערכו המוחלט נבצע: if( num1 > num2 ) printf("|num1 - num2|= %d", num1 - num2); else printf("|num1 - num2|= %d", num2 - num1); ניתן לתאר את הוראת if-else ע"י תרשים זרימה:
ההוראה if כוללת ביטוי לוגי בסוגריים, ובמידה וערכו אמת - כלומר num1 גדול מ- num2 - ההוראה העוקבת מבוצעת: if( num1 > num2 ) printf("|num1 - num2|= %d", num1 - num2); ההוראה else לאחר ההוראה if מציינת פעולה אלטרנטיבית לביצוע אם תוצאת הביטוי הלוגי הייתה שקר: else printf("|num1 - num2|= %d", num2 - num1); ההוראה else היא אופציונלית - ייתכן משפט if ללא הוראת else.
עיין/י בתכנית הדוגמא המובאת בעמ' 40-41. ביצוע מספר הוראות - בלוקאם רוצים לבצע מספר הוראות בהוראת if או else, יש להקיפם בסוגריים מסולסלות, לדוגמא: if( num1 > num2 ) { max = num1; printf("The maximum is: %d", max); } סדרת הוראות מוקפת בסוגריים {} נקראת בלוק. אופרטורים לוגייםהאופרטור '>' מציין את היחס הלוגי "גדול מ-". הטבלה הבאה כוללת את האופרטורים הלוגיים ב- C ואת משמעותם:
לולאת whileלולאת while משמשת לביצוע חוזר של הוראות כתלות בתנאי מסוים: תחביר הלולאה הוא while(ביטוי לוגי) { הוראות } כל עוד ערכו של הביטוי הלוגי אמת ההוראות שבגוף הלולאה מתבצעות. תרשים זרימה עבור ביצוע לולאת while:
נכתוב תוכנית להדפסת טבלת המרה מאינצ'ים לסנטימטרים. נוסחת ההמרה:
קוד התכנית: /* file: convert.c */ #include <stdio.h> /* print an Inch - Centimeter conversion table */ void main ( ) { int Xinch; float Xcm; int lower, upper, step; lower=0; /* lower limit of table */ upper=10; /* upper limit */ step=1; /* step size */ Xinch=lower; while(Xinch<=upper) { Xcm = Xinch * 2.54; printf("%d\t%f\n",Xinch,Xcm); Xinch=Xinch+step; } } פלט התכנית: 0 0.000000 1 2.540000 2 5.080000 3 7.620000 4 10.160000 5 12.700000 6 15.240000 7 17.780000 8 20.320000 9 22.860000 10 25.400000 הסבר התכניתבחלקה הראשון של התכנית מצהירים על משתנים : int Xinch; float Xcm; int lower, upper, step; כפי שניתן לראות, ניתן להכריז על משתנים מסוג שלם, אחר כך על משתנים מסוג ממשי ושוב על משתנים מסוג שלם. לאחר מכן מאתחלים אותם: lower=0; /* lower limit of table */ upper=10; /* upper limit */ step=1; /* step size */ Xinch=lower; לולאת while מתבצעת כל עוד התנאי הנמצא בסוגריים מתקיים: while(Xinch <= upper) הסימן "<=" מציין "קטן או שווה". משמעות התנאי היא "Xinch קטן או שווה ל- upper". הלולאה בכללותה : while(Xinch <= upper) { Xcm = Xinch * 2.54; printf("%d\t%f\n",Xinch,Xcm); Xinch=Xinch+step; } גוף הלולאה כולל 3 הוראות:
Xcm = Xinch * 2.54;
printf("%d\t%f\n",Xinch,Xcm); %d הוא מציין טיפוס שלם המתייחס לשלם Xinch , ו- %f מתייחס לממשי Xcm. התווים '\t' ו- '\n' הם תווים מיוחדים להדפסה :
תרגולקרא/י סעיף זה בספר ובצע/י את התרגיל שבעמ' 45. סידור פלט התכניתבעיה: פלט התכנית לא ערוך מספיק יפה:
פתרון: נשתמש בשדות רווח ושדות הצמדה בפלט: printf("%2d\t%5.2f\n", Xinch, Xcm); עבור הדפסת השלם:
עבור הדפסת הממשי:
כמו כן, נוסיף לטבלה הדפסת כותרות ע"י : printf("Inch\tCm\n"); printf("----\t-----\n"); התכנית המתוקנת מובאת בעמ' 46. והפלט: Inch Cm ---- ----- 0 0.00 1 2.54 2 5.08 3 7.62 4 10.16 5 12.70 6 15.24 7 17.78 8 20.32 9 22.86 10 25.40
סיכום שדות ההדפסה ב- printf שהכרנו עד כה:
תרגולקרא/י סעיף זה בספר ובצע/י את תר' 1-2 שבעמ' 47. הגדרת קבועיםשימוש במספרים בגוף התכנית הוא בדרך כלל רעיון לא "בריא" ממספר בחינות:
קבועים הם שמות המייצגים ערכים קבועים שאינם משתנים לאורך כל התכנית. הם מוגדרים בצירוף המילה השמורה const. שימוש בהגדרת קבועים בתכנית מקל על פעולות העריכה, התיעוד והתחזוקה של ערכים קבועים בתכנית. נבצע מספר שינויים בתכנית:
התכנית החדשה: /* file: convert3.c */ #include <stdio.h> /* print a Inch - Centimeter conversion table, define constants */ void main ( ) { const int LOWER=0, UPPER=10; const float FACTOR=2.54f; int Xinch; float Xcm; Xinch=LOWER; printf("Inch\tCm\n"); /* print table header */ printf("----\t--\n"); while(Xinch<=UPPER) { Xcm = Xinch * FACTOR; printf("%2d\t%5.2f\n",Xinch,Xcm); Xinch++; } } הגדרת הקבועים מתבצעת תוך שימוש במילה השמורה const : const int LOWER=0, UPPER=10; const float FACTOR=2.54f;
במקומות המתאימים בתכנית שמות הקבועים מוחלפים בשמות הערכים: Xinch=LOWER; printf("Inch\tCm\n"); /* print table header */ printf("----\t--\n"); while(Xinch<=UPPER) { Xcm = Xinch * FACTOR; printf("%2d\t%5.2f\n",Xinch,Xcm); Xinch++; } כעת לא ניתן לשנות את הקבועים בתכנית - ניסיון לשנותם ייתן הודעת שגיאה בהידור. האופרטור "++" מבצע קידום ב- 1 של המשתנה. לכן, הביטוי Xinch++ שקול לביטוי
Xinch = Xinch +1;
באופן דומה משמש האופרטור "--" להחסרה של 1. הגדרת קבועים ע"י הקדם-מעבד (Pre-processor)קיימת דרך נוספת להגדרת קבועים - ע"י שימוש בקדם-מעבד: /* file: convert4.c */ #include <stdio.h> #define LOWER 0 /* lower limit of table*/ #define UPPER 10 /* upper limit*/ #define FACTOR 2.54f /* conversion factor */ /* print a Inch - Centimeter conversion table, define constants */ void main ( ) { int Xinch; float Xcm; Xinch=LOWER; printf("Inch\tCm\n"); /* print table header */ printf("----\t--\n"); while(Xinch<=UPPER) { Xcm = Xinch * FACTOR; printf("%2d\t%5.2f\n",Xinch,Xcm); Xinch++; } } הסבר הגדרות הקבועים #define LOWER 0 /* lower limit of table*/ #define UPPER 10 /* upper limit*/ #define FACTOR 2.54f /* conversion factor */ הן הוראות לקדם-מעבד (pre-processor): זוהי תת-תכנית במהדר העוברת על הקוד שבקובץ ומבצעת את כל ההוראות המתחילות בסימן # (נרחיב בנושא בפרק 11, בסעיף "הקדם-מעבד"). שמות הקבועים מוחלפים על ידו למספרים המצויינים, לפני מעבר המהדר על התכנית. מה ההבדל בין שתי הצורות? בהגדרת קבוע מספרי ע"י const מוגדר תא בזיכרון עבור הקבוע, ואילו בהגדרה ע"י הקדם מעבד אין הגדרה של תא בזיכרון. הבדל זה יכול להיות משמעותי במערכות משובצות מחשב, כאשר משאבי הזיכרון מוגבלים, שאז כדאי לבחור בהגדרת קבועים מספריים ע"י הקדם-מעבד. הבדל נוסף הקיים בין 2 הצורות הוא במרחב השם (namespace) של הקבוע: קבוע המוגדר ע"י הקדם מעבד מוכר בכל קובץ התכנית, החל מהמקום בו הוגדר. קבוע המוגדר ע"י const לעומת זאת הוא בעל מרחב שם מוגבל יותר, עפ"י מקום הגדרתו, כפי שנראה בפרק 6, "פונקציות". קלט / פלט של תוויםלצורך ביצוע קלט/פלט של תווים נשתמש בפונקציות הספרייה התקנית stdio.h:
נכתוב תכנית להעתקת תווי הקלט ישירות לפלט . התכנית תקרא תווים בזה אחר זה ותדפיס אותם. אלגוריתם התכנית:
התכנית: /* file: copy.c */ #include <stdio.h> /* copy input to output: 1st version */ void main() { int c; c=getchar(); while (c!=EOF) { putchar(c); c=getchar(); } } הסבר: התכנית מגדירה משתנה מסוג int וקוראת תו ראשון לתוכו. הסימן "!=" מציין "שונה" - כלומר משמעות הביטוי (c!=EOF) היא "התו c שונה מתו סוף קובץ". מדוע הגדרנו את c כ"int" ולא כ"char"? מכיוון ש- c אמור גם לקבל את ערך הקבוע EOF (שהוא -1 ) בהגעה לסוף הקובץ. יש לשים לב לכך שבהגעה לסוף שורה בקלט נקרא תו סוף השורה '\n' (תו יחיד) ע"י הפונקציה getchar() ומודפס ע"י הפונקציה putchar(). כמו כן ניתן להדפיסו ישירות ע"י putchar('\n'); בפרק הבא נעסוק בהרחבה בטיפוסי התווים ובסוגי תווים מיוחדים. שימוש בביטוי הצבה כמחזיר ערךבשפת C ביטוי הצבה מחזיר ערך. לדוגמא: c = getchar(); הוא ביטוי שערכו כערך שהושם ל- c. כלומר, ערך ביטוי ההצבה הוא הערך שהושם. למשל, ניתן לכתוב x = ( c = getchar() ); ואז x יקבל את הערך שהושם ב- c. נשנה את התכנית כך שההצבה ובדיקת ההגעה לסוף הקלט יבוצעו באותה הוראה. אלגוריתם התכנית:
קוד התכנית: /* file: copy2.c */ #include <stdio.h> /* copy input to output: 2nd version */ void main() { int c; while ((c=getchar())!=EOF) putchar(c); } קיבלנו תכנית יותר קטנה מצד אחד אך פחות קריאה מצד שני. בדרך כלל לא מומלץ להכניס ביטויים מורכבים מדי בהוראה יחידה, אך לעיתים זה מפשט את התכנית. שאלה : מדוע נחוצים סוגריים סביב ההצבה c=getchar()? תשובה: מכיוון שלאופרטור =! יש עדיפות גבוהה מלאופרטור =. לו כתבנו c = getchar() != EOF הביטוי היה מחושב כ- c = ( getchar() != EOF ) כלומר c היה מקבל את הערך הבוליאני של אי-השוויון! בפרק הבא נעסוק בטיפוס בוליאני ובייצוגו בשפת C. הצבה כפולההואיל וביטוי הצבה מחזיר ערך, ניתן לבצע הצבה כפולה, לדוגמא:
x = y = 5;
ביטוי זה שקול לביטוי x = (y = 5); מחרוזותבשפת C לא קיים טיפוס מחרוזת בסיסי. מחרוזת מיוצגת כמערך תווים המסתיים בתו מסיים מחרוזת. על מערכים נלמד בהרחבה בפרק 7, "מערכים", לעת עתה נראה רק כיצד להגדיר מחרוזות באמצעותם. מציינים ערך מחרוזת ע"י גרשיים משני צידיה, לדוגמא: char str[17] = "www.mh2000.co.il"; /* home page of this book */ ההגדרה char str[17] מציינת ש- str הוא מערך תווים בעל 17 מקומות. לאחר ביצוע ההשמה של המחרוזת "www.mh2000.co.il" ל- str היא מחרוזת בת 16 תווים + תו מסיים מחרוזת (בלתי נראה):
התו '\0' (קו נטוי הפוך ואפס) הוא תו סוף מחרוזת. באופן דומה, ניתן להגדיר את str גם ללא ציון גודל המערך: char str[] = "www.mh2000.co.il"; המהדר יקצה אוטומטית ל- str את גדלו (17 תווים), עפ"י המחרוזת המוצבת לו. הדפסת מחרוזת מבוצעת ע"י הוראת printf באמצעות מזהה הטיפוס %s , לדוגמא: char str[] = "www.mh2000.co.il"; printf("Welcome to %s !",str); יודפס: Welcome to www.mh2000.co.il ! הגדרת טיפוס מחרוזת פשוטלשם טיפול נוח במחרוזות נגדיר טיפוס מחרוזת, String, באופן הבא: typedef char String[256]; typedef משמש להגדרת טיפוס חדש: במקרה זה הגדרנו את הטיפוס String כמערך של 256 תווים. בפרק הבא נכיר בפירוט את ההוראה typedef. כעת ניתן להגדיר משתנים מסוג String : String s1 = "hello"; String s2 = "world"; תכנית דוגמא: #include <stdio.h> typedef char String[256]; void main ( ) { String first_name; String second_name; printf("Enter your first name: "); scanf("%s", first_name); printf("Enter your second name: "); scanf("%s", second_name); printf("Your full name is %s %s\n", first_name, second_name); } דוגמא להרצת התכנית: Enter your first name: Louis Enter your second name: Armstrong Your full name is Louis Armstrong התכנית מגדירה את הטיפוס String כמו קודם. בתחילת הפונקציה main מוגדרים שני משתנים מסוג String: String first_name; String second_name; המשתנים נקראים מהקלט ע"י הוראות scanf עם מציין הטיפוס %s, אולם בניגוד למשתנים רגילים, משתני המחרוזת עצמם מועברים כפרמטרים ולא כתובותיהם: printf("Enter your first name: "); scanf("%s", first_name); printf("Enter your second name: "); scanf("%s", second_name); הסיבה לכך היא שמחרוזת, המיוצגת כמערך, היא בעצמה כתובת. נעסוק בכך בהרחבה בפרק 9, "מחרוזות". לאחר ביצוע הקלט, המחרוזות מודפסות ע"י הוראת printf, שוב עם מציין הטיפוס %s: printf("Your full name is %s %s\n", first_name, second_name); פעולות על מחרוזותבשפת C לא ניתן להציב מחרוזת אחת לשנייה ע"י s1=s2 או לבדוק שוויון ביניהן ע"י s1==s2. לביצוע פעולות אלו קיימות פונקציות הספרייה string.h שנכיר אותן בפרק 9, "מחרוזות". סיכום
תרגילי סיכוםבצע/י את תרגילי הסיכום 1-5 שבעמ' 55-56.
|