לכל תוכנית שמריצים מטרמינל אפשר להעביר ארגומנטים: gcc -Wall hello.c -o hello מעבירה ארבעה. תוכניות ה-C שלכם קוראות את שלהן דרך שני פרמטרים של main, וזה כל המנגנון: בלי קריאה לספרייה, בלי הכנות.
החתימה
עד עכשיו כל דוגמה בתיעוד הזה השתמשה ב-int main(void). הצורה הסטנדרטית השנייה מקבלת את שורת הפקודה:
int main(int argc, char *argv[]) {
/* ... */
}
argc("argument count") הוא כמה ארגומנטים יש, כולל שם התוכנית.argv("argument vector") הוא מערך של מחרוזות.char *argv[]פירושו "מערך של מצביעים ל-char", כלומר מערך של מחרוזות C.
char **argv הוא כתיב שקול שתראו באותה תדירות; פרמטרים מסוג מערך הופכים למצביעים, כך ששניהם מצהירים על אותו דבר.
השתמשו בחלונית Args של העורך כדי לספק ארגומנטים (כל שדה הוא ארגומנט אחד), ואז לחצו Run. נסו שלושה שדות: hello, world, 42.
המבנה
הרצת ./greet Ada Lovelace מפיקה:
| אינדקס | ערך | |
|---|---|---|
argv[0] | "./greet" | שם התוכנית כפי שהופעלה |
argv[1] | "Ada" | הארגומנט האמיתי הראשון |
argv[2] | "Lovelace" | הארגומנט האמיתי השני |
argv[3] | NULL | הסיום |
argc | 3 | הספירה כולל argv[0] |
ארבע עובדות שכדאי לזכור:
argcהוא לפחות 1 בפועל, כיargv[0]הוא שם התוכנית. הארגומנטים האמיתיים מתחילים באינדקס 1, ולכן לולאות עליהם מתחילות ב-i = 1.- הכול הוא מחרוזת.
./prog 42נותן לכם"42", שני התווים, אף פעם לא את המספר 42. ההמרה היא התפקיד שלכם. argv[argc]הואNULL. התקן מבטיח את זה, כך שאפשר לעבור על המערך בליargcאם מעדיפים:for (char **p = argv + 1; *p != NULL; p++).- ה-shell מפצל את המילים, לא התוכנית שלכם.
./prog "Ada Lovelace"הוא ארגומנט אחד; ה-shell צורך את המירכאות והן אף פעם לא מגיעות ל-argv.
בדיקה לפני קריאה
ארגומנט שלא הועבר אינו מחרוזת ריקה: זה זיכרון שאין לכם שום זכות לגעת בו. תמיד בדקו קודם את argc:
שתי מוסכמות בתוכנית הזעירה הזו, שתיהן סטנדרטיות בכלי Unix:
- הודעת השימוש הולכת ל-
stderr, לא ל-stdout, כדי שלא תזהם פלט שמועבר בצינור למקום אחר. - ערך החזרה שונה מאפס מ-
mainמאותת על כישלון למי שהריץ את התוכנית.0פירושו הצלחה; כל דבר אחר פירושו שמשהו השתבש. סקריפטים של shell בודקים את זה.
הדפסת argv[0] במקום שם קבוע בקוד פירושה שההודעה תתאים לאופן שבו המשתמש הפעיל את התוכנית, גם אם שמה שונה או שהגיעו אליה בנתיב אחר.
המרת ארגומנטים מספריים
argv[1] הוא טקסט. כדי לעשות חשבון חייבים להמיר אותו, והפונקציה לכך היא strtol מ-<stdlib.h>:
הוסיפו 10, 20, 30 כשלושה שדות Args, ואז החליפו אחד מהם ב-banana כדי לראות את מסלול השגיאה.
הפרמטר השלישי של strtol הוא הבסיס: 10 לעשרוני, 16 להקסדצימלי, או 0 לזיהוי אוטומטי לפי קידומת 0x או אפס מוביל. הפרמטר השני הוא המקום שבו נשמר מצביע לתו הראשון שהיא לא צרכה, וזה מה שמאפשר בדיקת שגיאות:
end == textפירושו שהיא לא ניתחה כלום: הארגומנט לא התחיל במספר.*end != '\0'פירושו שנשאר טקסט אחרי המספר, כך ש-"12abc"נדחה במקום להפוך בשקט ל-12.errno == ERANGEפירושו שהערך גלש מ-long.
השוו ל-atoi, שקצרה יותר ולא יכולה לדווח על שום דבר מזה:
int n = atoi(argv[1]); /* "banana" -> 0, "0" -> 0, overflow -> undefined */
atoi מחזירה 0 עבור קלט לא תקין, ואי אפשר להבדיל את זה מאפס אמיתי. השתמשו ב-strtol (וב-strtod עבור נקודה צפה) בכל פעם שהקלט מגיע מחוץ לתוכנית שלכם. עוד בנושא בהמרת מחרוזות.
מחשבון קטן
מחברים הכול יחד: תוכנית שמקבלת מספר, אופרטור ומספר:
נסו 12 + 30, אחר כך 7 x 6, ואחר כך 5 / 0.
שימו לב ל-strcmp(op, "+") == 0 במקום op == "+". השוואת ערכי char * עם == משווה כתובות, לא תוכן, והייתה נותנת שקר גם עבור טקסט זהה. ראו פונקציות מחרוזות.
ה-x במקום * הוא פרט מהעולם האמיתי: רוב ה-shells מרחיבים * חשוף לרשימת הקבצים בתיקייה הנוכחית עוד לפני שהתוכנית שלכם רצה. גם עטיפה במירכאות ('*') עובדת, אבל בחירת תו שה-shell מתעלם ממנו ידידותית יותר.
ניתוח דגלים
לפי המוסכמה, אפשרויות מתחילות ב--. לולאה פשוטה שכתובה ביד מטפלת במקרים הנפוצים:
נסו את זה עם ארבעה שדות Args: -v, -n, 3, hello.
הבדיקה i + 1 >= argc לפני argv[++i] היא השורה החשובה: אפשרות שמצפה לערך חייבת לוודא שהערך באמת קיים, אחרת -n כארגומנט האחרון קורא מעבר לסוף המערך. במערכות Unix אמיתיות getopt מ-<unistd.h> עושה את כל זה בשבילכם, כולל אפשרויות קצרות מקובצות כמו -vn3; כדאי לפנות אליה ברגע שלתוכנית יש יותר משניים או שלושה דגלים.
ארגומנטים בעורך שבדפדפן
בלוקי העורך בעמוד הזה מתקמפלים ורצים בשבילכם, וחלונית Args היא המקום שבו מספקים את מה שהיה בא אחרי שם התוכנית בטרמינל. כל שדה שמוסיפים עם Add arg הופך לבדיוק רשומה אחת ב-argv: אין shell באמצע, כך ששום דבר לא מפוצל לפי רווחים ולא צריך מירכאות: שדה שמכיל hello world מגיע כארגומנט יחיד hello world. כדי להעביר -n 3 hello, הוסיפו שלושה שדות. השארת החלונית ריקה נותנת argc == 1, וזו הסיבה שהשומר argc < 2 מופעל בהרצה הראשונה, והסיבה שכדאי לכתוב אותו.
טעויות נפוצות
- קריאת
argv[1]בלי לבדוק אתargc. התנהגות לא מוגדרת כשלא הועברו ארגומנטים, והקריסה הנפוצה ביותר בתחום הזה. - התחלת הלולאה ב-
i = 0. זה מעבד את שם התוכנית כאילו היה נתון. - התייחסות ל-
argv[1]כמספר. זה טקסט עד שממירים אותו. - שימוש ב-
atoiעל קלט משתמש. אין דרך להבדיל בין ארגומנט שגוי לבין אפס. - השוואת מחרוזות עם
==. השתמשו ב-strcmp. - כתיבה לתוך המחרוזות של
argv. התקן מתיר לשנות אותן, אבל המקום מוגבל ותלוי פלטפורמה; העתיקו לחוצץ משלכם אם צריך לשנות משהו.
שאלות נפוצות
מה הם argc ו-argv ב-C?
אלה שני הפרמטרים של int main(int argc, char *argv[]). argc הוא מספר הארגומנטים משורת הפקודה כולל שם התוכנית; argv הוא מערך של הארגומנטים האלה כמחרוזות. argv[0] הוא שם התוכנית, argv[1] הוא הארגומנט האמיתי הראשון, ו-argv[argc] הוא תמיד NULL.
איך מעבירים ארגומנטים לתוכנית C?
מקלידים אותם אחרי שם קובץ ההרצה בטרמינל: ./program hello 42. זה נותן argc == 3, כאשר argv[1] הוא המחרוזת "hello" ו-argv[2] הוא המחרוזת "42". בעורך שבעמוד הזה, הארגומנטים נכנסים לחלונית Args במקום לטרמינל.
איך ממירים ארגומנט משורת הפקודה ל-int ב-C?
השתמשו ב-strtol: char *end; long n = strtol(argv[1], &end, 10); ואז בדקו ש-end לא מצביע עדיין על ההתחלה (שום דבר לא נותח) וש-*end הוא '\0' (אין זבל בסוף). atoi קצרה יותר אבל מחזירה 0 גם עבור "0" וגם עבור "banana", כך שהיא לא יכולה לדווח על שגיאה.
מה זה argv[0] ב-C?
השם שבו התוכנית הופעלה, בדרך כלל ./program או הנתיב המלא. הוא שימושי בהודעות שימוש (fprintf(stderr, "usage: %s FILE\n", argv[0])) כדי שההודעה תתאים לאופן שבו המשתמש באמת קרא לתוכנית. במקרים חריגים הוא יכול להיות מחרוזת ריקה, אז אל תניחו שהוא קיים.