Transcript lesson5

‫קורס תכנות‬
‫שיעור חמישי‪ :‬מערכים ומחרוזות‬
‫‪1‬‬
‫בשיעור הקודם למדנו על רקורסיה‬
‫• פתרנו את בעיית מגדלי הנוי בעזרת רקורסיה‬
‫• כלומר בעזרת פונקציה שקוראת לעצמה‪.‬‬
‫• רקורסיה מאפשרת לנו לפתור בעיה "גדולה" בעזרת‬
‫פתרון של בעיות "קטנות" המרכיבות אותה‪.‬‬
‫• בכל קריאה רקורסיבית אנחנו "מקטינים" את הבעיה‬
‫ולבסוף מגיעים למקרה קצה שאותו קל לפתור באופן‬
‫ישיר‪.‬‬
‫פתרון בעיה "גדולה" בעזרת פתרון בעיות "קטנות"‬
‫!‪Easy‬‬
‫‪3‬‬
‫פונקציה רקורסיבית‬
‫• פונקציה רקורסיבית מורכבת משלושה חלקים‪:‬‬
‫‪ .1‬הקטנת הבעיה‬
‫‪ .2‬פתרון הבעיה המקורית בעזרת פתרון לבעיה המוקטנת‬
‫‪ .3‬מקרה בסיס שאותו אנחנו יודעים לפתור (בסיס הרקורסיה)‬
‫‪4‬‬
C ‫חישוב נוסחאות רקורסיביות בשפת‬
n! = n*(n-1)!
0! = 1
int factorial (int n) /* n >= 0 */
{
if (n == 0)
return 1;
return (n * factorial(n-1));
}
‫• עצרת‬
‫שאלה‬
‫• נרצה לקלוט ‪ N‬מספרים שלמים (‪ N‬ידוע)‪ ,‬ולאחר מכן להדפיס‬
‫אותם בסדר הפוך מהסדר שקלטנו אותם (‪ +‬אופציה לפעולות‬
‫נוספות על הקלט)‪ .‬כיצד נוכל לעשות זאת?‬
‫‪0 5 4‬‬
‫• לדוגמא‪ ,‬עבור ‪ N=3‬והקלט‬
‫‪4 5 0‬‬
‫הפלט צריך להיות‪:‬‬
‫• פתרון אפשרי בעזרת ‪ N‬משתנים‬
‫;‪a3‬‬
‫;)‪&a3‬‬
‫‪&a2,‬‬
‫‪a2,‬‬
‫)(‪int main‬‬
‫{‬
‫‪int a1,‬‬
‫‪scanf("%d%d%d", &a1,‬‬
‫;)‪printf("%d %d %d\n", a3, a2, a1‬‬
‫;‪return 0‬‬
‫‪6‬‬
‫{‬
N = 30
?N=30 ‫• איך תראה התכנית עבור‬
int main()
{
int a1,
a2, a3, a4, a5, a6, a7, a8, a9, a10,
a11, a12, a13, a14, a15, a16, a17, a18, a19, a20,
a21, a22, a23, a24, a25, a26, a27, a28, a29, a30;
scanf("%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d",
&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10,
&a11, &a12, &a13, &a14, &a15, &a16, &a17, &a18, &a19, &a20,
&a21, &a22, &a23, &a24, &a25, &a26, &a27, &a28, &a29, &a30);
printf("%d %d %d
"%d %d %d
a30, a29,
a20, a19,
a10, a9,
%d %d %d %d %d
%d %d %d %d %d
a28, a27, a26,
a18, a17, a16,
a8, a7, a6,
%d %d %d %d %d
%d %d %d %d %d
a25, a24, a23,
a15, a14, a13,
a5, a4, a3,
%d %d "
%d %d",
a22, a21,
a12, a11,
a2, a1);
return 0;
{
?N=1000000 ?N=1000 ‫• מה יקרה עבור‬
...‫• האם לולאות יעזרו? לא‬
)‫ אבל יש פתרון טוב יותר‬...‫• האם רקורסיה יכולה לעזור? (כן‬7
‫מה היינו רוצים‬
‫• הרבה פעולות זהות על משתנים מאותו סוג – היינו רוצים‬
‫לכתוב אותן פעם אחת בלבד‪:‬‬
‫• להגדיר בבת‪-‬אחת ‪ N‬משתנים מאותו סוג‬
‫• לכתוב פעם אחת את פקודת הקלט‪ ,‬ושהמחשב יבצע אותה‬
‫‪ N‬פעם‪ ,‬כל פעם למשתנה המתאים‬
‫• לכתוב פעם אחת את הדפסת הפלט‪ ,‬ושזה יבוצע עבור כל‬
‫אחד מהמשתנים בסדר המתאים‬
‫‪8‬‬
‫מערכים‬
‫• אוסף סדור של משתנים מאותו סוג‬
‫• הגדרה יחידה נותנת לנו מספר רב של משתנים‬
‫• הסדר יאפשר לנו לקרוא‪/‬לכתוב למשתנה מסוים‬
‫• המשתנים נמצאים ברצף בזיכרון‬
‫‪. . . . . . . 7‬‬
‫‪9‬‬
‫‪9 .‬‬
‫הגדרת המערך‬
‫• מערך של ‪ 30‬משתנים מטיפוס ‪int‬‬
‫;]‪int grades[30‬‬
‫• מערך של ‪ 100‬משתנים מטיפוס ‪double‬‬
‫;]‪double prices[100‬‬
‫• מערך של ‪ 256‬משתנים מטיפוס ‪char‬‬
‫;]‪char text[256‬‬
‫• באופן כללי‬
‫;]‪element-type variable-name[size‬‬
‫‪10‬‬
‫גישה לאיבר (משתנה) במערך‬
‫• הגישה לאיבר במערך תתבצע לפי מיקומו במערך‬
‫• האיבר הראשון נמצא במקום ‪ ,0‬השני במקום ‪ 1‬וכן הלאה‬
‫;‪int grade = 0‬‬
‫‪...‬‬
‫;]‪grade = grades[0‬‬
‫• כיצד ניגש לאיבר האחרון במערך בגודל ‪?30‬‬
‫;]‪grade = grades[29‬‬
‫• גישה לאיבר ה‪ i-‬במערך‬
‫;]‪grade = grades[i-1‬‬
‫‪11‬‬
‫השמה לאיבר (משתנה) במערך‬
‫• השמה לאיבר השני‬
‫;‪grades[1] = 0‬‬
‫• השמה לאיבר ה‪i-‬‬
‫;‪arrayName[i-1] = expr‬‬
‫• ניתן להשתמש באיבר במערך בכל מקום בו ניתן‬
‫להשתמש במשתנה!‬
‫‪12‬‬
‫גישה לתאים במערך‬
‫• ניתן לגשת לתא במערך ע"י כתיבת שם המערך ומספרו‬
‫הסידורי של האיבר בסוגריים מרובעים‪.‬‬
‫• מספור התאים מתחיל מ‪.0 -‬‬
‫• תא מסוים במערך שקול למשתנה רגיל מאותו הסוג‪.‬‬
‫‪13‬‬
‫מעבר על כל התאים במערך‬
‫• נוכל לעבור על כל תאי המערך בעזרת מעבר על כל‬
‫האינדקסים של המערך‬
int i;
for (i = 0; i < 30; i++)
{
grades[i] = 100;
}
‫• ממוצע הציונים בקורס‬
int i, sum = 0, number_of_student;
double average;
...
for (i = 0; i < number_of_students; i++)
sum += grades[i];
average = sum / number_of_students;
14
‫פתרון לשאלה‬
‫• כיצד נפתור כעת את השאלה מתחילת השיעור‬
‫הגדרת ‪ 30‬משתנים מטיפוס ‪int‬‬
‫)(‪int main‬‬
‫{‬
‫;]‪int numbers[30‬‬
‫;‪int i‬‬
‫קליטת ‪ 30‬ערכים (תאים ‪)0-29‬‬
‫)‪for (i = 0; i < 30; i++‬‬
‫;)]‪scanf("%d", &numbers[i‬‬
‫)‪for (i = 29; i >= 0; i--‬‬
‫;)]‪printf("%d ", numbers[i‬‬
‫;)"‪printf("\n‬‬
‫כתיבת ‪ 30‬ערכים (תאים ‪)29-0‬‬
‫;‪return 0‬‬
‫{‬
‫‪15‬‬
‫איתחול מערכים‬
‫• הקוד הבא מאתחל מערך המכיל את חמשת המספרים‬
‫האי‪-‬זוגיים הראשונים (‪)1, 3, 5, 7, 9‬‬
‫)(‪int main‬‬
‫{‬
‫;]‪int odds[5‬‬
‫;‪int i‬‬
‫)‪for (i = 0; i < 5; i++‬‬
‫‪odds[i] = i * 2 + 1‬‬
‫‪...‬‬
‫;‪return 0‬‬
‫{‬
‫‪16‬‬
‫איתחול מערכים‬
‫• עבור מערכים קטנים שערכי האיברים שלהם ידועים מראש‬
‫קיימת צורת איתחול בשורת ההגדרה‬
‫)(‪int main‬‬
‫{‬
‫;}‪int odds[] = {1, 3, 5, 7, 9‬‬
‫‪...‬‬
‫;‪return 0‬‬
‫{‬
‫• גודל המערך נקבע לפי מספר הערכים‬
‫‪17‬‬
‫אתחול מערכים בשורת ההגדרה‬
‫;}‪int A[]={0,1,2,3,4‬‬
‫;}‪int A[5]={0,1,2,3,4‬‬
‫• ניתן לאתחל מערכים בצורה זו אך ורק בשורת ההגדרה‪.‬‬
‫• הערכים מוצבים בתאים לפי הסדר החל מהתא הראשון‪.‬‬
‫• אם אין מספיק ערכים‪ ,‬שאר התאים מאותחלים לערך ‪.0‬‬
‫;}‪int A[5]={1,2‬‬
‫;}‪int A[5]={0‬‬
‫‪18‬‬
‫שימוש במערכים בתכנית‬
‫• לא ניתן לבצע פעולות על כל אברי המערך בו זמנית‬
‫• למעט באיתחול שבו ביצענו השמה לכל התאים במערך‬
‫• פעולות על המערך יבוצעו איבר איבר‬
‫• איפוס המערך‬
‫)‪for (i = 0; i < 100; i++‬‬
‫;‪array[i] = 0‬‬
‫• העתקת מערך אחד לשני‬
‫• לא ע"י ‪array1 = array2‬‬
‫)‪for (i = 0; i < 100; i++‬‬
‫;]‪array1[i] = array2[i‬‬
‫• כך גם פעולות השוואה‪ ,‬קלט‪/‬פלט וכדומה‬
‫‪19‬‬
‫שימו לב!‬
‫• חריגה מגבולות המערך היא טעות נפוצה‬
‫• כתוצאה‪:‬‬
‫• התכנית תקרוס (בסבירות גבוהה)‬
‫• לא תעבוד כראוי עם בעיות לא ברורות‬
‫;]‪double array[10‬‬
‫)‪for (i = 0; i <= 10; i++‬‬
‫;‪array[i] = 0‬‬
‫]‪ array[10‬אינו איבר במערך‬
‫‪20‬‬
‫הגדרת גודל המערך‬
‫• גודל המערך נקבע בעת הקומפילציה לפיכך הוא חייב להיות‬
‫ערך ידוע בזמן הקומפילציה ולא משתנה‬
‫‪21‬‬
‫;]‪double array[10‬‬
‫;]‪int grades[3 + 5‬‬
‫]‪char text[256‬‬
‫✓‬
‫;]‪double array[x‬‬
‫;]‪int grades[y‬‬
‫]‪char text[10 * i‬‬
‫✘‬
‫בחזרה לפתרון לשאלה‬
?‫ המופיעים בתכנית? האם הם קשורים‬30-‫• מהם כל ה‬
?29 ‫האם הם קשורים למספר‬
#include <stdio.h>
int main()
{
int numbers[30];
int i;
for (i = 0; i < 30; i++)
scanf("%d", &numbers[i]);
for (i = 29; i >= 0; i--)
printf("%d ", numbers[i]);
printf("\n");
return 0;
{
22
#define
‫• ניתן להגדיר קבועים סימבוליים במקום מספרים‬
#define SYMBOLIC_NAME value
#include ‫• ההגדרות תופענה בתחילת הקובץ לאחר ה‬
#define ARRAY_SIZE 30
#define TRUE 1
#define FALSE 0
23
‫בחזרה לפתרון לשאלה‬
?‫ המופיעים בתכנית? האם הם קשורים‬30-‫• מהם כל ה‬
?29 ‫האם הם קשורים למספר‬
#include <stdio.h>
#define ARRAY_SIZE 30
int main()
{
int numbers[ARRAY_SIZE];
numbers[30];
int i;
for (i = 0; i < ARRAY_SIZE;
30; i++)
i++)
scanf("%d", &numbers[i]);
for (i = ARRAY_SIZE
29; i >= 0;-i--)
1; i >= 0; i--)
printf("%d ", numbers[i]);
printf("\n");
‫ מספרים כמה שינויים נצטרך לבצע בתכנית‬100 ‫כדי לטפל ב‬
return 0;
?
define
‫? וכמה עם שימוש ב‬define ‫ללא‬
{
24
#define
#define ARRAY_SIZE 10
‫ גורמת לקומפיילר להחליף (בשלב העיבוד‬define-‫• שורת ה‬
.10-‫ ב‬ARRAY_SIZE ‫המוקדם) כל מופע בתוכנית של‬
.‫רק אח"כ מתבצע התרגום לשפת מכונה‬
:"‫ ההחלפות האלה לא מתבצעות בתוך "מחרוזת‬:‫• שימו לב‬
printf("Please enter ARRAY_SIZE numbers:\n");
Please enter ARRAY_SIZE numbers:
printf("Please enter %d numbers:\n", ARRAY_SIZE);
Please enter 10 numbers:
25
‫קונבנציות‬
‫מעתה והלאה‪...‬‬
‫• שמות משתנים ושמות פונקציות יכתבו באותיות קטנות‬
‫‪size, array, power, num1, is_digit‬‬
‫• ‪ Defines‬יכתבו רק באותיות גדולות‬
‫‪ARRAY_SIZE, TRUE, FALSE‬‬
‫‪26‬‬
‫ היסטוגרמה‬:‫ דוגמא נוספת‬- ‫מערכים‬
#include<stdio.h>
#define DIGITS_NUM 10
‫התוכנית קולטת מספר שלם חיובי ומדפיסה‬
‫כמה פעמים הופיעה בו כל סיפרה‬
int main()
{
int i, num, digits[DIGITS_NUM]={0};
printf(“Enter a positive integer:\n”);
scanf(“%d”, &num);
while (num!=0)
‫ במערך יכיל‬i ‫תא‬
{
‫את מספר ההופעות של‬
digits[num%DIGITS_NUM]++;
num ‫ במספר‬i ‫הספרה‬
num=num/DIGITS_NUM;
}
for (i=0; i<DIGITS_NUM; i++)
printf(“Digit %d appeared %d times\n”, i, digits[i]);
return 0;
}
27
‫העברת מערך לפונקציה‬
‫• אפשר להעביר לפונקציה מערכים‬
‫• גודל המערך לא מועבר!‬
‫• באחריותנו לדאוג שהפונקציה לא תחרוג ממנו‬
‫• בד"כ נרצה להעביר את גודלו כפרמטר נוסף‬
‫• בשונה ממשתנים אחרים‪:‬‬
‫• כשמעבירים מערך לפונקציה הוא אינו מועתק‬
‫• לפונקציה מועבר המערך המקורי‬
‫• שינוי של ערכי המערך בתוך הפונקציה ישנו את המערך המקורי!!‬
‫• מה שמועבר לפונקציה הוא כתובת המערך‬
‫• כלומר המיקום של המערך המקורי בזיכרון‬
‫‪28‬‬
‫העברת מערך לפונקציה ‪ -‬דוגמא‬
‫•‬
‫•‬
‫נדגים פונקציה שמקבלת מערך של מספרים שלמים ואת גודלו‪,‬‬
‫ומחזירה את סכום המספרים במערך‪.‬‬
‫הקריאה לפונקציה‪:‬‬
‫;)‪sum = array_sum(array, size‬‬
‫•‬
‫כותרת הפונקציה‪:‬‬
‫;)‪int array_sum(int array[], int size‬‬
‫•‬
‫‪29‬‬
‫אין צורך לציין את גודל המערך בתוך ה‪ ] [-‬בכותרת הפונקציה‬
‫מערכים ופונקציות‬
‫• נכתוב פונקציה המקבלת מערך של מספרים שלמים ומחזירה‬
‫את סכום ערכי המערך‬
‫)‪int array_sum(int array[], int size‬‬
‫{‬
‫;‪int i, sum‬‬
‫)‪for (i = 0; i < size; i++‬‬
‫;]‪sum += array[i‬‬
‫;‪return sum‬‬
‫{‬
‫• הפונקציה מקבלת את המערך וגודלו‪ ,‬ציון גודל המערך בין‬
‫הסוגריים המרובעים לא יעזור (הסבר בהמשך הקורס)‬
‫‪30‬‬
‫קריאה לפונקציה שמקבלת מערך‬
#include <stdio.h>
#define ARRAY_SIZE 10
int main()
{
int numbers[ARRAY_SIZE];
int i, sum;
printf("Please enter %d integers\n", ARRAY_SIZE);
for (i = 0; i < ARRAY_SIZE; i++)
‫מערך לפונקציה ע"י שימוש‬
scanf("%d", &numbers[i]);
‫בשם המערך‬
‫העברת‬
sum = array_sum(numbers, ARRAY_SIZE);
printf("The sum is %d\n", sum);
return 0;
{
31
‫פונקציה שממלאת מערך‬
‫)‪void array_fill(int array[], int size, int val‬‬
‫{‬
‫;‪int i‬‬
‫)‪for (i = 0; i < size; i++‬‬
‫;‪array[i] = val‬‬
‫{‬
‫• בתוך פונקציה ניתן לשנות את הערכים במערך המקורי!‬
‫• בניגוד למשתנים רגילים‪...‬‬
‫‪32‬‬
‫שינוי ערכי המערך‬
int main()
{
int i, numbers[ARRAY_SIZE];
array_fill(numbers, ARRAY_SIZE, 10);
for (i = 0; i < ARRAY_SIZE; i++)
printf("%d ", numbers[i])
return 0;
{
10 10 10 10 10 10 10 10 10 10 10
:‫• הפלט‬
33
‫דוגמא – פונקציה שהופכת מערך‬
‫• נניח שרוצים לכתוב פונקציה שמקבלת מערך והופכת אותו‬
‫• כלומר‪ ,‬בהינתן המערך‪:‬‬
‫‪9‬‬
‫‪8‬‬
‫‪67‬‬
‫‪2‬‬
‫‪1‬‬
‫‪13‬‬
‫‪5‬‬
‫• הפונקציה תשנה אותו למערך‪:‬‬
‫‪5‬‬
‫‪34‬‬
‫‪13‬‬
‫‪1‬‬
‫‪2‬‬
‫‪67‬‬
‫‪8‬‬
‫‪9‬‬
‫פונקציה שהופכת מערך ‪ -‬שלבים‬
‫• מה נרצה לעשות?‬
‫• להחליף בין התא הראשון לתא האחרון‬
‫• אח"כ להחליף בין התא השני לתא לפני אחרון‬
‫• וכו'‬
‫• מימוש‪:‬‬
‫• לולאה מתחילת המערך ועד אמצעו‬
‫• בכל איטרציה‪ ,‬להחליף את התא הנוכחי עם המתאים לו‬
‫מסוף המערך‬
‫‪35‬‬
‫‪9‬‬
‫‪8‬‬
‫‪67‬‬
‫‪2‬‬
‫‪1‬‬
‫‪13‬‬
‫‪5‬‬
‫‪5‬‬
‫‪13‬‬
‫‪1‬‬
‫‪2‬‬
‫‪67‬‬
‫‪8‬‬
‫‪9‬‬
‫ מימוש‬- ‫פונקציה שהופכת מערך‬
void reverse(int array[], int size)
{
int i, temp;
for (i = 0; i < size / 2; i++)
{
temp = array[i];
array[i] = array[size - 1 - i];
array[size - 1 - i] = temp;
}
{
36
‫דוגמא‪ :‬מחיקת איבר במערך‬
‫• נרצה למחוק ערך (‪ )value‬מהמערך‬
‫• נמצא את הערך במערך‬
‫• נזיז את הסיפא של המערך מקום אחד שמאלה‬
‫• שלבים‪:‬‬
‫• פונקציה (‪ )find‬שמוצאת ערך במערך ומחזירה את מיקומו‬
‫או ‪ -1‬אם איננו בנמצא‬
‫• פונקציה (‪ )delete‬שמוחקת ערך ומחזירה ‪ TRUE‬אם‬
‫מחקה ו‪ FALSE -‬אם לא‬
‫• פונקציית ‪ main‬שמחברת את הכל‬
‫‪37‬‬
‫פונקציה למציאת ערך במערך‬
int find(int array[], int size, int value)
{
int i;
for (i = 0; i < size; i++)
{
if (array[i] == value)
return i;
}
return -1;
{
38
‫פונקציה למחיקת איבר‬
#define TRUE 1
#define FALSE 0
int delete(int array[], int size, int value)
{
int i;
int position = find(array, size, value);
if (position == -1)
return FALSE;
for (i = position; i < size - 1; i++)
array[i] = array[i + 1];
return TRUE;
{
39
‫התכנית השלמה‬
int main()
{
int numbers[ARRAY_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int val = 0;
printf("Before delete: ");
print_array(numbers, ARRAY_SIZE);
printf("Choose value for deletion: ");
scanf("%d", &val);
if (delete(numbers, ARRAY_SIZE, val)) {
‫גודל המערך‬
printf("After delete: ");
print_array(numbers, ARRAY_SIZE - 1);
} else {
printf("couldn't find %d\n", val);
}
return 0;
‫לא לשכוח לשנות את‬
{
40
‫לא ניתן להחזיר מערך מפונקציה‬
‫‪Syntax Error‬‬
‫)‪int[] copy10(int array[], int size‬‬
‫{‬
‫;‪int i‬‬
‫;}‪int result[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0‬‬
‫)‪for (i = 0; i < size && i < 10; i++‬‬
‫;]‪result[i] = array[i‬‬
‫;‪return result‬‬
‫{‬
‫• לא ניתן להחזיר מערך כערך מוחזר‬
‫• ככל משתנה‪ ,‬בעת סיום הפונקציה הוא מפסיק להתקיים‬
‫‪41‬‬
‫רקורסיה במערך‬
‫• באופן כללי נוח להפעיל רקורסיה על מערכים‬
‫• אפשר להתייחס לתת‪-‬מערך כמערך קצר יותר ולהפעיל עליו‬
‫פונקציה רקורסיבית‪.‬‬
‫‪5‬‬
‫‪13‬‬
‫‪1‬‬
‫‪2‬‬
‫‪67‬‬
‫‪8‬‬
‫• לדוגמא – אם נרצה למצוא את האיבר המקסימלי במערך‬
‫‪42‬‬
‫• נמצא את האיבר המקסימלי במערך שמתחיל באיבר השני‬
‫• נשווה אותו לאיבר הראשון ונחזיר את הגדול מביניהם‬
‫• מה הוא בסיס הרקורסיה?‬
‫• במערך בגודל ‪ – 1‬נחזיר את הערך היחיד בו‪.‬‬
‫‪9‬‬
‫רקורסיה במערך – מציאת ערך מקסימלי‬
int maxNum(int arr[], int startIndex, int endIndex)
{
int max;
if (startIndex == endIndex)
return arr[startIndex];
max = maxNum(arr, startIndex+1, endIndex);
if (arr[startIndex] > max)
return arr[startIndex];
return max;
{
int main()
}
int arr[] = {9,8,67,2,113,5};
printf ("The maximal number is: %d\n", maxNum(arr,0,5));
43
{
‫מערכים דו ממדיים‬
‫• משמשים לייצוג מטריצות או טבלאות‬
‫• שימוש בשני אינדקסים – שורות ועמודות‬
‫• הגדרה‪:‬‬
‫;]‪data_type array_name[row_size][column_size‬‬
‫• מטריצה של ‪ int‬בגודל ‪10×20‬‬
‫]‪int matrix[10][20‬‬
‫• גישה לתא בעזרת שני אינדקסים‬
‫‪44‬‬
‫התא הראשון בשורה הראשונה‬
‫]‪matrix[0][0‬‬
‫התא האחרון בשורה האחרונה‬
‫]‪Matrix[9][19‬‬
‫איתחול מערך רב מימדי‬
‫• בהגדרה‬
‫;} }‪int my_matrix[3][2] = { {1, 0}, {0, 1}, {1, 1‬‬
‫;} }‪int array2d[][3] = { {1, 0, 0}, {0, 1, 0}, {0, 0, 1‬‬
‫אין חובה לציין את הממד הראשון‪ ,‬אך חובה‬
‫לציין את הממדיים האחרים‪.‬‬
‫• איתחול בעזרת לולאות מקוננות‬
‫) ‪void init_array(int array[][100], int rows, int value‬‬
‫{‬
‫;‪int row, column‬‬
‫בהעברת מערך רב‬
‫)‪for (row = 0; row < rows; i++‬‬
‫מימדי לפונקציה‬
‫)‪for (column = 0; column < 100; column++‬‬
‫חייבים לכתוב את גדלי‬
‫;‪array[row][column] = value‬‬
‫המימד השני והלאה‬
‫}‬
‫‪45‬‬
‫לוח הכפל‬
#define SIZE 10
void init_table(int table[SIZE][SIZE]) }
int i, j;
for (i = 0; i < SIZE; i++)
for (j = 0; j < SIZE; j++)
table[i][j] = (i + 1) * (j + 1);
}
void print_table(int table[SIZE][SIZE]) }
int i, j;
for (i = 0; i < SIZE; i++) {
for (j = 0; j < SIZE; j++)
printf("%4d", table[i][j]);
printf("\n");
}
}
int main() }
int table[SIZE][SIZE];
init_table(table);
print_table(table);
return 0;
{
46
‫כפל מטריצות‬
‫• נכתוב תכנית שמחשבת כפל של ‪ 2‬מטריצות ‪ a,b‬בגודל ‪.3x3‬‬
‫• את התוצאה נשמור במטריצה ‪.c‬‬
‫• נוסחא לכפל מטריצות‪:‬‬
‫‪SIZE 1‬‬
‫]‪ a[i][k ]  b[k ][ j‬‬
‫‪k 0‬‬
‫‪47‬‬
‫‪c[i][ j ] ‬‬
#define MATRIX_SIZE 3
c[i][ j ] 
SIZE 1
 a[i][k ]  b[k ][ j]
k 0
int main(void)
{
int a[][MATRIX_SIZE] = { {1,2,3}, {4,5,6}, {7,8,9}};
int b[][MATRIX_SIZE] = { {1,0,0}, {0,2,0}, {0,0,3}};
int c[MATRIX_SIZE][MATRIX_SIZE];
int i = 0, j = 0, k = 0;
/* Compute the product c = a * b */
for (i = 0; i < MATRIX_SIZE; ++i)
for (j = 0; j < MATRIX_SIZE; ++j)
{
c[i][j] = 0;
for (k = 0; k < MATRIX_SIZE; ++k)
c[i][j] += a[i][k] * b[k][j];
}
/* Print c */
...
return 0;
}
48
49
‫מחרוזות‬
‫‪50‬‬
‫מחרוזות‬
‫• מהי מחרוזת?‬
‫• רצף של תווים‪ ,‬למשל‪“hello world” :‬‬
‫• כיצד נייצג מחרוזת במחשב?‬
‫• ‪ char‬מייצג תו בודד‬
‫• מחרוזת נייצג בעזרת מערך של תווים‬
‫• מה אורך המחרוזת? האם תופסת את כל המערך או רק את חלקו?‬
‫• אפשרות א'‪ :‬בנוסף לגודל המערך נשמור גם את אורך המחרוזת‬
‫• אפשרות ב'‪ :‬תו מיוחד יציין את סוף המחרוזת‪.‬‬
‫‪51‬‬
‫מחרוזות ב ‪C -‬‬
‫;"‪char str[20] = "hello world‬‬
‫• מחרוזת‪:‬‬
‫• מערך של תווים המייצגים מחרוזת‪.‬‬
‫• למשל מילה או משפט‪.‬‬
‫• בדרך כלל אנחנו לא נתייחס אל מחרוזות כאל מערכים‪:‬‬
‫• נתייחס למחרוזת כולה בבת אחת‪.‬‬
‫• נתייחס רק לחלק המערך שהוא בעל משמעות כמחרוזת‪.‬‬
‫• בשפת ‪ C‬קיימות פקודות וספריות שמקלות את העבודה עם מחרוזות‬
‫‪52‬‬
‫מחרוזות‬
‫;"‪char str[20] = "hello world‬‬
‫• ההבדל בין מחרוזות ומערכים‪:‬‬
‫• מחרוזת היא רצף של תווים‪ ,‬נשתמש במערך כדי לממש‬
‫מחרוזת‬
‫•‬
‫•‬
‫•‬
‫•‬
‫‪53‬‬
‫מחרוזת מסתיימת בתו '‪( '\0‬ערכו ‪ 0‬בטבלת ה‪.)ascii -‬‬
‫כלומר לאחר תווי המחרוזת יופיע התו '‪.'\0‬‬
‫אם התו לא מופיע אז זהו מערך של תווים ולא מחרוזת!‬
‫כל הפעולות והפקודות המיוחדות למחרוזות מסתמכות על‬
‫הימצאותו של התו '‪ '\0‬בסוף המחרוזת‪.‬‬
‫מחרוזות ‪ -‬אתחול‬
‫;"‪char str[20] = "hello world‬‬
‫• אתחול‪:‬‬
‫• ניתן לאתחל מחרוזת על ידי מחרוזת קבועה‪.‬‬
‫• מחרוזת קבועה‪:‬‬
‫• " ‪ – " ...‬גרשיים כפולים משמשים לייצוג מחרוזת קבועה‪.‬‬
‫• בסוף המחרוזת מופיע הסימן ’‪.‘\0‬‬
‫‪d \0‬‬
‫]‪str[19‬‬
‫‪54‬‬
‫סוף המחרוזת‬
‫‪l‬‬
‫‪r‬‬
‫‪w o‬‬
‫‪o‬‬
‫‪l‬‬
‫‪l‬‬
‫‪e‬‬
‫‪h‬‬
‫]‪str[0‬‬
‫מחרוזות ‪ -‬איתחול‬
‫;"‪char str[20] = "hello world‬‬
‫;}'‪char str[20] = {'h','e','l','l','o',' ','w','o','r','l','d','\0‬‬
‫כאשר מגדירים מחרוזת יש לדאוג‬
‫לקיומו של תו סיום המחרוזת‬
‫‪d \0‬‬
‫]‪str[19‬‬
‫‪55‬‬
‫סוף המחרוזת‬
‫‪l‬‬
‫‪r‬‬
‫‪w o‬‬
‫‪o‬‬
‫‪l‬‬
‫‪l‬‬
‫‪e‬‬
‫‪h‬‬
‫]‪str[0‬‬
‫גרשיים‬
‫" "‬
‫• גרש אחת מתייחסת לתו‬
‫• '‪'5' , 'd' , 'A‬‬
‫• גרשיים מתייחסות למחרוזת‬
‫• ”‪."54" ,"I am a string" ,"Hello‬‬
‫• מתנהג לפי המוסכמות ומוסיף '‪ '\0‬לסוף כל מחרוזת‬
‫• לכן "‪ "A‬ו – '‪ 'A‬אינם זהים‪.‬‬
‫• "‪ "A‬זהה ל – '‪ 'A‬שלאחריו '‪'\0‬‬
‫• בפעולות על מחרוזות יש להקפיד על שימוש בגרשיים‪.‬‬
‫)”‪strcmp(input,”a‬‬
‫‪56‬‬
‫מחרוזת כפרמטר לפונקציה‬
‫• אין דרך מיוחדת להגדיר שהפרמטר לפונקציה זו מחרוזת ולא‬
‫מערך‬
‫• הטיפוס שיועבר לפונקציה הוא המערך שמכיל אותה‬
‫• אז איך יודעים מתי נתייחס לפרמטר כמערך ומתי כמחרוזת?‬
‫• זה נגזר מההגדרה לא פורמאלית של הפונקציה – מה היא‬
‫אמורה לעשות‬
‫• כאשר נקבל מחרוזת כקלט לפונקציה נניח שהתו ’‪ ‘\0‬נמצא‬
‫במערך ומציין את סוף המחרוזת‬
‫‪57‬‬
‫דוגמא – פונקציה למציאת אורך מחרוזת‬
‫הפונקציה מקבלת מחרוזת כקלט‬
‫נספור את מספר התווים המופיעים‬
‫לפני תו סיום המחרוזת‬
‫)][‪int my_strlen(const char str‬‬
‫{‬
‫;‪int counter = 0‬‬
‫)'‪while (str[counter] != '\0‬‬
‫;‪counter++‬‬
‫;‪return counter‬‬
‫}‬
‫‪58‬‬
‫העברת מערכים קבועים לפונקציה‬
‫• אפשר להגדיר מערכי קלט בפונקציה כקבועים (‪.)const‬‬
‫• כך נוכל להבטיח שלא נשנה בטעות את ערך המשתנה‬
‫• נבטיח למשתמש שלא נשנה את המערך‬
‫• לא ניתן להעביר מערך שהוגדר כקבוע (‪ )const‬לפונקציה שבה הוא‬
‫לא קבוע ועלול להשתנות‪.‬‬
‫)‪void init_array(const int array[], int size, int value‬‬
‫{‬
‫;‪int i‬‬
‫)‪for (i = 0; i < size; i++‬‬
‫;‪array[i] = value‬‬
‫{‬
‫‪error C3892: 'array' : you cannot assign‬‬
‫‪to a variable that is const‬‬
‫‪59‬‬
‫קלט של מחרוזות בעזרת ‪scanf‬‬
‫• קריאת תו אחד כל פעם‪ ,‬עד ש‪:‬‬
‫• נגמר המקום‬
‫• קראנו תו שמסמן לנו להפסיק (למשל סוף השורה)‬
‫>‪#include <stdio.h‬‬
‫‪#define MAX_STRING_LEN 200‬‬
‫{ )(‪int main‬‬
‫;]‪char tav, sentence[MAX_STRING_LEN + 1‬‬
‫;‪int i = 0‬‬
‫{ ‪do‬‬
‫נרצה לקלוט מחרוזת בעלת‬
‫;)‪scanf("%c", &tav‬‬
‫‪ 200‬תווים לכל היותר ‪...‬‬
‫{ )'‪if (tav != '\n‬‬
‫;‪sentence[i] = tav‬‬
‫לכן נצטרך מערך בגודל ‪201‬‬
‫;‪i++‬‬
‫}‬
‫;))‪} while ((tav != '\n') && (i < MAX_STRING_LEN‬‬
‫;'‪sentence[i] = '\0‬‬
‫‪...‬‬
‫ע"י הוספת ’‪ ‘\0‬בסוף אנו הופכים‬
‫;‪return 0‬‬
‫‪60‬‬
‫}‬
‫את מערך התווים למחרוזת‬
getchar ‫קלט של מחרוזות בעזרת‬
:‫ עד ש‬,‫• קריאת תו אחד כל פעם‬
‫• נגמר המקום‬
)‫• קראנו תו שמסמן לנו להפסיק (למשל סוף השורה‬
int main()
{
char tav, sentence[MAX_STRING_LEN + 1];
int i = 0;
for (tav = getchar();
tav != '\n' && i < MAX_STRING_LEN;
tav = getchar())
{
sentence[i] = tav;
i++;
}
sentence[i] = '\0';
...
return 0;
}
61
‫קלט‪/‬פלט של מחרוזת שלמה‬
‫• אפשר לקלוט מחרוזות שלמה ב‪ scanf-‬באמצעות הקידוד ‪%s‬‬
‫למשל‪:‬‬
‫;]‪char answer[100‬‬
‫;)‪scanf("%s", answer‬‬
‫• הקלט יהיה רק עד רווח או ירידת‪-‬שורה (כלומר זו דרך לקלוט מילים)‬
‫• התו ’‪ ‘\0‬מוכנס אוטומטית ע"י ‪ scanf‬אחרי תווי המחרוזת שנקלטה‬
‫• שימו לב שלא רושמים את הסימן & ב‪ scanf -‬של מחרוזת שלמה‪.‬‬
‫• שימו לב‪:‬‬
‫• באחריות המשתמש לדאוג שיש במערך מספיק מקום (אחרת התכנית תעוף)‬
‫• אפשר להגביל את מספר התווים שנקראים‪:‬‬
‫;]‪char answer[100‬‬
‫;)‪scanf("%99s", answer‬‬
‫• דרך אחרת להגבלת מספר התווים היא לקלוט תו‪-‬תו בלולאה‬
‫‪62‬‬