سؤال

لقد خصصت "صفيف" mystruct من الحجم n مثله:

if (NULL == (p = calloc(sizeof(struct mystruct) * n,1))) {
 /* handle error */
}

في وقت لاحق ، لا يمكنني الوصول إلى p, ، ولم يعد لديك n. هل هناك طريقة لتحديد طول الصفيف المعطى فقط للمؤشر p?

أنا أتعرف عليه يجب يكون ممكنا منذ ذلك الحين free(p) يفعل ذلك. أنا أعرف malloc() يتتبع مقدار الذاكرة التي خصصتها ، ولهذا السبب يعرف الطول ؛ ربما هناك طريقة للاستعلام عن هذه المعلومات؟ شيء مثل...

int length = askMallocLibraryHowMuchMemoryWasAlloced(p) / sizeof(mystruct)

أعلم أنه يجب علي إعادة صياغة الرمز حتى أعرف n, ، لكني لا أفضل إذا كان ذلك ممكنًا. أيه أفكار؟

هل كانت مفيدة؟

المحلول

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

void *p = calloc(sizeof(struct mystruct) * n + sizeof(unsigned long int),1));
*((unsigned long int*)p) = n;

n تم تخزينه الآن في *((unsigned long int*)p) وبداية صفيفك الآن

void *arr = p+sizeof(unsigned long int);

يحرر: لمجرد لعب Devil's Advocate ... أعرف أن هذه "الحلول" تتطلب جميعها إعادة تصميم ، ولكن دعنا نلعبها. بالطبع ، الحل المقدم أعلاه هو مجرد تنفيذ اختراق لهيكل (معبأ). قد تحدد أيضًا:

typedef struct { 
  unsigned int n;
  void *arr;
} arrInfo;

وتجول arrInfoS بدلا من المؤشرات الخام.

الآن نحن نطبخ. ولكن طالما كنت تعيد تصميم ، لماذا تتوقف هنا؟ ما تريده حقًا هو نوع بيانات مجردة (ADT). أي نص تمهيدي لفئة الخوارزميات وهياكل البيانات سيفعل ذلك. يحدد ADT الواجهة العامة لنوع البيانات ولكنه يخفي تنفيذ نوع البيانات هذا. وبالتالي ، قد تبدو ADT علنًا لصفيف

typedef void* arrayInfo;
(arrayInfo)newArrayInfo(unsignd int n, unsigned int itemSize);
(void)deleteArrayInfo(arrayInfo);
(unsigned int)arrayLength(arrayInfo);
(void*)arrayPtr(arrayInfo);
...

بمعنى آخر ، ADT هو شكل من أشكال البيانات والسلوك ... بمعنى آخر ، إنه أقرب ما يمكنك الوصول إلى البرمجة الموجهة للكائنات باستخدام C. المستقيم ما لم تكن عالقًا على منصة لا تفعل ذلك احصل على برنامج التحويل البرمجي C ++ ، يمكنك أيضًا الذهاب إلى الخنزير بالكامل واستخدام STL std::vector.

هناك ، لقد طرحنا سؤالًا بسيطًا حول C وانتهى بنا الأمر في C ++. الله يساعدنا جميعا.

نصائح أخرى

تتبع حجم الصفيف بنفسك ؛ يستخدم Free سلسلة Malloc لتحرير الكتلة تم تخصيص ذلك ، والذي لا يتمتع بالضرورة بنفس حجم الصفيف الذي طلبته

فقط لتأكيد الإجابات السابقة: لا توجد طريقة لمعرفة ، فقط من خلال دراسة مؤشر ، مقدار الذاكرة التي تم تخصيصها بواسطة malloc التي أعاد هذا المؤشر.

ماذا لو نجحت؟

مثال على سبب عدم قدرته هذا. دعونا نتخيل الكود مع وظيفة افتراضية تسمى get_size (void *) التي تُرجع الذاكرة المخصصة لمؤشر:

typedef struct MyStructTag
{ /* etc. */ } MyStruct ;

void doSomething(MyStruct * p)
{
   /* well... extract the memory allocated? */
   size_t i = get_size(p) ;
   initializeMyStructArray(p, i) ;
}

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   doSomething(s) ;
}

لماذا حتى لو نجحت ، لن تعمل على أي حال؟

لكن مشكلة هذا النهج هي أنه ، في C ، يمكنك اللعب مع ARITHMETEMS المؤشر. دعنا نعيد كتابة dosomethingelse ():

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   MyStruct * s2 = s + 5 ; /* s2 points to the 5th item */
   doSomething(s2) ; /* Oops */
}

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

استنتاج

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

يوفر بعض المترجمين MSIZE () أو وظائف مماثلة (_msize () وما إلى ذلك) ، والتي تتيح لك القيام بذلك بالضبط

هل لي أن أوصي طريقة رهيبة للقيام بذلك؟

تخصيص جميع المصفوفات الخاصة بك على النحو التالي:

void *blockOfMem = malloc(sizeof(mystruct)*n + sizeof(int));

((int *)blockofMem)[0] = n;
mystruct *structs = (mystruct *)(((int *)blockOfMem) + 1);

ثم يمكنك دائمًا إلقاء صفائفك int * والوصول إلى العنصر -1.

تأكد من free هذا المؤشر ، وليس مؤشر الصفيف نفسه!

أيضًا ، من المحتمل أن يتسبب هذا في حشرات فظيعة من شأنها أن تجعلك تمزق شعرك. ربما يمكنك لف funcs التخصيص في مكالمات API أو شيء من هذا القبيل.

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

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


int size;
struct mystruct *cur;

for (cur = myarray; cur->name != NULL; cur++)
    ;

size = cur - myarray;

راجع للشغل يجب أن يكون calloc (n ، sizeof (struct mystruct)) في مثالك.

ناقش آخرون حدود مؤشرات C العادية و stdlib.h تطبيقات malloc(). توفر بعض التطبيقات امتدادات تعيد مخصص حجم الكتلة الذي قد يكون أكبر من الحجم المطلوب.

اذا أنت يجب هل لديك هذا السلوك الذي يمكنك استخدامه أو كتابة تخصيص ذاكرة متخصصة. هذا أبسط شيء يجب القيام به هو تنفيذ غلاف حول stdlib.h المهام. شيء مثل:

void* my_malloc(size_t s);     /* Calls malloc(s), and if successful stores 
                                  (p,s) in a list of handled blocks */
void my_free(void* p);         /* Removes list entry and calls free(p) */
size_t my_block_size(void* p); /* Looks up p, and returns the stored size */
...

حقا سؤالك هو - "هل يمكنني معرفة حجم كتلة بيانات malloc'd (أو calloc'd)". وكما قال آخرون: لا ، ليس بطريقة قياسية.

ومع ذلك ، هناك تطبيقات Malloc مخصصة تقوم بذلك - على سبيل المثال http://dmalloc.com/

لست على دراية بالطريقة ، لكني أتخيل أنه سيتعامل مع التمسك بالداخلية في Malloc ، وهي فكرة سيئة للغاية بشكل عام.

لماذا لا يمكنك تخزين حجم الذاكرة التي خصصتها؟

تحرير: إذا كنت تعلم أنه يجب عليك إعادة صياغة الرمز حتى تعرف n ، حسنًا ، تفعل ذلك. نعم ، قد يكون من السهل محاولة الاستطلاع Malloc ، لكن معرفة N بالتأكيد من شأنها أن تقلل من الارتباك وتعزيز التصميم.

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

هذا اختبار من روتين الفرز الخاص بي. يقوم بإعداد 7 متغيرات لعقد قيم تعويم ، ثم يقوم بتعيينها إلى صفيف ، يتم استخدامه للعثور على قيمة الحد الأقصى.

السحر في مكالمة إلى mymax:

float mmax = mymax ((float *) & arr ، (int) sizeof (arr)/sizeof (arr [0])) ؛

وكان ذلك سحريًا ، أليس كذلك؟

يتوقع Mymax مؤشر صفيف تعويم (Float *) ، لذا أستخدم وتراجع للحصول على عنوان الصفيف ، وألقي به كمؤشر تعويم.

يتوقع MyMax أيضًا عدد العناصر الموجودة في الصفيف كـ INT. أحصل على هذه القيمة باستخدام SizeOF () لإعطائي أحجام بايت من الصفيف والعنصر الأول من الصفيف ، ثم أقسم إجمالي البايت على عدد البايتات في كل عنصر. (يجب ألا نخمن أو رمز صلب بحجم int لأنه 2 بايت على بعض النظام و 4 على بعض مثل OS X Mac ، ويمكن أن يكون شيئًا آخر على الآخرين).

ملاحظة: كل هذا أمر مهم عندما يكون لبياناتك عدد متفاوتة من العينات.

إليك رمز الاختبار:

#include <stdio.h>

float a, b, c, d, e, f, g;

float myMax(float *apa,int soa){
 int i;
 float max = apa[0];
 for(i=0; i< soa; i++){
  if (apa[i]>max){max=apa[i];}
  printf("on i=%d val is %0.2f max is %0.2f, soa=%d\n",i,apa[i],max,soa);
 }
 return max;
}

int main(void)
{
 a = 2.0;
 b = 1.0;
 c = 4.0;
 d = 3.0;
 e = 7.0;
 f = 9.0;
 g = 5.0;
 float arr[] = {a,b,c,d,e,f,g};

 float mmax = myMax((float *)&arr,(int) sizeof(arr)/sizeof(arr[0]));
 printf("mmax = %0.2f\n",mmax);

 return 0;
}

في Uclibc, ، هناك MALLOC_SIZE ماكرو في malloc.h:

/* The size of a malloc allocation is stored in a size_t word
   MALLOC_HEADER_SIZE bytes prior to the start address of the allocation:

     +--------+---------+-------------------+
     | SIZE   |(unused) | allocation  ...   |
     +--------+---------+-------------------+
     ^ BASE             ^ ADDR
     ^ ADDR - MALLOC_HEADER_SIZE
*/

/* The amount of extra space used by the malloc header.  */
#define MALLOC_HEADER_SIZE          \
  (MALLOC_ALIGNMENT < sizeof (size_t)       \
   ? sizeof (size_t)                \
   : MALLOC_ALIGNMENT)

/* Set up the malloc header, and return the user address of a malloc block. */
#define MALLOC_SETUP(base, size)  \
  (MALLOC_SET_SIZE (base, size), (void *)((char *)base + MALLOC_HEADER_SIZE))
/* Set the size of a malloc allocation, given the base address.  */
#define MALLOC_SET_SIZE(base, size) (*(size_t *)(base) = (size))

/* Return base-address of a malloc allocation, given the user address.  */
#define MALLOC_BASE(addr)   ((void *)((char *)addr - MALLOC_HEADER_SIZE))
/* Return the size of a malloc allocation, given the user address. */
#define MALLOC_SIZE(addr)   (*(size_t *)MALLOC_BASE(addr))
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top