תוכן הפרק
תוכנית המדפיסה תוצאת חישוב
וממתינה להקשת מקש
קליטת מילה אחת - לתוך מערך
תווים
משתמשים בלולאת for כאשר ידוע לפני תחילת
הלולאה כמה איטרציות תהינה
לולאת while משמשת בד"כ למספר
בלתי ידוע מראש של איטרציות
פירוט טיפוסיי המשתנים הבסיסיים
בשפה
מספרים שלמים Integral types: char, short, int, long
טיפוסי מספרים ממשיים או נקודה
צפה - float, double, long double
טיפוסי תווים char :
characters
מפעילים פשוטים simple operators
מפעילים מספריים numeric operators
קידום טיפוס במפעילים בינאריים
Type promotion
מפעילים של קידום והורדה ב
1 ++, --
מפעילי השמה נוספים +=, -=,
*=, /=, %=
מפעילים לוגיים boolean operators
הוראת הגדרת משתנה Varaiable dfinition statemens
הוראת ביטוי Expression Statement
הוראה מורכבת Compound Statement
איפה חשובים הרווחים - מפרידים
ואסימונים tokens
בחירת בצוע לפי אפשרויות של
ערכים קבועים switch statement
הוראות break, continue בלולאות
מחלקה פשוטה ועצמים פשוטים -
שימוש במבנה struct
הגדרת שם חילופי לטיפוס typedef
שלד
בסיסי של תוכנית שאינו מבצע דבר
void
main()
{
. .
.
}
הקוד שלפנינו הוא המינימום הנמצא בכל
תוכנית c++.
·
כל
תוכנית חייבת להכיל פעולה ראשית (פונקציה ראשית) בשם main().
·
השם main מציינת כי זוהי הפעולה הראשית של
התוכנית.
·
מה
מציינת המילה void נסביר בהמשך.
·
לאחר
מכן בא הגוף בתוך סוגריים
מסולסלים. { ... }. בתוך הגוף
צריכות להופיע סדרת ההוראות שאנו מעוניינים שתבצע התוכנית.
במקרה שלנו סדרת ההוראות ריקה ולכן התוכנית
אינה מבצעת דבר.
נתקדם לתוכנית המדפיסה את השורה Hello world על
מסך המחשב.
#include <iostream.h>
void
main()
{
cout << "Hello world";
}
------- Output -----------
Hello
world
·
התוכנית
מבצעת הוראה אחת, שהיא להדפיס את המילים Hello world.
·
הדפסה
על המסך (וקליטת נתונים מהמקלדת) מכונות פעולות קלט/פלט.
·
את
כל פעולות הקלט/פלט אנו מבצעים, בשפת
C++, באמצעות שימוש בספריות.
·
ספריה
היא אוסף של קטעי תוכניות, הכתובות מראש, שאנו יכולים לשבץ בתוכנית שאנו כותבים.
·
קיימות
אלפי פעולות שכבר נמצאות בספריות וזמינות לשימוש לפי הצרכים בתוכנית שלנו. הפעולות
מאורגנות לפי נושאים למאות ספריות.
·
בכדי
להשתמש בספריה אנו חייבים לכלול קובץ המכיל מידע חיוני לשימוש
בספריה. הכללת הקובץ מתבצעת באמצעות ההוראה
#include <library-name> כאשר בתוך סוגריים המשולשים מופיע
שם של קובץ המכיל את המידע החוץ.
·
Input-Output-Stream השם iostream מרמז על ספריה העוסקת בהזרמת נתוני
קלט/פלט
·
cout זהו עצם הפלט התקני (מייצג לרוב את
המסך).
·
האופרטור
(מפעיל) << פירושו שלח אל עצם הפלט.
·
"Hello world" מחרוזת של תווים.
·
כל
שורת הוראה חייבת להסתיים ב ;
מכאן פירוש שורת ההוראה cout
<< "Hello world"; הוא: שלח את מחרוזת התווים "Hello world" אל הפלט התקני (המסך).
#include<iostream.h>
void
main()
{
cout << "L1";
cout << "L2";
cout << endl;
cout << "L3";
cout << endl << "L4" << "L5"
<< endl << endl;
cout <<
"L6"
}
------------- Output
--------------
L1L2
L3
L4L5
L6
·
מספר
הדפסות המתבצעות אחת אחרי השניה מופיעות על המסך באותה השורה.
·
בכדי
לרדת שורה במסך אפשר להשתמש ב endl. שליחת endl לפלט פירושו לרדת שורה.
End Line -- endl.
·
אפשר
לשלב מספר הדפסות באותה השורה.
#include <iostream.h>
void
main()
{
cout << "1 + 2 = ";
cout << 1 + 2;
cout << "Press any Key to continue and press Enter...."
<< endl;
cin.get();
}
---------Output-----------
1 + 2 = 3
Press
any Key to continue and press Enter....
·
התוכנית
מדפיסה את ערך תוצאת חישוב הבטוי (1 + 2) שהוא 3.
·
+
הוא אופרטור החיבור המתבצע על שני אופרנדים: 2 ו 1 ותוצאת פעולתו היא 3.
·
עד
כה התוכניות הדפיסו על המסך ומייד סיימו עתה התוכנית ממתינה עד שהמשתמש יקיש על
מקש וילחץ Enter.
·
cin -- מייצג את עצם
הקלט התקני (בדרך כלל מקלדת) Common Input
·
הפעולה
get() המופעלת על עצם הקלט התקני ממתינה עד
לקבלת תו אחד מהקלט ולחיצה על מקש Enter.
·
cin.get() פירושו הפעלת הפעולה get() על העצם בשם cin.
·
משתנה
הוא מקום אחסון עבור ערך יחיד מסוג מסויים.
·
לכל
משתנה יש שם וכן סוג הנתון שהוא בנוי לאחסן.
·
משתנה
מסוג int בנוי לאחסון מספרים שלמים: ..., -1, 0, 1, 2, ...
·
תוכן
המשתנה יכול להשתנות תוך כדי בצוע התוכנית.
·
משתמשים
במשתנה עבור אחסון תוצאות ביניים בחישוב או מקום לקליטת נתונים מהקלט.
/*
Program sums two numbers
========================
*/
#include <iostream.h>
void
main()
{
int a; //
input location for first operand.
int b; //
input location for second operand.
int sum; // result of
a + b;
cin >> a; // read
first operand from keyboard.
cin >> b; // read
second operand from keyboard.
sum = a + b;
cout << "The Sum of " << a << " And
" << b <<" Is: ";
cout << sum << endl;
}
---------Example Output-----------
2
3
The
Sum of 2 And 3 Is: 5
·
החל
מהסימן // ועד לסוף השורה זוהי הערה. עוד סוג של
הערות הוא החל מצירוף התווים /*
ועד לצירוף התווים */ משתמשים
בהערה זו להערה בת מספר שורות
·
הערה
אינה משפיעה על בצוע התוכנית והיא משמשת אך ורק למידע עבור מתכנת הקורא את קוד
התוכנית.
·
בתוכני
זו אנו מגדירים שלושה משתנים: שניים,
a ו
b, כמקום אחסון לנתוני קלט והשלישי sum
לשמירת תוצאת ביניים.
·
כל
אחד מהמשתנים מיועד להכיל נתון מסוג
int שהוא מספר שלם (integer).
·
sum = a + b פירושו חשב את הסכום של a ו b ושים אותו במשתנה sum.
/*
Program prints absolute value of a number
=========================================
*/
#include <iostream.h>
void
main()
{
int n; // input
number.
cout << "Enter a number: ";
cin >> n;
if (n < 0) // if n is
negative change
n = -n;
cout << n << endl;;
}
If ( condition) הוראה לבצוע במידה והתנאי מתקיים |
---------Example Output-----------
Enter
a number: 4
4
---------Example Output-----------
Enter
a number: -5
5
·
n = -n הערך הבא במשתנה n הוא הערך השלילי של הערך הנוכחי הנמצא
בו.
·
משפט
if בודק את התנאי
בסוגריים. במידה והתנאי מתקיים
מתבצעת ההוראה הבאה אחרי if אחרת מדלגים על ה if.
/*
Program test if a number is positive
====================================
*/
#include <iostream.h>
void
main()
{
int n; //
input number.
cout << "Enter a number: ";
cin >> n;
cout << "The number " << n << " is
";
if (n > 0)
cout <<
"positive!" << endl;
else
cout <<
"negative!" << endl;
}
---------Example Output-----------
Enter
a number: 4
The
number 4 is positive!
---------Example Output-----------
Enter
a number: -5
The
number -5 is negative!
If ( condition ) הוראה
לבצוע במידה והתנאי מתקיים else הוראה לבצוע במקרה והתנאי אינו מתקיים |
·
משפט
if בודק את התנאי
בסוגריים במידה והתנאי מתקיים מתבצעת ההוראה הבאה אחרי if אחרת מתבצעת ההוראה הבאה אחרי ה else.
/*
Program reads two numbers.
print the one with low
value first
and then the other one.
====================================
*/
#include <iostream.h>
void
main()
{
int a, b; // input
numberes.
cout << "Enter two numbers: ";
cin >> a>> b;
if (a < b)
{
int
t; // temporary variable used for
swapping two numbers
t = a;
a = b;
b = t;
}
cout << "From low to high: " << a << ' '
<< b << endl;
}
---------Example Output-----------
Enter
two numbers: 1 2
From
low to high: 1 2
---------Example Output-----------
Enter
two numbers: 14 12
From
low to high: 12 14
במידה ורוצים שתתבצע יותר מהוראה אחת,
בעקבות תנאי, אפשר לקבץ בלוק הוראות בתוך סוגריים מסולסלים. אם התנאי ב if
נכון כל הבלוק מתבצע אחרת מדלגים על כל
הבלוק.
בתוך בלוק אפשר להגדיר משתנים חדשים.
בדוגמא שלנו משתנה העזר t.
·
לשם
החלפת שני מספרים נוהגים להשתמש במשתנה עזר.
תו בודד מייצגים באמצעות גרש תו גרש.
לדוגמא: ,'A' 'B'. זאת בניגוד למחרוזת תווים אותה מייצגים
באמצאות מרכאות לדוגמא: "abc"
/*
Program asks the user a question
if he type character Y amessage is printed.
if he type charater N another message is printed
if he type another charater error message is printed
*/
#include <iostream.h>
void
main()
{
char c; // varaiable used for storing a single character.
cout << "Would you like to here my message?" <<
endl;
cout << "Enter character [Y/N]: "
<< endl;
cin >> c;
if (c == 'Y' || c == 'y')
{
cout << "You
are very smart!!" << endl;
cout << "And
know how to choose :-)" << endl;
}
else if ( c == 'N' || c == 'n')
{
cout << "You
are not so bright" << endl;
cout << "And
a loser :-(" << endl;
}
else // not Y or N
cout <<
"Error: Next time try Y or N" << endl;
cout << "Press any Key to continue and press Enter...."
<< endl;
cin.get();
}
---------Example Output-----------
Would
you like to here my message?
Enter
character [Y/N]: Y <---- y is different from Y
You
are very smart!!
And
know how to choose :-)
Press
any Key to continue and press Enter....
---------Example Output-----------
Would
you like to here my message?
Enter
character [Y/N]: N
You
are not so bright"
And
a loser :-(
Press
any Key to continue and press Enter....
---------Example Output-----------
Would
you like to here my message?
Enter
character [Y/N]: x
Error:
Next time try Y or N
Press
any Key to continue and press Enter....
·
מציינים
תו בודד באמצעות גרשיים, לדוגמא: 'a' 'b'
·
אבל
כאשר רוצים לציין מחרוזת של יותר מתן אחד מציינים זרת באמצעות מרכאות,
לדוגמא: "abc".
·
ראינו
כי כאשר רוצים להציב לתוך משתנה ערך משתמשים באופרטור ההשמה: c = 'A' פירושו שמשימים את הערך 'A'
לתוך המשתנה c.
·
כאשר
רוצים לבדוק (ניח בתנאי) האם הערך
של משתנה זהה לערך מסויים משתמשים באופרטור אחר שהוא ==.
לדוגמא: c == 'A' זוהי שאלה האם הערך במשתנה c הוא התו 'A'.
·
חשוב
לא להתבלבל בין אופרטור ההשמה
= לבין אופרטור השוואת
הערך == !
·
האופרטור
הלוגי ||
משמעותו OR
(או) כלומר מספיק שאחד מהתנאים
מתקיים בכדי שהתנאי כולו יתקיים
·
אנו
משלבים את התנאי c == 'Y' || c == 'y' בכדי להבטיח כי אם המשתמש יקיש על המקש y או על Y
נקבל את התשובה כן אחרת רק אות קטנה או גדולה היתה נחשבת לתשובה
נכונה.
/*
Program reads 3 numbers from user
and prints them in increasing order.
Algorithm for sorting three numbers:
1.
compare first two - swap values if needed.
2.
compare last two - swap values if needed.
3.
and again compare first two - swap values if needed.
*/
#include <iostream.h>
void
main()
{
// array - sequence of 3 varaibles from type int: a[0], a[1], a[2]
int a[3];
int t; // used
for swapping array elements.
// reads three numbers
cout << "Enter 3 numbers: ";
cin >> a[0]; // read
first int
cin >> a[1]; // read
second int
cin >> a[2]; // read
third int
if (a[0] > a[1]) //
compare the first two
{ t = a[0];
a[0] = a[1];
a[1] = t;
}
if (a[1] > a[2]) //
compare the last two
{ t = a[1];
a[1] = a[2];
a[2] = t;
}
if (a[0] > a[1]) //
compare the first two
{ t = a[0];
a[0] = a[1];
a[1] = t;
}
// print the sorted array elements.
cout << endl << "Sorted numbers are:" <<
endl;
cout << a[0] << ' ' << a[1] << ' ' << a[2]
<< endl;
}
---------Example Output-----------
Enter
3 numbers: 3 1 4
1 3 4
---------Example Output-----------
Enter
3 numbers: 3 2 1
3 2 1
---------Example Output-----------
Enter
3 numbers: 123 ? waiting for
aditional two numbers
·
מערך
הוא שיטה מקוצרת להגדרת סדרה של משתנים.
·
צורת
ההגדרה int
a[3]; מגדירה מערך, שהוא
סדרה של שלושה משתנים מסוג int.
·
ה int הראשון מכונה a[0] השני a[1] והשלישי a[2].
·
ובאופן
כללי T array[N] הגדרנו מערך בגודל N איברים
כל איבר הוא משתנה מסוג T.
·
המספר N הוא מספר קבוע שנקבע בזמן כתיבת
התוכנית. וכן אי אפשר לשנות את גודל המערך בזמן ריצת התוכנית.
·
אברי
המערך ממוספרים החל מאינדקס 0 ועד
אינדקס N-1.
·
אסור
לפנות לאינדקסים מחוץ לגבולות שהוגדרו.
·
העבודה
עם איבר של מערך, לדוגמא a[1], זהה לצורת העבודה עם כל משתנה רגיל מסוג int.
/*
Program asks the user for his name
and say "Hi" to him.
*/
#include <iostream.h>
void
main()
{
char OneWord[21]; // for a word with max length of 20 chractes
cout << "What is your name? (max 20 chracters): ";
cin >> OneWord;
cout << "Hi " << OneWord << '!' <<
endl;
cout << "Press any Key to continue and press Enter...."
<< endl;
cin.get();
}
---------Example Output-----------
What
is your name? (max 20 chracters): Avi
Hi
Avi!
Press
any Key to continue and press Enter....
---------Example Output-----------
What
is your name? (max 20 chracters): Beni Cohen
Hi
Beni!
Press
any Key to continue and press Enter....
·
אחסון
מחרוזות תווים נעשה במערך תווים.
·
הגדרת
מקום לאחסון מילה או משפט הוא מערך של תווים.
·
הגדרת
מערך תווים, באופן כללי: char array_name[N+1];
·
N הוא מספר קבוע המסמל
את אורך המילה המירבי הניתן לאחסן במערך תווים. גודל המערך חייב להיות תמיד גדול ב
1 מ N.
·
כאשר
מבצעים את השורה cin >> OneWord רק מילה אחת (עד הרווח) מוכנסת למערך
מהקלט.
/*
program solves: a*X*X + b*x + c =
0.
prints the solution(s) if they exist (are real).
*/
#include<iostream.h>
// for input/output
#include<math.h> // for sqrt()
function
void main()
{
cout << "Program
solves: a*X*X + b*X + c = 0" << endl;
cout << "Enter a
b c :";
double a, b, c;
cin >> a >> b >> c;
double delta = b * b - 4 *
a * c;
if (delta > 0)
{
double sqrtDelta = sqrt(delta);
sol = (-b +
sqrtDelta) / (2 * a);
cout << "First solution : " << sol << endl;
sol = (-b -
sqrtDelta) / (2 * a);
cout << "Second solution: " << sol << endl;
}
else if
(delta == 0)
{
sol = -b / (2 * a);
cout << "Single solution is : " << sol << endl;
}
else
cout << "There is no real solution" << endl;
}
---------Example
Output-----------
Program solves: a*X*X + b*X + c = 0
Enter a b c :-3 6 9
First solution : -1
Second solution: 3
---------Example
Output-----------
Program solves: a*X*X + b*X + c = 0
Enter a b c :1 6 9
Single solution is : -3
---------Example
Output-----------
Program solves: a*X*X + b*X + c = 0
Enter a b c : 12 13 14
There is no real solution
·
המשתנים
מסוג int מתאימים לאחסון ערכים רק מסוג של מספרים
שלמים
... -1, 0, 1, 2, ... כאשר אנו זקוקים למשתנים שצריכים להכיל
ערכים עם חלק שבר, כגון 3.1415 וכו', אנו חייבים
להשתמש במשתנים מסוג של נקודה
צפה (floating point) הנפוץ בהם הוא double.
·
מותר
להגדיר סדרה של משתנים באותה השורה:
int
a;
int
b;
int
c;
same
as:
int
a, b, c;
·
מותר
להגדיר משתנה ולאתחל אותו באותו הזמן:
int
a;
a
= 2;
same
as:
int
a = 2;
·
האתחול
יכול להיות מסובך יותר ולהכיל חישוב:
int
a = 2 + 3;
·
החישוב
המשמש לאתחול משתנה יכול להכיל משתנים אחרים:
int
a = 2, b = 3,
int
c = 2 * a + b;
·
פונקציית
הספריה המתמטית sqrt( ) מקבלת בתוך הסוגריים ערך והיא מחזירה ערך השורש הריבועי
שלו.כלומר לאחר שורה a = sqrt(b) במשתנה a יהיה הערך שהוא שורש ריבועי של ערך b.
/*
Program prints the numbers from 1 to 10
demonstrating while and for loops
*/
#include<iostream.h> // for input/output
void
main()
{
int i; // loop variable
i = 1; // initializing loop
counter
while (i <= 10)
{
cout << i <<
' ';
i = i + 1; //
incrementing loop counter
}
cout << endl;
// Performs exactly the same operation.
// but initialization and increment of loop counter are
// performed in firs and third fields of for loop.
for (i = 1; i <= 10; i = i+1)
cout << i <<
' ';
cout << endl;
}
---------Example
Output-----------
1 2 3 4 5 6 7 8 9
10
1 2 3 4 5 6 7 8 9
10
·
לולאה - האפשרות לחזור שוב ושוב על פקודה או על
מספר פקודות כל עוד מתקיים תנאי כלשהו.
·
לולאת
) תנאיwhile( בודקת את התנאי ואם התנאי נכון מבצעים את גוף
הלולאה. גוף הלולאה הוא פקודה בודדת
או סדרה
של פקודות בתוך סוגריים מסולסלים.
·
i הוא משתנה הלולאה - המשתנה שקובע את
התנאי האם תתקיים האיטרציה הבאה של הלולאה.
·
אתחול
משתנה הלולאה:
בתחילה i מקבל ערך התחלתי 1.
·
בצוע
פקודת הדפסת המספר.
·
קידום
משתנה הלולאה:
הפקודה i
= i + 1; מבצעת קידום ערכו של i ב 1.
·
לולאת
)
קידום ; תנאי ; אתחול for( שקולה לחלוטין ללולאת ה while רק שחלק האתחול והקידום מתבצעים
בשדות של הפקודה
בעצמה ואילו ב while האתחול נעשה לפני
תחילת הלולאה והקידום בגוף הלולאה.
/*
program calculates and print the sum and average of 10 numbers.
*/
#include <iostream.h>
void
main()
{
cout << "Enter 5 numbers: " << endl;
int n, i = 0, sum = 0; // initialize i and sum to 0.
for(i = 0; i < 5; i++)
{
cin >> n;
sum = sum + n;
}
cout << "Sum is: " << sum
<< endl;
cout << "Average is: " << sum / 5.0 << endl;
}
---------Example
Output-----------
Enter
5 numbers:
1 2 3 4 5
Sum
is: 15
Average
is: 3
·
הלולאה
תתבצע בדיוק 5 פעמים ולכן נשתמש בלולאת
for.
בדרך כלל משתמשים בלולאת while כאשר
מספר האיטרציות שתבצע הלולאה אינו ידוע מראש.
/*
program calculates and print the sum and average of a sequence of
positive numbers. A negative number
read indicates end
of sequence.
*/
#include <iostream.h>
void
main()
{
cout << "Enter sequence of non negative numbers"
<< endl;
cout << "End the sequence with a negative number "
<< endl;
int n, i = 0, sum = 0; // initialize i and sum to 0.
cin >> n;
while(n >= 0)
{
i++; // same as: i = i + 1;
sum = sum + n;
cin >> n;
}
if (i != 0) // if numers where entered
{ cout <<
"Sum is: " << sum << endl;
cout <<
"Average is: " << sum / double(i) << endl;
}
cin.get();
}
---------Example
Output-----------
Enter
sequence of non negative numbers
End
the sequence with a negative number
1 2 3 4 -1
Sum
is: 10
Average
is: 2.5
---------Example
Output-----------
Enter
sequence of non negative numbers
End
the sequence with a negative number
-1
·
האופרטור
!= פירושו שונה בערכו.
/*
program read 5 numbers and print them in the reverse order
*/
#include <iostream.h>
const
int N = 5; // constant declaration
void
main()
{
cout << "Enter " << N << "
numbers" << endl;
int a[N];
int i;
for (i = 0; i < N; i++)
cin >> a[i];
for (i = N-1; i >= 0; i--)
cout << a[i]
<< ' ';
cout << endl;
}
---------Example
Output-----------
Enter 5 numbers
1 2 3 4 5
5 4 3 2 1
כחלק
משפת C++ יש לנו מספר טיפוסי עצמים בסיסיים,
המאפשרים למתכנת להגדיר מספרים ותווים (אותיות). עבור טיפוסים בסיסיים אלו מוגדרים
אוסף של מפעילים (operators) אשר אפשר להפעיל עליהם.
· טיפוסים של ערכים שלמים מייצגים מספרים
כמו: -100, -1,
0, 2, 100.
· יש ארבעה סוגים של מספרים שלמים אשר המבדיל
ביניהם הוא השטח שהם תופסים בזיכרון:
char £ short £ int £ long
· יחידת האחסון הבסיסית היא בית (8 סיביות בינאריות). בית יכול
לייצג: 28 = 256 ערכים שונים.
· יש שני אופני שימוש במספרים שלמים: signed (מספרים חיוביים ושליליים), unsigned
(מספרים אי שליליים). טיפוס שמשתמשים בו כ unsigned יכול
להכיל מספר חיובי גדול בערך פי שניים לעומת השימוש בו כ- signed.
· מובטח כי טיפוס גדול יותר מכיל כל מספר
שמכיל טיפוס קטן יותר (בתנאי שהשימוש הוא באותה הצורה). הגודל המדויק של כל טיפוס
נקבע על ידי היצרן של המהדר וזה תלוי במימוש (implementation). המרכיב העיקרי בהחלטה הוא מבנה המחשב.
· לרוב מייצגים מספר שלם על ידי int, כאשר אין סיבה מיוחדת לעשות אחרת (short, long).
· ערכים קבועים (literals): 12, -10, 0
(בבסיס עשרוני - רגיל) 017, 0777
(מספר המתחיל ב0 הוא בבסיס אוקטלי - בסיס 8) 0x10, 0x1f (מספר המתחיל ב 0x הוא בבסיס הקסהדצימלי -
בסיס 16). הטיפוס של קבוע מספרי שלם הוא int.
בכדי לשנות זאת אפשר לדוגמא: 123L הוא מטיפוס long, 33U הוא מטיפוס unsigned וגם 1UL הוא מטיפוס unsigned
long.
תחום ערכים unsigned |
תחום ערכים signed |
גודל בבתים |
טיפוס |
0 « 255 |
-128 « 127 |
1 |
char |
0 « 65535 |
-32768 « 32767 |
2 |
short |
0 « 65535 |
-32768 « 32767 |
[1]2 |
int |
0 « 4294976295 |
-2147483648 « 2147483647 |
4 |
long |
טבלא 1 טיפוסי מספרים שלמים
· טיפוסי נקודה צפה (floating point) מייצגים מספרים
העשויים להכיל שבר כגון:
1.2,
0.001, 3.1415926536, -1.1 וכו'.
· יש לנו שלושה גדלים של טיפוסי נקודה צפה: float, double, long double.
· לרוב נשתמש בטיפוס double.
· ערכים קבועים של טיפוסי נקודה צפה, מכילים
נקודה עשרונית ו/או סימן חזקה: 1.2, .2, 1e3, 3E5, 2., 2e-4, 1.3e14, 0.0002 וכו'. הטיפוס של
קבוע מספרי של טיפוס נקודה צפה הוא double.
בכדי לשנות זאת אפשר לדוגמא: 1.2f (float) או 1e5L (long double) .
סה"כ
ספרות עשרוניות |
תחומי
גדלים |
גודל בבתים |
טיפוס |
|
7 |
3.4e-38 « 3.4e+38 |
4 |
float |
|
16 |
1.7e+308 « 1.7e-308 |
8 |
double |
|
21 |
3.4e-4932 « 1.1e+4932 |
[1] |
long double |
|
טבלא 2 טיפוסי מספרים ממשיים
· מיועד להכיל תו: 'a',
'F', '$', '?'.
· כל תו מיוצג על ידי מספר ('A' מיוצג ע"י 65),
אבל שימוש בטיפוס char יבטיח כי נקבל את
התרגום המתאים בין מספרים לתווים, למטרות קלט פלט.
· קבוע תו תחום בתוך מירכאות ויש הבדל בין
אותיות קטנות וגדולות.
· ישנם תווים אשר אין להם צורה קריאה על
המסך, ולכן מציגים אותם על ידי escape sequence:
ממקם את הסמן בתחילת השורה הבאה. |
Newline |
\n |
מזיז את הסמן לנקודת ה tab הבאה. |
Horizontal tab |
\t |
מזיז את הסמן לנקודת ה tab האנכי הבאה. |
Vertical Tab |
\v |
מזיז את הסמן מקום אחד אחורה בשורה. |
Backspace |
\b |
מזיז את הסמן לתחילת השורה הנוכחית. |
Carriage return |
\r |
הורדת שורה. |
Form Feed |
\f |
משמיע צפצוף. |
Alert |
\a |
משמש להדפסת התו '\'. |
backslash |
\\ |
סימן שאלה. |
Question Mark |
\? |
גרש. |
Single Quote |
\' |
משמש להדפסת התו '"'. |
Double Quote |
\" |
ערך 0 מספרי. |
NULL |
\0 |
ערך מספרי לפי בסיס אוקטלי (בסיס 8). |
Octal Number |
\ooo |
ערך מספרי לפי בסיס הקסהדצימלי (בסיס 16). |
Hex Number |
\xhhh |
טבלא 3 קבועי
תווים
·
מחרוזת
של תווים תחומה בתוך גרשיים: "hello, world\n"
·
אם
נדפיס למסך מחרוזת כזו ע"י הפקודה: cout <<
"hello, world\n" נקבל גם הורדת
הסמן לשורה הבאה בעקבות הדפסת התו '\n'.
·
טבלת
ערכי ascii הבאה מגדירה את התו המתאים לערכים בתחום 0..127 בערכים הגבוהים יותר 128..255 ישנם תוים שונים עבור אותם ערכים (תלוי במערכת ההפעלה).
הטיפוס bool מיועד להכיל ערך אמת או ערך שקר: true, false.
· הקבוע true מסמל ערך אמת וערכו
המספרי 1.
· הקבוע false מסמל ערך שקר וערכו
המספרי 0.
לתשומת לב: בשפת C++ כל ערך שונה מ-
0 נחשב כאמת ורק הערך- 0 נחשב כשקר.
· משתנה (variable)הוא מקום אחסון לערך בתוכנית, שערכו אינו ידוע ו/או הערך עשוי להשתנות
במהלך בצוע התוכנית.
· הגדרת משתנה (definition) היא הוראה בקוד
התוכנית המגדירה את שם המשתנה, טיפוס הערך שהוא מסוגל
להכיל, ולפעמים גם ערך התחלתי.
· חייבים להגדיר משתנה לפני שכותבים קוד המשתמש בו.
· כל משתמה בזמן ריצה תופש מקום
בזכרון בגודל המתאים לטיפוס הנתונים שהוא מכיל.
· הגדרת משתנה בסיסי (הקצאת שטח עבורו
בזיכרון) אינה גורמת למתן ערך התחלתי למשתנה, אלא אם יש אתחול מפורש..
· משתנה גלובלי מאותחל אוטומטית ל 0.
· ערך של משתנה (לא גלובלי) לא מאותחל אינו
ידוע (זבל garbage).
· הגדרת משתנה:
int
i;
· הגדרת מספר משתנים מאותו טיפוס באותה שורת
הגדרה:
int
j, k, n;
· השמת ערכים למשתנים:
j
= 3;
k
= 10;
n
= 1E10;
· אפשר וגם נהוג לאתחל משתנה בזמן הגדרתו:
int
i, j = 3, k = 10, n = 1E10;
לגבי האתחול:
· משתמשים במפעיל ההשמה '=' (assignment) למתן ערך התחלתי
בזמן הגדרת המשתנה.
· המשתנה i לא
אותחל וערכו בהתחלה יכול להיות כל מספר מטיפוס int.
· רצוי בדרך כלל להגדיר משתנה במקום בו רוצים
להשתמש בו ומייד לאתחלו בזמן הגדרתו.
· לפעמים אנו מעונינים להגדיר משתנה כך שערכו
לא ישתנה במשך ריצת התוכנית.
· בכדי להגדיר משתנה עם ערך קבוע (constant) אנו מוסיפים את המילה const לפני הגדרת המשתנה, ומאתחלים את הערך
הקבוע שלו.
const
double PI = 3.1415926535897932385;
· שיפור קריאות התוכנית (readability)
ונוחיות הכתיבה:
לדוגמא:
2 * PI * r פחות קריא ונוח להקליד מ- 2 *
3.1415926535897932385 * r
· קלות בהכנסת שינויים.
בעיה: נניח כי כתבנו תוכנית
לניהול 5 מחלקות במפעל. המספר 5
מופיע פעמים רבות בתוכנית.
לאחר זמן הוסיפו מחלקה למפעל. הרי אז צריך לעדכן את כל המופעים של המספר 5
שמשמעותו מספר המחלקות
ל 6. דבר זה גורר חיפוש בכל התוכנית אחר המספר 5 והחלפת המספר ב 6 רק כאשר
משמעות המספר 5 היא מספר המחלקות ולא 5 ימי עבודה בשבוע.
פתרון: שימוש בקבוע const int
departmentsNumber = 5; יחסוך לנו את החיפוש אחרי המספר 5 ונבצע
את ההחלפה של 5 ב 6 רק במקום אחד - בהגדרת הקבוע המתאים.
בשפת C++ יש לנו אוסף של
מפעילים המאפשרים לנו לבצע פעולות על טיפוסי נתונים בסיסיים.
ישנם
חמישה מפעילים אריתמטיים בינאריים (שני ארגומנטים) פשוטים:
· +
חיבור
· -
חיסור
· *
כפל
· / חילוק
· %
שארית החילוק בין מספרים שלמים
(מוגדר רק לטיפוסים integral)
ישנם זוג מפעילים מספריים אונריים (עם
ארגומנט אחד):
· - מינוס לפני ערך כגון: -4, -n
· + פלוס לפני ערך כגון: +4, +n (למעשה אינו משפיע
אלא נועד לצורך סימטרייה למפעיל -).
1 +
2 is 3
1 -
2 is -1
1 *
2 is 2
7 /
2 is 3
7 %
2 is 1
קוצץ ולא מעגל / / 11 / 4 is 2
-11 / 4 is -2
מפעיל החילוק / מתנהג אחרת עבור מספרים שלמים (integer)
ואחרת עבור מספרים ממשיים (floating): עבור מספרים שלמים
הוא נותן את הערך השלם של החלוקה, ועבור מספרים ממשיים הוא נותן את התוצאה של
החלוקה כולל השבר
חלוקה
של מספרים שלמים:
1 / 2
is 0 int / int
חלוקה של מספרים ממשיים:
1.0
/ 2.0 is 0.5 double / double
כאשר הטיפוסים של שני הנפעלים (operands) שונים יש תהליך של הסבה (conversion).
1.0 / 2 is
0.5 double / int --> double / double
· בשפת C++ יש סדר קדימויות בין המפעילים, זאת בכדי
לקבוע חד משמעית את משמעות החישוב.
דוגמא:
כמו
במתמטיקה * קודם ל +
לכן 3 + 4 * 5
פירושו 3 + (4 * 5)
הקדימות של המפעילים האריתמטיים הוא מהגבוה
לנמוך:
· האונריים -,
+
· *, /, %
· הבינאריים -,
+
· תמיד אפשר להשתמש בסוגריים בכדי להשפיע על
סדר הפעולות:
(31 + 8)*(5 - 7)
· למפעילים יש גם תכונה של תסמוך ימני/שמאלי(left/right associative) .
כאשר משתמשים במספר אופרטורים בעלי
אותה עדיפות כוון התסמוך קובע מי עדיף.
· המפעילים האונריים ומפעיל ההשמה (=) הם
בעלי תכונת תסמוך ימני (right
associative).
· המפעילים האחרים בעלי תכונת תסמוך
שמאלי (left associative). לדוגמא המפעילים
הבינאריים הבאים: +, -, *, /, %:
3 - 4 + 5 is ((3 - 4) + 5)
and not (3 - (4 + 5))
3 / 2 * 3 is ((3 / 2) * 3)
which is 3 and not (3
/ (2 * 3)) which is 0
למפעיל '-' האונרי יש קדימות על
המפעיל '-'
הבינארי:
-5 - 4
is ((-5) - 4) and not -(5 - 4)
· בכל מקרה שיש ספק לגבי משמעות ביטוי,
הפתרון הוא הוספת סוגריים '(' ')'.
· לתיאור של כל המפעילים קדימויותיהם
והאסוציאטיביות שלהם ראה טבלאת מפעילים בסוף פרק זה.
· לעיתים קרובות מקדמים ערך של משתנה
ב- 1, או מורידים ב- 1 ערך של משתנה. הדבר כה נפוץ עד שמצאו לנכון לספק מפעילים
מיוחדים למטרות אלו.
· המפעיל ++ נועד לקדם ב- 1.
· המפעיל -- נועד להוריד ב- 1.
· יש שני אופני שימוש במפעילים הללו, לפני
המשתנה (prefix),
דהינו משמאלו, ואחרי המשתנה (postfix), דהינו מימינו.
בכדי להבין פעולת מפעילים אלו צריכים להבין
את המושגים:
++a (prefix) |
a++ (postfix) |
expression value: a + 1 |
expression value: a |
side effect: add 1 to a |
side effect: add 1 to a |
· המפעיל -- מתנהג באופן דומה.
// for all examples, a starts with value 2, x starts with value 3
the
expression:
values of a and x after calculation
a
+ x++ // is 2 + 3 ->
5
a is 2, x is 4
a
+ ++x // is 2 + 4 ->
6
a is 2, x is 4
--a + x // is 1 + 3 -> 4
a is 1, x is 3
a--
+ x-- // is 2 + 3 -> 5
a is 1, x is 2
· lvalue
- משתנה שמותר לשנות
את ערכו. (כל מה שיכול להופיע באגף
שמאל של השמה)
· מפעילים של קידום והורדה משנים את ערך
המשתנה שעליו הם מופעלים, לכן חייבים להפעילם רק על lvalue . אסור להפעילם על קבועים, מספרים,
וביטויים.
// ERRORS:
++123 ,
--(a + b), ++(--a), --PI
· מפעיל השמה קובע את הערך בארגומנט השמאלי
שלו להיות התוצאה של החישוב באגף הימני שלו.
· המפעיל אינו מתנהג בצורה סימטרית:
· באגף שמאל שלו מצוין מקום לאחסון הנתון (lvalue).
· באגף ימין ישנו ביטוי שחייב להחזיר ערך (rvalue).
// OK
a
= 3 * (b + c); // sets the value of a.
double
x = y; // defines x and
sets it to y’s value.
a
= b = 5;
// sets both a and b to 5.
y
= x++; // sets y to x’s
value and then increments x.
// ERROR
a
+ 2 = b;
// a + 2 isn’t a storage location.
PI
= 3; // constant
can’t be modified.
x++
= y; // x++ isn’t a storage
location.
a
+ 2, PI, x++ are not lvalues!
· המפעילים ++ -- פועלים רק על lvalue, וכמובן אגף שמאל של המפעיל = חייב
להיות lvalue.
· מפעיל ההשמה הוא מפעיל בינארי רגיל במובנים
הבאים:
· מפעיל ההשמה מחזיר את הערך של אגף שמאל
לאחר ההשמה.
· למפעיל ההשמה תופעת לואי שהיא שינוי הערך
של המשתנה באגף שמאל.
· מפעיל ההשמה הוא right
associative:
a
= b = 5; is (a = (b = 5));
· קודם מבצעים את ההשמה ל b.
· הערך של הביטוי (b = 5) הוא הערך של b לאחר ההשמה, ז.א.
5.
· משימים את הערך 5
לתוך a.
· למפעיל ההשמה יש הקדימות הנמוכה ביותר
שראינו עד כה. לכן:
a
= b + c; is (a = (b + c));
· לשם נוחיות הגדירו מפעילי השמה
נוספים אשר עוזרים לקצר את הכתיבה של הביטויים.
Operator |
Equivalent Expression |
v += e; |
v = v + e; |
v -= e; |
v = v - e; |
v *= e; |
v = v * e; |
v /= e; |
v = v / e; |
v %= e; |
v = v % e; |
· בשפת C++ יש לרוב
מספר דרכים להשיג אותה תוצאה:
n
= n + 1;
n
+= 1;
n++;
++n;
כל אלה דרכים לגרום להשפעה צדדית על n שהיא
קידומו באחד. שים לב כי ערך הביטוי n++ אינו שקול לערך יתר הביטויים.
· צריך להבדיל בין מפעיל ההשמה = לבין מפעיל השוויון == . (באלגברה
הסימן = פירושו שוויון, בC++
אין הדבר כך - לכן יש לשים לב).
· אנו מעוניינים ביכולת לבדוק האם יחס מסוים
מתקיים בין ערכים. אם היחס מתקיים נאמר שהוא אמת (true) ואם היחס אינו מתקיים נאמר שהוא שקר (false).
· מוגדרים בשפה מספר מפעילים של יחס בין
מספרים:
Operator |
true (returns 1) when |
a == b |
a is equal to b |
a != b |
a is not equal to b |
a < b |
a is less than b |
a <= b |
a is less than or equal to b |
a > b |
a is greater than b |
a >= b |
a is greater than or equal to b |
All of these return: 0 (false) or 1 (true) |
· מפעילי היחס מחזירים ערך true ששווה ל 1 במקרה שהיחס מתקיים.
· מפעילי היחס מחזירים ערך false ששווה ל 0 במקרה שהיחס אינו מתקיים.
לדוגמא:
int
a = 1, b = 2, r;
r
= a < b; // r
has 1
r
= a >= b; // r has
0
r
= a => b; // Error:
operator => doesn't exist
r
= b >= -2.0; // r has 1. The value of b is converted to double, b itself is
unchanged.
bool
b1;
// new C++ type
b1
= a < b; // b
has true (which is 1)
b1
= a >= b; // b has
false (which is 0)
· ישנם מקרים בהם רוצים לשלב מספר תנאים
לתנאי מורכב. יש לנו מספר מפעילים המשמשים לשילוב תנאים.
Operator |
true (returns 1) when |
a && b |
both a and b are true |
a || b |
either a or b or both are true |
!a |
a is false |
All of these return: 0 (false) or 1 (true) |
דוגמאות:
int
a = 1, b = 2, r;
r
= (a < b) && (b >= 2); // r receives 1
r
= (a > b) || (b >= 2);
// r receives 1
· בשפת C++ כל ערך מספרי שונה מ 0 נחשב כ true,
וערך שווה ל 0 נחשב כ false.
r
= 5 && -1; // r
receives 1
r
= (a < b) && b
// r receives 1
r
= !a + !b; // r
receives 0 + 0 which is 0
r
= !!a;
// r receives 1
r
= !0;
// r receives 1
· המפעילים && || בעלי מספר תכונות מיוחדות:
· בניגוד לרוב המפעילים (האריתמטיים לדוגמא)
במפעילים הללו מוגדר בשפה כי סדר חישוב האופרנדים הוא משמאל לימין. כלומר קודם
מחשבים את האופרנד השמאלי ורק לאחר מכן את הימני.
· המפעילים מחשבים את האופרנד השמאלי, ורק אם
התנאי לא הוכרע מחשבים גם את האופרנד הימני.
דוגמא:
if
((items > 0) && (sum / items > 90)) // avoiding division by 0.
grade = 'A';
· אם items שווה
ל 0 לא נחשב את החלוקה: sum /
items.
כאשר משתמשים במפעיל אריתמטי, מחשבים תמיד
את שני האופרנדים:
x
= (items > 0) * (sum / items > 90)
int
j = -1, k;
k
= ++j || ++j; // k receives 1
(true), j receives 1.
k
= ++j && ++j; // k
receives 0 (false), j receives 0
// (++j on the right was not evaluated).
· סדר החישוב משמאל לימין. מפסיקים את החישוב
אם התנאי הוכרע.
· פרט למפעילים המיוחדים ('&&' '||' ',' '?:' ) סדר החישוב אינו מוגדר בשפה (זאת בכדי לאפשר
למהדר לסדר את הפעולות בצורה הנראית לו יעילה ביותר)
לדוגמא: אותו חישוב בדיוק במהדרים ובמחשבים שונים נותן ערך אחר. הדבר
נובע, כאמור מכך שלא מוגדר בשפה מהוא סדר הפעלת המפעילים וכל מהדר בוחר סדר
כרצונו.
int a = 17;
expression |
VAX GCC |
VAX CC |
SUN CC |
BIRISC CC |
SPARK GCC |
++a * a + a * a |
648 |
648 |
648 |
648 |
613 |
a * ++a + a * a |
648 |
648 |
648 |
648 |
613 |
a * a + ++a * a |
613 |
648 |
648 |
648 |
648 |
a * a + a * ++a |
613 |
648 |
648 |
648 |
648 |
a++ * a + a * a |
578 |
630 |
630 |
548 |
595 |
a * a++ + a * a |
578 |
630 |
630 |
548 |
595 |
a * a + a++ * a |
578 |
630 |
659 |
548 |
630 |
a * a + a * a++ |
578 |
630 |
659 |
548 |
630 |
· מכאן אפשר להסיק כי כאשר המפעילים ++, -- מופעלים על משתנה המופיע יותר מפעם
אחת בביטוי ערך הבטוי אינו מוגדר (נכון עבור המפעילים שבהם סדר החישוב אינו
מוגדר).
1 |
:: :: :: :: |
scope resolution scope resolution global global |
class_name :: member namespace_name :: member ::name ::
qualified-name |
2 |
. -> [] () () ++ -- typeid typeid dynamic_cast static_cast reinterparate_cast const_cast |
member selection member selection subscripting function call value construction post increment post decrement type identification run-time type identification run-time checked convertion compile-time checked convertion unchecked convertion const convertion |
object.member pointer->member pointer[expr] expr(expr_list) type(expr_list) lvalue++ lvalue-- typeid( type ) typeid( expr ) dynamic_cast<type>(expr) static_cast<type>(expr) reinterparate_cast<type>(expr) const_cast<type>(expr) |
3 |
sizeof sizeof ++ -- ~ ! + - & * new new new new delete delete[] () |
size of object size of type pre increment pre decrement complement not unary plus unary minus address of dereference create (allocate) create (alloc. and init.) create (place) create (place and init.) destroy (de-allocate) destroy array cast (type conversion) |
sizeof expr sizeof (type) ++lvalue --lvalue ~expr !expr +expr -expr &lvalue *expr new type new type (expr-list) new (expr-list) type new (expr-list)type(expr-list) delete pointer delete[] pointer (
type ) expr |
4 |
.* ->* |
member selection member selection |
object.*pointer-to-member pointer.*pointer-to-member |
5 |
* / % |
multiply divide modulo (remainder) |
expr * expr expr / expr expr % expr |
6 |
+ - |
add (plus) subtract (minus) |
expr + expr expr - expr |
7 |
<< >> |
shift left shift right |
expr << expr expr >> expr |
8 |
< <= > >= |
less than less than or equal greater than greater than or equal |
expr < expr expr <= expr expr > expr expr >= expr |
9 |
== != |
equal not equal |
expr == expr expr != expr |
10 |
& |
bitwise AND |
expr & expr |
11 |
^ |
bitwise exclusive
OR (XOR) |
expr ^ expr |
12 |
| |
bitwise inclusive
OR |
expr | expr |
13 |
&& |
logical AND |
expr &&
expr |
14 |
|| |
logical inclusive
OR |
expr || expr |
15 |
? : |
conditional
expression |
expr ? expr : expr |
16 |
= *= /= %= += -= <<= >>= &= |= ^= |
simple assignment multiply and assign divide and assign modulo and assign add and assign subtract and assign shift left and assign shift right and assign AND and assign OR and assign XOR and assign |
lvalue = expr lvalue *= expr lvalue /= expr lvalue %= expr lvalue += expr lvalue -= expr lvalue <<= expr lvalue >>= expr lvalue &= expr lvalue |= expr lvalue ^= expr |
17 |
throw |
throw exception |
throw expr |
18 |
, |
comma (sequencing) |
expr , expr |
· מפעילים אונריים ומפעילי השמה הם right
associative.
· כל המפעילים האחרים הם left associative.
כל קופסא מכילה מפעילים באותה העדיפות,
קופסא גבוהה יותר מכילה מפעילים בעדיפות גבוהה יותר (עדיפות 1 הגבוהה ביותר).
הוראה (statement) היא יחידת בצוע בתוכנית. סדר בצוע
התוכנית הוא לפי סדר ההוראות המושג הוראה מתייחס לארגון סדר הבצוע ולא לחישובים
וערכים.
אי אפשר להשתמש במשתנה לפני שמגדירים אותו.
יש לנו מספר דרכים להגדיר משתנים.
מבנה הוראות להגדרת משתנים חדשים:
הגדרת משתנה
TypeName identifier, ...;
//
הגדרת משתנה עם אתחול /
/ TypeName identifier = expression, ...;
הצהרה על קבוע const TypeName identifier =
expression,...; //
· כל הוראה המגדירה על משתנה מסתיימת
בסימן הסיום ';' .
· ה ... מציינות את האפשרות להגדיר עוד
משתנים מאותו טיפוס המופרדים ב- ','
int
a = 2, b = 3;
int
c, d = 3, e = a + b;
const
double rate = 0.0072;
· משתנה אפשר לאתחל על ידי ערך שאפשר לחשבו
על ידי ערכים (משתנים, קבועים וכו') שהוגדרו קודם.
· קבוע אפשר לאתחל רק בערך קבוע.
· אסור להגדיר שני משתנים (או קבועים) בעלי
אותו שם.
· הוראה מסוג ביטוי (expression) גורמת במהלך הבצוע לחישוב ערך הביטוי וכן לרוב לבצוע השפעה צדדית
(כמו קידום של משתנה או השמת ערך למשתנה).
· להוראת ביטוי יש הצורה: expression;
· כל הוראת ביטוי מסתיימת ב ';'.
לגבי הביטוי:
· לכל ביטוי יש טיפוס לתוצאה, הנקבע
בזמן כתיבת התוכנית.
· ערך הביטוי נקבע בזמן ריצת התוכנית
כאשר מעריכים (evaluate) את הביטוי.
נניח כי הגדרנו את המשתנים הבאים:
int
i = 2;
float
fsum, f1 = 2, f2 = 4;
double
d1 = 2, d2, d3;
int
i1 = 1, i2 = 2, i3 = 3, i4 = 4;
bool
b;
expression
statements |
value of expression
at run time |
type of
expression value |
side effect |
i++; |
2 |
int |
add 1 to I |
5; |
5 |
int |
- |
f1 + f2; |
6 |
float |
- |
i1 + d1; |
3 |
double |
- |
fsum = f1 + f2; |
6 |
float |
assign 6 to fsum |
d1 += d2 = 3; |
5 |
double |
assign 3 to d2 and 5 to d1 |
b = f1 < f2; |
true |
bool |
assign true to b |
d3 = 2 / 5 * 5 |
0 |
double |
assign 0 to d3 |
d3 = 2.4 / 5 * 5; |
2.4 |
double |
assign 2.4 to d3 |
· ישנם מקרים שאנו מעונינים להתייחס
לקבוצה של הוראות כיחידה אחת. עושים זאת על ידי קיבוץ סדרה של הוראות בתוך
סוגריים { } . צורה כזו נקראת
הוראה מורכבת (compound
statement) או בלוק הוראות (block).
· כל סוג של statement יכול להופיע בתוך הוראה מורכבת.
· בסוף הוראה מורכבת לא צריך ; .
{ int j = 3; double x = 2.01, y =
-3.89; int k = 2 - j; x = 2.4 * (x - y); }
{
statement;
statement;
statement;
statement;
}
· שפת C++ מתייחסת
למרווחים, טאבים, ושורות חדשות כאל מפרידים. לכן אין הבדל בין רווח אחד לעשרה
רווחים או לשורה חדשה. לפיכך אפשר לכתוב את התוכנית בצורה חופשית.
אפשר לכתוב הוראה מורכבת גם כך, אבל ברור
כי קשה מאד לקרא אותה.
{int j = 3; double x = 2.01, y = -3.89; int k = 2
- j; x = 2.4 * (x - y);}
· בכדי לשפר משמעותית את קריאות התוכנית יש
להקפיד על עימוד של ההוראות. לדוגמא:
· כל ההוראות בבלוק מוכנסות 2 תווים פנימה
לעומת עמודת ההתחלה הקודמת.
· כל חישוב יופיע בשורה נפרדת.
· מצהירים על מספר משתנים באותה שורה רק אם יש
קשר הגיוני בין המשתנים בתוכנית.
למרות מה שאמרנו למעלה קיימים מקרים בהם יש
חשיבות לרווחים. בכדי להבין מהם מקרים אלו צריך להכיר את השיטה לפיה המהדר מנתח את
קוד התוכנית:
· המהדר סורק את קוד התוכנית במטרה למצוא את
היחידות הבסיסיות של התוכנית (מזהים, מפעילים, קבועים, מילים שמורות וכו').
· המהדר סורק משמאל לימין. בזמן הסריקה המהדר
מנסה למצוא את הרצף הארוך ביותר של תווים המהווה יחידה חוקית בשפה (אסימון token).
· רווחים חשובים רק כאשר הם מפרידים בין
אסימונים.
a
+ ++ b --> a + (++ b)
a+
++b
--> a+ (++ b)
a+++b
--> (a++)+b
a
++ + + b --> (a ++) + (+ b)
a+++
+b --> (a++)+(+b)
a++++b --> (a++)(++b) ERROR: binary operator is
missing!
· לעיתים קרובות, אנו מעוניינים שהתוכנית
תבצע דברים שונים, עבור ערכים שונים הנקבעים רק בזמן ריצת התוכנית. ב C++ יש לנו מספר אפשרויות לבחור איזו הוראה
לבצע על פי ערכים הנקבעים בזמן ריצת התוכנית.
· control flow statements - הוראות המיועדות
לבחור איזו הוראה לבצע לפי תנאי המחושב בזמן ריצה.
|
if (expression)
statement1
1. מחשבים את ערך הבטוי expression,
שהוא יכול להיות מכל טיפוס מספרי (שלמים או ממשי).
2. אם הערך נכון (שונה מ 0) מבצעים את ההוראה
היחידה או בלוק ההוראות. אם הערך שקרי (שווה 0) אין מבצעים דבר.
דוגמא:
·
בצוע
או אי בצוע של הוראה בודדת.
if
(a < 0)
// count negatives
negatives++;
·
בצוע
או אי בצוע של בלוק הוראות.
if
((n < 0) && (d < 0))
// if both negative change sign.
{
n = -n;
d = -d;
}
if (expression) statement1 else statement2 if (expression) { ... } else { ... } if (expression) { ... } else statement2
1. מחשבים את ערך הבטוי expression.
2. אם הערך נכון מבצעים את ההוראה statement1. אם הערך שקרי מבצעים statement2.
דוגמא: חלוקה ב 0 הינה טעות חמורה בזמן
ריצה ועלולה לגרום לסיום בצוע התוכנית. בכתיבת תוכנית צריך
להתכונן
לגרוע ביותר (אשר בדרך כלל קורה).
תוכנית טובה צריכה לטפל בשגיאות אפשריות!
if
(numScores == 0)
cout << "No scores to average";
else
average = total / numScores;
· ה
statement בתוך ה if יכול להיות מכל סוג, אפילו if:
if
(a > 100)
if (b <= 0)
bigAndNeg = 1; // done
if a > 100 and b <= 0
else
bigAndPos = 1; // done
if a > 100 and b > 0
else
small = 1; // done if a <=
100 regardless of b
· העימוד חשוב ביותר (לקריאות התוכנית, לא
למהדר)! - נסה להבין מה קורה כאן:
if
(a > 100)
if
(b <= 0)
x
= 1;
// done if a > 100 and b <= 0
else
y
= 1;
// when would this be done?
· אותו הדבר עם עימוד טוב מדגיש את הכלל:
ה else מוצמד תמיד ל if הקרוב ביותר:
if
(a > 100)
if (b <= 0)
x = 1;
// done if a > 100 and b <= 0
else
y = 1;
// done if a > 100 and b > 0
· בכדי להצמיד את ה else
ל if הראשון אפשר להשתמש בהוראה מורכבת:
if
(a > 100)
{
if (b <= 0)
x = 1;
// done if a > 100 and b <= 0
}
else
y = 1;
// done if a <= 100
· התנאי הראשון שיתקיים יגרום להשמה
לתוך grade, וכל
יתר התנאים לא יבדקו (חיסכון בהשוואות).
if
(score >= 90)
grade = 'A';
else
if (score >= 80)
grade = 'B';
else
if (score >= 70)
grade = 'C';
else
if (score >= 60)
grade = 'D';
else
grade = 'F';
· תנאים מקוננים: צורת עימוד מקובלת יותר
(בכדי שתנאי מקונן ארוך לא יצא מהמסך ימינה):
if
(score >= 90)
grade = 'A';
else
if (score >= 80)
grade = 'B';
else
if (score >= 70)
grade = 'C';
else
if (score >= 60)
grade = 'D';
else
grade = 'F';
switch(expr)
{
case
const-expr1:
statement-list1
case
const-expr2:
statement-list2
case
const-expr3:
statement-list3
default:
// optional
statement-list4
}
ההוראה נועדה להחליט מה לבצע לפי בדיקת ערך expr.
· expr וכל ה const-expr הם מטיפוס אינטגרלי (מספרים שלמים char short int
long )
· const-expr
הם ערכים שלמים, קבועים, שערכיהם ידועים בזמן ההידור.
פעולה
· מחשבים את ערך ה expr .
אם ערכו שווה לאחד מה const-expr
מתחילים לבצע את ה statement-list החל מה label case
const-expr: ועד סוף ה switch block.
· אם ערך ה expr אינו
שווה לאף אחד מערכי ה const-expr מתחילים לבצע החל מה default: . אם לא הגדרנו default: אין מבצעים דבר.
· אפשר למקם את ה default: בכל מקום בתוך הבלוק (לאו דווקא בהתחלה
או בסוף). ואז לפעמים החישוב משתנה בעקבות זאת.
· ה const-expr חייבים להיות שונים.
· statement-list פירושו שמותר לכתוב סדרה של הוראות לאחר ה case const-expr: ולא מוגבלים ל statement
בודד. כאשר כותבים מספר הוראות אין
צורך בהוראה מורכבת (
{..} ).
· אסור להגדיר משתנים בתוך switch block אלא רק בתוך בלוק המוגדר בתוך ה switch.
· בדרך כלל, מעונינים לבצע רק מקרה אחד. אז
משתמשים בהוראת break, גורמת ליציאה מיידית
מתוך הבלוק של ה switch.
דוגמא "רגילה":
· בדרך כלל משתמשים ב switch במקום if-ים מקוננים.
#include <iostream.h>
void
main()
{
char c, grade;
int score;
cout << "enter your score (0..100): ";
cin >> score;
switch(score / 10) {
case 10:
case 9:
grade = 'A';
break;
case 8:
grade = 'B;
break;
case 7:
grade = 'C';
break;
case 6:
grade = 'D';
break;
default:
grade = 'F';
break; // Not needed
now, but advisable in case we add
}
// another mark.
cout << "Your grade is: " << grade << endl;
}
דוגמא "מיוחדת" (בלי break) לחישוב מס הכנסה:
· דוגמא זו היא שימוש חריג בהוראת switch.
#include <iostream.h>
// program calculates the income tax for a given
salary
void
main()
{
double salary;
cout << "Enter your salary: "
cin >> salary;
if (salary < 0) {
cout <<
"Error in IncomeTax(): Negative salary.\n";
return 0;
}
double tax = 0;
int bracket = salary / 1000;
switch(bracket) {
default:
tax = (salary - 4000) * 0.4;
salary = 4000;
case 3:
tax += (salary - 3000) *
0.3;
salary = 3000;
case 2:
tax += (salary - 2000) *
0.2;
salary = 2000;
case 1:
tax += (salary - 1000) *
0.1;
salary = 1000; // used only if we add another
case 0:
// tax bracket below.
;
}
cout << "Your tax is: " << tax << endl;
}
· ישנם הרבה מקרים בהם רוצים לקבל אחד מבין
שני ערכים לפי תנאי מסוים:
expr1 ?
expr2 : expr3
· אם expr1 נכון, אזי ערך כל הבטוי יהיה כערכו של expr2.
אחרת יהיה ערך הביטוי כערכו של expr3
· בכל מקרה יחושב expr1. אחריו יחושב expr2 או expr3, אבל לא שניהם.
· המפעיל קובע את סדר החישוב :
1.
קודם
מחשבים expr1 .
2.
אם
התוצאה שונה מ 0 מחשבים expr2 וערכו הוא ערך כל
הביטוי.
3.
אם
התוצאה שווה ל 0 מחשבים expr3 וערכו הוא ערך כל
הביטוי.
·
הטיפוס
של כל הבטוי הוא הטיפוס הגבוה מבין expr2 ו expr3.
דוג:
void
main()
{
int x = 1, y = 2, r;
r = x > y ? x : y;
// max of x y
r = x >= 0 ? x : -x; //
absolute value of x
r = 1.0 / ( n != 0 ? n : 1); // one over
}
הסוגריים חיוניים אחרת (בגלל סדר קדימויות)
משמעות הבטוי היתה:
((1.0
/ n) != 0) ? n : 1
( 1 ? 2 : 3.0)
זהו ביטוי מטיפוס double וערכו 2.
x
= a++ ? a++ : a++; // x = a+1
זהו ביטוי שערכו מוגדר היטב כי המפעיל ? : קובע את סדר החישוב.
ה
side effect על a הוא הוספה של 2.
·
הוראות
הלולאה: while,
do-while, for .
·
הנחיות
לשימוש יעיל בלולאות.
·
המפעיל
, וההוראות continue, break בלולאות.
·
בדרך
כלל משתמשים בהוראת while כאשר לא יודעים
לפני תחילת בצוע הלולאה כמה איטרציות תתבצענה.
while
(expression)
statement
next
statement
·
מחשבים
את הבטוי expression,
שהוא יכול להיות מכל טיפוס מספרי (שלמים או ממשי).
·
אם
התנאי נכון (ערכו שונה מ 0) מבצעים את
statement, וחוזרים לבדוק את התנאי.
·
אם
התנאי אינו נכון (ערכו 0) מפסיקים את בצוע הלולאה (לא מבצעים את ה statement) וממשיכים ל next statement.
·
לולאה
המבקשת מהמשתמש להכניס קלט תקין (בחירת כן/לא).
#include <iostream.h>
void
main()
{
cout << "Want a gift? Please choose [y/n]: ";
char c;
int n_err = 0;
cin >> c;
while(n_err < 3 && c != 'y' && c != 'Y' && c
!= 'n' && c != 'N')
{
cerr <<
"Sorry, Please choose [y/n]: ";
cin >> c;
n_err++;
}
if(n_err == 3)
cerr <<
"can't get your intention" << endl;
else if (c != 'y' || c != 'Y')
cout <<
"Greedy you :-(" << endl;
else
// (c != 'n' || c != 'N')
cout <<
"Gifts hater will live :-)" << endl;
}
·
היכן
מקודם משתנה הלולאה: שים לב כי בתוך גוף הלולאה i כבר התקדם.
#include <iostream.h>
void
main()
{
int i = 1;
while(i++ < 3)
cout << i <<
endl;
}
---------Output----------
2
3
4.
עוצרים
את הלולאה כאשר יש שלשה ערכים עוקבים גדולים מ 200.
int
temp, consecutiveHighs = 0;
cin
>> temp;
while
(consecutiveHighs < 3)
{
if (temp > 200) consecutiveHighs++;
else
consecutiveHighs = 0;
cin >> temp;
}
do
statement
while
(expression);
·
מבצעים
את ה statement.
·
מחשבים
את הבטוי expression,
שהוא יכול להיות מכל טיפוס מספרי (שלמים או ממשי).
·
אם
התנאי נכון (ערכו שונה מ 0) מבצעים את
statement, וחוזרים לבדוק את התנאי.
·
אם
התנאי אינו נכון (ערכו 0) מפסיקים את בצוע הלולאה (אין מבצעים את ה statement) וממשיכים לבצע את ההוראה הבאה אחרי
הלולאה.
·
בלולאת while קודם בודקים את התנאי ורק אם הוא נכון
מבצעים את גוף הלולאה.
·
בלולאת do-while תמיד מבצעים את גוף הלולאה בפעם הראשונה
ורק לאחר מכן בודקים את התנאי.
·
בלולאה,
שמוצאת שלושה ערכים עוקבים גדולים מ- 200, האיטרציה הראשונה תמיד מבוצעת ולכן do-while עדיף:
int
temp, consecutiveHighs = 0;
do
{
cin >> temp;
if (temp > 200) consecutiveHighs++;
else
consecutiveHighs = 0;
}
while
(consecutiveHighs < 3);
·
אנו
רוצים לחשב סכום של מספר לא ידוע של מספרים חיוביים אשר מכניס המשתמש. נסיים את
הלולאה אם המספר שנקרא לא יהיה חיובי. שימוש
ב do-while אינו מתאים כאן כי הנתון הלא חיובי מתוסף
לסכום:
int
n, sum = 0;
do
{
cout << "Entry?\n";
cin >> n;
sum += n;
} while (n > 0);
cout
<< "the sum is " << sum;
·
סדרת
פיבונצ'י מוגדרת: a0=0, a1=1, a2=1,
a3=2, a4=3,... ai = ai-1 + ai-2
void
main()
{
int n;
long f0 = 0, f1 = 1, t;
cin >> n;
// read the element number.
if (n > 0)
{
f0 = 0; f1 = 1;
while (n-- > 1)
{
t = f0 + f1;
f0 = f1;
f1 = t;
}
}
else
n = 0;
cout
<< "the "
<< n << "'th elemnt of Fibonachi sequence is:
<< f1;
}
·
מממ gcd מחלק
משותף מירבי (greatest
common divisor) של המספרים השלמים A ו
B מוגדר להיות המספר הטבעי הגדול ביותר
המחלק את A וגם את B ללא שארית. אוקלידס המציא את השיטה (האלגוריתם) הבאה:
1. חלק את במספר הגדול במספר הקטן. אם שארית החלוקה היא 0 הרי המספר הקטן
הוא ה מממ.
2. קח את שארית החלוקה להיות המספר הקטן החדש
ואת המספר הקטן הישן להיות המספר הגדול החדש. חזור לשלב 1.
void
main()
{
int i, j;
cin >> i >> j;
// read two numbers
if (i == 0 || j == 0) // if i or j is zero return the other value.
cout << "The
gcd is: " << i + j;
else {
while ((i %= j) != 0)//
remainder replaces dividend.
{
// but remainder should replace divisor!
int t = i; // swap dividend and divisor.
i = j;
j = t;
}
cout << "The
gcd is: " << j << endl;
}
}
·
המפעיל , מיועד לסדר בצוע של חישובים:
expr1 , expr2
·
הבטוי expr1, expr2 מחושב משמאל לימין: קודם מחשבים את expr1
ולאחר מכן את expr2.
·
ערך
הבטוי הוא הערך של expr2 והטיפוס של הביטוי
הוא הטיפוס של expr2.
·
הקדימות
של המפעיל , היא הנמוכה ביותר.
דוגמא:
int
x, y, z, w;
x
= (1, 2, 3);
// x<-3
x
= (y = 1, z = 2, w = 5); // x<-5 and side effects to: y, z, w
if
(i < 20)
i++, j = 8, break; // use
to concatenate expressions
·
בהרבה
מקרים מספר האיטרציות שהלולאה צריכה לבצע נקבע לפי נתונים הידועים לפני תחילת בצוע
הלולאה. במצב זה נהוג להשתמש בלולאה מסוג
for. אך אפשר להשתמש
בלולאת for גם למקרים אחרים.
for
(initStatement; testExpression; iterationExpression)
statement
ראינו כי זה שקול ללולאה
initStatement;
while
(testExpression)
{
statement
iterationExpression;
}
1.
בתחילה
מבצעים initStatement. זהו אתחול הלולאה
המתבצע פעם יחידה.
2.
מחשבים
את הבטוי testExpression,
שהוא יכול להיות מכל טיפוס מספרי (שלמים או ממשי).
3.
אם
הוא נכון מבצעים את גוף הלולאה: statement.
4.
אם
התנאי אינו נכון מפסיקים את בצוע הלולאה.
5.
לאחר
בצוע ה statement מבצעים את שלב קידום הלולאה: iterationExpression, ולאחר מכן חוזרים
לבדוק את התנאי הלולאה.
לדוגמא:
·
חישוב
ממוצע של 100 מספרים
void
main()
{
int n, sum = 0;
cout << "Enter 100 integers: " << endl;
for (int i = 1; i <= 100; i++)
{ cin >> n;
sum += n;
}
cout << "The average is " << double(sum) / 100
<< '\n';
}
·
בדרך
כלל נהוג לבצע N איטרציות באופן הבא:
for
(int i = 0; i < N; i++)
הערה: אנו מניחים שאין משנים את i בתוך האיטרציה עצמה.
for(;;)
// infinite loop
same
as:
while(true)
דוגמאות:
for
(int i=-20, j=30; i + j ; i+=2, j-=4)
cout << i+j;
output:
10 8 6 4 2
·
חישוב
שונות (מושג מסטטיסטיקה)
נוסחת
השונות:
הערך הממוצע של x :
פתוח הנוסחא כדי שלא נהיה חיבים לדעת מהו ה
x הממוצע מראש:
הנוסחא
הסופית:
.
// calculates the average and variance for a user
defined number
// of x's.
void
main()
{
int n;
cout << "Enter number of x's: ";
cin >> n; // get the
number of elements
double sumx = 0;
// sum of x
double sumSqrx = 0; // sum of x*x
double x;
for (int i = 0;i < n; i++)
{
cout << "Enter
x " << i << " :";
cin >> x;
sumx += x;
sumSqrx += x * x;
}
double avgx
= sumx / n;
double variance = sumSqrx / n - avgx * avgx;
cout << "The results for " << n << "
x's are: " << endl;
cout << "Average:
" << avgx << endl; // endl same as '\n'
cout << " variance:" << variance << endl;
}
break
· יציאה מלולאה או switch
הפנימיים ביותר שהפקודה נמצאת בה.
· הפקודה פועלת על: for, while,
do-while, switch
· השימוש הנפוץ ביותר של פקודת break הוא בתוך switch והמטרה למנוע בצוע של ה
caseים
הבאים.
continue
· עובר לאיטרציה הבאה של הלולאה הפנימית
ביותר שנמצאת בה הפקודה:
· בלולאת for עובר מיד לחלק הקידום (ומשם לבדיקת
התנאי).
· בלולאת while,
do-while עובר מיד לחלק של
בדיקת התנאי.
· הפקודה מתעלמת לחלוטין מ switch!
דוגמאות:
דוגמא זו קוראת מהמשתמש 10 מספרים ומדפיסה את סכום החיוביים מביניהם.
// loop to sum positive numbers
const
int N = 10;
double
sum = 0, x;
for
(int j = 0; j < N; j++)
{
cin >> x;
if (x < 0)
// skip negative elements
continue;
sum += x;
// sum positive elements
}
דוגמא לכך שההוראה continue מתעלמת מההוראה switch
void
main()
{
int i, j;
for (i = 1; i < 6; i *= 2)
// i takes the values 1, 2, 4.
{
switch (i)
{
case 1:
j = 17;
break; //
'break' belongs to switch.
case 2:
j = 35;
continue; // 'continue' belongs to for. Hence no printing
case 4:
j = 71;
break;
default:
j = 0;
}
cout << ' '
<< i << ' ' << j << ';';
}
}
OUTPUT:
1 17; 4 71;
1.
חייבים
לדאוג שבכל מקרה יסתיים בצוע הלולאה. בדרך כלל הסיום נגרם על ידי הפיכת תנאי
הלולאה להיות שקר. צריך להבטיח שזה יקרה עבור כל האפשרויות של הפעלת הלולאה. אם
התוכנית אינה מסתיימת תוך זמן סביר יש לחשוש שהדבר קרה בגלל שהתוכנית מבצעת לולאה
אינסופית.
2.
בדרך
כלל התנאי נקבע על ידי בדיקת יחס בין ערכים, ובהרבה מקרים היחס הוא בין משתנה
וקבוע.
int
j;
cin
>> j;
while
(--j != 0)
מה
יקרה כאשר קראנו ערך שלילי לתוך j? - "לולאה
אינסופית"! פתרון אפשרי:
cin
>> j;
if
(j > 0)
while (--j != 0)
int
j = 9;
while
(j != 0)
j -= 2;
במקרה זה j יקבל
ערכים אי זוגיים ובכל מקרה ערכו לא יגיע ל 0, שהוא מספר זוגי! - לולאה אינסופית!
דרך טובה יותר:
while
(j > 0)
j -= 2;
for
(double d = 0; d != 1; d += 0.1)
{
...
}
· מוצגים כאן 13 הערכים הראשונים של d, כפי שהתקבלו על מחשב PC. שים לב שהערך 1.0 לא התקבל, ולכן הלולאה לא הסתיימה
"כצפוי".
· הסיבה היא שאין ייצוג מדוייק למספר 0.1 אלא
רק קרוב אליו.
// values of d
0.000000000000000000
0.100000000000000006
0.200000000000000011
0.300000000000000044
0.400000000000000022
0.500000000000000000
0.599999999999999978
0.699999999999999956
0.799999999999999933
0.899999999999911
0.999999999999999889
1.099999999999999870
1.199999999999999960
פתרון מומלץ הוא:
for
(double d = 0; d < 0.99; d += 0.1)
אם d עולה
בצעדים של 0.01 אזי כדאי לכתוב:
for
(double d = 0; d < 0.999; d += 0.01)
3.
שים
לב כי בתוך גוף הלולאה ישנן הוראות שצריכות להתבצע בכל איטרציה של הלולאה.
4.
באופן
כללי, יש להמנע מהגדרה בתוך גוף הלולאה של משתנים ובמיוחד של עצמים. הסיבה היא
הזמן הדרוש להכין מקום בזכרון עבור משתנה ובמיוחד הקריאה ל constructor עבור
עצם, בכל איטרציה.
5.
אם
גוף הלולאה מכיל הרבה הוראות כך שלא ברור מה נעשה בו, כדאי לשקול להגדיר פונקציות
נוספות אשר תבצענה את פרטי החישוב ולקרא להן מגוף הלולאה. במקרה וקיימת בעיה של
מהירות ביצוע אפשר לשקול להגדירן כ inline.
6.
זכור
כי ההוראות המופיעות בגוף הלולאה מבוצעות בכל איטרציה של הלולאה. לכן אם יש לנו
לולאה בתוך גוף של לולאה אחרת הרי הלולאה הפנימית תתבצע בשלמותה בכל איטרציה של
הלולאה החיצונית.
// multiplication table
const
int N = 9;
for
(int i = 1; i <= N; i++)
{
for (int ij, j = 1; j <= N; j++) {
if ((ij = i * j) <
10)
cout
<< ‘ ‘;
cout << ij;
}
cout << ‘\n’;
}
·
משחק
לניחוש מספר. במשחק זה המחשב בוחר מספר והמשתמש בוחר מספר. המחשב והמשתמש מנסים
לנחש את המספר של השני. בכל שלב המשתמש נותן ניחוש למספר שהמחשב בחר ומקבל כתשובה
האם המספר גדול/קטן או שזהו בדיוק המספר שבחר המחשב. לאחר מכן המחשב מנסה לנחש את
המספר של המשתמש וממתין לתשובה: גדול/קטן/בדיוק.
·
הפונקציה rand() מחזירה מספר אקראי בין 0..RAND_MAX. הפונקציה נותנת את אותה סדרה של מספרים
אקראיים בכל הרצה (חשוב ל debugging) אם רוצים סדרת מספרים שונה חייבים,
להפעיל את הפונקציה srand(n) כאשר לכל מספר שלם n rand() תתן סדרה שונה של מספרים. בכדי ש n יהיה שונה בכל הרצה קוראים לפונקציה time(0) אשר מחזירה את הזמן כרגע.
#include <iostream.h>
#include <stdlib.h> // srand(), rand(),
exit()
#include <time.h> // time()
void
main()
{
cout << "Number-Guessing game" << endl;
cout << "First one who gusses the others number win"
<< endl;
cout << "Write down your number [1..1000]" <<
endl;
srand(time(0));// set rand() to a different sequence each run.
int my_n = 1 + rand() / (RAND_MAX/999); // choose from: [1..1000]
cout << "I wrote down my number" << endl;
int range_l = 1, range_h = 1000;
// computer gussing range
while (range_l != range_h)
{
cout <<
"Enter your guess [1..1000]: ";
int users_guess;
cin >>
users_guess;
if (users_guess == my_n)
{
cout <<
"you won! my number is: " << my_n << endl;
exit(0);
}
if (users_guess >
my_n)
cout << "My number is smaller" << endl;
else
cout << "My number is bigger" << endl;
int my_guess = range_l +
rand() % (range_h - range_l + 1);
cout << "My
guess for your number is: " << my_guess << endl;
cout <<
"Enter Y-yes B-Bigger S-Smaller: ";
char c;
do {
cin >> c;
c = toupper(c);
switch(c) {
case 'Y':
cout << "I wone!!!" << endl;
exit(0);
case 'B':
range_l = my_guess + 1;
break;
case 'S':
range_h = my_guess - 1;
break;
default:
cerr << endl << "Enter Y-yes B-Bigger S-Smaller:
";
}
} while(c != 'B'
&& c != 'S');
}
cout << "I won your number is: " << range_l
<< endl;
}
קיימים מקרים בהם רוצים
להתייחס אל גוש נתונים כאל מייצגים מושג אחד. לדוגמא המושג נקודה דו מימדית כולל
בתוכו שני נתונים: קורדינטות x,y.
הגדרת מושג חדש נעשית בשני
שלבים:
1.
הצהרה
על המושג החדש. מכונה הצהרה על מחלקה
חדשה.
2.
הגדרת
עצמים (כמו משתנים) מסוג אותה המחלקה החדשה.
3.
נהוג
להצהיר על מחלקות פשוטות באמצעות המילה
struct (ועל מסובכות יותר
באמצעות המילה class).
נראה את ההבדל,
הקטן, בין class ל
struct בהמשך.
·
כאשר
רוצים להגדיר מושג חדש בתוכנית על ידי גוש נתונים, אפשר לעשות זאת באמצעות מבנה.
#include <iostream.h>
struct p2d {
int x;
int y;
};
void main()
{
p2d
p1, p2;
p2d p3
= {1, 4};
//
error: p3 = {5, 6}; - {...} used only in initialization.
cout
<< p3.x << ' ' << p3.y << endl;
p1.x =
2;
p1.y =
3;
cout
<< p1.x << ' ' << p1.y << endl;
p2 =
p1; // copy the entier struct.
cout
<< p2.x << ' ' << p2.y << endl;
cout
<< "Enter x y: ";
cin
>> p1.x >> p1.y;
cout
<< "You Entered: " << p1.x << ' ' << p1.y
<< endl;
// cin
>> p1 - Error - dosn't know the user defined type!
//
cout << p1 - Error - dosn't know the user defined type!
}
------- Output ------------
1 4
2 3
2 3
Enter x y: 7 8
You Entered: 7 8
·
הצהרה
על מבנה נעשית באופן הבא:
struct name {
T1 m1;
T2 m2;
...
};
·
name שם המבנה.
·
T1 טיפוס של האיבר(member) הראשון m1 שם האיבר הראשון.
·
T2 טיפוס של האיבר(member) השנין m2 שם האיבר השני., וכו'
·
ההצהרה
על מבנה היא הגדרת טיפוס חדש. שם המבנה הוא טיפוס חדש שניתן להגדיר משתנים מסוגו.
·
אסור
להצהיר על מבנה באותו שם פעמיים (אפילו אם ההצהרות זהות).
·
מותר
לאתחל מבנה עם {...} אבל אסור להשתמש בסוגריים מסולסלים
בהשמות.
·
השמה
'=' בין שני מבנים מעתיקה את כל המבנה.
·
אופרטור
הנקודה מאפשר לגשת לנתון פנימי בתוך עצם מסוג המחלקה.
·
מותר
להצהיר על מחלקה המורכבת מעצמים מסוג של מחלקות אחרות:
#include <iostream.h>
struct p2d {
int x;
int y;
};
struct line2d {
p2d
p1;
p2d
p2;
};
void main()
{
line2d
l1, l2 = {{1,2}, {3, 4}};
l1 =
l2;
cout
<< l1.p1.x << ' ' l1.p1.y
<< ' '
l1.p2.x << ' ' << l1.p2.y << endl;
}
---- Output -------
1 2 3 4
T
name[n];
7.
זוהי
הגדרה של מערך בשם name המכיל n איברים מטיפוס T.
8.
מספור
האיברים מ 0 עד n-1, ושמותיהם: name[0],
name[1], ..., name[n-1]
int numList[2];
char
charArray[8];
p2d points[3];
0
1
numList
0 1 2 3 4 5 6 7
charArray
0
1
2
points
9.
תמיד
האינדקס מתחיל ב 0.
10. אפשר להגדיר מערך של איברים מכל טיפוס - גם
טיפוס שהגדיר המשתמש (Fraction)
.
11. מותר גם להגדיר מערך שכל איבר שלו הוא מערך
בעצמו.
int
t[2][3];
12.
t[0]
t[1]
הגדרנו כי t הוא מערך של 2 איברים כאשר כל איבר הוא
מערך של 3 איברים מטיפוס int.
t[1][0]
t[1][1]
t[1][2]
t[0][0] t[0][1] t[0][2]
13. זוהי הצורה בה מסודר המערך בזכרון המחשב
(כסדרה של נתונים).
14. אפשר לחשוב על המערך כעל טבלא בצורה:
0 1 2
0 |
|
|
|
1 |
|
|
|
15. גודל המערך חייב להקבע בזמן ההידור. לכן
כאשר מגדירים מערך הגודל חייב להיות ערך קבוע הידוע בזמן ההידור.
const
int sz = 10;
int size = 10;
double
a1[sz]; // OK
double
a2[size]; // Error: size is a
variable, not a constant value.
double
a[sz * 6]; // OK: still a constant value.
16. אפשר בזמן ההגדרה (רק בהגדרה) לאתחל את
המערך.
int a[3] = {5, 10, 15};
char
s[5] = {'a', 'b', 'c', 'd', 'e'};
17. אתחול מערך של מערכים:
int
tbl[2][3] = {{1, 2, 3}, {4, 5, 6}};
18. ב
C++ הטיפוס של מחרוזת תווים הוא מערך של
תווים.
19. אנו יכולים לאתחל מערך של תווים על ידי
קבוע מחרוזת.
20. כל מחרוזת מסתיימת בתו מיוחד (null '\0')
המסמן את סופה. לכן, כאשר מגדירים גודל של מחרוזת צריך להוסיף תו אחד עבור סימן
סוף המחרוזת: '\0'.
char
st[6] = "hello"; // short version of:
char
st[6] = {'h', 'e', 'l', 'l', 'o', '\0'};
st:
21. אפשר שהאתחול יקבע את גודל המערך.
char
st[] = "hello"; //
set array size to 6!
int a[] = {1, 2, 3}; // set array size to 3.
// tbl has two elements! each one is an array of 3
int's.
int
tbl[][3] = {{1, 2, 3}, {4, 5, 6}};
22. כאשר מגדירים מערך חייבים להגדיר במפורש את
טיפוס האיבר. במקרה של tbl היינו חייבים לציין במפורש שטיפוס כל
איבר של tbl הוא מערך של שלושה int.
23. אם אין מציינים במפורש את טיפוס האיבר
(מערך של שלושה int) מקבלים טעות:
int
tbl[][] = {{1, 2, 3}, {4, 5, 6}}; // Error!
24. מסקנה: כאשר מגדירים מערך של מערכים מותר
לתת לאתחול לקבוע את מספר האיברים רק ברמה הראשונה של המערך (מותר להשאיר רק את
הסוגריים השמאליים ביותר ריקים).
25. אתחול חסר מושלם תמיד עם 0 -ים:
int
a[4] = {2, 3};
// same as: = {2, 3, 0, 0};
int
tbl[2][3] = {{1}, {4, 5}};
// same as: = {{1, 0, 0}, {4, 5, 0}};
int
tbl[][3] = {{1}, {4}};
// same as: = {{1, 0, 0}, {4, 0, 0}};
26. אסור להגדיר מערך בגודל מסוים ולאתחל אותו
עם יותר איברים:
int
a[3] = {1, 2, 3, 4}; // Error!
T arrName[size]; // definition.
arrName[integral-expression]; // using array element.
27. הגדרנו מערך בשם arrName בגודל size וכל איבר מטיפוס T.
28. הגישה לאיבר במערך היא על ידי ציון שם
המערך ו integral-expression ערך מטיפוס של מספר שלם בתוך
סוגריים [].
29. זכור: אין בדיקה אם ערך האינדקס מצביע על איבר בתוך המערך או שישנה
גלישה מגבולות המערך. אין בדיקה לכך, לא בזמן הידור ולא בזמן ריצה! חריגה מגבולות
המערך תגרור התנהגות בלתי צפויה של התוכנית. זהו אחד מה bugs הקשים ביותר לגילוי. המתכנת הוא האחראי הבלעדי לכך שמשתמשים
במערך בצורה נכונה (פתרון אפשרי נמצא בעמוד 92).
void
main()
{
int i = 10;
int a[3] = {2, 4, 5}; //
a[0], a[1], a[2].
int j = 20;
a[2] = a[1]; // OK
a[2]++;
// OK
a[3] = 4; //
runtime silent error: a[3] is not in range,
// no run time, or
compilation time warnings!
// i OR j might receive value
of 4!
a[-20] = a[12]; // runtime silent error!!!
a[a[0] * a[1] % 3] += 2; // OK: a[2] += 2;
a[func(a[0], a[2]) % 3] = -10; // OK: function appears in index.
cin >> a[0] >> a[1] >> a[2]; // read values to array
elements.
}
30. הוראת goto מיועדת להמשיך את הבצוע ממקום מסוים המתואר על ידי תווית (label).
31. בדרך כלל זהו רעיון גרוע להשתמש
בהוראת goto, במיוחד לקפוץ אחורה בבצוע ולדלג על אתחול
משתנים.
32. לעיתים נדירות הוראת goto מסייעת לנו לכתוב תוכנית ברורה וטובה
יותר.
#include <iostream.h>
const
int N = 2;
const
int M = 3;
const
int L = 2;
const
int K = 2;
// initialize 23 values, last value is zero.
int
v[N][M][L][K] = {
0,1,2,1,0,3,0,0,1,2,3,4,6,7,8,9,0,1,2,3,-6,5,7
};
// Using goto in a manner acceptable to some.
void
main()
{
int n,m,l,k;
for (n = 0; n < N; n++)
for (m = 0; m < M;
m++)
for (l = 0; l < L; l++)
for (k = 0; k < K; k++)
if (v[n][m][l][k] < 0)
goto
found;
found:
cout
<<"v["<<n<<"]["<<m<<"]["<<l<<"]["<<k<<"]="<<
v[n][m][l][k]
<< endl;
}
OUTPUT:
v[1][2][0][0]=-6
v[1][2]
33. המערך הרב מימדי מסודר בזכרון המחשב כרצף
של נתונים. לכן אפשר גם לאתחל אותו כרצף.
34. המערך v הוא של שני איברים, כל איבר מכיל שלושה
איברים, כל אחד מהם מכיל שני איברים שכל איבר מורכב משני איברים מטיפוס int.
·
מתוארת
כאן הפריסה של המערך בזכרון.
ישנם שלושה מקרים בהם נשקול להשתמש בהגדרת
שם חילופי לטיפוס:
1. מתן כינוי משמעותי יותר לטיפוס.
2. לתת שם מקוצר לטיפוס שהגדרתו ארוכה.
3. להגדיר טיפוס מורכב בשלבים.
35. הצהרת typedef נכתבת בדיוק כפי שנכתבת הגדרת משתנה
מאותו טיפוס פרט ל:
1. ההצהרה מתחילה במציין typedef.
2. שם המשתנה הוא השם הנרדף לטיפוס המוצהר.
· מתן כינוי משמעותי:
typedef
double dimension;
struct
Box {
dimension length,
width,
height;
double
price; // price isn't
a dimension - use double.
};
36. הכוונה להדגיש שהמשתנה width לדוגמא, מיועד להכיל מידה.
37. צריך להדגיש שאין מגדירים טיפוס חדש, אלא
מגדירים שם חילופי (alias) לטיפוס,
שהוא משמעותי יותר למתבונן האנושי. עבור המהדר, משתנה מטיפוס dimension הוא משתנה מטיפוס double! כלומר עבור המהדר, בכל מקום שמופיע dimension
בעצם מופיע double.
· מתן כינוי מקוצר
אני משתמש שלוש פעמים במערך של 20 תווים.
הייתי מעוניין להגדיר טיפוס של מערך של 20 תווים.
typedef
char chars20[20];
chars20
response;
struct
Employee {
chars20 name;
chars20 address[3];
};
· הגדרת טיפוס מורכב בשלבים:
struct
Complex {
double real, imaginary;
};
typedef
Complex Cvector[4]; // type: array
of 4 Complexes
typedef
Cvector Cmatrix[4]; // type:
matrix 4´4 Complexes