سؤال

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

لماذا يتم استخدام alloca() بالإحباط على الرغم من الميزات المذكورة أعلاه؟

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

المحلول

والجواب هو الحق هناك في الصفحة man (على الأقل على ):

<اقتباس فقرة>   

وإرجاع القيمة          وظيفة alloca () ترجع مؤشر إلى بداية   المساحة المخصصة. إذا كان   أسباب تخصيص          تجاوز سعة مكدس، والسلوك برنامج غير معرف.

والذي لا يعني أنه لا ينبغي أبدا أن تستخدم. واحد من المشاريع OSS أعمل على يستخدم على نطاق واسع، وطالما أنك لا استغلال ذلك (alloca'ing القيم ضخمة)، أنه بخير. مرة واحدة ذهبت الماضي علامة "بضع مئات بايت"، وحان الوقت لاستخدام malloc والأصدقاء، وبدلا من ذلك. قد لا يزال الحصول على فشل التخصيص، ولكن على الأقل سيكون لديك بعض المؤشرات على فشل بدلا من مجرد تطاير المكدس.

نصائح أخرى

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

في ملف الرأس:

void DoSomething() {
   wchar_t* pStr = alloca(100);
   //......
}

في ملف التنفيذ:

void Process() {
   for (i = 0; i < 1000000; i++) {
     DoSomething();
   }
}

وإذا ما حدث كان المترجم inlined وظيفة DoSomething وجميع المخصصات كومة كان يحدث داخل وظيفة Process() وبالتالي تهب كومة. في الدفاع عن بلدي (وكنت ليست واحدة والذين وجدوا هذه المسألة؛ كان علي أن أذهب والبكاء إلى واحد من كبار المطورين عندما لا يمكن إصلاحه)، لم يكن alloca على التوالي، وكان واحدا من سلسلة ATL وحدات الماكرو التحويل.

وهكذا الدرس هو - لا تستخدم alloca في الوظائف التي تعتقد أنه قد يكون inlined

وقديم السؤال ولكن لا أحد ذكر أنه ينبغي أن تحل محلها صفائف طول متغير.

char arr[size];

وبدلا من

char *arr=alloca(size);

وانها في C99 القياسية وجدت امتدادا المترجم في العديد من المجمعين.

يعد alloca() مفيدًا جدًا إذا لم تتمكن من استخدام متغير محلي قياسي لأن حجمه يجب تحديده في وقت التشغيل ويمكنك ذلك أضمن تمامًا أن المؤشر الذي تحصل عليه من alloca() لن يُستخدم أبدًا بعد إرجاع هذه الوظيفة.

يمكنك أن تكون آمنًا إلى حد ما إذا كنت

  • لا ترجع المؤشر أو أي شيء يحتوي عليه.
  • لا تقم بتخزين المؤشر في أي بنية مخصصة على الكومة
  • لا تدع أي موضوع آخر يستخدم المؤشر

الخطر الحقيقي يأتي من احتمال أن ينتهك شخص آخر هذه الشروط في وقت لاحق.مع أخذ ذلك في الاعتبار، من الرائع تمرير المخازن المؤقتة إلى الوظائف التي تقوم بتنسيق النص فيها :)

كما لوحظ في نشر مجموعة الأخبار هذه, ، هناك عدة أسباب لاستخدام alloca يمكن اعتبارها صعبة وخطيرة:

  • ليس كل المترجمين يدعمون alloca.
  • يفسر بعض المترجمين السلوك المقصود لـ alloca بشكل مختلف، لذلك لا يتم ضمان قابلية النقل حتى بين المترجمين الذين يدعمونها.
  • بعض التطبيقات هي عربات التي تجرها الدواب.

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

لا يزال يتم تثبيط استخدام التخصيص، لماذا؟

لا أرى مثل هذا الإجماع.الكثير من الايجابيات القوية.بعض السلبيات:

  • يوفر C99 مصفوفات ذات طول متغير، والتي غالبًا ما يتم استخدامها بشكل تفضيلي باعتبارها أكثر اتساقًا مع المصفوفات ذات الطول الثابت وبديهية بشكل عام
  • تحتوي العديد من الأنظمة على مساحة ذاكرة/عنوان إجمالية أقل للمكدس مقارنةً بالكومة، مما يجعل البرنامج أكثر عرضة لاستنفاد الذاكرة (من خلال تجاوز سعة المكدس):قد يُنظر إلى هذا على أنه أمر جيد أو سيئ - أحد أسباب عدم نمو المكدس تلقائيًا بالطريقة التي تعمل بها الكومة هو منع البرامج الخارجة عن السيطرة من التأثير السلبي على الجهاز بأكمله
  • عند استخدامها في نطاق محلي أكثر (مثل while أو for حلقة) أو في عدة نطاقات، تتراكم الذاكرة لكل تكرار/نطاق ولا يتم تحريرها حتى تخرج الوظيفة:وهذا يتناقض مع المتغيرات العادية المحددة في نطاق بنية التحكم (على سبيل المثال. for {int i = 0; i < 2; ++i) { X } سوف تتراكم alloca-ed الذاكرة المطلوبة عند X، ولكن سيتم إعادة تدوير الذاكرة الخاصة بمصفوفة ذات حجم ثابت لكل تكرار).
  • عادة لا يقوم المترجمون الحديثون بذلك inline الوظائف التي تستدعي alloca, ولكن إذا أجبرتهم ثم alloca سيحدث في سياق المتصلين (أيلن يتم تحرير المكدس حتى يعود المتصل)
  • منذ وقت طويل alloca تم الانتقال من ميزة/اختراق غير محمول إلى ملحق قياسي، ولكن قد تستمر بعض التصورات السلبية
  • يرتبط العمر بنطاق الوظيفة، والذي قد يناسب أو لا يناسب المبرمج بشكل أفضل mallocالسيطرة الصريحة
  • الاضطرار إلى استخدام malloc يشجع على التفكير في إلغاء التخصيص - إذا تمت إدارته من خلال وظيفة التغليف (على سبيل المثال. WonderfulObject_DestructorFree(ptr))، فإن الدالة توفر نقطة لتنفيذ عمليات التنظيف (مثل إغلاق واصفات الملف، أو تحرير المؤشرات الداخلية أو القيام ببعض التسجيل) دون إجراء تغييرات صريحة على كود العميل:في بعض الأحيان يكون نموذجًا جيدًا يجب اعتماده باستمرار
    • في هذا النمط الزائف من البرمجة، من الطبيعي أن تريد شيئًا كهذا WonderfulObject* p = WonderfulObject_AllocConstructor(); - هذا ممكن عندما يكون "المنشئ" عبارة عن وظيفة تعود malloc-ed الذاكرة (حيث تظل الذاكرة مخصصة بعد أن تقوم الدالة بإرجاع القيمة المراد تخزينها فيها p)، ولكن ليس إذا كان "المنشئ" يستخدم alloca
      • نسخة ماكرو من WonderfulObject_AllocConstructor يمكن تحقيق ذلك، ولكن "وحدات الماكرو شريرة" من حيث أنها يمكن أن تتعارض مع بعضها البعض ومع التعليمات البرمجية غير الماكروية وإنشاء بدائل غير مقصودة وما يترتب على ذلك من مشاكل يصعب تشخيصها
    • مفتقد free يمكن اكتشاف العمليات بواسطة ValGrind وPurify وما إلى ذلك.ولكن لا يمكن دائمًا اكتشاف مكالمات "المدمرة" المفقودة على الإطلاق - وهي فائدة ضعيفة للغاية فيما يتعلق بإنفاذ الاستخدام المقصود؛بعض alloca() تستخدم التطبيقات (مثل دول مجلس التعاون الخليجي) ماكروًا مضمنًا لـ alloca(), ، لذا فإن استبدال مكتبة تشخيص استخدام الذاكرة في وقت التشغيل ليس ممكنًا كما هو الحال الآن malloc/realloc/free (على سبيل المثالسياج مكهرب)
  • بعض التطبيقات لها مشكلات دقيقة:على سبيل المثال، من صفحة Linux الرئيسية:

    في العديد من الأنظمة، لا يمكن استخدام alloca() داخل قائمة وسيطات استدعاء دالة، لأن مساحة المكدس المحجوزة بواسطة alloca() ستظهر على المكدس في منتصف المساحة الخاصة لوسائط الدالة.


أعلم أن هذا السؤال يحمل علامة C، ولكن باعتباري مبرمج C++، اعتقدت أنني سأستخدم C++ لتوضيح الفائدة المحتملة لـ alloca:الكود أدناه (و هنا في ideone) ينشئ متجهًا يتتبع أنواعًا متعددة الأشكال ذات أحجام مختلفة يتم تخصيصها للمكدس (مع ربط العمر بإرجاع الوظيفة) بدلاً من الكومة المخصصة.

#include <alloca.h>
#include <iostream>
#include <vector>

struct Base
{
    virtual ~Base() { }
    virtual int to_int() const = 0;
};

struct Integer : Base
{
    Integer(int n) : n_(n) { }
    int to_int() const { return n_; }
    int n_;
};

struct Double : Base
{
    Double(double n) : n_(n) { }
    int to_int() const { return -n_; }
    double n_;
};

inline Base* factory(double d) __attribute__((always_inline));

inline Base* factory(double d)
{
    if ((double)(int)d != d)
        return new (alloca(sizeof(Double))) Double(d);
    else
        return new (alloca(sizeof(Integer))) Integer(d);
}

int main()
{
    std::vector<Base*> numbers;
    numbers.push_back(factory(29.3));
    numbers.push_back(factory(29));
    numbers.push_back(factory(7.1));
    numbers.push_back(factory(2));
    numbers.push_back(factory(231.0));
    for (std::vector<Base*>::const_iterator i = numbers.begin();
         i != numbers.end(); ++i)
    {
        std::cout << *i << ' ' << (*i)->to_int() << '\n';
        (*i)->~Base();   // optionally / else Undefined Behaviour iff the
                         // program depends on side effects of destructor
    }
}

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

وبعبارة أخرى، alloca( 0x00ffffff ) أمر خطير ويحتمل أن يسبب طفح، تماما بقدر char hugeArray[ 0x00ffffff ]; هو. كن حذرا ومعقولة، وسوف يكون على ما يرام.

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

ويمكنك التقاط هذا الاستثناء SEH وندعو _resetstkoflw لإعادة كومة والاستمرار في طريقه مرح بك. ليست مثالية ولكنها لآلية أخرى لمعرفة ما لا يقل عن شيء قد ذهب خطأ عندما الاشياء يضرب المروحة. * لا شىء قد يكون شيئا من هذا القبيل بأنني لست على علم.

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

وأيضا، بالإضافة إلى عدم السماح لتسرب الذاكرة alloca لا يسبب تجزئة الذاكرة وهو أمر مهم جدا. لا أعتقد alloca هو ممارسة سيئة إذا كنت تستخدم بذكاء، وهو صحيح في الأساس لكل شيء. : -)

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

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

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

إذا كنت حقًا في حاجة إليها بلغة C، فيمكنك استخدام VLA (لا يوجد vla في C++، وهو أمر سيء للغاية).إنها أفضل بكثير من alloca() فيما يتعلق بسلوك النطاق والاتساق.كما اراه فلا هي نوع من تخصيص () جعل الحق.

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

الكثير من الإجابات المثيرة للاهتمام على هذا السؤال "القديم"، وحتى بعض الإجابات الجديدة نسبيًا، لكنني لم أجد أيًا منها يذكر هذا ....

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

وجدت هذا الاقتباس في....حسنًا، لقد قمت بإعداد هذا الاقتباس.لكن في الحقيقة فكر في الأمر....

@ j_random_hacker محق جدًا في تعليقاته تحت إجابات أخرى:تجنب استخدام alloca() لصالح المصفوفات المحلية كبيرة الحجم لا يجعل برنامجك أكثر أمانًا من تجاوزات المكدس (ما لم يكن المترجم الخاص بك قديمًا بما يكفي للسماح بتضمين الوظائف التي تستخدم alloca() وفي هذه الحالة يجب عليك الترقية، أو إلا إذا كنت تستخدم alloca() الحلقات الداخلية، وفي هذه الحالة يجب عليك...ليس للاستخدام alloca() الحلقات الداخلية).

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

alloca() (أو VLAs) قد تكون الأداة المناسبة لهذه المهمة.

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

ولكن إذا اخترت استخدام alloca()...

استنادًا إلى الإجابات الأخرى في هذه الصفحة، يبدو أن VLAs يجب أن تكون آمنة (لا تقوم بتركيب عمليات تخصيص المكدس إذا تم استدعاؤها من داخل حلقة)، ولكن إذا كنت تستخدم alloca(), ، احرص على عدم استخدامه داخل حلقة، واصنع بالتأكيد لا يمكن تضمين وظيفتك إذا كان هناك أي احتمال لاستدعائها داخل حلقة وظيفة أخرى.

وهنا لماذا:

char x;
char *y=malloc(1);
char *z=alloca(&x-y);
*z = 1;

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

وتقريبا كافة التعليمات البرمجية باستخدام alloca و / أو فلاس C99 ديه الخلل الخطيرة التي من شأنها أن تؤدي إلى حوادث (إذا كنت محظوظا) أو المساومة امتياز (إذا كنت لم يحالفهم الحظ).

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

إذا كتبت عن طريق الخطأ خارج الكتلة المخصصة لها alloca (بسبب تجاوز سعة المخزن المؤقت على سبيل المثال)، فسوف تقوم بالكتابة فوق ملف اعد العنوان من وظيفتك، لأن تلك الوظيفة تقع "أعلاه" على المكدس، أي. بعد الكتلة المخصصة لك.

_alloca block on the stack

والنتيجة من ذلك أمران:

  1. سوف يتعطل البرنامج بشكل مذهل وسيكون من المستحيل معرفة سبب أو مكان تعطله (من المرجح أن يتم إرجاع المكدس إلى عنوان عشوائي بسبب مؤشر الإطار المكتوب).

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

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

وشرك واحد مع alloca هو أن longjmp يلف عليه.

وهذا يعني، إذا قمت بحفظ السياق مع setjmp، ثم alloca بعض الذاكرة، ثم longjmp للسياق، قد تفقد الذاكرة alloca (بدون أي نوع من إشعار). مؤشر المكدس الظهر حيث كان وهكذا لم تعد تحتفظ الذاكرة. إذا كنت استدعاء دالة أو القيام alloca آخر، سوف هزم في alloca الأصلي.

لتوضيح، ما أنا في اشارة على وجه التحديد إلى هنا وضعا longjmp لا يرجع للخروج من وظيفة حيث أخذ alloca المكان! وبدلا من ذلك، وهي وظيفة يحفظ السياق مع setjmp. ثم يخصص الذاكرة مع alloca وأخيرا longjmp يحدث في هذا السياق. ذاكرة alloca أن الدالة لا يتم تحرير جميع؛ فقط كل الذاكرة التي خصصت منذ setjmp. بالطبع، أنا أتحدث عن سلوك الملاحظ. تم توثيقه لا يوجد مثل هذا الشرط في أي alloca أن أعرف.

والتركيز في وثائق عادة على مفهوم أن alloca يرتبط الذاكرة مع وظيفة <م> التنشيط، وليس مع أي كتلة. أن دعوات متعددة من alloca مجرد انتزاع المزيد من الذاكرة كومة الذي صدر عن عند إنهاء الدالة. ليس كذلك؛ ويرتبط الذاكرة فعلا مع سياق الإجراء. عند استعادة السياق مع longjmp، لذلك هي الدولة alloca السابقة. انها نتيجة لسجل مؤشر مكدس نفسها المستخدمة لتخصيص، وأيضا (بالضرورة) حفظ واستعادة في jmp_buf.

وبالمناسبة، هذا، اذا كان يعمل بهذه الطريقة، يوفر آلية معقولة لتحرير عمدا الذاكرة التي تم تخصيصها مع alloca.

ولقد واجه هذا هو السبب الجذري لوجود خطأ.

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

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

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

تحديث:نظرًا لأنه تمت إضافة المصفوفات المحلية ذات الطول المتغير إلى C وC++، وبما أن هذه تمثل مشكلات مشابهة جدًا في إنشاء التعليمات البرمجية للمترجم مثل alloca، فإنني أرى أن "ندرة الاستخدام والحالة المشبوهة" لا تنطبق على الآلية الأساسية؛لكنني ما زلت أشك في أن استخدام alloca أو VLA يميل إلى الإضرار بإنشاء التعليمات البرمجية داخل الوظيفة التي تستخدمهما.سأرحب بأي تعليقات من مصممي المترجمات.

للأسف رهيبة حقا alloca() مفقود من tcc الرائع تقريبًا.دول مجلس التعاون الخليجي لديها alloca().

  1. إنها تزرع بذور دمارها.مع العودة كمدمر.

  2. يحب malloc() يقوم بإرجاع مؤشر غير صالح عند الفشل والذي سوف ينقسم إلى الأنظمة الحديثة باستخدام MMU (ونأمل إعادة تشغيل تلك التي لا تحتوي عليها).

  3. على عكس المتغيرات التلقائية، يمكنك تحديد الحجم في وقت التشغيل.

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

إذا قمت بالدفع عميقًا جدًا، فتأكد من حدوث خطأ جزئي (إذا كان لديك MMU).

لاحظ أن malloc() لا يقدم المزيد لأنه يُرجع NULL (والذي سيتم تقسيمه أيضًا إذا تم تعيينه) عندما يكون النظام خارج الذاكرة.أي.كل ما يمكنك فعله هو الكفالة أو مجرد محاولة التنازل عنها بأي شكل من الأشكال.

ليستخدم malloc() أستخدم الكرات العالمية وأخصصها فارغة.إذا لم يكن المؤشر فارغًا، فأنا أقوم بتحريره قبل استخدامه malloc().

تستطيع ايضا استخذام realloc() كحالة عامة إذا أردت نسخ أي بيانات موجودة.تحتاج إلى التحقق من المؤشر قبل معرفة ما إذا كنت ستقوم بالنسخ أو التسلسل بعد realloc().

3.2.5.2 مزايا التخصيص

والعمليات لديها سوى كمية محدودة من مساحة مكدس المتاحة - أقل بكثير من كمية الذاكرة المتوفرة malloc()

وباستخدام alloca() لك زيادة كبيرة فرصك في الحصول على خطأ تجاوز المكدس (إذا كنت محظوظا، أو حادث لا يمكن تفسيره إذا كنت لا).

وليس جميلة جدا، ولكن إذا كان الأداء يهم حقا، هل يمكن أن preallocate بعض المساحة على المكدس.

إذا كنت بالفعل الآن حجم الحد الأقصى من الذاكرة منع حاجتك وتريد للحفاظ على الشيكات تجاوز، هل يمكن أن تفعل شيئا مثل:

void f()
{
    char array_on_stack[ MAX_BYTES_TO_ALLOCATE ];
    SomeType *p = (SomeType *)array;

    (...)
}

في الواقع، ليست مضمونة alloca لاستخدام المكدس. في الواقع، وتنفيذ دول مجلس التعاون الخليجي، 2.95 من alloca يخصص الذاكرة من كومة باستخدام malloc نفسها. كما أن التنفيذ هو عربات التي تجرها الدواب، فإنه قد يؤدي إلى حدوث تسرب للذاكرة وبعض سلوك غير متوقع إذا كنت اسميها داخل كتلة مع زيادة استخدام غوتو. لا، ليقول أنه يجب أن تستخدم أبدا، ولكن في بعض الأحيان alloca يؤدي إلى الحمل أكثر من releaves ذلك فروم.

وظيفة alloca كبيرة ووجميع الرافضين ببساطة تنتشر FUD.

void foo()
{
    int x = 50000; 
    char array[x];
    char *parray = (char *)alloca(x);
}

وصفيف وparray هي نفسها تماما مع بالضبط نفس المخاطر. قائلا واحد هو أفضل من الآخر هو خيار النحوي، وليس واحد التقني.

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

لماذا هذا سيء؟

IMHO، يعتبر التخصيص ممارسة سيئة لأن الجميع يخافون من استنفاد الحد الأقصى لحجم المكدس.

لقد تعلمت الكثير من خلال قراءة هذا الموضوع وبعض الروابط الأخرى:

أستخدم alloca بشكل أساسي لجعل ملفات C العادية قابلة للتجميع على msvc وgcc دون أي تغيير، ونمط C89، وبدون #ifdef _MSC_VER، وما إلى ذلك.

شكرًا لك !هذا الموضوع جعلني اشترك في هذا الموقع :)

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

لاستخدام جزءا لا يتجزأ، حيث يعرف حجم المكدس ويمكن فرض حدود عبر اتفاقية والتحليل على حجم التخصيص، وأين المترجم لا يمكن أن تتم ترقية لدعم C99 +، واستخدام alloca () على ما يرام، وأنا 'لقد كان معروفا لاستخدامها.

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

في أبنية التي تقوم بتخزين البيانات والعودة عناوين / مؤشرات الإطار على كومة (من ما أعرف، هذا كل واحد منهم)، أي متغير كومة المخصصة يمكن أن تكون خطيرة لأن عنوان متغير يمكن اتخاذها، والمدخلات دون رادع قد تسمح القيم كل أنواع الأذى.

وقابلية هو اقل مدعاة للقلق في الفضاء جزءا لا يتجزأ، ومع ذلك فإنه حجة جيدة ضد استخدام alloca () خارج الظروف التي تسيطر عليها بعناية.

وخارج الفضاء جزءا لا يتجزأ، لقد استعملت alloca () معظمها داخل قطع الأشجار وتنسيق وظائف للكفاءة، وماسح ضوئي المفردات غير متكررة، حيث هياكل مؤقتة (المخصصة باستخدام alloca) يتم إنشاء (خلال tokenization والتصنيف، ثم كائن المستمر يتم تعبئة (المخصصة عبر malloc ()) قبل أن يعود وظيفة، واستخدام alloca () لهياكل مؤقتة أصغر يقلل بدرجة كبيرة تجزئة عندما يتم تخصيص الكائن المستمرة.

ومعظم الأجوبة هنا يغيب إلى حد كبير هذه النقطة: هناك سبب لماذا باستخدام _alloca() يحتمل أن تكون أسوأ من مجرد تخزين كائنات كبيرة في كومة

.

والفرق الرئيسي بين التخزين التلقائي و_alloca() هو أن الأخير يعاني من مشكلة إضافية (خطيرة): كتلة المخصصة هو <م> غير الخاضعة لمترجم ، لذلك ليس هناك طريقة لمترجم لتحسين أو إعادة تدويرها.

وقارن:

while (condition) {
    char buffer[0x100]; // Chill.
    /* ... */
}

ومع:

while (condition) {
    char* buffer = _alloca(0x100); // Bad!
    /* ... */
}

يجب أن تكون المشكلة مع هذا الأخير واضحا.

وأنا لا أعتقد أن أحدا قد ذكر هذا، ولكن لديه alloca أيضا بعض القضايا الأمنية الخطيرة لا يقدم بالضرورة مع malloc (على الرغم من أن تنشأ هذه القضايا أيضا مع أي صفائف كومة القائمة على الحيوية أو لا). منذ يتم تخصيص الذاكرة على المكدس، عازلة الفيضانات / underflows لها عواقب أكثر خطورة بكثير من malloc عادل.

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

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top