PHP לעומת C ו-C++

  • פותח הנושא jskdj
  • פורסם בתאריך

jskdj

New member
PHP לעומת C ו-C++

אני לומד עכשיו PHP ואני רוצה קצת לתרגל את השפה ע"י פתירת שאלות מעולם התכנות (לאו דווקא קשור לפיתוח אתרים). לא מצאתי תרגילים ל-PHP אז חשבתי לקחת תרגילים שהיו לי כשלמדתי C ולפתור אותם ב-PHP ואז עלתה בי התהייה: האם כל דבר שאפשר לפתור ב-C וב-C++ אני יכול לפתור בעזרת PHP או לא? אני יודע ש-PHP מיועדת יותר ל-web ושכאשר לא מדובר ב-web אז השפה פחות יעילה מאשר C ו-C++ אבל עדיין מעניין אותי אם זה אפשרי ברמה העקרונית.
 

BravoMan

Active member
כן ולא:

זה מאוד תלוי מה התרגיל בה לתרגל.
&nbsp
למיטב ידיעתי, ויתקנו אותי חברי פורום שמכירים את PHP טוב יותר, היא שפת תכנות מלאה, מה שנקרא "Turing complete" וזה אומר שניתן לפתור בה בעיות כלליות כמו בכל שפת תכנות אחרת.
&nbsp
אז אם יש לך תרגיל לשחק עם מחרוזות, לפתור משוואות מתמטיות, לשחק עם מטריצות וכו' אין בעיה.
מצד שני, אם יש תרגיל שנועד לתרגל פיצ'ר ספציפי של שפה - למשל מצביעים, תהיה לך בעיה, כי ב-PHP אין אותם וב-C יש.
או, תרגיל של Templates של ++C.
&nbsp
מהצד השני, לא תמצא תרגילים עבור פיצ'רים שיש ב-PHP, כמו ההבדל בין אופרטור == ל-=== בספרים על C ו++C, כי הטמטום הזה אינו קיים בהם.
 

eladts

New member
לצורך תרגילי תכנות כלליים

כדאי להשתמש בממשק ה-CLI של PHP ולא בממשק ה-WEB שמוכר יותר.
 

jskdj

New member
תודה

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

BravoMan

Active member
רק זכור דבר אחד חשוב:

כל פיצ'ר של שפת תכנות הוא רק כלי לצורכי נוחות. כל מה שעושים איתו - אפשר לעשות גם בלעדיו.
בסופו של יום, שפות עיליות כמו C, PHP ושאר חברים, הן רק אבסטרקציות חוסכות זמן, כאשר המעבד עצמו אינו מכיר את כל הפיצ'רים המתוחכמים שלהן.
&nbsp
יש ספרייה גרפית שנקראת GTK, היא כתובה ב-C אך מממשת יפה מאוד תכנות מונחה עצמים כולל ירושה ופולימורפיזם.
אני אוהב להזכיר אותה בתור דוגמה לכך שלא חייבים תמיכה מובנית בשפה כדי לממש ראיון תכנותי כלשהו.
בסופו של יום, ברמת שפת מכונה, אין אפילו פונקציות ומשפטי תנאי, ועדיין מצליחים לממש אותם.
 
המשפט האחרון שלך לא נכון
"ברמת שפת מכונה, אין אפילו פונקציות ומשפטי תנאי" - זה פשוט לא נכון. קריאה וחזרה מפונקציות נתמכות ישירות ע"י פקודות מכונה CALL ו- RET. הפקודה CALL דוחפת את רגיסטר ה PC למחסנית (על מנת שיהיה לאן לחזור) ואז מציבה בו את כתובת הפונקציה, והפקודה RET מבצעת את הפעולה ההפוכה. ללא תמיכת חומרה היה קשה מאוד לבצע דבר כזה. כנ"ל לגבי תנאי, מבצעים את התנאים ע"י פעולות מכונה כגון CMP ואז קופצים לבלוק המתאים (IF או ELSE) ע"י JE או JNE (שמבצעות Jump if Equal או Jump if not equal, בהתאמה). וגם זה פיצ'ר שמחייב תמיכת חומרה.
&nbsp
זה נכון שאובייקטים ופולימורפיזם זו אבסטרקצית תוכנה נטו, ללא מימוש מקביל בחומרה.
 

BravoMan

Active member
כבר היה דיון על זה בפורום:

מה שהפקודות שציינת עושות, זה לקפוץ למקומות אחרים בקוד ללא תנאי, או לפי תנאי בסיסי של תוכן סיבית מסוימת באוגר מסוים.
&nbsp
אפשר לקרוא ל-CALL ו-RET מכל מקום ולקפוץ איתן לכל מקום.
הן אכן הופכות מימוש של פונקציות לנוח, אבל פונקציה זו עדיין אבסטרקציה אנושית - המעבד לא מבדיל בין אזור אחד בקוד לאחר.
&nbsp
אם תסתכל על קונספט של "פונקציה" אפילו בשפת C - תראה שיש הרבה יותר בקונספט הזה מאשר הפעולה הבסיסית של CALL ו-RET וזו גם הסיבה שיש בעולם כמה calling conventions והמתכנת הוא שחייב להכיר אותן, כי מבחינת המעבד, עדיין מדובר בסתם קפיצות ומשחקי כתובות ואוגרים.
 
לא בדיוק נכון
זה נכון שפונקציה היא אבסטרקציה אנושית, אבל בשביל לממש אותה נדרשת תמיכת חומרה ספציפית, ולא היה ניתן ליצור בשפות עיליות קריאות לפונקציה אם לא היתה את התמיכה הזו.
&nbsp
הפקודה JMP קופצת ללא תנאי, אבל לא תוכל לממש פונקציות בעזרתה. הפקודות CALL ו- RET קופצות על מנת לחזור לנקודה שממנה יצאו, ולצורך כך בנו מנגנונים בחומרה שמאפשרים לאחסן את כתובת החזרה במבנה נתונים חומרתי (המחסנית). זה לא רק נוחיות, זה צורך. אגב, זה גם מה שמאפשר קריאות מקוננות. לכן, להגיד שברמת שפת מכונה אין פונקציות זה לא בדיוק נכון, כי המנגנונים האלו נבנו במיוחד אך ורק על מנת לאפשר את הקונספט העילי של הפונקציות.
&nbsp
מה שבאמת אין בחומרה זה אובייקטים, ירושות, פולימורפיזם. אלו הן אבסטרקציות תוכנה גבוהות יותר שנבנות מעל אבסטרקציות תוכנה נמוכות יותר.
 

BravoMan

Active member
כאן אתה טועה:

RET ו-CALL הן תוספות חדשות יחסית ולא קיימות בכל הארכיטקטורות (אינני זוכר מתי נוספו ומי חוץ מ-x86 תומך בהן).
לא רק שאפשר לממש קריאה לפונקציה בעזרת JMP, אלא שמתכנתים עשו זאת במשך עשורים.
זו הסיבה שיש מה שנקרא calling convention - שוב קרא על המושג הזה. זה מושג חשוב שנמצא בשימוש עד היום, ולו חומרה תמכה בפונקציות, הוא היה מיותר מזמן.
&nbsp
אפשר בהחלט לדחוף את כתובת החזרה למחסנית עם אותה פקודת PUSH שדוחפים איתה כל מידע אחר, ואפשר (וכמובן שגם מימשו) את RET ע"י פקודת POP ו-JMP נוסף.
&nbsp
אגב, אם יש יש ריבוי אוגרים ויש פונקציה קטנה שאינה צריכה את כולם, לא חייבים לדחוף את כתובת החזרה למחסנית.
כמו שב-ARM או x86-64 לא משתמשים במחסנית רוב הזמן להעברת פרמטרים, בעוד ש-x86 הרגילה כן משתמשים.
&nbsp
נתקלתי פעם בדיון מאוד מעניין ב-SO (אני לא מוצא אותו כרגע משום מה) שמדבר על כך שהרבה פעמים קומפיילרים מודרניים דווקא מוותרים על שימוש ב-CALL כי היא יותר אטית (אאל"ט במעבדים מסוימים היא דוחפת לא רק IP אלא עוד אוגרים), ומשתמשים ב-JMP במקום בתור אופטימיזציה.
&nbsp
בכלל, אם תסתכל ב-SO, תראה שיש הרבה דיונים על מימוש פונקציות עם JMP מול מימוש עם CALL, ואפשר לראות בקלות ש-CALL היא תוספת לצורך נוחות ולא יותר מזה. היא כלל אינה הכרחית.
&nbsp
אגב, אין שום "מבנה נתונים חומרי", לפחות לא ב-x86 או ARM. המחסנית אומנם מקבלת פקודות כמו PUSH ו-POP שהופכים את השימוש בה לפשוט, אבל עדיין אם נסתכל במה שהפקודות האלה עושות בתחלס, נראה שמדובר בהעתקת ערכים מאוגרים לזיכרון וחזרה ושינוי ערך (כתובת) באוגר מסוים.
למשה, יש מקרים בהם משחקים עם המחסנית ללא הפקודות הייעודיות לה, ובניגוד ל-execution bit שהוא פיצ'ר חומרה שלא יאפשר למעבד להתייחס לזיכרון נתונים כאילו הוא מכיל קוד ולהריץ אותו, אין שום פיצ'ר חומרה שמגן על אזור זיכרון "מחסנית" מפני דריסה או שימוש רגיל לחלוטין.
 

Javali

New member
כמעט

פקודות מקבילות ל-call קיימות כבר הרבה שנים ונתמכות כמעט בכל המעבדים המודרניים. (אני אומר כמעט כי יכול להיות שיש ארכיטקטורות שאני לא מכיר.) הן היו קיימות כבר ב-8008 וב-6502.
 
לא כל כך ברור לי למה אנחנו מתווכחים
אבל פשוט שום דבר ממה שאתה אומר הוא לא נכון.
- פקודות call והמקבילות להן קיימות בכל מעבד שעבדתי עליו (אינטל x86, וגם ARM ו- ARC), וכנראה גם באחרים. חדש? במעבדי אינטל זה קיים לפחות 35 שנה (מאז יציאת x86 הראשון), ואולי לפני כן.
- לא טענתי שיש איזור זיכרון נפרד למחסנית. טענתי שיש תמיכת חומרה במבנה הנתונים שנקרא מחסנית, שמשולב עם מנגנון הקריאה לפונקציות. בפעולת אסמבלי אחת מועתק ה PC למחסנית (שהמעבד יודע מבעוד מועד איפה היא), מקודם מצביע המחסנית, ומוקפץ ה PC לכתובת חדשה. בפקודת ה RET מתבצע התהליך ההפוך. אז להגיד שאין פונקציות בחומרה זה פשוט עובדתית לא נכון - יש מכונות מצבים, שערים לוגיים ופליפ-פלופים במעבד שתפקידם לממש את הפיצ'ר הזה.
&nbsp
- המושג של Calling Convention מתייחס לאופן העברת הפרמטרים וערך ההחזרה בין קוד קורא וקוד נקרא. זה באמת לא ממומש בחומרה והקומפיילר מציב בעצמו פקודות מתאימות. זה לא סותר שום טענה שטענתי.
&nbsp
- יש מקרים שבהם קומפיילר מעדיף לעשות inlining, כלומר לוותר על הקריאה לפונקציה ובמקום זה פשוט להעתיק את גוף הפונקציה הנקראת במלואו לנקודת הקריאה, משיקולי יעילות. זו זכותו וגם זה לא סותר שום טענה שטענתי.
&nbsp
 

BravoMan

Active member
אוקיי, נראה שטעיתי בעניין ותק CALL ו-RET

אני לא בטוח למה זכרתי שהן חדשות יחסית, טעות שלי.
&nbsp
אני חושב שהוויכוח הוא על מהות המילה "פונקציה".
מצד אחד, כמו שכתבתי, עדיין אפשר היה לממש פונקציות ללא RET ו-CALL גם אם זה היה פחות נוח, ואגב, לא דיברתי כלל על inlining.
בצד שני, אני טוען ש-RET ו-CALL לא מספיקים כדי לממש באמת מה שנקרא "פונקציה", לא כמו שהתרגלנו למושג הזה משפות עיליות.
לפונקציה יש הרבה יותר מאפיינים מאשר "קפוץ למקום מסוים בקוד, תזכור מהיכן באת ואחרי זמן מסוים תחזור לשם".
&nbsp
זו בכל אופן דעתי.
אני מניח שאפשר להסכים שלא להסכים
 

הפרבולה

New member
בDSP שאני מכיר משתמשים ב CALL RET

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

Javali

New member
פונקציות בשפת מכונה

עם כל הכבוד "קריאה לפונקציה" ו"פונקציה" זה שני דברים נפרדים. פונקציה היא לא "קריאה לפונקציה", היא גם לא "חזרה מפונקציה" ואפילו לא צירוף של שניהם. חלק מהמהות של פונקציה, שמהווה אחד המאפיינים המרכזיים שלה, הוא שהמדובר על קטע קוד שיש לו כניסה אחת ושאפשר לצאת ממנו רק במספר מוגבל של דרכים שמוגדרות בשפה. ב-C אין דרך (שמוגדרת בשפה) לקרא לאמצע של פונקציה, והדרכים היחידות לצאת מפונקציה הן כשמגיעים לפקודת return, כשמגיעים לסוף הפונקציה וכשמפעילים כמה פונקציות אזוטריות פחות או יותר (longjmp, exit וכדו'). הקונספט הזה, של קטע קוד שמבוצע כיחידה, אינו קיים בשפת מכונה. אתה לא יכול להגדיר בשפת מכונה קטע קוד שהשפה לא נותנת לקפוץ לאמצע שלו. זה פשוט לא קיים בשפת מכונה. כל מה שיש שם (ב-X86 וב-IA64) זה פקודות שנקראות "קריאה לשגרה", "כניסה לשגרה", "יציאה משגרה" ו"חזרה משגרה" שמקילות על חלק מהמימוש של פונקציוץ. וזה כל מה שהן נותנות, כי הפקודות הללו לא נותנות לך את המהות של פונקציה. אבל, בהגדרתן, הן לא מונעות מהמתכנת לעשות כל מה שעולה על רוחו.
 

פרסאוס

New member
עוד פעם אתה מתלונן על ===...

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

BravoMan

Active member
זרקתי את זה, כי זה אחד הפאקים הבודדים הרציניים

של PHP שאני מכיר.
ייתכן מאוד שיש גרועים ממנו, כאמור אני לא מכיר את השפה מספיק טוב.
&nbsp
גם Python היא שפה דינמית, והיא מסתדרת מצוין בלי ===.
האמת - אני לגמרי לא בטוח שלא מכניסים פיצ'רים רק בשביל הכיף - אחרי שהתחלתי לקרוא את הספר על 11++C וראיתי שפתאום && קיבל עוד משמעות, וכמה דברים הכניסו לשפה (everything including a kitchen sink אפילו לא מתקרב לתאר את זה), התחלתי להבין שיש דבר כזה "יותר מידי".
&nbsp
בכל אופן, אני חושב שהסרטון הזה מסביר יותר טוב למה התכוונתי ממה שאני יכול כאן ע"ג הפורום:
https://skillsmatter.com/skillscasts/6088-the-worst-programming-language-ever
 
למעלה