בס"ד

מאת: יצחק שליסל

מדריך זה מיועד לכל מי שרוצה ללמוד שפת C. הוא מתחיל מהיסודות ואינו מצריך ידע קודם. כולו מובא בעמוד אחד, על מנת לחסוך ממך את הצורך בדפדוף מיותר וכן כדי לקבל אינדקציה כמה מהחומר כבר למדת. בסוף כל נושא מובאים שאלות ופתרונות. היחודיות של המדריך היא היכולת לערוך ו'לשחק' עם הקוד וכן להריצו, כדי להבין כיצד משפיעים השינויים שעשית. אשמח לקבל פידבקים והערות במייל yitshaksc@ariel.ac.il.

תודות:
על עורך הקוד ל ace.c9.io
על הסקריפטים ל jQuery.com
על הפונט ל fonts.google.com
על הקומפיילר ל rextester.com

הקדמה

תכנית מחשב הינה אוסף של פקודות המכונה בשם "קוד" ומיועד לביצוע ע"י המחשב. קל יותר לכתנת ב"שפת תכנות עילית" (high-level programming language) משום שהיא משתמשת בניסוח ידידותי הדומה לשפת בני האדם. אם זאת, היא צריכה לעבור תהליך שבו מהדר (compiler) או מפרש (interpreter) יתרגם אותה לשפה שהמחשב מבין - שפת מכונה (machine language). "שפת תכנות נמוכה" (low-level programming language) לא צריכה את התרגום הזה, לכן הביצועים שלה טובים יותר. צריכת הזיכרון נמוכה יותר ומהירות הריצה גבוהה יותר, אך היא אינה ידידותית ולכן התכנות בה פחות נפוץ.

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

1. נכתוב הערות עם הסברים קצרים.
2. נבחר שמות הגיוניים למשתנים ולפונקציות.
3. נדחוף פנימה ב TAB כל קטע קוד המתבצע בתוך בלוק (תנאי, לולאה, פונקציה וכדו').
4. נשאיר רווח לפני ואחרי כל אופרטור, למעט "פסיק" ו"נקודה פסיק" שלא אמור לבוא לפניהם רווח.

הערה

משמשת להסבר עבור האדם שקורא את הקוד. המחשב מתעלם ממנה.
האופרטור // הופך את כל מה שכתוב אחריו עד סוף אותה השורה להערה.
האופרטור */ הופך את כל מה שכתוב אחריו עד האופרטור /* להערה. בדרך זו ניתן לכתוב הערה בתוך שורת קוד או לפרוש הערה על פני מספר שורות.

פונקציה

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

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

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

פונקציה לא תבוצע אם לא היתה קריאה אליה.

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

include

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

לדוגא, הפונקציה printf מקבלת ערך של מחרוזת ומציגה אותו על המסך. היא אינה חלק מהשפה, אלא כתובה בספריה stdio.h (קיצור של STandarD Input Output).

כדי לקשר את הספריה לתכנית שלנו נשתמש בפקודה include.

כעת נכתוב את הפונקציה main:

במקרה הזה, לא חשוב לנו ש main תחזיר ערך, לכן היינו יכולים לכתוב לפניה void, אך כדי שהתכנית שלנו תעבוד בכל הקומפיילרים, נצמד לתקן, שמחייב לפני main לכתוב int. הטיפוס int הוא עבור ערך של מספר שלם. בהמשך נרחיב על הטיפוסים השונים.
כמו כן, התקן מאפשר לנו ש main גם תקבל ערכים באופן הבא: (int main (int argc, char **argv, אך זאת הוא אינו מחייב ולכן בשלב זה נשאיר את הסוגריים ריקות.
מכיוון שהפונקציה חייבת להחזיר ערך שלם, נחזיר את הערך 0 ע"י הפקודה return.
מכיוון שכאשר הפקודה return מופעלת מתבצעת יציאה מהפונקציה, כל מה שאנו רוצים לבצע בפונקציה נכתוב לפני ה return.
מה שנרצה לבצע זה, לקרוא לפונקציה printf ולשלוח לה את המחרוזת Hello World כדי שהיא תוצג על המסך. מחרוזת יש להגדיר בתוך גרשיים.

זיכרון, טיפוס ומשתנה

נניח שאנו מפתחים משחק מחשב ורוצים לשמור את כמות פסילות השחקן להן הוא זכאי בטרם יסתיים המשחק. אם השחקן יקבל בונוס, נעלה אותן ואם אם הוא יבצע מהלך אסור, נקטין אותן. אין טעם לשמור נתון כל כך זמני בקובץ. הנתון הזה משתנה ולכן נשמור אותו במה שמכנים "משתנה". כשאנו יוצרים משתנה, למעשה אנו פונים למחשב בבקשה לתת לנו כתובת של זיכרון שבה נוכל לאחסן נתון שחשוב לנו ושאנו רוצים מידי פעם לצפות בו או לבצע בו שינויים. אנחנו לא יכולים לבקש את הכתובת שמתחשק לנו, כי אם היינו יכולים, גם תכנית אחרת היתה יכולה לבקש את אותה הכתובת, לצפות בנתון שלנו, לבצע בו שינויים, או להציב במקומו נתון משלה, כך שהנתון שלנו לא היה מוגן. לכן קיים במחשב מנגנון, שאחראי על ניהול הכתובות והוא המחליט איזו כתובת תקבל איזו תכנית. כמובן שהוא פועל על פי אלגוריתם כל שהוא, אך מבחינתנו, הכתובת שנקבל היא אקראית, לכן הזיכרון הזה מכונה: Random Access Memory ובקיצור RAM. זיכרון המחשב הוא משאב מוגבל המבוקש ע"י כל התכניות הפועלות ברקע. ככל שהוא מתמלא, פעילות המחשב מואטת עד שלבסוף המחשב נתקע. לכן המנגנון הזה תוכנן כך שברוב המקרים הוא ידע לזהות שאנו כבר לא זקוקים לזיכרון, ולשחרר אותו לטובת תכניות אחרות. הוא לא ינקה את הנתונים, אלא רק יתן את הכתובת לתכניות אחרות שיבקשו מקום בזיכרון. לכן, אם מדובר בנתונים חשאיים, כמו מספר כרטיס אשראי לדוגמא, נדאג לשנות את הערך שבאותו הזיכרון למשהו אחר, למשל להציב בו את המספר 0, או במילים אחרות "לאפס" את הזיכרון. לעיתים, כשנבקש זיכרון, נקבל כזה שתכנית אחרת עשתה בו שימוש לפנינו ולא דאגה "לנקות" אחריה את הנתון. נוהגים לכנות נתון זה בשם "זבל". במקרים מסויימים, כשלא נזדקק לזיכרון שקיבלנו, המנגון לא יוכל לזהות זאת ולכן נאלץ לשחרר את הזיכרון בעצמנו. בהפעלת המחשב מחדש, כל ה RAM מתאפס ומשתחרר, לכן נוהגים לכנותו גם "זיכרון נדיף".

אנו רגילים לספור בבסיס בעל 10 ספרות (0 עד 9). נניח שאנו סופרים במספרים חיוביים שלמים. הספרה האחרונה היא 9, ולכן כשנרצה להמשיך הלאה, נשתמש ב 2 ספרות. נהפוך אותה ל 0 ונשים משמאלה 1 (כלומר 10). שוב נגדיל אותה כל פעם ב 1 ושוב כשהיא תגיע ל 9 נהפוך אותה ל 0 ונגדיל את ה 1 משמאלה ל 2 (כלומר 20). נמשיך בתהליך עד שהספרה משמאלה תגיע ל 9 (כלומר 99) ואז נעבור ל 3 ספרות וכן הלאה. לו היו לנו רק 2 ספרות, היינו סופרים כך: 0, 1, 10, 11, 100, 101, 110, 111, 1000 וכן הלאה... זיכרון ה RAM מורכב מיחידות. באנגלית מכנים כל יחידה כזו בשם binary digit ובקיצור Bit. בעברית מכנים אותה "ספרה בינארת" ובקיצור סיבית. סיבית כבויה נחשבת 0 ויחידה שזורם בה חשמל נחשבת 1. ברוב המחשבים, קבוצה המורכבת מ 8 סיביות נקראת "Byte". בעזרת רצף של סיביות ניתן לבטא מספר בבסיס 2. בסיס בעל 2 ספרות מכונה "בינארי". בסיס בעל 10 ספרות מכונה "דצימלי" ובעברית "עשרוני". למרות שכתובות מכילות ערכים בינאריים, נהוג לייצג את הכתובות עצמן בבסיס בעל 16 ספרות (מ 0 עד 9 ומ A עד F) המכונה "הקסדצימלי". קיימות נוסחאות המאפשרות בקלות המרה של מספר כלשהו מבסיס כלשהו לבסיס כלשהו.

כשאנו מעוניינים להגדיר משתנה, מובן שלא נוכל לקבל את כל זיכרון ה RAM, אלא נצטרך לבחור בגודל המתאים לנו ביותר. לא נוכל לבחור כל כמות סיביות מדויקת שנרצה, אלא נבחר מתוך טבלה הבאה את הטיפוס שהכי מתאים לצרכנו.
טיפוס גודל ב Byte טווח ערכים מינימלי
תלוי בגודל ה Byte שלרוב מורכב מ 8 סיביות (Bit)
פורמט
char 1
טיפוס זה בגודל יחידת הזיכרון הבסיסית של המחשב.
ניתן לייצג את הערך שהיא יכולה להכיל
ע"י תו (character) של ASCII או ע"י מספר שלם (integer).
מ 128- עד 127 c%
signed char 1 מ 128- עד 127 ייצוג תווי c%
ייצוג מספרי hhi%
unsigned char 1 מ 0 עד 255 ייצוג תווי: c%
ייצוג מספרי: hhu%
short
short int
signed short
signed short int
2 מ 32768− עד 32767 hi%
unsigned short
unsigned short int
2 מ 0 עד 65535 hu%
int
signed
signed int
2 מ 32768− עד 32767 d% או i%
unsigned
unsigned int
2 מ 0 עד 65535 u%
long
long int
signed long
signed long int
4 ‎מ -2147483648 עד 2147483647‎ li%
unsigned long
unsigned long int
4 מ 0 עד 4294967295 lu%
long long
long long int
signed long long
signed long long int
8 מ 9223372036854775808- עד 9223372036854775807 lli%
unsigned long long
unsigned long long int
8 מ 0 עד 18446744073709551615 llu%
float התקן לא מגדיר התקן לא מגדיר ייצוג רגיל: %f או F%
ייצוג ע"י מנטיסה ואקספוננט: a% או A% או e% או E% או g% או G%
double התקן לא מגדיר התקן לא מגדיר ייצוג רגיל: %lf או lF%
ייצוג ע"י מנטיסה ואקספוננט: la% או lA% או le% או lE% או lg% או lG%
long double התקן לא מגדיר התקן לא מגדיר ייצוג רגיל: %Lf או LF%
ייצוג ע"י מנטיסה ואקספוננט: La% או LA% או Le% או LE% או Lg% או LG%

אל דאגה, לא צריך לזכור את כל סוגי הטיפוסים, אלא רק לדעת שהם קיימים ובמקרה הצורך להעזר בטבלה. רוב הזמן נשתמש ב char עבור תוים, int עבור מספרים שלמים ו double עבור מספרים עשרוניים. כדי לקלוט אליהם ערכים או כדי להציג את הערכים שבתוכם נשתמש בפורמט המובא בעמודה השמאלית שבטבלה. לדוגמא:
שימו ❤
1. בשפת C יש ליצור את כל המשתנים בהם אנו מעוניינים בראש הפונקציה (בניגוד לשפת ++C שבה ניתן ליצור גם בהמשך הפונקציה).
2. כפי שראינו קודם וכפי שנראה בהמשך, מחרוזת יש להגדיר בתוך שני גרשיים.
3. כפי שניתן לראות בתכנית הנ"ל, תו בודד יש להגדיר בתוך שני גרשים. (את הערך שהצבנו ב character הגדרנו בין שני גרשים). זה בכדי להראות שהתכוונו להציב תו ולא ערך מספרי. לכל תו אסקי יש גם ערך מספרי. התכנית הבאה מציגה את ההבדל:

Casting

אם נרצה לראות את ערך ה ASCII של התו 8 נעשה זאת ע"י פעולת המרה שנקראת "Casting":
כעת, שאנו יועים כי ערך ה ASCII של התו 8 הוא 56, נוכל להציב את התו 8 בצורה ישירה או ב Casting:
אם ננסה להציב ערך של int ל double בחלק מהקומפיילרים נקבל הודעת אזהרה, ובעיקר אם ננסה להציב ערך של double ל int, משום שאז ה int לא יקבל את כל מה שמשמאל לנקודה העשרונית. אם אנחנו יודעים שזו אינה טעות ונרצה לבטל את האזהרה, נוכל לעשות זאת גם כן בעזרת Casting:

צריך לפניכן הסבר על תנאי

הסתכלו בקוד הבא. מה לדעתכם יהיה הפלט?
מדוע הפלט הראה שהביטוי 0.2 - 0.3 אינו שווה ל 0.1? משום שהמחשב מוגבל בביטים ואינו יכול לשמור במדויק את כל הערכים העשרוניים, לכן הוא שומר ערכים דומים אליהם בקירוב. אם נרצה להתבונן בערכים שהוא שומר, נוסיף 17 ספרות לאחר הנקודה, ע"י הוספת "17." בין ה "%" ל "lf":

עד כאן הסיכום מסודר



https://he.wikibooks.org/wiki/C%2B%2B/משתנים
https://en.wikipedia.org/wiki/C_data_types


נתבונן לדוגמא בטיפוס הראשון. char מורכב מ 1 בית, כלומר 8 סיביות, שכל אחת מהן יכולה להיות 2 אפשרויות (0 או 1). כלומר יש 256 = 28 אפשרויות. לכן הטווח שלו יכול להכיל 256 מספרים שלמים. הסיבית הראשונה משמשת לסמן האם המספר שלילי או חיובי, נשארו עוד 7 סיביות. 128 = 28. נחלק את ה 256 כך שחצי יהיו בשליליים, חצי בחיוביים וה 0 באמצע. נקבל שהמספר השלילי הנמוך ביותר הוא 127- והמספר החיובי הגבוה ביותר הוא 128.


לאחר מכן, ניתן שם למשתנה שלנו (השם לא יכול להיות מילה של שפת התכנות איתה אנו עובדים, לא יכול להכיל רווחים וחייב להתחיל באות. כמו כן, לא יכול להיות שם זהה לשם של משתנה שכבר קיים באותו הבלוק). השם הזה, יאפשר לנו לפנות לנתון שנציב בכתובת הזיכרון, מבלי שנצטרך לזכור מהי הכתובת. לכן כדאי לנו לתת שמות בעלי היגיון. דוגמא:
נתבונן בקוד שבדוגמא הנ"ל. כדי להציב או להציג תו בודד, נשתמש בגרשים. כדי להציב או להציג מחרוזת, נשתמש בגרשיים. כדי להציב ערך מספרי נכתוב אותו ללא גרשים וללא גרשיים. כדי להציג ערך שבתוך משתנה, נכתוב את המשתנה ללא גרשים וללא גרשיים. int משמש ליצירת משתנה המכיל מספרים שלמים. גדלו של int הוא 4 byte, כלומר 32 bit, כלומר 32 ספרות בינאריות, כלומר מספר בינארי בין 00000000000000000000000000000000 ל 11111111111111111111111111111111. כדי שנוכל לבטא מספרים שליליים, נשתמש בסיבית הראשונה לסמן 0 אם המספר שלילי או 1 אם המספר חיובי ונשאיר למספר עצמור את ה 31 ביטים הנותרים. במהמרה לעשרוני נקבל מספר בין 2^31- ל 2^31, כלומר -2147483648 ל 2147483647. כלומר מספר עשרוני בין או להציג ערך שבתוך משתנה נכתובג ערך שבתוך משתנה נכתוב כדי להציג מחרוזת נשתמש בגרשיים. כדי להציג ערך שבתוך משתנה נכתוב את שם המשתנה מחוץ לגרשיים. נריץ ונראה כי & לפני משתנה מחזיר את
חלון הסבר לא מוסתר ולא ניתן להרצה

חלון הסבר לא מוסתר וכן ניתן להרצה

3. כתבו את הפונקציה (void triangle3 (int num הפועלת על פי אותו העיקרון, כשלדוגמא צורת המשולש עבור הקלט 3 הינה כזו:
  *
 ***
*****

4. כתבו את הפונקציה (void circle (double height, double width, double x, double y, double r, double thick המקבלת גובה ורוחב של דף ציור, שיעורי נקודה של מרכז המעגל, רדיוס ועובי ומציגה בהתאם מעגל מכוכביות.

הוראות:

א. כדי לדעת איזה תו להציג באיזו משבצת, ניתן לבצע מעבר על כל משבצות דף הציור ע"י לולאה בתוך לולאה (לולאה חיצונית שתעבור על כל שורות הדף ובתוכה לולאה פנימית שתעבור בכל שורה על כל העמודות שלה).
ב. בכל משבצת ניעזר בנוסחת מרחק בין 2 נקודות. אם המרחק שלה ממרכז המעגל שווה לרדיוס, יש להציג '*'.
ג. יש לקחת בחשבון גם את עובי הרדיוס. לכן נחלק אותו ב 2 ואם המרחק שווה לרדיוס ועוד מחצית העובי או פחות מחצית העובי, גם יש להציג '*'.
ד. אחרת, יש להציג מרווח ' ' (כדי להקל, במקום מרווח, ניתן להציג סימן הניתן לצפיה, כגון מקף '-').