לימודי מחשבים בשיטת בטא/סטנדרט הכתיבה בC

במסמך זה מתוארת הדרך המקובלת בבטא לכתיבת קוד בשפת C.
המסמך זמין גם דרך הכתובת http://tinyurl.com/beta-c-style.

הסטנדרט מבוסס על סגנון K&R Mandatory braces.

איך לומדים את הסטנדרט edit

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

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

הקומפיילר הקובע edit

הקומפיילר הסטנדרטי בו ייבדק קוד הוא gcc על לינוקס, או על mingw. הקוד צריך לעבוד בשניהם ללא שגיאות ואזהרות עם הדגל Wall-.

לדוגמה:

gcc code.c -o program.exe -Wall

שמות קבצים edit

  • כל מה שנאמר כאן לגבי קבצים רלוונטי גם לשמות תיקיות.
  • שמות קבצים חייבים להסתיים ב.c
  • התוים המותרים בשמות קבצים הם a-z, 0-9, _
  • אין להשתמש באותיות גדולות בשמות קבצים, בגלל בעיות פוטנציאליות במעברים בין מערכות windows למערכות תואמות לינוקס.
  • שמות שלא מרמזים מה התוכנה עושה (למשל solution14.c) סתם מעצבנים את הבודקים, ולכן לא בהכרח יתקבלו.

תיעוד edit

  • התיעוד צריך להיות הדבר הראשון שמופיע בקובץ.
  • התאריכים בתיעוד יהיו במבנה dd.mm.yyyy, למשל: 04.07.2016. אין להשמיט אפסים ואין להשתמש בסלאש במקום בנקודה.
  • מלבד השורה הראשונה, כל השורות בתיעוד מתחילות ברווח לפני הכוכבית (תיקון לפי emacs ב14.04.2016).
  • טכניקת התיעוד בבטא בנויה על גבי Doxygen javadoc at-sign style. כך שכל תוספת שרוצים לעשות בתיעוד צריכה להתאים לפורמט הזה.

התיעוד יהיה במבנה הבא:

/**
 * @file <name>.c
 * @brief <description>
 *
 * 
 *
 * version log
 * ------------
 * version <number2> by <author>, <day>.<month>.<full year>
 * version <number1> by <author>, <day>.<month>.<full year>
 */

הטקסט בתגיות brief וfile יהיה בשורה אחת יחידה. עדיף שהטקסט באותה שורה לא יהיה ארוך עד כדי כך שהוא ירד שורה אוטומטית.

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

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

דוגמה לתיעוד:

/**
 * @file histogram.c
 * @brief the program show you how many times the numbers 0-9 are in the array.
 *
 * 
 *
 * version log
 * ------------
 * version 2 by the_duke, 07.01.2015
 * version 1 by the_duke, 05.01.2015
 */

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

/**
 * prints a name
 *
 * @param name - the user's name
 *
 * @return Bool - False if the name is bad, true if the name is good.
 */
int my_func(int name)
{


ניתן להשתמש בתבנית הקוד הבאה כדי לחסוך כתיבה של גוש התיעוד:

/**
 * @file <name>.c
 * @brief <description>
 *
 * 
 *
 * version log
 * ------------
 * version ? by <author>, ?.?.20??
 *
 */

#include <stdio.h>

int main()
{
  return 0;
}

/**
 * <description>
 *
 * @param <name> - <description>
 *
 * @return <type> - <description>
 */
int my_func()
{
  return 0;
}

לבודקים edit

דוגמה לקוד עם אישור בדיקה:

/**
 * @file histograma.c
 * @brief the program show you how many times the numbers 0-9 are in the array.
 *
 * ++ approved by knr, 08.12.2015
 *
 * version log
 * ------------
 * version 1 by the_duke, 07.12.2015
 */

דוגמה לקוד שלא עבר אישור

/**
 * @file histograma.c
 * @brief the program show you how many times the numbers 0-9 are in the array.
 *
 * -- sent to fix by knr, 08.12.2016
 *
 * version log
 * ------------
 * version 1 by the_duke, 07.12.2015
 */

משתנים ושמות edit

  • שמות משתנים צריכים להיות בעלי משמעות ברורה. לא סתם אותיות.
  • הפרדת מילים בשמות משתנים ובשמות בכלל תיעשה בעזרת underscore ולא בסגנון camelCase.
  • אין להצהיר על משתנים באמצע הקוד, אלא רק בתחילת פונקציה. העדיפות בבטא היא שניתן יהיה לראות בתחילת הפונקציה את המשתנים שמשמשים בה. (אם יש סיבה טובה, אפשר להצהיר גם בתחילת כל scope block.)
  • תמיד צריך לאתחל משתנה כשיוצרים אותו. אם לא מאתחלים זה יכול לגרום קשיים בגילוי שגיאות, וחורי אבטחה.
  • בכל מצב שבו יש תובן בתוך סוגריים מכל סוג שהוא, התוכן שבפנים לא יהיה מופרד ברווחים מהסוגריים.
    int b[] = {5, 6, 7};
  • שמות קבועים שהוגדרו עם #define יהיו באותיות גדולות.

פונקציות edit

  • פונקציית main צריכה להחזיר ערך int.(כפי שנדרש מאז C99, ראה [1]])
  • סוגריים של פונקציה, בניגוד למבני בקרה, נפתחים מתחת לשם הפונקציה. פונקציה היא המקרה היחיד שסוגריים נפתחים בשורה נפרדת.

דוגמה:

int main()
{
    char b = 5;
}
  • בקריאה לפונקציה, כמו בהגדרה שלה, הסוגריים של הפרמטרים יהיו צמודים לשם הפונקציה.

דוגמה:

    printf("foo bar");
  • תוכנות בבטא בC רצות בטרמינל אלא אם נאמר במפורש אחרת. הפונקציה main צריכה להסתיים מבלי לתקוע את המשתמש (או הסקריפט) שמשתמש בה בטרמינל. לכן היא לא יכולה להכיל בקשת קלט שנועדה סתם כדי שהחלון לא ייסגר כשמפעילים את התוכנה בדאבל קליק. זאת שגיאה בהבנת המדיום שחוסמת קבלת נקודות. למעשה גם עצם הפעלת התוכנה בדאבל קליק במהלך כתיבתה מראה על עבודה בסביבת עבודה לא מומלצת ולא מתאימה, אבל זאת כבר בעיה של מי שמחליט לסבך את עצמו.

אופרטורים edit

  • בדרך כלל כדאי לשים רווח לפני אופרטור ואחריו, אם כי לפעמים הבודקים יכולים להעביר גם בלי הרווחים.
  • אופרטורים שהם unary (פועלים על גורם יחיד) כמו ++ או -- הם יוצאים מן הכלל ויהיו צמודים לגורם שהם פועלים אליו, ללא רווח.

דוגמה:

    a + 4;
    b == 5;
    c++;

כללי edit

  • אחרי פסיק יופיע רווח, כמו בשפה אנושית. אחרת הקוד נראה דחוס.
  • בinclude יופיע רווח לפני הסוגריים המשולשים של שם הספריה: include <stdio.h>.

אינדנטציה (הזחה) edit

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

קלט משתמש edit

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

דוגמה:

    printf("please enter height: ");
    scanf("%d", &height);
    printf("please enter height:\n");
    scanf("%d", &height);

גושי קוד edit

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

דוגמה:

    int height = 0;
       
    printf("please enter height: ");
    scanf("%d", &height);
   
    printf("This is the height: %d\n", height);
    
    return 0;

מבני בקרה edit

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

אנחנו לא רוצים את הבאג של אפל.

if(a) {
    a = b;
}

מבנה else if edit

הצומדיים של הבלוק צריכות להיות באותה שורה עם הelse. משני הצדדים.

if(a) {
    a = b;
} else if(c) {
    a = c;
} else {
    a = d;
}

אין להשתמש בif מקונן (if בתוך if) אם ניתן להשתמש במבנה else if. אחרת זה נחשב שימוש מסורבל.

מבנה switch-case edit

אין להשתמש במבנה if-else כאשר ניתן להשתמש בswitch-case.

switch(a) {
  case 61:
    a = b;
    break;
  case 'b':
    a = c;
    break;
  default:
    a = d;
    break;
}

למשתמשים בemacs, גם ההזחה הבאה מותרת:

switch(a) {
case 61:
  a = b;
  break;
case 'b':
  a = c;
  break;
default:
  a = d;
  break;
}

לולאת for edit

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

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

for(i = 0; i < 3; i++) {
    a += i;
}

לולאה מקוננת:

for(i = 0; i < 3; i++) {
    for(j = 0; j < 3; j++) {
        a += i+j;
    }
}

לולאת while edit

i = 0;
while(i == 0) {
    i++;
}

לולאת do-while edit

i = 0;
do {
    i++;
} while (i < 3)