דוא"ל:
תפריט משתמש




שתף |

קורס:"שפת C"

שיעור 3: אבני היסוד

[ <<< הקודם ] [ תוכן עניינים ] [ הבא >>> ]

מתוך הספר: C - מדריך מקצועי       C - מדריך מקצועי
תוכן עניינים



מזהים ( Identifiers)

מזהים הם שמות בתכנית המייצגים משתנים, קבועים, טיפוסים, פונקציות וכו'.

כללים לקביעת שמות מזהים:

1. מזהה הוא רצף של אותיות, ספרות והתו '_' (underscore) . רצף זה חייב להתחיל באות בשפה האנגלית או בתו '_'.

2. אורך מזהה אינן מוגבל

3. אין להשתמש במילים שמורות כמזהי טיפוסים, משתנים וכו'.

4. קיימת ההבחנה בין אותיות גדולות וקטנות. לדוגמא, המזהים top, Top, TOP הם מזהים שונים.

5. על שמות המזהים להיות משמעותיים. לדוגמא אם נרצה לתת שם למונה בתוכנית ניקרא לו counter.

  • דוגמאות למזהים חוקיים:
  - x
  -  y20
  - a_very_long_variable
  -  _counter
  • דוגמאות למזהים לא חוקיים:
::   7_Eleven            ::   - מתחיל בספרה
::   hello!               ::   - התו "!" לא חוקי
::   my-var          ::   - התו "-" לא חוקי

הערות (Comments)

אם נרצה להוסיף לתכנית מלל אשר נרצה שהמהדר יתעלם ממנו (לדוגמא, הסברים על התוכנית או על פקודות מסוימות) נסמנו כהערה. הערה מתחילה בצמד התווים */  ומסתיימת בצמד התווים /*.  לדוגמא:

 
/* this is a comment. */
 int x = 5 * 2;
 

לא ניתן לבצע קינון הערות - לדוגמא, במבנה ההערה הבא

ההערה מסתיימת כאן.

כל הקוד אשר מופיע לאחר סיום ההערה המסומן יחשב כחלק מהתוכנית עבור המהדר. כאשר סימן תחילת או סיום הערה נמצא בתוך מחרוזת, הוא לא יזוהה והמהדר לא יתייחס אליו.



מילים שמורות (keywords)

מילים שמורות הן מילים בשימושה של השפה ואסורים לשימוש כשמות מזהים. רשימת המילים השמורות בשפת C :

struct

if

do

auto

switch

int

double

break

typedef

long

else

case

union

register

enum

char

unsigned

return

extern

const

void

short

float

continue

volatile

signed

for

default

while

static

sizeof

goto

טיפוסי נתונים

הטבלה הבאה מציגה את הטיפוסים הבסיסיים בשפת C :

טיפוס

תאור

char

תו בודד

short

שלם קצר

int

שלם

long

שלם ארוך

float

ממשי

double

ממשי כפול

long double

ממשי ארוך

טיפוסים שלמים

int הוא הטיפוס הבסיסי לייצוג מספר שלם. אופן הגדרת משתנה מסוג שלם:

 
int    i;
 

i יכול כעת לקבל ערכים שלמים, חיוביים ושליליים לדוגמא:

 
i = 34; 
i = -2456;
 

קיימים תתי-טיפוסים של שלם:

short             

- שלם קצר

long

- שלם ארוך

כמו כן ניתן לציין עבור טיפוס שלם כלשהו שהוא מקבל ערכים חיוביים בלבד ע"י המציין unsigned. דוגמאות:

 
short                  i1 =  - 23;
unsigned         i2 =  3000;
unsigned         short  i3 = 23;
 

טיפוסים ממשיים

float הוא הטיפוס הבסיסי לייצוג מספר ממשי. אופן הגדרת משתנה מסוג ממשי:

 
float f;
 

f יכול כעת לקבל ערכים ממשיים (ראה/י טבלה להלן):

 
f = 34.56;
f = -123.458997;
f = 23E+12;
 

קיימים טיפוסים ממשיים בעלי טווח מספרים וערך דיוק גדול יותר:

    double - ממשי בעל דיוק כפול וטווח ערכים גדול
    long double - תוספת דיוק וטווח ל- double

טיפוסים תוויים

תווים מיוצגים במחשב ע"י טבלה הנקראת טבלת ASCII. הטבלה מכילה 256 תווים (0 עד 255) הכוללים ספרות, אותיות (גדולות וקטנות) וסימנים שונים. הטבלה במלואה מופיעה  בנספח הספר.

דוגמאות לתווים בטבלת ASCII:

A       2        %      *        !        a        .         ~        )       

הטיפוס הקיים בשפת C לייצוג  תו בודד הוא char. אופן הגדרת משתנה תווי:

 
          char  c;
 

וכעת c יכול לקבל ערכים מסוג תווי - ערך תווי מסומן ע"י שני גרשים משני צידיו:

 
          c = 'a';
          c = 'A';
          c = '!';
 

האם char הוא signed או unsigned ?

התשובה לשאלה תלויה במערכת עליה עובדים. כלומר הגדרת משתנה מסוג char כגון:

 
          char ch;
 

אינה חד משמעית בשפת C אלא תלויית מערכת וסביבת פיתוח. דבר זה יכול ליצור הבדלים חדים בהידור והרצת תכניות על מחשבים שונים. לדוגמא, אם נגדיר את המשתנה כך

 
          char ch = 255;
 

ערכו של המשתנה השלם x לאחר ההצבה

 
          int x = ch;
 

יהיה בעל ערך 255 במערכת שבה char הוא unsigned ובעל ערך -1 במערכת שבה הוא signed!!!

גדלי הטיפוסים

בשפת C אין הגדרה מדויקת לגודלי הטיפוסים הבסיסיים בשפה, כלומר למספר הבתים המוקצים למשתנה מסוג אותו הטיפוס.

גדלי הטיפוסים תלויים במחשב ובמערכת ההפעלה עליה עובדים.

לדוגמא, במחשב מבוסס Intel - Pentium, עם מערכת ההפעלה Windows 95 / NT גדלי הטיפוסים הבסיסיים הם:

טיפוס

מספר בתים

טווח ערכים

char

1

-128..127

short

2

-32KB .. 32KB

int

4

- 2GB .. 2GB

long

4

- 2GB .. 2GB

float

4

-3.4E +/- 38  .. 3.4E +/ - 38

double

8

 -1.7E +/- 308 .. 1.7E +/- 308

long double

8

-1.7E +/- 308 .. 1.7E +/- 308

enum 

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

 
enum Day { SUN=1, MON=2, TUE=3, WED=4, THU=5, FRI=6, SAT=7};
 

והגדרת משתנה מסוג ה- enum הנ"ל:

 
enum Day  today;
 

בדוגמא זו Day הוא שם תג (tag name) . הגדרת המשתנה מצוינת ע"י המילה enum ושם התג (Day) מיד אחריה. תכנית דוגמא:

 
#include <stdio.h>
void main()
{
          enum Day { SUN=1, MON=2, TUE=3, WED=4, THU=5, FRI=6, SAT=7};
          enum Day day1, day2;
          
          day1 = SUN;
          day2 = THU;
}
 

אם לא מצוין ערך של קבוע בתוך הגדרת ה- enum ערכו שווה לערך האיבר הקודם + 1. אם ערכו של האיבר הראשון לא מצוין ערכו הוא 0. לכן ניתן להגדיר את ה- enum הנ"ל בדרך מקוצרת:

 
enum Day { SUN=1, MON, TUE, WED, THU, FRI, SAT};
 
הערה : אם משתנה מסוים מוגדר מסוג enum בעל תת-תחום נתון ובתכנית נותנים לו ערך שאינו מתת-התחום המהדר אינו מודיע על שגיאה, וכמו כן זו אינה שגיאה בזמן ריצה. כלומר מנגנון ה- enum אינו נאכף על המתכנת בשפת C, אלא משמש כאמצעי עזר תכנוני ותיעודי.

הגדרת טיפוס חדש ע"י typedef

בנוסף לטיפוסים הקיימים בשפת C, קיימת אפשרות להגדיר טיפוסים חדשים ע"י ההוראה typedef.

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

 
          typedef enum { SUN=1, MON, TUE, WED, THU, FRI, SAT} Day;
 
          Day today, yesterday, tomorrow;
 

כעת Day הוא טיפוס עצמאי ולא רק שם תג.

דוגמא שימושית נוספת - הגדרת טיפוס בוליאני בעל 2 ערכים TRUE ו- FALSE :

 
          typedef enum { FALSE=0, TRUE=1 } Boolean;
          Boolean flag = FALSE;
 

שימוש שכיח בהגדרת טיפוסים חדשים בשפת C הוא לצורך כתיבת תוכנה עבור מחשבים שונים.

אחת הבעיות הקשות בהעברת תוכנה ממערכת אחת למערכת שנייה היא בהתייחסות לגדלי הטיפוסים.

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

אחת הדרכים להקל על מלאכת הסבת התוכנה היא להגדיר את הטיפוסים הבסיסיים מחדש ע"י typedef . דוגמא להגדרות כאלו:

 
typedef      char           CHAR;
typedef      int              INT;
typedef      unsigned           UNS;
 

ובתכנית עצמה משתמשים אך ורק בהגדרות אלו:

 
CHAR       c1;
INT           i;
 

כעת, אם נרצה להסב את התוכנה למחשב ומערכת הפעלה בהם הטיפוסים הבסיסיים שונים - למשל אם במערכת המקורית השלם הוא 32 סיביות ובמערכת החדשה הוא 16 סיביות, נבצע שינוי בהגדרת INT כך שגדלו לא ישתנה:

 
typedef      long                                  INT;
typedef      unsigned long  UNS;
 

וכעת אם long במערכת החדשה הוא 32 סיביות גדלו של INT יישאר זהה ביחס למערכת הישנה.

קבועים 

קבועים

קבועים הם שמות של ערכים שאינם משתנים לכל אורך התכנית. כפי שכבר ראינו, ניתן להגדיר קבועים ע"י שימוש במילה const

 
const  int MAX          = 28;
 

או ע"י הגדרתם בקדם-מעבד בהוראת #define:

 
#define   MAX             28
 

מהו ההבדל בין השניים? במקרה הראשון אנחנו מגדירים תא בזיכרון בשם MAX שערכו הוא 28 ושלא ניתן לשנותו. במקרה השני לא מוגדר תא בזיכרון: זוהי הוראה לקדם-מעבד העובר על התכנית ומחליף כל מופע של MAX במספר 28.

הבדל נוסף הקיים בין 2 הצורות הוא במרחב השם (namespace) של הקבוע: קבוע המוגדר ע"י הקדם מעבד מוכר בכל קובץ התכנית, החל מהמקום בו הוגדר. קבוע המוגדר ע"י const לעומת זאת הוא בעל מרחב שם מוגבל יותר, עפ"י מקום הגדרתו, כפי שנראה בפרק 6, "פונקציות".

ניתן להגדיר בשתי הצורות קבועים מטיפוסים שונים. דוגמאות:

 
#define      MIN                    -34.55
#define      STREET            "Ben Yehuda"
 
const char  STR[] = "hello";
const char  C = 'c';
const float  NUMBER = 34.88;
 
הערה : כמוסכמה, נהוג לציין שמות קבועים באותיות גדולות (capitals).

ליטרלים

ליטרלים הם ערכים המופיעים ישירות בקוד התכנית. הם יכולים להיות מטיפוסים שונים - שלם, ממשי, תווי או מחרוזת. טיפוס הליטרל נקבע ע"י המהדר עפ"י הערך.

לדוגמא, המספר 390 יובן ע"י המהדר כליטרל מטיפוס שלם, והערך 'f' יובן כליטרל מטיפוס תווי.

ליטרלים שלמים

ליטרלים שלמים הם מספרים שלמים הנכתבים ישירות בקוד התכנית. לדוגמא, בהוראות

 
          int x;
          x = 34;
 

המספר 34 הוא קבוע מספרי ממשפחת השלמים. מהו טיפוסו המדויק? הטיפוס, אם לא מצוין אחרת הוא int . במידה ורוצים לציין שהוא מסוג long יש להוסיף סיומת "l" או "L" למספר, לדוגמא:

 
          long x;
          x = 34L;
 

ניתן לציין בסיס שונה מהבסיס העשרוני עבור שלמים: קידומת של 0 (אפס) בראש המספר מציינת שהמספר נתון בבסיס אוקטלי (בסיס 8), קידומת 0x מציינת שהוא בבסיס הקסהדצימלי (בסיס 16).

לדוגמא, ההוראות הבאות שקולות - בכולן מוצב הערך 34 (דצימלי) ל- x:

 
int x;
x = 34;       /* decimal */
x = 042;     /* octal */
x = 0x22;   /* hexa */
 

ליטרלים ממשיים

ליטרלים ממשיים, בדומה לליטרלים שלמים, נכתבים ישירות בקוד התכנית. לדוגמא, בהוראות

 
float y;
y = 34.55;
 

המספר 34.55 הוא ליטרל מספרי ממשפחת הממשיים. מהו טיפוסו? אם לא צוין אחרת הטיפוס הוא double.

מכיוון שהליטרל מוצב למשתנה מסוג float מתבצע קיצוץ, ומהדרים מסוימים יציגו הודעות אזהרה.

ניתן לציין שטיפוס הליטרל הממשי הוא float ע"י הוספת סיומת f או F למספר, לדוגמא:

 
y = 34.55f;
 

במקרה זה לא תוצג הודעת אזהרה.

ליטרלים ממשיים ניתנים לכתיבה בצורה מעריכית ע"י ציון המעריך בתוספת האות e או E.

לדוגמא, המספר 34.55 ניתן לרישום כ- 3.455E1 או  3455E-2 או 0.3455E2 .

צורת רישום זו מתייחסת לחזקה של בסיס 10:

ערך

ייצוג הליטרל ב- C

3.455 * 10

3.455E1

3455 * 10-2

3455E-2

0.3455* 102         

0.3455E2

ליטרלים תוויים

ליטרלים תוויים מצוינים ע"י התו המיוצג ושני גרשים משני צידיו. לדוגמא, 'x' מציין את ערך ה- ASCII של התו x (טבלת ה- ASCII נתונה בנספח הספר).

ליטרלים תוויים שייכים למעשה למשפחת השלמים: הם משמשים בפעולות חשבוניות ולוגיות כמספרים שלמים לכל דבר כשתחום הערכים שלהם מוגבל (בית בודד).

קיימים ליטרלים תוויים מיוחדים המשמשים בעיקר בהדפסה. שניים מהתווים המיוחדים הכרנו בפרקים הקודמים: '\n' כתו שורה חדשה, ו- '\t' כטאב בפעולות הדפסה.

התו '\' מציין משמעות מיוחדת עבור תווים מסוימים. חלק מתווים אלו נתון בטבלה הבאה (הרשימה המלאה נמצאת בנספח):

 שם התו

שם מקוצר

ערך ה- ASCII

סימון בקוד

Newline

 NL (LF)

 10

 \n

Tab

 HT

 9

 \t

Backslash

 \

 92

 \\

Single quotation mark

 '

 39

 \'

Double quotation mark

 "

 34

 \"

Null character

 NUL

 0

 \0

לדוגמא, כדי להדפיס את השורות הבאות:

 
First    line
Second   line
Third    line
 

נכתוב:

 
printf("First\tline\nSecond\tline\nThird\tline");
 

ואם נרצה להדפיס את התו " (גרשיים) שערך ה- ASCII שלו הוא 34 נוכל לעשות זאת במספר דרכים:

 
putchar('\"');    /* as character with backslash*/
putchar(34);      /* direct decimal ASCII value */
putchar('\x22');         /* direct hexadecimal ASCII value */
putchar('\42'); /* direct octal ASCII value */
printf("\"");                 /* as part of a string, with backslash*/
 

תרגיל: כיצד יודפס התו '\' עצמו? הצע/י 5 דרכים.

אופרטורים

אופרטורים הם סימנים המוצבים ליד ובין נתונים, ומורים למהדר על ביצוע פעולה מסוימת. האופרטורים נחלקים למספר קבוצות:

    - אופרטורים חשבוניים
    - אופרטורים לוגיים
    - אופרטורי סיביות
    - אופרטורי הצבה

אופרטורים חשבוניים

האופרטורים החשבוניים פועלים על טיפוסים מספריים שלמים או ממשיים:

אופרטור

משמעות

+

חיבור 

-

חיסור

*

כפל

/

חילוק

++

קידום משתנה ב- 1

--

חיסור משתנה ב- 1

%

שארית החלוקה (מודולו) - בשלמים בלבד

    

האופרטורים מופעלים עפ"י טיפוסי האופרנדים - שלמים או ממשיים. לדוגמא:

 
          int    i;
          float f;
          
          i = 4 / 5;             /* i=0 */
          f = 4 / 5;             /* f=0.0 */
          f = 4.0 / 5.0;      /* f=0.8 */
 

כלומר, תוצאת פעולת החילוק שונה בין שלמים לממשיים: 4/5 הוא חלוקת שלמים שתוצאתה השלם 0. לעומת זאת 4.0/5.0 היא חלוקת ממשיים שתוצאתה ממשית - 0.8.

אופרטורי קידום וחיסור: "++" ו- "--"

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

  -  אם האופרטור  נמצא מימין למשתנה (postfix) , לדוגמא:
 
int  i = 9;
int j =  i ++;       
                   /* è j=9, i=10 */
 
    אז הדבר שמתבצע הוא שימוש בערך של המשתנה בביטוי ולאחר מכן קידום ב- 1 .
  - לעומת זאת, אם האופרטור נמצא משמאל למשתנה (prefix) ,  לדוגמא:
 
int  i = 9;
int j =  ++ i ;
                   /* è j=10, i=10 */
 
    מתבצעת הוספה של 1 ל - i,  ולאחר מכן הצבה ל- j.
הערה : כאשר הביטוי בו נמצא אופרטור קידום/חיסור הוא משפט עצמאי ולא כחלק מביטוי אחר אין הבדל בין שני האופנים.

תכנית דוגמא:

 
#include <stdio.h>
void main ( )
{
          int    i = 5;                    
          int    j = 0;
 
          printf("%d", i++);        /* output: 5, i = 6    */
          printf("%d", --j);          /* output: -1,            j = -1   */
          printf("%d", j = i++); /* output: 6,  j = 6    i=7 */
}
 

אופרטור שארית החלוקה %

האופרטור % פועל רק על טיפוסים שלמים, ומבצע פעולת מודולו, כלומר, הוא מחזיר את השארית של תוצאת החלוקה. לדוגמא:

 
          int    s;
          
          s  =  10 % 3;       /* s = 1 */
          s  =  8 % 8;         /* s = 0 */
          s  =  8 % 0;         /* Error! */
          s  =  8 % 9;         /* s = 8 */ 
          s  = -8 % 9;         /* s = -8 */ 
          s  =  8 % -9;        /* s = 8 */
 

אופרטורים וביטויים לוגיים

אופרטורי היחס מרכיבים ביטויים לוגיים שתוצאתם היא אמת או שקר:

אופרטור

משמעות

x == y           

x שווה ל- y

x != y           

x שונה מ- y

x > y

x גדול מ- y

x >= y         

x גדול מ- y או שווה לו

x < y 

x קטן מ- y

x <= y           

x קטן מ- y או שווה לו

    

ניתן להרכיב ביטויים ממספר ביטויים בסיסיים ע"י האופרטורים הלוגיים הבאים:

אופרטור

משמעות

&&

            וגם

| |

            או

!

            היפוך

דוגמאות:

::    x >= y ||  w == z ::   - x גדול או שווה ל- y או w  שווה ל-z
::   x == y &&  y == z ::   - x שווה ל- y וגם y שווה ל- z
::   x == y &&  !(y == z) ::   - x שווה ל- y וגם y שונה מ- z
    

הביטויים הלוגיים מופיעים בדרך כלל כחלק ממשפטי תנאי, לדוגמא:

 
if(x > y)
          printf("x is bigger than y");
else
          if(y > x)
                   printf("y is bigger than x");
          
          else
                   printf("x and y are equals");
 

טיפוס שלם כתחליף לטיפוס בוליאני

תוצאת ביטוי לוגי יכולה להיות אמת (true) או שקר (false). בשפות מסוימות קיים טיפוס בוליאני שאלו שני הערכים היחידים שהוא מקבל. בשפת C לא קיים טיפוס כזה - טיפוס השלם משמש לצורך כך.

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

 
          int              i;
          int              x=5, y=5;
 
          i = (x > y); /* i = 0 */
          i = (x == y);        /* i = 1 */
 

וכן ניתן להשתמש בתוצאת הביטוי במשפט תנאי:

 
          i = (x == y);
          if(i)
                   printf("x is equal to y");
 

מכיוון ששלם מייצג גם טיפוס בוליאני ניתן גם להשתמש במשתנים שלמים בתנאי הלולאה. לדוגמא:

 
#include <stdio.h>
void main ( )
{
          int    i = 4;
          while(i) 
          {
                   i--;
                   printf("i = %d\t",i);
          }
}
 

הפלט:

 
i = 3   i = 2   i = 1   i = 0
 

משפט תנאי מקוצר

האופרטור "?:" הוא אופרטור טרינרי - כלומר בעל שלושה אופרנדים - המשמש במקרים מסויימים כקיצור למשפט if-else. לדוגמא, המשפט

 
if(x > y)
          max = x;
else
          max = y;
 

יכול להיכתב בקיצור ע"י

 
max = x > y ? x : y;
 

תחביר האופרטור "?:" :

<ביטוי-לוגי> ? <ביטוי 1> : <ביטוי 2>

הביטוי הלוגי שמופיע לפני הסימן "?" הוא ביטוי שתוצאתו "אמת" או "שקר". אם התוצאה היא "אמת", תוצאת המשפט היא <ביטוי 1>, אחרת תוצאתו היא <ביטוי 2>.

ניתן להשתמש בביטוי זה באופן מקוצר כחלק מהוראות אחרות. לדוגמא, אם נרצה להדפיס את המקסימום מבין 2 המשתנים שלעיל מבלי להשתמש במשתנה הנוסף max נוכל לכתוב:

 
printf("The maximum is = %d", x > y ? x : y);
 

אופרטורים הפועלים על סיביות (bitwise operators)

בשפת C קיימים אופרטורים הפועלים על סיביות של טיפוסים שלמים (שלם, שלם קצר/ארוך, תו). קבוצה זו כוללת 6 אופרטורים:

אופרטור

משמעות

&

AND

|

OR

^

XOR

~

NOT, משלים ל- 1 (one's complement)

>>

הזזה שמאלה

<<

הזזה ימינה

    

ארבעת האופרטורים הראשונים מבצעים פעולות לוגיות בינריות על סיביות. שני האופרטורים האחרונים מבצעים הזזה של הסיביות במשתנים.

פעולות לוגיות על סיביות

בפעולות לוגיות בין סיביות מתקיימים הכללים הבאים:

    פעולת AND :
 
1       &      1       =       1
1       &      0       =       0
0       &      1       =       0
0       &      0       =       0
 
    פעולת OR:
 
1       |       1       =       1
1       |       0       =       1
0       |       1       =       1
0       |       0       =       0
 
    פעולת XOR:
 
1       ^       1       =       0
1       ^       0       =       1
0       ^       1       =       1
0       ^       0       =       0
 
    פעולת NOT:
 
~1              =       0
~0              =       1
 

נניח שנתון השלם x בבסיס 16:

int x = 0x52;

במערכת שבה השלם הוא מגודל 4 בתים, כלומר 32 סיביות, המשתנה ייראה כך בזיכרון:

x =

0              0

0              0

0              0

2              5

0000       0000

0000       0000

0000       0000

0010       0101

השורה התחתונה מייצגת את הסיביות של המספר, ובשורה העליונה מוצגים הערכים בבתים בייצוג הקסה. נגדיר משתנה נוסף:

int y = 0x8472;

y ייראה כך בזיכרון:

y =

0              0

0              0

4              8

2              7

0000       0000

0000       0000

0100       1000

0010       0111



פעולת AND בין שני המשתנים מסומנת כ-  x & y. תוצאתה היא משתנה שלם שבו כל סיבית היא תוצאת הפעולה AND הבינרית בין שתי הסיביות המתאימות ב- x וב- y. לדוגמא, אם נבצע

int z = x & y;

מה יהיה ערכו של z ?

x =

0              0

0              0

0              0

2              5

0000       0000

00000    0000

0000       0000

0010       0101

                                                &      

y =

0              0

0              0

4              8

2              7

0000       0000

0000       0000

0100       1000

0010       0111

                                                =

z =

0              0

0              0

0              0

2              5

0000       0000

0000       0000

0000       0000

0010       0101

כלומר z = 0x52.

באופן דומה:

    |  מבצע פעולת OR בינרי,
    ^ מבצע XOR בינרי,
    ~ מבצע פעולת NOT בינרית בשיטת המשלים ל- 1.

דוגמאות נוספות מובאות בעמ' 74.

פעולות הזזה

פעולות ההזזה גורמות להזזת כל סיביות הנתון מספר מקומות ימינה או שמאלה תוך מילוי המקומות החדשים ב- 0 או ב- 1. 

האופרטור >> מבצע הזזה שמאלה, והאופרטור << מבצע הזזה ימינה. צורת הסימון:

    x >> 2         x מוזז שני מקומות ימינה
    x << 2                   x מוזז שני מקומות שמאלה

לדוגמא, אם x הוא כמו קודם:

x =

0              0

0              0

0              0

2              5

0000       0000

0000       0000

0000       0000

0010       0101

אז תוצאת הפעולה x << 2 היא

 x << 2

0              0

0              0

1              0

8              4

0000       0000

0000       0000

0001       0000

   0100      1000

כלומר התוצאה היא 0x148. פעולה זו זהה להכפלת x  ב- 4.

כאשר הערך מוזז ימינה, המקומות משמאל ממולאים ב- 0 עבור מספרים חיוביים וב- 1 עבור מספרים שליליים.

לדוגמא, אם ערכו של x הוא -4 , הוא מיוצג במחשב בשיטת המשלים ל -2 ע"י:

x =

F              F

F              F

F              F

C             F

1111       1111

1111       1111

1111       1111

1100       1111

תוצאת הפעולה x >> 2 היא

x>>2

F              F

F              F

F              F

F              F

1111       1111

1111       1111

1111       1111

1111       1111

כלומר, ערכו יהיה -1 . פעולה זו זהה לחילוק x ב- 4.

 דוגמאות נוספות מובאות בעמ' 75.

אופרטורי הצבה 

האופרטור "=" הוא אופרטור ההצבה בשפת C. לדוגמא:

 
          int x;
          x = 5;
 

הערך 5 מוצב למשתנה x. ניתן גם להציב ערך למשתנה תוך כדי הגדרתו, לדוגמא:

 
int  x=5;
double  y=6.2,  z=1.45E10;
 

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

 
1. printf("%d", x = y);
 
2. z = x = y;
 
3. w = z = x = y;
 

יש להבחין בין אופרטור ההצבה "=" לבין אופרטור השוויון "==". ההוראה

 
          x = y;
 

היא הוראת הצבה של הערך של y למשתנה x. לעומת זאת, ההוראה

 
          x == y
 

היא ביטוי לוגי שתוצאתו "אמת" או "שקר", עפ"י ערכי המשתנים, שמשמשת בדרך כלל במשפטי תנאי (if) או בלולאות.

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

 
int x=5, y=2;
if(x=y)
          printf("x and y are equal");
 

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

 
if(2)
          printf("x and y are equal");
 

ותודפס הודעת השוויון!

ביטויי הצבה מקוצרים

ניתן לכתוב ביטויי הצבה באופן מקוצר: למשל את הביטוי

 
          x  =  x + 5;
 

ניתן לכתוב כך:

 
          x += 5;
 

קיצור זה אפשרי עבור כל האופרטורים הבאים:

+

-

*

/

%

<<

>>

&

^

|

חמשת האופרטורים משמאל הם האופרטורים החשבוניים, וחמשת האופרטורים מימין הם אופרטורים הפועלים על סיביות. דוגמאות:

 
          int x=2, y=8, z=0;
 
          x += 3;       /* x = 5 */
          z -= x+y;    /* z = -13 */
          z /= x;       /* z = -13/5 = -2 */
          z %= 8;      /* z = -2 % 8 = -2 */
          z <<= 2;    /* z = -8 */
          z &= 0;      /* z = 0 */
 

שים/י לב: ההוראה

 
          x = - 3;
 

אינה הצבה מקוצרת אלא הצבה של הערך -3 למשתנה x. בהצבה מקוצרת מופיע האופרטור משמאל לפעולת ההצבה:

 
          x -= 3;
 

האופרטורים ב- C עפ"י קדימויות

הטבלה שבעמ' 78 מציגה את האופרטורים ב- C עפ"י קדימויות.

אופרטורים בשורה נתונה בטבלה הם בעלי קדימות גבוהה מאופרטורים בשורה נמוכה יותר. לדוגמא, הביטוי

 
          3 + 4 * 5
 

יחושב ע"י הכפלת 4 ב- 5 ואח"כ חיבור ל- 3. הביטוי שקול ל-

 
          3 + (4 * 5)
 

הסוגריים ( ) הם בעלי הקדימות הגבוהה ביותר בטבלה. לכן, אם רוצים לתת קדימות לביטוי מסויים, ניתן להקיפו בסוגריים. לדוגמא:

 
          (3 + 4) * 5
 

העמודה השמאלית קובעת את כוון הפעלת האופרטור. לדוגמא, הביטוי

 
          x1 / x2  / x3
 

יחושב ע"י חילוק x1 ב- x2, ואחר כך חילוק התוצאה ב- x3. כלומר, אם נשתמש בסוגריים לציון הקדימות, הביטוי השקול הוא

 
          (x1 / x2) / x3
 

ולא

 
          x1 / (x2 / x3)
 

זאת מכיוון שלאופרטור / סדר ביצוע משמאל לימין. לעומת זאת, הביטוי

 
          x1 = x2 = x3 + 1;
 

יחושב ע"י הצבת (x3+1) ל- x2, ואחר כך הצבת התוצאה ל- x1. זאת מכיוון שלאופרטור ההצבה "=" סדר ביצוע מימין לשמאל.

דוגמאות:

 
#include <stdio.h>
void main ( )
{
          int    i = 2,  j=6, k=10;
          int    s;
 
          s = i + j * k;                 /* s = 62 */
 
          s = (i + j)* k;      /* s = 80 */
 
          s = k / i * j;                 /* s = 30 */
 
          s = j / i++ ;                  /* s = 3  */
}
 

בביטוי האחרון, לאופרטור ++ יש עדיפות על פני אופרטור החילוק /, אולם מכיוון שהוא בצורת postfix הוא מבוצע רק לאחר חישוב הביטוי כולו.

המרת טיפוסים

המרה מרומזת

ניתן לכתוב ביטויים בהם מעורבים משתנים מטיפוסים שונים - במקרה זה מבוצעת המרה אוטומטית לטיפוס המתאים. בדרך כלל נקבל אזהרה מהמהדר על ביצוע המרה כזו.

לדוגמא, המרה מרומזת של מספר ממשי לשלם גורמת לקיצוץ חלק השבר ולאיבוד מידע, ולכן גוררת הודעת אזהרה מהמהדר:

 
          int    i;
          float f=14.5;
 
          i = f;          /* i = 14 */
 

בהמרת שלם לממשי אין איבוד מידע:

 
          int    i=14;
          float f;
 
          f = i;          /* f = 14.0 */
 

בנוסף לפעולת הצבה, המרה מרומזת יכולה להתרחש במהלך ביטויים חשבוניים:

 
          float f;
          
          f = 4 / 5;   /* f=0.0 */
          f = 4.0 / 5;         /* f=0.8 */
 

הסבר: בביטוי הראשון מבוצעת פעולת חילוק שלמים שתוצאתה היא 0. רק לאחר מכן התוצאה מומרת לממשי  0.0. כלומר ההמרה מבוצעת באופרטור ההצבה.

בביטוי השני, אופרטור החילוק / נדרש לבצע חילוק של ממשי בשלם - לצורך כך השלם מומר תחילה לממשי ורק אז מתבצעת פעולת החילוק.

ככלל, בביטוי מעורב משתנה מטיפוס "נמוך" יותר מומר לטיפוס ה"גבוה". טבלת הקדימויות לצורך המרה:

קדימות

טיפוס

1

long double

2

double

3

float

4

long

5

int

6

short

7

char

כיצד מומר משתנה מטיפוס נמוך לטיפוס גבוה? הטבלה הבאה מתארת מה מתבצע בפועל בהמרה:

מ-

ל-

אופן ההמרה

int

char

מודולו 256 (התייחסות לבית הנמוך)

float / double

char

קיצוץ השבר, מודולו 256 

float­ / double

int

קיצוץ השבר, מודולו גודל int

double

float­

חצי של הספרות המשמעותיות

המרה מפורשת (casting)

לפעמים נדרשת המרה מפורשת בתכנית כדי לאלץ פרשנות מסוימת של הביטוי החשבוני. לדוגמא, בתכנית הבאה קיימת בעיה:

 
float f;
int i = 5, j=12;
f =  j / i;                  /* f = 2.0 */
 

כדי לאלץ פעולת חילוק בממשיים נכתוב:

 
          float f;
          int i = 5, j=12;
          f =     j / (float) i;       /* f = 2.4 */
 

פעולת המרה יזומה בין טיפוסים מוגדרת כך:

 ביטוי  (טיפוס)

תוצאת הביטוי תומר לטיפוס המצוין.

האופרטור sizeof 

האופרטור sizeof הוא אופרטור אונרי המחזיר את הגודל בבתים (bytes) של ביטוי, טיפוס או משתנה. אופן השימוש:

1)      sizeof(ביטוי)

2)      sizeof(טיפוס)

3)      sizeof(משתנה)

אופרטור זה תמיד מחזיר שלם חיובי. לדוגמא, תכנית להדפסת הגדלים של מספר טיפוסים בסיסיים ב- C:

 
#include <stdio.h>
void main ( )
{
          printf("the size of integer is %d\n", sizeof(int));
          printf("the size of short is %d\n", sizeof(short));
          printf("the size of long is %d\n", sizeof(long));
          printf("the size of double is %d\n", sizeof(double));
}
 

פלט התכנית, כפי שהתקבל במערכת הפעלה Windows על מחשב Intel-Pentium :

 
the size of integer is 4
the size of short is 2
the size of long is 4
the size of double is 8
 

סיכום

  • מזהים ( Identifiers) הם שמות בתכנית המייצגים משתנים, קבועים, טיפוסים, פונקציות ותוויות בתוכנית.
  • הערות (Comments) הן מלל שהמהדר מתעלם ממנו ובדרך כלל משמשות להסברים על התוכנית או על פקודות מסוימות. הערה מתחילה בצמד התווים */  ומסתיימת בצמד התווים /*.
  • מילים שמורות (keywords) הן מילים בשימושה של השפה. אסור לתת שמות מזהים הזהים למלים אלה.
  • הטיפוסים (types) מציינים את סוג הנתונים בתכנית. הטיפוסים נחלקים ל- 3 קבוצות עיקריות: שלמים, תוויים וממשיים. ניתן בנוסף להגדיר טיפוסים חדשים ע"י שימוש בהוראה typedef. enum משמש להגדרת תת-תחום של מספרים שלמים.
  • קבועים הם שמות המייצגים ערכים המופיעים בקוד התכנית. ניתן להגדיר שמות של קבועים ע"י המהדר תוך שימוש בהוראת const , או ע"י הקדם-מעבד בהוראת #define.            
  • ליטרלים הם ערכים הנכתבים ישירות בקוד ויכולים להיות טיפוסים שונים: שלמים, ממשיים, תוויים ותתי-סוגים שלהם.
  - ליטרלים שלמים יכולים להיות בבסיס דצימלי, אוקטלי או הקסהדצימלי
  - ליטרלים ממשיים יכולים להיכתב בצורה עשרונית או מעריכית.
  - ליטרלים תוויים מצוינים ע"י התו ושני גרשים משני צידיו. תווים מיוחדים מצוינים בצירוף התו  \ , לדוגמא, '\n', '\b', '\\'.
  • אופרטורים הם סימנים המוצבים ליד ובין נתונים ומורים למהדר על ביצוע פעולה מסוימת. האופרטורים נחלקים למספר קבוצות: חשבוניים, לוגיים, הצבה, הצבה+חשבוניים, אופרטורי סיביות (bitwise operators) ואחרים (המרה, sizeof). לאופרטורים יש עדיפויות שונות  - סדר הפעלתם בביטוי תלוי בעדיפות.
  • המרת טיפוסים של משתנים מתרחשת בשני מקרים: באופן מרומז (implicit) בכל ביטוי בו משולבים נתונים מטיפוסים שונים, או במפורש (explicit) ע"י אופרטור ההמרה (type). המרה מפורשת נקראת גם casting.

תרגילי סיכום

בצע/י את תר' 1-3 שבעמ' 83-84.



[ <<< הקודם ] [ תוכן עניינים ] [ הבא >>> ]