עזרה ב-C

saar85

New member
עזרה ב-C

היי חברים. אני צריך לבנות פונקציה שקולטת 9 ספרות של תעודת זהות, הבדיקה היחידה שצריך זה שיהיו 9 ספרות ושכל ספרה תהיה בין 0-9. אסור לי להשתמש במערך. איך אני עושה את הבדיקה הזאת?
כתבתי את הקוד הבא, אבל הוא לא ממש עובד:
קוד:
void GetId(){
	int count=0;
	char idDigit;
	do{
		scanf("%c", &idDigit);
		count++;
	} while (idDigit >= '0' && idDigit >= '9' && count < 9);
}

תודה מראש
 

BravoMan

Active member
לא ממש עובד או ממש לא עובד?

תראה, אני באמת לא רוצה לצחוק עליך או להעליב אותך, אני מבין שאתה חדש בפורום ובשפת C, אבל זה מעיק שהרבה אנשים כמוך, כולל אותך, באים לפה וכותבים "לא עובד" או משהו בנוסך דומה, ומשאירים לנו לנחש מה בדיוק לא טוב להם.
&nbsp
כשאתה בא לרופא אתה אומר לו: "אני לא מרגיש טוב" ומשאיר לו לנחש מה הסימפטומים שלך, או שאתה אומר: "כואבת לי הבטן" או "יש לי חום ושיעול"?
כשאתה בא למוסך, אתה אומר להם "משהו לא בסדר עם האוטו שלי" או שאתה מתאר מה בדיוק התקלה?
&nbsp
אותו הדבר בפורום - אל תגיד "הקוד לא עובד לי", אלא ספר בדיוק מה קורה, ומה ציפיתה שיקרה במקום, כדי שנדע מה הבעיה ונוכל לפתור לך אותה.
עזור לנו לעזור לך ולכולנו יהיה טוב יותר!
&nbsp
ספציפית לשאלתך:
במבט חטוף, יש בעיה עם התנאי של הלולאה: כתבת פעמיים =< (גדול שווה) במקום לכתוב פעם אחת => (קטן שווה) ולכן התנאי מתקיים רק אם הוכנסו תווים גדולים מ-'9'.
 

saar85

New member
תודה

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

BravoMan

Active member
יופי, עכשיו התקדמנו:

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

saar85

New member
זה עדיין לא עובד

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

קוד:
void GetId(){
	int count=0;
	char idDigit;
	do{
		idDigit = getchar();
		count++;
	} while (idDigit >= '0' && idDigit <= '9' && count < 9);
	if (count < 9)
		printf("Invalid ID");
}
 

saar85

New member
טוב קצת שיניתי את הקוד ועכשיו זה עובד בשעה טובה

זה הקוד שכתבתי בסוף:
קוד:
void GetId(){
	int count=0;
	char idDigit;
	idDigit = getchar();
	while (idDigit >= '0' && idDigit <= '9')
	{
		idDigit = getchar();
		count++;
	}
	if (count != 9)
	{
		printf("Invalid ID. Please enter your ID again:\n");
		fflush(stdin);
		GetId();
	}
}
 

EyesToSee

New member
לוגיקה מוזרה, פונקציה רקורסבית,בדוק שוב.

לוגיקה:
קוראים תוו,תוו ואזי יוצאים מהלולאה המרכזית כאשר נקראו , בדיוק, 9 תווים. אז למה בודקים שמספר התווים שנקראו שונה מ 9?

למה קוראים לפונקציה שוב (בצורה רקורסיבית)?
 

saar85

New member
במידה והקלט לא נכון

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

BravoMan

Active member
באיזו מערכת הפעלה אתה עובד?

על פניו, הקוד המתוקן הראשון שרשמת הוא הפתרון הנכון, בעוד שהגרסה השנייה כוללת בעיה רצינית:
כל עוד המשתמש מקליד ספרות, הלולאה לעולם לא תסתיים!
הרי אתה כלל לא מתייחס ל-count בתנאי שלך.
&nbsp
בעולם האמתי, ע"ג מערכות NIX למיניהן, הפונקציה getchar באמת לא תיכנס לפעולה עד אשר תתקבל לחיצה על enter, אבל לא בגלל הפונקציה עצמה, אלא בגלל הצורה בה עובדים המסופים.
&nbsp
הדרכים היחידות להתגבר על זה הן להשתמש בספריה חיצונית כמו ncurses לקלט או לשנות הגדרות מסוף, ואף אחת מאלה לא מתאימה לסטודנטים מתחילים, לכן, בזמנו כעשינו קורסי מבוא ב-C, המנחה אמר לנו להתעלם מההתנהגות הזו, ולדאוג לאלגוריתם שהיה נכון מבקרה שהפונקציה כן היית מגיבה מיד.
&nbsp
אני לא זוכר מה ההתנהגות ב-Windows, אבל אם אתה עובד על המערכת הזו עם VS, יש להם פונקציה לא סטנדרטית שנקראת getch, והיא בוודאות לא ממתינה ל-enter, אבל היות והיא לא סטנדרטית, לא בטוח אם מותר לך להשתמש בה.
&nbsp
וספציפית לגבי הפתרון הרקורסיבי - הוא נכון, אבל האם מותר לך להשתמש בו?
אני מניח שאם תרם למדת מערכים, גם תרם למדת רקורסיה, אז כנראה שאסור לך להשתמש בה, והמרצה מצפה שתמצא פתרון אחר לחזור שוב על הקלט, בלי לקרוא לפונקציה מחדש.
 

saar85

New member
אני משתמש בווינדוס

ומתכנת בVisual Studio 2013. האמת אחרי שהעליתי את הקוד עשיתי תיקון אחד נוסף. כן למדנו מערכים אבל בעבודה הנוכחית עדיין אסור לנו להשתמש בהם. אני חושב שהפתרון הכי פשוט זה אם התנאי לא נכון אז לקרוא לפונקציה מחדש. שמע בינתיים זה עובד לי מעולה. אני מעלה את הקוד אחרי התיקון הנוסף שעשיתי.

קוד:
void GetId(){
	boolean FLAG = TRUE;
	int count=0;
	char idDigit;
	idDigit = getchar();
	while (idDigit >= '0' && idDigit <= '9')
	{
		idDigit = getchar();
		if (!(idDigit >= '0' && idDigit <= '9' || idDigit == '\n')) //Checks if your put char between 0 to 9
			FLAG = FALSE;
		else
			FLAG = TRUE;
		count++;
	}
	if (count != 9 || FLAG == FALSE) //If the ID is invalid
	{
		printf("Invalid ID. Please enter your ID again:\n");
		fflush(stdin);
		GetId(); //Call to function again
	}
}
 

BravoMan

Active member
למה הוספת סיבוך מיותר?

מה עוזר לך שוב לבדוק בדיוק את אותו התנאי שכבר בדקת בתחילת הלולאה?
המשתנה FLAG הוא בזבוז זיכרון!
&nbsp
אתה עדיין לא יוצא מהלולאה כל עוד המשתמש מקליד ספרות, מה שאומר שלא פתרת נכון את התרגיל.
האם לפי תנאי התרגיל אתה אמור לתת למשתמש להזין ספרות עד אין קץ, ורק אם הוא הזין יותר מ-9 ספרות לומר לו שזה לא תקין?
אם כן, אז אין בעיה, אם לא, אז כדאי שתחשוב שוב על התנאי!
&nbsp
בכל מקרה, אם תנסה להריץ את התוכנה ידנית - לעקוב אחריה על דף נייר, תראה שהבדיקה הנוספת של התנאי, כולל 'n\' מיותרת לחלוטין, וקוד מיותר מוריד ציון!
&nbsp
אתה צודק שלקרוא שוב לפונקציה זו הדרך הפשוטה*, אבל השאלה היא האם המרצה שלכם מוכן לקבל פתרון כזה?
זה כבר משהו שעליך לברר איתו...
&nbsp
&nbsp
* למעשה, אם זה היה בעולם האמתי ולא באקדמיה, היית מקבל על הראש על פתרון כזה, כי אם המשתמש יזין יותר מידי פעמים מספר שגוי תקבל stack overflow מרוב רקורסיה, אבל מן הסתם בפתרון שאלה אקדמית זה לא משנה.
 

Javali

New member
למעשה בעולם האמיתי

למעשה, בעולם האמיתי, stack overflow לרוב לא ייקרה כי המהדר יזהה את הtail recursion ויעיף אותו כך שלא משנה כמה פעמים מזינים מספר שגוי התוכנית לא תעוף. דוגמה:
קוד:
$ cat tail.c
#include <stdio.h>
#include <stdlib.h>

long long  tail(long long  n) {
  if (n == 0LL)
    return n;
  return n+tail(n-1);
}

int main(int ac, char **av) {
  printf("%lld\n", tail(atoll(av[1])));
  return 0;
}
$ cc -O2 -o tail tail.c
$ ulimit -s 16
$ time ./tail 1000000000
500000000500000000

real	0m0.279s
user	0m0.279s
sys	0m0.000s
$
במאק שלי הקומפילר מזהה שזו סדרה חשבונית ומחליף את הקוד בנוסחת הסכום...
 

saar85

New member
לפני שהוספתי את התנאי הזה

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

BravoMan

Active member
אתה שוכח משהו קטן:

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