Menu

תחביר C: המבנה של תוכנית C בהסבר מלא

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

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

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

התוכנית השלמה הקטנה ביותר

זה מתקמפל, רץ ולא עושה כלום:

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

הוסיפו פלט ותצטרכו הכללה אחת:

הוראות קדם-מעבד

שורות שמתחילות ב-# מטופלות לפני הקומפילציה עצמה, על ידי קדם-המעבד (preprocessor). הן אינן פקודות C, ואין בהן נקודה ופסיק.

#include <stdio.h>    /* standard library header - angle brackets */
#include "myutils.h"  /* your own header - quotes, searched locally first */
#define MAX 100       /* a macro - MAX is replaced by 100 everywhere below */

#include הוא הדבקה מילולית של טקסט: כל התוכן של הקובץ שצוין נשתל בנקודה הזו. stdio.h מכיל את ההצהרה של printf, וכך הקומפיילר יודע אילו ארגומנטים printf מקבלת. השמיטו את ההכללה ותקבלו implicit declaration of function 'printf'.

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

הפונקציה main

int main(void) {
    return 0;
}

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

  • int: טיפוס ההחזרה של main. הערך עובר למערכת ההפעלה כקוד היציאה, שבו 0 פירושו הצלחה.
  • main: השם השמור שסביבת הריצה קוראת לו. לכל תוכנית C רגילה יש בדיוק אחת.
  • (void): רשימת הפרמטרים. void כאן פירושו "לא מקבלת ארגומנטים". כתיבת main() במקום זה חוקית אבל חלשה יותר: סוגריים ריקים ב-C פירושם "פרמטרים לא מוגדרים", וזה מבטל את בדיקת הארגומנטים.
  • { ... }: גוף הפונקציה.

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

int main(int argc, char *argv[]) { ... }

השתמשו ב-int main(void) עד שתצטרכו אותם.

פקודות ונקודה ופסיק

פקודה היא הוראה יחידה, והיא מסתיימת בנקודה ופסיק.

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

מה לא מקבל נקודה ופסיק:

#include <stdio.h>        /* preprocessor line - no semicolon */

int add(int a, int b) {   /* function definition header - no semicolon */
    return a + b;
}                         /* closing brace of a function - no semicolon */

if (x > 0) {
    ...
}                         /* closing brace of a control block - no semicolon */

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

if (score > 90);              /* BUG: this ; is the entire if body */
    printf("Excellent!\n");   /* always runs, whatever score is */

זה C חוקי לגמרי. ה-if שולט בפקודה ריקה, וה-printf היא פשוט השורה הבאה ברצף. -Wall מזהיר על זה; הקומפיילר לא יעצור אתכם.

בלוקים וסוגריים מסולסלים

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

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

בלוקים גם יוצרים טווח (scope): משתנה שמוצהר בתוך { ... } מפסיק להתקיים בסוגר המסולסל הסוגר.

רגישות לאותיות ורווחים

C רגישה לאותיות גדולות וקטנות בכל מקום. total, Total ו-TOTAL הם שלושה שמות נפרדים. כל מילות המפתח באותיות קטנות, כך ש-Int, Return ו-Printf הם כולם שגיאות, והודעת השגיאה עבור Printf מבלבלת, כי הקומפיילר מניח שהתכוונתם לפונקציה כלשהי שהוא מעולם לא שמע עליה.

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

int    main ( void )
{
    printf
    ( "Hello\n" ) ;
    return 0 ;
}

וגם זה:

int main(void){printf("Hello\n");return 0;}

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

שני מקומות שבהם רווחים כן משנים: אי אפשר לפצל מילת מפתח או מזהה (in t אינו int), ואי אפשר לשים שורה חדשה בתוך מחרוזת מילולית רגילה.

מזהים: כללי שמות

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

  • מורכב מאותיות, ספרות וקווים תחתונים.
  • אסור שיתחיל בספרה. total2 בסדר, 2total לא.
  • לא יכול להיות מילת מפתח: אין משתנה בשם int, for או return.
  • יש משמעות לאותיות גדולות וקטנות.
  • שמות שמתחילים בקו תחתון, וכל דבר עם שני קווים תחתונים ברצף, שמורים למימוש: אל תמציאו כאלה.
int score;        /* good */
int total_items;  /* good - snake_case is the C convention */
int _hidden;      /* legal but reserved - avoid */
int 2fast;        /* error */
int float;        /* error - keyword */

תוכנית עם הערות מלאות

כל מה שלמעלה, במקום אחד:

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

שאלות נפוצות

מה המבנה הבסיסי של תוכנית C?

קודם הוראות קדם-מעבד (#include <stdio.h>), אחר כך הגדרות פונקציות, שאחת מהן חייבת להיות main. בתוך פונקציה, פקודות מסתיימות בנקודה ופסיק ובלוקים עטופים בסוגריים מסולסלים. תוכנית מינימלית היא #include <stdio.h> ועוד int main(void) { return 0; }.

למה כותבים int main(void) ולא רק main()?

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

האם כל פקודה ב-C צריכה נקודה ופסיק?

כל פקודה כן, אבל לא כל שורה. הוראות קדם-מעבד (#include, #define), הגדרות פונקציות והסוגר המסולסל הסוגר של בלוק או של if/for/while לא מקבלים אחד. נקודה ופסיק תועה אחרי if (x) הוא C חוקי, והוא גורם בשקט לגוף לרוץ בלי תנאי.

האם C רגישה לאותיות גדולות וקטנות?

כן, לגמרי. count, Count ו-COUNT הם שלושה מזהים שונים, ו-Int אינו מילת המפתח int. כל מילות המפתח של C באותיות קטנות, כך שאות גדולה ב-Return או ב-Printf גורמת לשגיאה.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל