עבור לתוכן

מציאת גודל מערך מסוג שלם(int) בC++

Featured Replies

פורסם

לכאורה נשמע שזאת שאלה פשוטה, אך נתקלתי בבעיה הבאה:

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

חשבתי על משהו בסגנון הזה: (אך זה לא עבד)

int findLength(int arr[])
{
int size;
size = sizeof(arr)/sizeof(int);
return size;
}

תודה מראש,

גל

נ.ב.

אני מדגיש שמדובר על מערך מסוג int, כמו כן איני יכול לערוך את המערך (למשל להגדיר שהאיבר הראשון הוא גודל המערך) אלא רק לקבלו as is.

פורסם

אין דרך לעשות כזה דבר.

אתה חייב לשמור את הגודל של המערך כשאתה מקצה אותו.

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

int arr[1000];
int size = sizeof(arr) / sizeof(int);

size יכיל 1000. אבל כיוון שבזמן ההקצאה הסטטית אתה כבר יודע את גודל המערך, זה לא עוזר לך בכלום.

פורסם
  • מחבר

תודה רבה!

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

חשבתי שאולי תהיה דרך לעשות זאת למערך בגודל לא ידוע.

תודה על ההבהרה.

פורסם

חשוב לזכור משהו:

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

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

פורסם

1) אם נורא חשוב לך להשתמש בדבר כזה, תשתמש ב- MACRO ולא בפונקציה (שם לא מועבר מצביע).

2) לא מומלץ להשתמש בקוד כזה מכיוון שאתה יכול להכניס לו בטעות מצביע ואז לא תקבל את התוצאה שרצית.

פורסם

אם אתה עובד בC++, תשקול לעבוד עם vector (או מבנה נתונים סטנדרטי אחר שמתאים לך) ולא עם מערך.

vector הוא מערך דינמי מוגן ש"זוכר" את הגודל השלו, וזורק לך ארור בזמן ריצה אם אתה חורג מהגבולות, וגם מביא לך הרבה כלים נפוצים (כמו clear / sort). כמו כן, אתה יכול להוסיף לו בקלות איברים בלי להשתמש בrealloc ודברים מזוויעים שכאלה.

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

פורסם

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

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

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

פורסם

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

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

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

לא בזכרון הפיזי, אבל כן בזכרון הוירטואלי של התכנית, שזה תכל'ס מה שחשוב.

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

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

פורסם

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

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

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

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

פורסם

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

ועוד איך שיש - כשאתה מנסה לפנות לזכרון שלא שייך לתוכנית שלך תיתקל באינספור מנגנוני בטחון שיהרגו לך את התוכנית - זה כבר דרך לדעת :facepalm:

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

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

לא בזכרון הפיזי, אבל כן בזכרון הוירטואלי של התכנית, שזה תכל'ס מה שחשוב.

אני לא יודע מה זה אומר אבל אני יודע שזה נכון:


int_array[i] <=> (int_array +i)*

כאשר i יכול לקבל כל ערך בתחום המוקצה. וזה מה שחשוב.

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

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

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

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

פורסם

:facepalm:

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

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

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


int_array[i] <=> (int_array +i)*

כאשר i יכול לקבל כל ערך בתחום המוקצה. וזה מה שחשוב.

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

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

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

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

א. לא בהכרח - הAPI לא תמיד חושף הכל, וגם אם כן - אתה מגביל את הקוד שלך לעבוד על פלטפורמה ספציפית ושיביאו לך זיכרון שמנוהל באופן ספציפי (חשבת מה קורה אם מביאים לך פוינטר שמצביע ל .bss ולא בתוך הHeap?)

ב. על סמך מה אתה משער שברגע שאתה חורג מגבולות המערך אתה לא דורך על הזכרון של משתנה אחר בתוכנה שלך? בוא ואספר לך סוד קטן - ב99 אחוז מהמקרים אתה לא תקבל לא page fault ולא segmentation error אם אתה חורג במספר ספור של בתים מהפוינטר.

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

פורסם

:facepalm:

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

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

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

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

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

אני לא יודע מה זה אומר אבל אני יודע שזה נכון:


int_array[i] <=> (int_array +i)*

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

[/right

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

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

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

חוץ מזה, מחזק את דבריו של Moonblade.

פורסם

קודם כל, כמו שאני רואה את זה, זה דיון של אפשר, לא דיון של צריך.

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

אני בסה"כ מעלה רעיונות.

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

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

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

וגם PHP אגב.

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

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

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

סבבה, ואני בחרתי להגיב לך.

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

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

זה משנה רק כשאתה כותב למקומות מסוימים בחומרה. וגם אז אתה לא ניגש ישירות.

חוץ מזה, מחזק את דבריו של Moonblade.

טוב מאוד. זה מוסיף לגיבוש בפורום.

פורסם

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

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

ברמה הכי יבשה - זה נכון. בפועל:

א)מכונות וירטואליות יכולות לבצע אופטימזציות לפי פלטפורמה בזמן ריצה

למשל: תנסה להריץ לולאה שמחשבת שורשים של כמה מיליונים מספרים בC ובC# ותופתע לגלות שדווקא הלולאה שכתובה ב.Net רצה מהר יותר על מעבדים מודרניים. כמובן שאם תכתוב פונקציה יעודית למעבד בC שמשתמשת ביכולות המתקדמות של המעבד ולא להשתמש בsqrt שמשתמש בפקודות x86 גנריות - הביצועים של C יהיו יותר מהירים. אבל דבר כזה מחייב אותך לנהל מערכת שמזהה איזה מעבד יש ללקוח, ולבצע קריאה מתאימה לפי החומרה, או לחלופין להגביל את המשתמשים שלך לפלטפורמה מסוימת. בשני המקרים - זה לא כיף במיוחד (בפרט שאתה כותב תוכנה פשוטה למדי ולא איזה Matlab :smile1:)

ב)בהרבה מקרים - שמדובר בקוד אפלקטיבי, או אלגוריתמים שעובדים על data sets קטנים - זה פשוט לא משנה. באמת. ולכן עדיף לך לכתוב בשפה שמורידה ממך את הטרחה של ניהול משאבים (לפחות באופן חלקי). לא משנה מה תגיד - אבל גם מתכנתי C++ מנוסים שמשתמשים בshared_ptr-ים וכל הדברים המדליקים שיש בboost עדיין נאבקים בדליפות זיכרון.

לגבי הכוח - פה אני מסכים לחלוטין, C היא שפה מעולה לתכנות low-level שצריך להתעסק הרבה עם החומרה ישירות ולהכיל הרבה inline assembly

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

עושים את זה (בעיקר בסביבות embedded, כי ברוב הקוד שרץ על PC זה פשוט לא משמעותי) - זה נקרא memory pool.

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

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

ניהול הזיכרון זה משהו שנקבע במשותף עם החומרה והמערכת ההפעלה (וברוב המקרים דווקא הבוטלודר ולא במערכת ההפעלה עצמה, אבל לא נכנס לקטנות) - אין לשפה שאתה כותב בה שום קשר לאיך שהזיכרון מנוהל. למשל בx86 protected mode - שזה מה שאתה עובד איתו מאז 386 - כל גישה לכתובת זיכרון עוברת דרך רכיב חומרתי שנמצא על המעבד שנקרא MMU שמכיל look-up table שמתרגם מהכתובת שנתת לו ומאיזה תהליך אתה ניגש (וכן - הדבר הזה מנוהל במשותף עם החומרה ולא רק בתוך מערכת ההפעלה) לכתובת אמיתית.

אני לא מתכוון להרחיב על כל מנגנון הvirtual memory \ טכניקות paging / segmentation כאן - כי זה נושא מורכב למדי וגם ככה הפוסט הזה מספיק ארוך, אבל בקצרה אגיד לך שבwindows אין לך שום דרך לגשת לכתובת פיזית מסוימת בזיכרון דרך user-mode (רק דרייברים שרצים בkernel-mode יכולים לעשות שטיקים שכאלה).

ארכיון

דיון זה הועבר לארכיון ולא ניתן להוסיף בו תגובות חדשות.

דיונים חדשים