מדוע שפת התכנות C עדיין שולטת

שום טכנולוגיה לא מסתדרת במשך 50 שנה אלא אם כן היא עושה את עבודתה טוב יותר מכל דבר אחר - במיוחד טכנולוגיית מחשבים. שפת התכנות C חיה ובועטת מאז 1972 והיא עדיין שוררת כאחד מאבני היסוד הבסיסיות של עולמנו המוגדר בתוכנה.

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

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

C לעומת C ++

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

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

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

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

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

C לעומת Java

לאחר עשרות שנים, ג'אווה נותרה מצרך מרכזי לפיתוח תוכנה ארגונית - ובכלל מצרך פיתוח. רבים מפרויקטי התוכנה הארגוניים המשמעותיים ביותר נכתבו בג'אווה - כולל הרוב המכריע של פרויקטים של קרן Apache Software Foundation - וג'אווה נותרה שפה קיימא לפיתוח פרויקטים חדשים עם דרישות ארגוניות.

תחביר ג'אווה לווה הרבה מאוד מ- C ו- C ++. בניגוד ל- C, לעומת זאת, Java כברירת מחדל לא מקודדת לקוד מקורי. במקום זאת, סביבת זמן הריצה של Java, ה- JVM, JIT (בדיוק בזמן) קובעת את קוד ה- Java להפעלה בסביבת היעד. בנסיבות הנכונות, קוד Java JITted יכול להתקרב או אפילו לעלות על הביצועים של C.

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

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

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

עם זאת, ישנם כמה אזורים בהם ג'אווה יכולה להתקרב ל- C מבחינת מהירות. מנוע ה- JIT של JVM מייעל את השגרה בזמן הריצה על סמך התנהגות התוכנית, ומאפשר שיעורי אופטימיזציה רבים שאינם אפשריים עם גיבוש מבעוד מועד. ולמרות שזמן הריצה של Java הופך את ניהול הזיכרון לאוטומטי, כמה יישומים חדשים יותר עובדים סביב זה. לדוגמה, Apache Spark מייעל את העיבוד בזיכרון בין השאר באמצעות קוד ניהול זיכרון מותאם אישית העוקף את ה- JVM.

C לעומת C # ו- .Net

כמעט שני עשורים לאחר הצגתם, C # ו- .Net Framework נותרו חלקים עיקריים בעולם התוכנה הארגונית. נאמר כי C # ו- .Net היו התגובה של מיקרוסופט לג'אווה - מערכת מהדר קוד מנוהל וזמן ריצה אוניברסלי - ולכן כל כך הרבה השוואות בין C ל- Java מחזיקות מעמד גם ל- C ו- C # /. Net.

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

יתרון נוסף כמו .NET כמו Java הוא אופטימיזציה של JIT. תוכניות C # ו- .Net יכולות להיאסף מבעוד מועד לפי C, אך הן מורכבות בעיקר בזמן בזמן על ידי זמן הריצה של NET ומותאמות למידע זמן ריצה. אוסף JIT מאפשר כל מיני אופטימיזציות במקום עבור תוכנית .Net פועלת שלא ניתנת לביצוע ב- C.

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

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

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

ג לעומת גו

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

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

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

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

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

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

ג לעומת חלודה

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

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

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

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

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

C לעומת פייתון

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

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

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

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

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