מבוא לשפת - C
בפרק זה נעבור על חלק ממרכבי השפה ונלמד כיצד לתכנן ולכתוב תוכניות בסיסיות.מהי תוכנית מחשב ?
באופן לא פורמלי תוכנית מחשב היא סדרת פקודות לביצוע עבור המחשב. סדרת פקודות זו מתחילה ממקום קבוע מראש ומסתיימת באמצעות פקודת סיום שנכתבת ע"י המתכנת.
מהי תוכנית מחשב בשפת - C ?
תוכנית מחשב בשפת - C הינה תוכנית אשר אוסף הפקודות שבה ( פקודות ביצוע למחשב ) שייכות לשפה מוסכמת מראש - שפת C . אוסף פקודות זה מוכר למתכנת ולתוכנה מסוימת הנקראת "מהדר לשפת C " ( או c compiler ) . הקומפילר הינו אחראי לתרגום התוכנית משפת C לתוכנית המכילה פקודות המוכרות למחשב.
תוכנית לדוגמא ( בשפת C ) :
#include <stdio.h>int main( void )
{
printf(Hello World) ;
return 0 ;
}
בשלב זה נתעלם מהחלקים לפני ה"התחלה" ואחרי ה"סיום" ונתייחס אליהם כאל "מעטפת" שתופיע בכל התוכניות הראשונות שתכתבו.
אם כך, תוכנית בשפת C כפי שהוגדרה לעיל תתחיל אחרי הסוגר השמאלי " } " ( שמופיע אחרי
int main( void ) ) ותסתיים במשפט " return 0 " .
ניתן לראות שהתוכנית מכילה פקודה אחת לביצוע - התו " ; " מסמן את סיום הפקודה . פקודה זו מורה למחשב להפעיל פונקציה ( printf() )ששייכת לשפה . הפונקציה printf() הינה למעשה סדרת פקודות למחשב ( שאינם נראות למתכנת ) הגורמות לכך שמה שמופיע בין הסוגריים ובין המירכאות הכפולות יודפס למסך.
במקרה שלנו יודפס למסך :
Hello World
סדרת התווים בין המירכאות הכפולות, שמועברת לפונקציה printf() נקראת מחרוזת בקרה . בדוגמא הקודמת מחרוזת הבקרה היא : Hello World .
דוגמא נוספת :
#include <stdio.h>int main( void )
{
printf(Hello \n World) ;
return 0 ;
}
ההבדל בין תוכנית זו לקודמת הוא התו \n שמופיע במחרוזת הבקרה. תו זה אינו מודפס למסך אלא מסמן לפונקציה printf() לרדת שורה במסך ולהמשיך את ההדפסה מתחילת השורה.
כלומר הפלט של התוכנית הוא :
Hello
World
מהו פלט התוכנית שתכיל את שתי הפקודות הבאות במקום הפקודה בתוכנית הקודמת ?
printf(Hello \n W) ;
printf(orld) ;
זהה מכיוון שההדפסה למסך הינה סדרתית - ממשיכים להדפיס מהמקום האחרון שבו סיימנו .
דוגמא נוספת :
נניח שאנו רוצים להדפיס למסך את השורה :
It is clear that 5 is bigger then 3 !
דרך אחת לעשות זאת היא באמצעות הפקודה :
printf(It is clear that 5 is bigger then 3 !) ;
דרך נוספת היא באמצעות השורה :
printf(It is clear that %d is bigger then %d ! , 5 , 3) ;
כעת אנו מעבירים לפונקציה שני פרמטרים נוספים מלבד מחרוזת הבקרה - את 5 ו- 3 . בנוסף הכנסנו במקום שבו אנו רוצים ש - 5 ו- 3 יודפסו את התו %d . ה- %d הראשון מורה לפונקציה printf() להדפיס את הפרמטר הראשון אחרי מחרוזת הבקרה ןה- %d השני מורה להדפיס את הפרמטר השני .
אילו רצינו להדפיס מספרים ממשיים היינו כותבים :
printf(It is clear that %f is bigger then %f ! , 5.0 , 3.0) ;
כלומר התו %f מתאים לממשיים והתו %d לשלמים
עד כה ראינו תוכניות שרק מדפיסות למסך. כעת נפתח מעט את התוכנית שלנו כך שנבצע גם כן חישוב.
התוכנית שלנו תחשב את ההיקף והשטח של מעגל עם רדיוס 2 :
#include <stdio.h>
int main(void)
{
int radius;
double area ,circum; radius = 2 ;
circum = 2*3.14*radius ;
area = 3.14*radius*radius ;
printf("Area = %f , circumference = %f \n", area , circum ) ;
return 0 ;
}
החלק של ההצהרה על המשתנים מכיל סדרה של פקודות המורות למחשב להקצות מקום בזיכרון למשתנים ולתת להם שמות :
int radius;
בעקבות שורה זו המחשב יקצה מקום למשתנה מטיפוס שלם ( integer ) ושמו יהיה radius . המילה השמורה int מציינת משתנה מטיפוס שלם .
המילה השמורה double מציינת משתנה מטיפוס ממשי .
החלק של החישוב מפורש כך :
1) הכנס למשתנה radius את הערך 2 .
2) הכנס למשתנה circum את תוצאת הביטוי 2*3.14*radius .
3) הכנס למשתנה area את תוצאת הביטוי 3.14*radius*radius .
הפלט של התוכנית הוא :
Area = 12.560000 , circumference = 12.560000
כעת נוסיף גמישות לתוכנית שלנו בכך שנאפשר לחשב את היקפו ושיטחו של כל מעגל שיקבע המשתמש ( בתוכנית ). לצורך כך נצטרך לקרוא מהמקלדת את ערכו של הרדיוס .
נשתמש בפונקציה "מוכרת" נוספת בפורמט הבא :
scanf(%d”, &radius ) ;
פירוש פקודה זו הוא לקרוא מספר שלם מהמקלדת והכנסת ערך זה למשתנה radius . פירוש הביטוי &radius הוא "מיקומו של radius בזיכרון" מפני שהפונקציה scanf() צריכה לדעת את המיקום בשביל לשים שם את הערך שנקרא מהמקלדת.
לכן התוכנית תראה כך :
#include <stdio.h>int main(void)
{
int radius;
double area ,circum;
scanf("%d", &radius) ;
circum = 2*3.14*radius ;
area = 3.14*radius*radius ;
printf("Area = %f , circumference = %f \n", area,circum);
return 0 ;
}
אם שם התוכנית הוא my.c אז הרצה של התוכנית יכולה להראות כך :
c:\> my
2
Area = 12.560000 , circumference = 12.560000
c:\>
משפטי תנאי
עד כה הביצוע של התוכניות שראינו היה סדרתי וללא תנאי אך לפעמים נהיה מעונינים להתנות את ביצועם של פקודות בתוכנית בקיומם של תכונות מסוימות.
למשל אם נרצה לחשב את הערך המוחלט של מספר שלם נצטרך לדעת ראשית אם הוא חיובי או שלילי. תוכנית המחשבת זאת יכולה להראות כך :
#include <stdio.h>
int main(void)
{
int x ; printf("Enter the number\n");
scanf("%d",&x);
if( x < 0 )
x = -x ;
printf("The absolute value is : %d \n" , x );
return 0 ;
}
וההרצה של התוכנית יכולה להראות כך :
c:\>my1
Enter the number
-3
The absolute value is : 3
c:\>
בתוכנית זו התננו את ביצוע הפקודה x = -x בכך ש-x יהיה שלילי ( משיקולים ברורים ) .
משפט ה - if הינו משפט התניה בשפת - C והפורמט הכללי והפשוט ביותר שלו הוא :
if ( תנאי )
פקודה
כשהמשמעות היא : בצע את הפקודה רק אם התנאי מתקיים.
אם נרצה גם לבצע פקודה מסוימת כאשר התנאי מתקיים ופעולה אחרת רק כאשר התנאי לא מתקיים נשתמש ב - if else .
למשל אם נרצה לחשב את הפתרון של משוואה לינארית ax + b = 0 נרצה לדעת ראשית את ערכו של a . אם a שווה ל - 0 אז אין פתרון ( נניח ש-b שונה מ- 0 ) ואחרת הפתרון הוא -b/a .
התוכנית :
#include <stdio.h>int main(void)
{
double a , b ;
scanf("%lf%lf", &a , &b) ;
if( a == 0 )
printf("There is no solution\n");
else
printf("The solution is %lf", -b/a ) ;
return 0 ;
}
הפעם השתמשנו הפונקציה scanf() אם התו %lf בכדי לקרוא מספרים ממשיים . הסיבה לכך שהשתמשנו במשתנים מטיפוס double היא שבשפת C חלוקה של שלם בשלם נותנת את הערך
( השלם ) התחתון של החלוקה ( למשל 5/2 = 2 ) ועובדה זו יכלה להוביל אותנו לפתרון שגוי בחלוקה -b/a .
הפורמט הכללי והפשוט ביותר של משפט ה - if - else הוא :
if ( תנאי )
פקודה1
else
פקודה2
כאשר המשמעות היא : בצע את פקודה1 אם התנאי מתקיים ואחרת את פקודה2 .
אם נרצה שבעקבות קיום / אי קיום התנאי יתבצעו מספר פקודות ( או משפטים ) נקיף אותם בסוגריים מסולסלים :
if ( תנאי ) {
.......
.......
} else {
...........
...........
}
למשל :
if ( x < 0 ) {
x = x + 1 ;
y = z x ;
scanf(%d, &z ) ;
}
בדוגמא זו לא השתמשנו באופציה של ה - else . אם x קטן מ - 0 אז נבצע את שלושת הפקודות אחרת נמשיך לפקודה הבאה אחרי ה - if .
עכשיו נוכל לשנות מעט את התוכנית הקודמת כך שנבדוק גם מה ערכו של b ונוכל לתת פתרון נוסף :
#include <stdio.h>int main(void)
{
double a , b ;
scanf("%lf%lf", &a , &b) ;
if( a == 0 ) {
if ( b == 0 )
printf("There are infinite solutions\n") ;
else
printf("There is no solution\n") ;
} else
printf("The solution is %lf", -b/a ) ;
return 0 ;
}
ה if else הפנימי מתבצע רק אם a שווה ל - 0 .
לולאות
המאפיין העיקרי של התוכניות שכתבנו עד כה הוא שכל פקודה בוצעה מקסימום פעם אחת. יתכן שבאופן טבעי נרצה לבצע מספר מסוים של פעולות יותר מפעם אחת .
למשל, אם נרצה לחשב את מכפלת x ב- y ללא שימוש באופרטור " " , נצטרך לחבר ל-y את y, x - 1 פעמים ( או ההיפך ) . התוכנית :
#include <stdio.h>int main(void)
{
double x , y , first_y , first_x ;
scanf("%lf%lf", &x , &y );
first_y = y ;
first_x = x ;
while ( x > 1 ) {
y = y + first_y ;
x = x - 1 ;
}
printf("%lf * %lf = %lf\n", first_x , first_y , y ) ;
return 0 ;
}
כאשר הפורמט למשפט ה - while הוא :
while ( תנאי ) {
פקודות לביצוע
}
והפירוש הוא : בצע את הפקודות לביצוע כל עוד התנאי מתקיים .
הערות בתוכנית ( comments )
הקומפילר של שפת C מתעלם מכל טקסט שמופיע בין הסימן " / " לבין הסימן " / " . באופן טבעי נשתמש בעובדה זו ( שאינה מקרית כמובן ) להכנסת הערות על קטעי קוד בתוכנית ועל התוכנית כולה .
לדוגמא :
/ This program solves linear equation /
#include <stdio.h>int main(void)
{
double a , b ; / we assume that b is not zero /
scanf("%lf%lf", &a , &b) ;
if( a == 0 )
printf("There is no solution\n"); / b is not zero /
else
printf("The solution is %lf", -b/a ) ;
return 0 ;
}
כעת מי שיקרא את התוכנית, יקבל מושג טוב יותר ומהר יותר מה התוכנית עושה ומהן ההנחות שנלקחו .
פורמט כללי של הערות :
/ a comment /
/
also
a comment
/
אך לא ניתן לבצע קינון של הערות :
/ / a comment / not anymore /
השימוש ב - #define
נתבונן בתוכנית הבאה :
#include <stdio.h>
#define PI 3.1415927int main(void)
{
int radius;
double area ,circum;
scanf("%d", &radius) ;
circum = 2PIradius ; /* replaced to 23.1415927radius */
area = PIradiusradius ; /* replaced to 3.1415927radiusradius */
printf("Area = %f , circumference = %f \n", area , circum );
return 0 ;
}
כשאנו מבצעים קומפילציה לתוכנית שכתבנו אז בשלב הראשון "עובר על התוכנית" מה שנקרא המעבד המקדים ( preprocessor ) . המעבד המקדים מציית לפקודות שמתחילות ב - # .
במקרה של :
#define PI 3.1415927
המעבד המקדים מבין שמנקודה זו בתוכנית ו"מטה" הוא צריך להחליף כל מופע של PI ב- 3.1415927 , מלבד מופעים בתוך הערות ובתוך מחרוזות. ההחלפה היא של טקסט בטקסט לא קשר למשמעות.
לכן בתוכנית האחרונה כל מופע PI מוחלף בזמן קומפילציה ב - 3.1415927 .
דוגמאות ל - define :
#define TRUE 1
#define FALSE 0
#define N 100
השימוש ב- define תורם לקריאות התכנית ומאפשר ביצוע שינויים נקודתיים המשפיעים לכל אורך התוכנית. למשל בתוכנית האחרונה אם נרצה לשנות את מספר הספרות אחרי הנקודה העשרונית ב - PI אז נצטרך לעשות זאת רק במקום אחד - ב define .