הצגת כתובת של מערך

epsilonnow

New member
הצגת כתובת של מערך

רשמתי את התוכנית הקצרה הבאה:
int a[4]; printf("%p %p %p",a,&a,&a[0]);​
ועבור כל אחד מהפלטים קיבלתי את אותה הכתובת בזיכרון. מישהו אולי יכול להסביר לי למה ה & לא משנה במיקרה הזה ומה ההבדל בין שלושת המיקרים ?
 

אלדד28

New member
ובכן,

a הוא מערך, ולכן זה לא משנה. a שווה ל- a&. [0]a הוא האיבר הראשון במערך, משתנה רגיל מסוג int, ו-[0]a& היא כתובתו, שהיא גם כתובת תחילת המערך.
 

epsilonnow

New member
והקומפיילר

לא מודיע על שום שגיאה או אזהרה אם מוסיפים/מחסירים "&" ? ומה בעצם יותר נכון, עם או בלי "&" ?
 

DNile

New member
אין סיבה להודיע על שגיאה.

כי אין שגיאה. תאר לך שהגדרת מאקרו כזה:
PrintAddressOf(x) printf("%p", &x);​
מאחר שהמערך הוא פשוט אוסף של בתים, ולא משתנה שמצביע לשם, מן הסתם, הכתובת של המערך זה בדיוק הכתובת שבו מתחיל המערך, ולא מצביע לשם. לעומת זאת, בגלל שמערך מתפקד כמו מצביע, הדפסה של הערך שלו בלי & צריך להיות הכתובת של תחילת המערך.. וזהו בערך.. מה יותר נכון? אין יותר נכון. הכל נכון.
 

gmorphus

New member
צריך לשים לב ש

אם המערך היה מוגדר כך
int *a = new int[7]; printf("%p %p %p", a, &a, &a[0]);​
אז המצב הוא אחר. כי למעשה מעבר למערך שמוקצה יש גם מצביע שמצביע לתחילת המערך. ולכן הפלט במקרה הזה לא יהיה שלוש כתובות זהות אלא הערך שנמצא במצביע a, אחריו הכתובת של המצביע a כלומר, איפה מאוחסנת הכתובות של המערך ולא איפה המערך עצמו. ולבסוף הכתובת של האיבר הראשון במערך, שהיא זהה לערך שמופיע בתוך מצביע a, כלומר לערך הראשון שמודפס.
 

epsilonnow

New member
ואם היינו מקבלים מצב כזה:

int a[4]; int *p; int **pp; p=a; pp=&a;​
איך באמצעות pp היינו יכולים להדפיס את הכתובת של תחילת מערך a ?
 

gmorphus

New member
ככה

אם אמרנו ש-a ייתן לך את הכתובת של תחילת המערך, אז מאחר ואתה עושה p=a אז p יהיה מצביע שהערך שלו יהיה הכתובת של תחילת מערך a בזיכרון. pp הוא מצביע למצביע. נתת לו את הכתובת של p ולכן הערך של pp יהיה הכתובת של p. כלומר, pp* ייתן לנו את הערך שנמצא בpp, כלומר את הכתובת של p. pp** ייתן לנו את הערך שנמצא בכתובת (pp*) שהיא הכתובת של p כלומר pp** ייתן לנו את הערך שנמצא בכתובת של p וזה הכתובת בזיכרון של המערך a. ולשאלתך, pp** ייתן לנו את הכתובת של המערך a. נראה שהייתי קצת מבולגן...
 

Fingertip

New member
../images/Emo31.gifלא נכון../images/Emo70.gif

דוגמה מספרית:
a <--- 500, 501, 502, 503 :a הכתובות של תאי p <--- 504 :p הכתובת של pp <--- 505 :pp הכתובת של​
כעת:
p == 500 pp == 504​
ולכן מצב הזכרון הוא:
500: 0 501: 0 502: 0 503: 0 504: 500 505: 504 *pp = *(506) = 500 **pp = *(500) = 0​
ולכן pp* היא הכתובת של התא הראשון של a ואילו pp** הוא הערך של התא הראשון ב-a. מקווה שעזרתי, אהד.
 

Fingertip

New member
דווקא במקרה הזה...

... נראה לי יותר קריא לכתוב:
*p[0]​
על מנת לגשת לאבר הראשון במערך. מן הסתם בשביל לקבל את ערך התא הראשון במערך נשתמש ב-p*... אהד.
 

זהיר

New member
בכל מקרה לא אמור להתקמפל

אם הקומפיילר שלכם מאפשר
int a[4]; int **p = &a;​
בלי לפחות אזהרה, גם ברמת אזהרות נמוכה,תזרקו אותו, כי הטיפוס של
&a​
הוא
int (*)[4]​
ולא
int**​
ומי שלא מבין את הכתוב כאן למד מספר/מורה גרוע. אולי זה לא נעים לקרוא/לשמוע (וגם לא נעים לכתוב/להגיד) אבל מישהו צריך להגיד את האמת.
 

Fingertip

New member
צב"ר.

אנחנו מדברים על C? איזה מין טיפוס זה [4](*) int? מערך לא זוכר את הגודל שלו. לפחות, לא ב-C סטנדרטית, ואם אתה מצפה שהמהדר שלך יתריע על זה אז יכולות התכנות שלך לא יבילות
. (זה מזכיר לי כשהראיתי השבוע לחבר שלי את "סביבת הפיתוח" שלי בג'אווה (קובץ מצורף) הוא היה המום איך אני לא משתמש במשהו שמזכיר את ה-Visual Studio -- פטטט...) אהד.
 

Fingertip

New member
התנצלות

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

זהיר

New member
תודה על הנימוס - עכשיו תודה בטעות

תריץ את הקוד הבא וספר לנו מה קרה
#include <stdio.h> int main() { int a[7]; printf("sizeof array = %d\n", sizeof(a)); printf("sizeof object = %d\n", sizeof(a[0])); printf("array length = %d\n", sizeof(a)/sizeof(a[0])); return 0; }​
 

אלדד28

New member
שיט, הפורום מדרדר אותך? ../images/Emo6.gif

איך אוכל לחיות עם עצמי עכשיו
 

voguemaster

New member
מזכיר לי משהו

לא מזמן נתקלתי בקומפיילר שצעק על הדבר הבא:
// a function that performs gauss jordan elimination void gaussj( float **a, int n, float **b, int m); . . . . float dataa[5][5], datab[5][5]; gaussj( dataa, 5, datab, 5);​
הקומפיילר נתן warning שהטיפוסים לא מתאימים: צריך להעביר pointer to float pointer ומועבר array של 5 על 5. אני שואל את עצמי WTF. לצורך העניין, עבדתי מספיק עם GCC כדי לדעת שכשמעבירים מערך דו מימדי לפונקציה כזו שמקבלת מצביע למצביע הוא לא צועק גם אם משתמשים ב-Wall. יש לך הסבר ?
 

זהיר

New member
הדבר היחיד שמפתיע אותי

זה שאצלך GCC לא הזהיר! אצלי הוא מזהיר " INCOMPATIBLE POINTER TYPE ..." אפילו בלי Wall- !!! בדקתי עכשיו (גירסה 3.3) (ולפי מה שאני זוכר עשה זאת גם בגירסות קודמות - אבל מזמן כבר לא היתה לי הערה כזאת
) לא, אין לי הסבר. לעניינינו, אם מריצים את זה, זה אמור לקרוס (SEGMENTATION FAULT במחשב UNIX שלי)
 

voguemaster

New member
וואלה ? ../images/Emo12.gif

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