שאלה

TomE82

New member
שאלה

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

*ראו קובץ מצורף.
 
לדעתי

אבל היה לי קצת קשה לקרוא את הקוד שלך יש לך שם


else

{
s=0
}

שאני חושב שהוא מהווה את הגורם לבעיה (אבל שוב קשה לי לקרוא את הקוד שלך הוא עם צילומי מסך )
 

TomE82

New member
תגובה

1) למה שזו תהייה הבעייה?
2) להבא, איך להציג שורות קוד בפורם?
תודה על התגובה הזריזה... (-:
 

TomE82

New member
סליחה על הבורות...

אבל איך מעלים את זה? באיזה פורמט?
 

פרסאוס

New member
בפורמט הטקסט שבו הם כתובים

דרך נוספת היא ליצור קובץ זיפ מכולם ולהעלות אותו.
 

make clean

New member
תוכנית בC היא קובץ טקסט

פשוט תחפש איפה הקובץ יושב ותעלה אותו .
אם אתה לא מוצא ומתעצל לחפש (לא בריא למתכנת) פשוט תעתיק את הטקסט לעורך טקסט ותעלה את התוצר. תמונה הרבה פחות ברורה ואנחנו לא יכולים לקמפל ולבדוק אצלנו בלי להקליד הכל מחדש.

בכל זאת אתיחס למה שאני מבין ממה שצרפת:
1. ה main צריך להחזיר int ולא void.
2. לדעתי זה בכלל לא אמור להתקמפל כי ה scanf השני מקבל את v ולא את הכתובת שלו.
3. יש לך שלושה משתנים מספיק כאן משתנה אחד לקלט.
 

nocgod

New member
לא מדוייק

1. הפונקציה main בVC לא חייבת להחזיר int, אם אני לא טועה GCC גם לא מחייב כבר.
2. זה אמור להתקמפל אבל - בגלל זה התוכנית קורסת. scanf מקבל כתובת של משתנה אליו הוא אמור לכתוב. כתובת היא רק מספר ולכן הcast בין int לבין int* (הם שניהם באותו גודל לרוב גם) עובר מבלי שגיאות, השגיאה קוראת כאשר scanf מנסה לכתוב לכתובת לא מוקצת בזיכרון
3. נכון, מספיק פה משתנה קלט אחד... לולאת do-while ובערך 5 שורות קוד :)
 

make clean

New member
תגובה

1. התקן של השפה חייב מחייב ש main תחזיר int . זה שקומפילרים "סולחים" לא הופך את זה לנכון וכדאי מאד שמי שלומד את השפה ילמד נכון.
2. לפי מה שזכור לי לא אמור להיות cast אוטומטי בין int ומצביע ל int .
 

nocgod

New member
תראה

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

אגב, לא רק שיש המרה בין int לבין מצביע של int אז אין אפילו warning ב VC2012
 

make clean

New member
וואלה אתה צודק

לגבי ה cast האוטומטי אתה צודק. אצלי ה gcc 4.6.3 אמנם נותן warning אבל כן מקמפל . כנראה שאני רגיל מידי ל CPP .

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

BravoMan

Active member
רק שאין פה שום cast

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

למעשה, אפשר היה לשים שם char, double או מבנה מיוחד וזה עדיין היה מתקמפל.

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

נקודה מעניינת לגבי main שלמדתי לא מזמן:
היא בכלל לא נקראת ישירות ע"י מערכת הפעלה (לפחות לא בלינוקס).

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

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

להערכתי, תופעת לוואי יחידה לאי החזרת ערך מ-main היא שלתוכנית יהיה "קוד יציאה" אקראי (תלוי מה היה הדבר האחרון שעשו עם אוגר EAX).
 

make clean

New member
לגבי scanf אתה כמובן צודק

בכ"מ GCC אצלי כן הצליח לקמפל עם implicit cast בין int ו int* .

והערה\שאלה - עד כמה שאני יודע ערך ההחזרה הוא לא חלק מה mangaled name גם ב C++ , לא?
 

BravoMan

Active member
למען האמת - אין לי מושג.

אני יודע ששיטת ה-mangling היא לא חלק מהסטנדרט ולכן הקומפיילר של MS מערבב בצורה שונה מ-gcc ולקומפיילרים אחרים יש עוד שיטות.

אבל אני לא בקיע בפרטי השיטה.
גיגול מהיר מגלה את זה: http://en.wikipedia.org/wiki/Name_mangling
 
אתה לא מדייק קודם כל לעניין main:

- מבחינת התקן, main חייבת להחזיר int. במידה ולא רשום ערך חזרה בכלל, הקומפיילר יכול להניח שהיא מחזירה int. במידה וכתוב במפורש ערך חזרה שונה מ int (למשל void), זו שגיאה לא-תקנית. VC מאפשר את זה כהרחבה לא-תקנית.

- מבחינת פרמטרים לפונקציות: אין בשפת C המרה מ int ל pointer to int. מה שקורה כאן הוא ש scanf היא פונקציה שלא מצהירה מראש איזה פרמטרים היא הולכת לקבל (מה שנקרא variable arguments function). ה format string הוא סתם פרמטר נוסף לפונקציה, שנקרא ע"י הפונקציה בזמן ריצה והוא לא חלק מהקומפילציה.

במילים אחרות, אם היית כותב:

int f(int *p);

int main()
{
int i=5;
f(i);
}

זו היתה שגיאה מיידית, משום שאין cast שמאפשר קריאה כזו.
אבל המקרה שלנו הוא כזה:

int f(char* fmt, ...);

int main()
{
int i=5;
f("kukuriku", i);
}


במקרה כזה הקומפיילר לא יודע מה הפרמטר הנכון לפונקציה, ולכן לא יכול לבדוק או להתריע.

מבולבלים? כדי לבלבל ולסבך את העניינים עוד יותר, המפתחים של GCC החליטו שבגלל שהשגיאות האלו הן כל כך נפוצות, ובכלל ש printf, scanf ודומותיה הן חלק מהספריה הסטנדרטית של השפה, הם יוסיפו בדיקות מיוחדות של פרמטרים לפונקציות האלו בהתבסס על פרסור של ה format string. זה טוב ויפה ונחמד, אבל זה לא סטנדרטי ולא קיים ב VC או בקומפיילרים אחרים.
 

make clean

New member
להפתעתי דווקא יש המרה

הקוד לקמן התקמפל אצלי gcc 4.6.3:

#include <stdio.h>

void foo(int *p) {
printf ("the adress is %p\n",p);
}


main() {
int i;
foo(i);
return 0;
}
 
הממ... צודק בערך בקימפול כ C אני מקבל:

temp.c:10: warning: passing argument 1 of ‘foo’ makes pointer from integer without a cast


בקימפול כ CPP אני מקבל:

temp.cpp:10: error: invalid conversion from ‘int’ to ‘int*’


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

selalerer

New member
לגבי main זה נכון ב-hosted environments.

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

בכל מקרה, כנראה שהיו קומפיילרים שיצאו לפני התקן (89) ויכול להיות שמשם התחיל כל העניין הזה של ה-void main.
 
למעלה