יש הבדל בין Here* ל-Here**. אולי זה יעשה לך קצת סדר...כשאתה מבצע malloc אתה בעצם מבקש ממערכת ההפעלה מקום בזכרון בגודל X כלשהו.
כשאתה מבצע Here* h = malloc... אז בעצם יש לך פויינטר (מצביע) למקום בזכרון שאתחלת בגודל X שבו אתה יכול להשתמש.
Here**, לעומת זאת, זה בעצם סוג של מערך דו מימדי. כלומר, מערך של פויינטרים (או ליתר דיוק מבנה נתונים של מערך פויינטרים).
בהתחלה אתה מקצה זכרון למערך של הפונייטרים (ע"י Here** h = malloc(Here*) ורק אח"כ אתה לכל אחד מתאי המערך נכנס ומקצה גם לו זכרון לשימושך האישי (כל תא מצביע לפויינטר שמצביע לזכרון שהקצתה לך מערכת ההפעלה).
לכן, כשאתה משחרר אתה משחרר או את כל האינפורמציה שהקצתה בהתחלה (ע"י free) או שאתה ניגש לכל אחד מהשדות שהקצת במערך הד-מימדי, משחרר אותם ורק אז משחרר את מבנה הנתונים הגדול.
אצלך בדוגמא אתה לא הקצת מקום לפויינטרים של המערך הדו-מימדי אלא ניגשת לזכרון שהקצת כבר מקודם ולכן אין צורך לבצע free גם שם.
ואפשר לומר שכן. שוב, מהסיבה שבהצלחה אתה מקצה מבנה נתונים של char שזה בעצם בית 1 * size.
אח"כ, אתה מקצה מבנה נתונים של מערך פויינטרים מסוג char*
וכו'. דווקא את ה-size בשאר המקרים לא צריך (זה תלוי איך אתה מממש האמת היא).
וההבדל בין malloc ל-calloc זה שגם השדות מאותחלות באפסים.
לפי עקרון הסופרמרקט, כל דבר כזה גורע מהביצועים ולכן אין סיבה להשתמש ב-calloc.
לכן אין צורך בשלב הראשון להגדיר סדר גודל
בברכה,
עידן