Menu

הידור C++: מקוד מקור לקובץ הרצה עם g++

איך C++ הופכת קוד מקור .cpp לקובץ הרצה נייטיב: מהדרים עם g++ (או clang++/MSVC), מריצים את הבינארי וקוראים את שגיאות המהדר כשמשהו משתבש.

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

מהדרים, ואז מריצים

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

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

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

מחזור ההידור וההרצה על המחשב שלכם

נניח ששמרתם את זה בשם main.cpp:

#include <iostream>
using namespace std;

int main() {
    cout << "Hello from the terminal" << endl;
    return 0;
}

פתחו טרמינל בתיקייה שמכילה את הקובץ והפעילו את המהדר. המהדר הנפוץ ביותר הוא g++ (חלק מ-GCC); clang++ עובד בדיוק אותו דבר עבור כל מה שכאן:

g++ main.cpp -o main

אם הקוד מתהדר בלי בעיות, g++ לא מדפיס כלום ויוצר קובץ חדש ליד קוד המקור: קובץ הרצה בשם main (או main.exe ב-Windows). הבינארי הזה הוא קוד מכונה: לא טקסט קריא, וקשור למערכת ההפעלה ולמעבד שלכם. עכשיו הריצו אותו:

./main
Hello from the terminal

ב-Windows מריצים אותו כ-main.exe (או פשוט main) מאותו טרמינל. ה-./ ב-macOS וב-Linux אומר ל-shell "התוכנית נמצאת ממש כאן בתיקייה הזו, לא איפשהו ב-PATH", שלב שמתחילים שוכחים ואז תוהים למה main מחזיר command not found.

מה -o עושה (ו-a.out)

הדגל -o נותן שם לפלט. השמיטו אותו ו-g++ עדיין יהדר, אבל יכתוב את קובץ ההרצה לשם ברירת מחדל: a.out ב-macOS/Linux, a.exe ב-Windows.

g++ main.cpp        # produces a.out, not main
./a.out

זה מפיל אנשים כל הזמן: הם מהדרים, לא רואים שגיאות, מריצים ./main ומקבלים No such file or directory, כי הבינארי בעצם נקרא a.out. תמיד העבירו -o כדי לדעת בדיוק מה אתם מריצים.

מהדרים פעם אחת לכל שינוי בקוד המקור. אחרי זה קובץ ההרצה עצמאי: אפשר להריץ ./main מאה פעמים בלי להדר מחדש. מריצים g++ שוב רק אחרי שעורכים את קובץ ה-.cpp.

בחירת תקן C++

ל-C++ יש גרסאות, C++11, C++14, C++17, C++20, C++23, וכל אחת מוסיפה תכונות לשפה. המלכוד: המהדר שלכם בוחר תקן ברירת מחדל שעשוי להיות ישן מכפי שאתם מצפים, כך שקוד מודרני עלול לא להתהדר בלי סיבה ברורה. קבעו את התקן במפורש עם -std:

g++ -std=c++17 main.cpp -o main
g++ -std=c++20 main.cpp -o main

הנה קוד שמשתמש בתכונה של C++17 (structured bindings). הוא מתהדר בלי בעיה בעורך, אבל על המחשב שלכם הוא צריך -std=c++17 או חדש יותר:

אם תראו אי פעם שגיאה כמו 'structured bindings' only available with '-std=c++17', התיקון לא נמצא בקוד שלכם: הוא הוספת הדגל -std הנכון. לאורך כל הקורס הזה, הניחו C++17 או חדש יותר.

הפעילו אזהרות

תוכנית C++ יכולה להתהדר בלי בעיות ועדיין להיות שגויה. אם תבקשו, המהדר יצביע על קוד חשוד לפני שהוא יכאיב לכם בזמן ריצה. הוסיפו -Wall -Wextra:

g++ -std=c++17 -Wall -Wextra main.cpp -o main

הסתכלו על התוכנית הזו. היא מתהדרת בלי -Wall, אבל יש בה באג אמיתי: קריאה של משתנה שמעולם לא קיבל ערך, וזה undefined behavior:

#include <iostream>
using namespace std;

int main() {
    int count;                 // never initialized
    cout << count << endl;     // reads garbage - undefined behavior
    return 0;
}

כשהאזהרות מופעלות, המהדר מסמן את זה:

warning: 'count' is used uninitialized [-Wuninitialized]

התרגלו להדר עם -Wall -Wextra מהיום הראשון. אזהרות הן סקירת קוד בחינם מהמהדר; התעלמות מהן היא הדרך שבה באגים עדינים שורדים. התיקון כאן הוא פשוט int count = 0;.

קריאת שגיאות מהדר

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

#include <iostream>
using namespace std;

int main() {
    cout << "Oops"      // no semicolon
    return 0;
}
main.cpp:5:5: error: expected ';' before 'return'
    5 |     return 0;
      |     ^~~~~~

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

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

תוכנית לבדיקת שפיות

הריצו את זה בעורך, או שמרו בשם main.cpp והריצו g++ -std=c++17 -Wall main.cpp -o main ואז ./main על המחשב שלכם. אם שלוש השורות מופיעות, סביבת העבודה שלכם עובדת מקצה לקצה:

מופיעים כאן שלושה דברים שתכירו כמו שצריך בקרוב: משתנה int, vector (המערך בגודל המשתנה של C++), ו-cout לפלט. בינתיים מספיק שהתוכנית מתהדרת ומדפיסה את שלוש השורות לפי הסדר.

הבא בתור: תחביר C++

הידרתם והרצתם כמה תוכניות, אבל עד עכשיו עברנו על סימני הפיסוק בלי להתעכב: שורות ה-#include, הסוגריים המסולסלים, הנקודה-פסיק, int main(), ולמה כל שורה נראית כמו שהיא נראית. העמוד הבא מפרק את התחביר של C++ חלק אחרי חלק, כך שהמבנה יפסיק להרגיש כמו קוד תבנית ויתחיל להיות הגיוני.

שאלות נפוצות

איך מהדרים ומריצים תוכנית C++?

שמרו את הקוד בשם main.cpp, פתחו טרמינל בתיקייה הזו והריצו g++ main.cpp -o main כדי ליצור קובץ הרצה. אחר כך הריצו אותו עם ./main ב-macOS/Linux או main.exe ב-Windows. מהדרים פעם אחת; את הבינארי שנוצר אפשר להריץ כמה פעמים שרוצים.

מה עושה הדגל -o ב-g++?

-o נותן שם לקובץ הפלט. g++ main.cpp -o hello יוצר קובץ הרצה בשם hello. בלי -o, g++ משתמש בשם ברירת המחדל a.out (או a.exe ב-Windows), ולכן מתחילים מוצאים את עצמם לא פעם מריצים קובץ שלא ידעו שיצרו.

איך מהדרים C++ עם תקן מסוים כמו C++17 או C++20?

העבירו את הדגל -std: g++ -std=c++17 main.cpp -o main או -std=c++20. בלעדיו, המהדר משתמש בברירת מחדל משלו, שעשויה להיות ישנה מכפי שאתם מצפים, כך שתכונות חדשות כמו structured bindings או <ranges> עלולות לא להתהדר עד שתקבעו את התקן במפורש.

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

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

להתחיל