מבוא לתכנות ולמדעי המחשב בשפת C/לולאות for ומערכים: הבדלים בין גרסאות בדף

מתוך ויקיספר, אוסף הספרים והמדריכים החופשי
Hidro (שיחה | תרומות)
אין תקציר עריכה
Matanya (שיחה | תרומות)
מ 9 גרסאות
(אין הבדלים)

גרסה מ־18:06, 31 באוקטובר 2012

תבנית:ניווט מבוא


לולאת for

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

#include <stdio.h>

int main() {
	int i; 

	i=0; 
	while(i<7) {
		printf("i=%d ",i);
		printf("!\n");  

		++i; 
	}

	return 0; 
}

בגלל שלולאות אלה כל כך נפוצות, קיימת ב C דרך מקוצרת לכתוב אותן. הכתיב המקוצר נקרא לולאת for. הנה אותו קוד בדיוק כתוב בעזרת לולאה כזו:

#include <stdio.h>

int main() {
	int i; 

	for(i=0; i<7; ++i) {
		printf("i=%d ",i);
		printf("!\n");  
	} 
	
	return 0; 
}

התחביר של לולאת for מורכב משלושה חלקים מופרדים בסמני ; (נקודה פסיק):

 
for(מתבצע בסוף כל איטרציה; תנאי סיום לולאה;אתחול, מתבצע לפני הלולאה) {..}

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

הנה דוגמה נוספת:

#include <stdio.h>

int main() {

	int i; 
	for(i=100; i>0; i -= 10)
		printf("*"); 

	printf("\n"); 

	return 0; 
}

פלט:

**********

מכיוון שלולאת for היא לולאת while לכל דבר אפשר לכתוב בעזרתה גם לולאות שאינן לולאות מונות פשוטות. לדוגמה:

	for(;;) 
		printf("repeat forever!!\n");

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

הערה בקשר לשימוש ב continue בתוך לולאת for: החלק שמבוצע אחרי כל איטרציה מבוצע גם אחרי ה continue. לדוגמה, הקוד שכתבנו בעזרת while ו continue יראה כך:

#include <stdio.h>

int main() {
	int i; 
	
	for(i=0; i<7; ++i) {
		if(i == 4) {
			printf("Hey, it's four!\n"); 
			// no need for ++i here!  
			continue;
		} 
		printf("i=%d\n",i);
	}
	
	return 0; 
}

קינון לולאות

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

#include <stdio.h>

int main() {
	int i,j; 

	for(i=1; i<=10; ++i) {
		for(j=1; j<=10; ++j)
			printf("%d\t",i*j);
		printf("\n");  
	}

	return 0; 
}

פלט:

1	2	3	4	5	6	7	8	9	10	
2	4	6	8	10	12	14	16	18	20	
3	6	9	12	15	18	21	24	27	30	
4	8	12	16	20	24	28	32	36	40	
5	10	15	20	25	30	35	40	45	50	
6	12	18	24	30	36	42	48	54	60	
7	14	21	28	35	42	49	56	63	70	
8	16	24	32	40	48	56	64	72	80	
9	18	27	36	45	54	63	72	81	90	
10	20	30	40	50	60	70	80	90	100

סימן ההדפסה t\ מייצג TAB, רווח בגודל קבוע שאינו תלוי במספר הספרות שהודפסו (דרוש ליישור העמודות בפלט).


הנה דוגמה נוספת הכוללת קינון של לולאות. הקוד הבא מנסה להדפיס משהו שנראה כמו כביש בנסיעה.

#include <stdio.h>
#include <stdlib.h> 

int main() {
	int i, width = 7, loc = 15, d, t; 
	char c = '|'; 
	
	while(1) {

		d = rand()%3 - 1; // -1,0 or 1 
		t = loc + d; 

		if(t >= 0 && t+width <= 70) 
			loc = t; 

		for(i=0; i<loc; ++i)
			printf(" "); 
		printf("%c",c); 

		for(i=0; i<width; ++i)
			printf(" "); 

		printf("%c\n",c);  
		
		for(i=0; i<1e8; ++i); // pause 
	}

	return 0; 
}

פלט:

               |       |
               |       |
              |       |
              |       |
               |       |
               |       |
               |       |
              |       |
             |       |
             |       |
              |       |
              |       |
               |       |
               |       |
                |       |
                |       |

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

מערכים

עד עכשיו השתמשנו במשתנים בעלי שמות כמו a, result או myVar. לכל משתנה כזה היינו צריכים להצהיר מראש ולהגדיר מה הטיפוס שלו. מערך הוא סידרה של משתנים המקובצים יחד תחת שם אחד, שאפשר להגדיר בהצהרה בודדת. לדוגמה:

#include <stdio.h>

int main() {
	int a[7]; 

	a[4] = 24; 
	a[1] = 23; 
	a[3] = a[1]+a[4]; 
	printf("a[3] = %d\n",a[3]); 
	return 0; 
}

פלט:

a[3] = 47


ההצהרה

int a[7];

יוצרת סדרה של שבעה משתנים ושמה a. שם לכל אחד מהמשתנים בסדרה נוצר ע"י תוספת אינדכס, שערכו בין 0 ל 6:

a[0],a[1],a[2],...a[6]

note that a[7] was not declared!

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

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

#include <stdio.h>

int main() {
    int a[7],i; 
    for(i=0; i<7; ++i) {
	printf("Please enter a number:\n"); 
	scanf("%d",& a[i] ); 
    }
    
    printf("The numbers you have entered: "); 
    for(i=0; i<7; ++i)
	printf("%d, ",a[i]); 
    printf("\n"); 
    
    return 0; 
}

פלט:

Please enter a number:
1
Please enter a number:
22
Please enter a number:
333
Please enter a number:
44444
Please enter a number:
555
Please enter a number:
66
Please enter a number:
77
The numbers you have entered: 1, 22, 333, 44444, 555, 66, 77,

היכולת להתייחס למשתנה ששמו מורכב משם המערך בתוספת אינדקס שהוא משתנה או ביטוי, פותחת בפנינו אפשרויות רבות. הביטוי [a[i יכל להתייחס לכל אחד משבעת המשתנים שבמערך, תלוי בערכו של i המייצג כאן את האינדקס. אילו היינו רוצים לכתוב את התכנית שלעיל ללא שימוש במערך היינו צריכים להגדיר שבעה משתנים שונים, ולהתייחס לכל אחד בהוראה נפרדת. חישבו על מצבים שבהם אנו צריכים לטפל בעשרות (למשל, אם נרצה לשמור ציונים של תלמידים בכיתה, ולחשב ממוצעים, ציון מקסימלי או מינימלי, וכו'), מאות או אלפי משתנים.

בכדי לתת דוגמה נוספת ליכולות שנובעות משימוש באינדקס משתנה, נשכלל קצת את הקוד הקודם ונוסיף היפוך של סדר הערכים במערך:

#include <stdio.h>

int main() {
    int a[7],i; 
    for(i=0; i<7; ++i) {
	printf("Please enter a number:\n"); 
	scanf("%d",& a[i] ); 
    }
    
    for(i=0; i<7/2; ++i) {
	int t = a[i];  
	a[i] = a[6-i]; 
	a[6-i] = t;
    } 

    printf("The numbers you have entered (in a reverse order): "); 
    for(i=0; i<7; ++i)
	printf("%d, ",a[i]); 
    printf("\n"); 
    
    return 0; 
}

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


תבנית:ניווט מבוא