تحديد حجم الذاكرة المخصصة ديناميكيًا في C.

سئل على ١٥ أغسطس ٢٠٠٩  ·  تمت مشاهدة 79k مرة  ·  مصدر

s_itbhu picture
في ١٥ أغسطس ٢٠٠٩

هل توجد طريقة في لغة C لمعرفة حجم الذاكرة المخصصة ديناميكيًا؟

على سبيل المثال ، بعد

char* p = malloc (100);

هل توجد طريقة لمعرفة حجم الذاكرة المرتبطة بـ p ؟

الإجابات

ars picture
في ١٥ أغسطس ٢٠٠٩
54

لا توجد طريقة قياسية للعثور على هذه المعلومات. ومع ذلك ، توفر بعض التطبيقات وظائف مثل msize للقيام بذلك. على سبيل المثال:

ضع في اعتبارك أن malloc سيخصص الحد الأدنى من الحجم المطلوب ، لذلك يجب عليك التحقق مما إذا كان متغير msize للتنفيذ الخاص بك يعيد بالفعل حجم الكائن أو الذاكرة المخصصة بالفعل في الكومة.

Alex Reynolds picture
في ١٥ أغسطس ٢٠٠٩
52

قائمة الأسئلة الشائعة لـ comp.lang.c · السؤال 7.27 -

س: هل يمكنني الاستعلام عن الحزمة malloc لمعرفة حجم الكتلة المخصصة؟

ج: لسوء الحظ ، لا توجد طريقة قياسية أو محمولة. (توفر بعض المجمعين امتدادات غير قياسية.) إذا كنت تريد أن تعرف ، فسيتعين عليك تتبعها بنفسك. (انظر أيضًا السؤال 7.28 ).

Artelius picture
في ١٥ أغسطس ٢٠٠٩
13

عقلية C هي تزويد المبرمج بالأدوات التي تساعده في وظيفته ، وليس لتقديم أفكار مجردة تغير طبيعة وظيفته. يحاول C أيضًا تجنب جعل الأمور أسهل / أكثر أمانًا إذا حدث ذلك على حساب حد الأداء.

تتطلب بعض الأشياء التي قد ترغب في القيام بها بمنطقة من الذاكرة تحديد موقع بداية المنطقة فقط. تتضمن مثل هذه الأشياء العمل مع سلاسل منتهية بصفر ، ومعالجة أول n بايت من المنطقة (إذا كانت المنطقة معروفة بهذا الحجم على الأقل) ، وهكذا دواليك.

في الأساس ، يعد تتبع طول المنطقة عملاً إضافيًا ، وإذا قام C بذلك تلقائيًا ، فسيكون في بعض الأحيان القيام بذلك دون داع.

تتطلب العديد من وظائف المكتبة (على سبيل المثال fread() ) مؤشرًا لبداية المنطقة ، وكذلك حجم هذه المنطقة. إذا كنت بحاجة إلى حجم منطقة ما ، فيجب عليك تتبعها.

نعم ، عادةً ما تتعقب تطبيقات malloc () حجم المنطقة ، لكنها قد تفعل ذلك بشكل غير مباشر ، أو تقريبها إلى قيمة معينة ، أو لا تحتفظ بها على الإطلاق. حتى لو كانوا يدعمونها ، فإن العثور على الحجم بهذه الطريقة قد يكون بطيئًا مقارنة بتتبعه بنفسك.

إذا كنت بحاجة إلى بنية بيانات تعرف حجم كل منطقة ، فيمكن لـ C القيام بذلك نيابة عنك. ما عليك سوى استخدام بنية تتعقب حجم المنطقة بالإضافة إلى مؤشر إلى المنطقة.

Greg Hewgill picture
في ١٥ أغسطس ٢٠٠٩
9

لا ، لا توفر مكتبة وقت تشغيل C مثل هذه الوظيفة.

قد توفر بعض المكتبات وظائف خاصة بالنظام الأساسي أو المترجم يمكنها الحصول على هذه المعلومات ، ولكن بشكل عام تكون طريقة تتبع هذه المعلومات في متغير عدد صحيح آخر.

Leslie Godwin picture
في ٢٠ أبريل ٢٠١٦
8

إليك أفضل طريقة رأيتها لإنشاء مؤشر بعلامات لتخزين الحجم مع العنوان. ستظل جميع وظائف المؤشر تعمل كما هو متوقع:

مسروق من: https://stackoverflow.com/a/35326444/638848

يمكنك أيضًا تطبيق غلاف لـ malloc وإضافة علامات مجانية (مثل الحجم المخصص ومعلومات التعريف الأخرى) قبل إرجاع المؤشر بواسطة malloc. هذا هو في الواقع الأسلوب الذي يقوم مترجم c ++ بتمييز الكائنات بالإشارة إلى الفئات الافتراضية. فيما يلي مثال عملي واحد:

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

void * my_malloc(size_t s) 
{
  size_t * ret = malloc(sizeof(size_t) + s);
  *ret = s;
  return &ret[1];
}

void my_free(void * ptr) 
{
  free( (size_t*)ptr - 1);
}

size_t allocated_size(void * ptr) 
{
  return ((size_t*)ptr)[-1];
}

int main(int argc, const char ** argv) {
  int * array = my_malloc(sizeof(int) * 3);
  printf("%u\n", allocated_size(array));
  my_free(array);
  return 0;
}

ميزة هذه الطريقة على هيكل بالحجم والمؤشر

 struct pointer
 {
   size_t size;
   void *p;
 };

هو أنك تحتاج فقط إلى استبدال malloc والمكالمات المجانية. لا تتطلب جميع عمليات المؤشر الأخرى إعادة بناء ديون.

Enno picture
في ١٥ أغسطس ٢٠٠٩
4

كما قال الجميع بالفعل: لا يوجد.

أيضًا ، سأتجنب دائمًا جميع الوظائف الخاصة بالبائع هنا ، لأنه عندما تجد أنك بحاجة فعلاً إلى استخدامها ، فهذه إشارة عامة على أنك تفعل ذلك بشكل خاطئ. يجب عليك إما تخزين الحجم بشكل منفصل ، أو عدم معرفته على الإطلاق. يعد استخدام وظائف البائع أسرع طريقة لفقدان إحدى الفوائد الرئيسية للكتابة بلغة C ، وهي قابلية النقل.

Quuxplusone picture
في ٤ فبراير ٢٠١٨
4

كل شخص يخبرك أنه مستحيل هو صحيح تقنيًا (أفضل نوع صحيح).

لأسباب هندسية ، من السيئ الاعتماد على النظام الفرعي malloc لإخبارك بحجم الكتلة المخصصة بدقة. لإقناع نفسك بهذا ، تخيل أنك تكتب تطبيقًا كبيرًا ، مع العديد من مخصصات الذاكرة المختلفة - ربما تستخدم libc malloc الخام في جزء واحد ، لكن C ++ operator new في جزء آخر ، ثم بعض واجهات برمجة تطبيقات Windows المحددة في جزء آخر. إذاً لديك جميع أنواع void* تحلق حولك. من المستحيل كتابة دالة يمكن أن تعمل على أي من هذه void* s ، إلا إذا كان بإمكانك أن تعرف بطريقة أو بأخرى من قيمة المؤشر أي من الكومات الخاصة بك جاء منها.

لذلك قد ترغب في اختتام كل مؤشر في برنامجك ببعض الاصطلاحات التي تشير إلى المكان الذي جاء منه المؤشر (والمكان الذي يجب إعادته إليه). على سبيل المثال ، في C ++ ، نسمي ذلك std::unique_ptr<void> (للمؤشرات التي يجب أن تكون operator delete 'd) أو std::unique_ptr<void, D> (للمؤشرات التي يجب إرجاعها عبر آلية أخرى D ). يمكنك أن تفعل نفس الشيء في لغة سي إذا أردت ذلك. وبمجرد الانتهاء من المؤشرات في كائنات أكبر أكثر أمانًا على أي حال ، struct SizedPtr { void *ptr; size_t size; } ومن ثم لا داعي للقلق بشأن حجم التخصيص مرة أخرى.

ومع ذلك.

هناك أيضًا أسباب وجيهة وراء رغبتك بشكل شرعي في معرفة الحجم الأساسي الفعلي للتخصيص. على سبيل المثال ، ربما تكتب أداة تحديد ملفات تعريف لتطبيقك والتي ستبلغ عن المقدار الفعلي للذاكرة المستخدمة من قبل كل نظام فرعي ، وليس فقط مقدار الذاكرة التي اعتقد المبرمج أنه يستخدمها. إذا كان كل تخصيص من 10 بايت يستخدم سراً 16 بايت تحت الغطاء ، فمن الجيد معرفة ذلك! (بالطبع سيكون هناك أحمال الآخرين أيضا، والتي كنت لا قياس على هذا النحو. ولكن هناك بعد أدوات أخرى لهذا المنصب.) أو ربما كنت مجرد التحقيق في سلوك realloc على النظام الأساسي الخاص بك . أو ربما ترغب في "تقريب" قدرة التخصيص المتزايد لتجنب عمليات إعادة التخصيص المبكرة في المستقبل. مثال:

SizedPtr round_up(void *p) {
    size_t sz = portable_ish_malloced_size(p);
    void *q = realloc(p, sz);  // for sanitizer-cleanliness
    assert(q != NULL && portable_ish_malloced_size(q) == sz);
    return (SizedPtr){q, sz};
}
bool reserve(VectorOfChar *v, size_t newcap) {
    if (v->sizedptr.size >= newcap) return true;
    char *newdata = realloc(v->sizedptr.ptr, newcap);
    if (newdata == NULL) return false;
    v->sizedptr = round_up(newdata);
    return true;
}

للحصول على حجم التخصيص خلف مؤشر غير فارغ والذي تم إرجاعه مباشرة من libc malloc - وليس من كومة مخصصة ، ولا يشير إلى منتصف كائن - يمكنك استخدام واجهات برمجة التطبيقات التالية الخاصة بنظام التشغيل ، والتي مجمعة في وظيفة غلاف "محمولة" للراحة. إذا وجدت نظامًا مشتركًا لا يعمل فيه هذا الرمز ، فالرجاء ترك تعليق وسأحاول إصلاحه!

#if defined(__linux__)
// https://linux.die.net/man/3/malloc_usable_size
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return malloc_usable_size((void*)p);
}
#elif defined(__APPLE__)
// https://www.unix.com/man-page/osx/3/malloc_size/
#include <malloc/malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return malloc_size(p);
}
#elif defined(_WIN32)
// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/msize
#include <malloc.h>
size_t portable_ish_malloced_size(const void *p) {
    return _msize((void *)p);
}
#else
#error "oops, I don't know this system"
#endif

#include <stdio.h>
#include <stdlib.h>  // for malloc itself

int main() {
    void *p = malloc(42);
    size_t true_length = portable_ish_malloced_size(p);
    printf("%zu\n", true_length);
}

اختبارها على:

nik picture
في ١٥ أغسطس ٢٠٠٩
2

أتوقع أن يعتمد هذا على التنفيذ.
إذا حصلت على هيكل بيانات الرأس ، فيمكنك إعادته إلى المؤشر والحصول على الحجم.

Nick Dandoulakis picture
في ١٥ أغسطس ٢٠٠٩
1

إذا كنت تستخدم malloc فلا يمكنك الحصول على الحجم.

من ناحية أخرى ، إذا كنت تستخدم OS API لتخصيص الذاكرة ديناميكيًا ، مثل وظائف Windows

Erich Horn picture
في ١٢ نوفمبر ٢٠١٥
1

حسنًا ، أعلم الآن أن هذا لا يجيب على سؤالك المحدد ، ولكن التفكير خارج الصندوق كما كان ... يخطر ببالي أنك ربما لست بحاجة إلى معرفته. حسنًا ، حسنًا ، لا ، لا أقصد أن لديك تطبيقًا سيئًا أو غير تقليدي ... أعني أنك ربما (بدون النظر إلى الكود الخاص بك أنا فقط أخمن) أنك تريد فقط معرفة ما إذا كانت بياناتك مناسبة في الذاكرة المخصصة ، إذا كان هذا هو الحال ، فقد يكون هذا الحل أفضل. لا ينبغي أن يقدم الكثير من النفقات العامة وسيحل مشكلة "الملائمة" إذا كان هذا هو ما تتعامل معه بالفعل:

if ( p != (tmp = realloc(p, required_size)) ) p = tmp;

أو إذا كنت بحاجة إلى الحفاظ على المحتويات القديمة:

if ( p != (tmp = realloc(p, required_size)) ) memcpy(tmp, p = tmp, required_size);

بالطبع يمكنك فقط استخدام:

p = realloc(p, required_size);

وتنتهي من ذلك.