3.2 הבסיס הרצאה
פונקציית main¶
כל תוכנת C חייבת להכיל פונקציית main, למעשה ברוב מערכות ההפעלה- וגם לינוקס, כל תוכנה מתחילה בפונקציית main מוגדרת שהקרנל קופץ אלייה כשהוא מריץ את התוכנה.
נגדיר פונקציית main בצורה כזו:
כאשר, נסמן בהתחלה "int" כדי להגדיר שהפונקציה מחזירה משתנה מסוג int (מספר שלם) (כמו -> int בפייתון), נגדיר את אזור הפונקציה באמצעות סוגריים מסולסלים {}, (כמו tab בפייתון) וכמובן שהפונקציה תיקרא main.
הפונקציה אמורה להחזיר מספר, המספר אמור לסמן את ה"return code" של התוכנה.
נהוג להחזיר 0 כאשר התוכנה הצליחה לרוץ בהצלחה, ו- -1 כדי לסמן שהתוכנה החזירה שגיאה.
שימו לב, שכל שורה בC אמורה להסתיים עם ; אם היא לא מסתיימת ב- {}
כל תוכנה מגדירה "return code" שונה, והוא בדרך כלל מסמן משהו לגבי האם התוכנה הצליחה או לא.
כמובן שאפשר גם להגדיר פרמטרים לפונקציות, ופונקציית הmain תמיד מקבלת 3 פרמטרים מוגדרים.
הפרמטר הראשון מסמן את כמות הארגומנטים שהועברו לתוכנה
הפרמטר השני מסמן מערך של מחרוזות שמכיל את כל הארגומנטים שהועברו לתוכנה
הפרמטר השלישי מסמן מערך של מחרוזות שמכיל את משתני הסביבה של התוכנה.
כך שנהוג להגדיר את main כך:
בהמשך אסביר על הסוגים השונים של המשתנים בC, ומה זה char**.
סוגי משתנים¶
בשפת C ישנם המון סוגי משתנים וחשוב שנכיר אותם ואת גודלם בזכרון.
char - שומר בית אחד בזכרון
short - שומר 2 בתים בזכרון
int - שומר 4 בתים בזכרון
long long - שומר 8 בתים בזכרון
float - שומר 4 בתים בזכרון ויודע לשמור שברים
double - שומר 8 בתים בזכרון ויודע לשמור שברים
כל המשתנים האלו דיפולטית הם signed, כלומר הם נועדו להכיל מספרים חיובים ושלילים, הם שומרים את הביט האחרון לציון שלילי או חיובי (הוסבר בפרק 0 בייצוג בינארי)
אם נרצה לעשות unsigned למשתנים ולהשתמש בכל הביטים לצורך מספרים חיוביים נוכל לציין זאת כך:
unsigned char - שומר בית אחד בזכרון
unsigned short - שומר 2 בתים בזכרון
unsigned int - שומר 4 בתים בזכרון
unsigned long long - שומר 8 בתים בזכרון
למעשה, אחרי שהגדרנו משתנה בC, בין אם הוא גלובלי או בתוך פונקציה- נוכל לתת לו שם וערך, בדומה לכל שפת תכנות שאנחנו מכירים.
פעולות לוגיות ומתמטיות¶
בC ישנם המון פעולות לוגיות ומתמטיות שנוכל לעשות על מספרים.
פעולות מתמטיות שאנחנו מכירים משפות אחרות:
פעולת הוספה ב1
עכשיו a יהיה שווה ל2.
פעולת חיסור ב1
טריק מגניב, מה יקרה אם ננסה לשמור במשתנה את a++?
למעשה b יהיה שווה ל1, בגלל שבC, פעולת הa++ מוסיפה ערך למשתנה, אבל כביטוי מחזירה את הערך הקודם של המשתנה. כלומר- a יהיה שווה ל2, אבל הפעולה כביטוי תחזיר 1, ואז b יהיה שווה ל1.
אם נרצה שפעולת ה++ גם תעלה את הערך של a וגם תחזיר כביטוי את הערך החדש של a נצטרך לעשות ++ הפוך:
עכשיו b יהיה שווה ל2.
ואותו הדבר נכון גם ל--.
פעולות לוגיות:
פעולת and
פעולת or
פעולת xor
פעולת not
פעולת not על שערים כדי ליצור nand, nor, xnor
ערכים¶
למדנו על משתנים שונים ופעולות מתמטיות, עכשיו נלמד על ערכים שונים שניתן לשים במשתנים.
למעשה ניתן לשים כל סוג של משתנה שאנחנו מכירים משפות תכנות גבוהות במשתנים- ובגלל שסוג המשתנה מציין רק את הגודל שלו בזכרון- ניתן כעקרון לשמור כל סוג של ערך בכל סוג של משתנה.
int a;
a = 1; // הערך יהיה בזכרון 1
a = 'a'; // הערך בזכרון יהיה יצוג האסקי של האות, במקרה הזה 95
a = true; // הערך בזכרון יהיה 1
a = false; // הערך יהיה בזכרון 0
a = "hello" // הערך בזכרון יהיה כתובת למחרוזת המצוינת המוגדרת בסגמנט הדאטה
אומנם אפשר לשמור המון סוגי ערכים בגלל עקרון זה, אבל לא נהוג לעשות זאת.
כל ערך נהוג להישמר במשתנה שתואם את הגודל המקסימלי שהערך יכול להיות,
כלומר-
בוליאנים, כמו true ו- false נשמור בchar- כי זה הסוג משתנה הכי קטן בC ובוליאנים זה לשמור רק ביט אחד כך שזה הכי הגיוני.
חבל לשמור משתנה בולייאני בint, שהוא 4 בתים, כאשר ערכים בוליאנים הם רק ביט אחד.
אותיות גם נשמור בchar, כי אותיות בC הן מיוצגות בascii, שזה רק 7 ביטים.
מספרים נהוג לשמור בint, כי 4 בתים- יכול להכיל 4 מיליארד מספרים שונים- 2^32
אבל כמובן שאם צריך לשמור מספרים גדולים יותר אפשר להשתמש בlong long, ואפשר גם לשמור מספרים קטנים יותר בshort ו- char.
לעומת זאת מספרים עשרוניים נשמור רק בfloat ובdouble- כי הם סוגים מיוחדים של משתנים שהמעבד יודע להתייחס אליהם כעשרוניים. (הוסבר בפרק 0 על float וכיצד הוא עובד במעבד.)
כאשר אנחנו מגדירים מחרוזות כערך, הקומפיילר יודע לשמור את המחרוזת בdata segement- ולדאוג שהמשתנה יכיל כתובת בזכרון למחרוזת.
המחרוזת, היא אוסף של אותיות- (של char-ים עם ערכי ascii) כאשר בסוף האותיות קיים הnull terminator (כמו שלמדנו באסמבלי), אות שמכילה 0.
בC נהוג להשתמש במשתנה מסוג "char" כדי לסמן מחרוזת, כי char הוא משתנה שמכיל כתובת שמצביעה על char.
בהמשך נסביר יותר בהרחבה על משתנים שאחראיים לשמור כתובת למקום אחר בזכרון כמו char*.
נוכל גם להציב ערכי הקס ובינארים במשתנים
פונקציית printf¶
אחת הפונקציות השימושיות ביותר בC, היא printf.
הפונקציה printf היא פונקציה שמוגדרת מראש על ידי ספריית libc (שכל תוכנית C משתמשת בה) שיודעת להדפיס סוגי משתנים.
כדי להשתמש בספרייה, עלינו ליידע את הקומפיילר שאנחנו רוצים להשתמש בקובץ "stdio.h" שמגדיר את הפונקציה, עושים זאת כך:
כדי להדפיס עם printf, עלינו לציין באמצעות "%" ואות מסוימת את מה שנרצה להדפיס.
כדי להדפיס מספר שלם, נעשה זאת כך:
#include <stdio.h>
int main(int argc, char** argv, char** envp) {
int a = 1;
printf("%d", a);
return 0;
}
הקוד הבא ידפיס את המספר 1.
כדי להדפיס מחרוזת, נוכל להשתמש ב%s
כדי להדפיס ערך הקסה, נוכל להשתמש ב%x
כדי להדפיס כתובת בזכרון, נוכל להשתמש ב%p
ניתן לשרשר הדפסות עם printf כך:
```c
char* str1 = "hello";
char* str1 = "amit";
int age = 20;
printf("%s my name is %s, and my age is %d", str, str, age);
מוזמנים לעשות man 3 printf כדי לקרוא עוד על הפונקציה.