سؤال

وكما يعلم الجميع فإن فيجوال سي++ يحدد وقت التشغيل كتل الذاكرة غير المهيأة أو التي تم تحريرها للتو بعلامات خاصة غير صفرية.هل هناك أي طريقة لتعطيل هذا السلوك بالكامل دون تعيين كافة الذاكرة غير المهيأة يدويًا على الأصفار؟إنه يسبب الفوضى في الشيكات الصالحة وغير الفارغة منذ ذلك الحين 0xFEEEFEEE != 0.

حسنًا، ربما يجب أن أشرح بشكل أفضل قليلاً.أقوم بإنشاء متغير وتهيئته (عبر جديد)، وكل شيء يسير على ما يرام.عندما أقوم بتحريره (عبر الحذف)، فإنه يضبط المؤشر على 0xFEEEFEEE بدلاً من NULL.عندما أقوم بإدخال الاختيار المناسب ل NULL, ، كما ينبغي لجميع البرامج الجيدة التي تدير ذاكرتها الخاصة، أواجه مشاكل مثل 0xFEEEFEEE يمر أ NULL تحقق دون مشاكل.هل هناك أي طريقة جيدة، بخلاف ضبط جميع المؤشرات يدويًا على NULL عند حذفها، لاكتشاف متى تم تحرير الذاكرة بالفعل؟أفضّل عدم الاستخدام يعزز ببساطة لأنني لا أريد النفقات العامة، رغم أنها قد تكون صغيرة، لأن هذا هو الشيء الوحيد الذي سأستخدم Boost من أجله.

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

المحلول

إنها ليست مسؤولية delete لإعادة تعيين كافة المؤشرات إلى الكائن NULL.كما لا يجب عليك تغيير تعبئة الذاكرة الافتراضية لوقت تشغيل Windows DEBUG ويجب عليك استخدام شيء مثل boost::shared_ptr<> للمؤشرات بأي شكل من الأشكال.

ومع ذلك، إذا كنت تريد حقا أطلق النار على نفسك في القدم أنت تستطيع.

أنت تستطيع يتغير ال التعبئة الافتراضية للنوافذ وقت تشغيل التصحيح باستخدام ربط مخصص مثل هذا.سيعمل هذا فقط على الكائن المخصص لـ HEAP!

int main(int argc,char** arv)
{
  // Call first to register hook    
  _CrtSetAllocHook(&zero_fill);
  // Do other stuff
  malloc(100);
}


int zero_fill(int nAllocType, 
              void* pvData, 
              size_t nSize,
              int nBlockUse, 
              long lRequest, 
              const unsigned char *szFileName, 
              int nLine )
{
  /// Very Importaint !! 
  /// infinite recursion if this is removed !!
  /// _CRT_BLOCK must not do any thing but return TRUE
  /// even calling printf in the _CRT_BLOCK will cause
  /// infinite recursion
  if ( nBlockUse == _CRT_BLOCK )
    return( TRUE );
  switch(nAllocType)
  {
  case _HOOK_ALLOC:
  case _HOOK_REALLOC:
    // zero initialize the allocated space.
    memset(pvData,0,nSize);
    break;
  case _HOOK_FREE:
    break;
  }
  return TRUE;
}

نصائح أخرى

عندما تقوم بإنشاء مؤشر، قم بتهيئته بشكل صريح NULL.وكذلك بعد أ delete.الاعتماد على قيمة البيانات غير المهيأة (إلا في حالات قليلة محددة) أمر يطرح مشكلة.

يمكنك أن تنقذ نفسك من الكثير من الصداع باستخدام فئة المؤشر الذكي (مثل boost::shared_ptr) والذي سيتعامل تلقائيًا مع ما إذا كان قد تمت تهيئة المؤشر أم لا.

لا ينبغي لسلوك VC++ أن يسبب الفوضى لأي منها صالح تحقق مما يمكنك القيام به.إذا كنت ترى 0xfeeefeee، فأنت لم تكتب إلى الذاكرة (أو قمت بتحريرها)، لذلك لا ينبغي أن تقرأ من الذاكرة على أي حال.

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

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

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

قول انت:

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

حتى إجراءات كومة التصحيح الخاصة بـ MSVC لن تغير قيمة ملف المؤشر أنت تقوم بالحذف - لن تتغير قيمة المؤشر الذي تقوم بحذفه (حتى إلى NULL).يبدو أنك تصل إلى مؤشر ينتمي إلى الكائن الذي قمت بحذفه للتو، وهو خطأ واضح وبسيط.

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

@ جيف هوبارد (تعليق):

هذا في الواقع يوفر لي عن غير قصد الحل الذي أريده:يمكنني ضبط pvData على NULL على _HOOK_FREE وعدم مواجهة مشكلات مع 0xFEEEFEEE لعنوان المؤشر الخاص بي.

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

هذا خطأ.

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

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

إذا كان يعمل في وضع الإصدار، فهذا بسبب حظ القص.

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

أعتقد أننا نتفق جميعًا على أن الكود التالي لا ينبغي أن يعمل.

char * p = new char[16];     // 16 bytes of random trash
strcpy(p, "StackOverflow");  // 13 characters, a '\0' terminator, and two bytes of trash
delete [] p;                 // return 16 bytes to the heap, but nothing else changes;

if (p != NULL)               // Why would p be NULL?  It was never set to NULL
    ASSERT(p[0] == 'S');     // In debug, this will crash, because p = 0xfeeefeee and 
                             // dereferencing it will cause an error.
                             // Release mode may or may or may not work, depending on
                             // other memory operations

كما قال كل ملصق آخر تقريبًا، يجب ضبط المؤشرات على ذلك NULL بعد الاتصال delete.سواء كنت تفعل ذلك بنفسك أو تستخدم التعزيز أو أي غلاف آخر أو حتى الماكرو في هذا الموضوع فهذا أمر متروك لك.

ما يحدث هو تعطل الكود الخاص بي تحت تجميع تصحيح الأخطاء ، لكنه ينجح تحت تجميع الإصدار.

سوف يتعطل إصدار الإصدار على جهاز العميل.إنه كذلك دائمًا.

لقد راجعت ذلك تحت تصحيح الأخطاء ويتم تعيين مؤشراتي على 0xfeefeee بعد أن أتصل بحذفها.

المؤشرات لا يتم تغييرها بعد استدعاء الحذف عليها.إنها الذاكرة التي يشيرون إليها والتي يتم ضبطها على 0xfeeefeee، 0xfeeefeee، ...، 0xfeeefeee.

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

@ [جيف هوبارد]:

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

هذا سلوك غريب جدًا - ما زلت مقتنعًا بأنه من المحتمل أن يكون هناك خطأ كامن يتم إخفاؤه بواسطة _CrtSetAllocHook() الحل البديل.

ال 0xFEEEFEEE يتم استخدام التوقيع بواسطة مدير كومة نظام التشغيل للإشارة إلى الذاكرة المحررة (انظر http://www.nobugs.org/developer/win32/debug_crt_heap.html).بأي حال من الأحوال، هل يمكنك نشر بعض التعليمات البرمجية المكررة والإشارة بالضبط إلى إصدار المترجم الذي تستخدمه؟

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

أفضل ما لديك هو الاعتياد على ضبطها على 0 في المقام الأول، فهي عبارة عن حرفين إضافيين فقط.

int *ptr=0;

يمكنك أيضًا استخدام الماكرو NULL، والذي تم تعريفه على أنه 0 (ولكن ليس افتراضيًا، لذا كن حذرًا مع التعريفات المتعددة عند تضمين أشياء مثل windows.h وتعريفها بنفسك!)

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

http://wiki.answers.com/Q/What_is_the_difference_between_malloc_and_calloc_functions

لماذا لا تنشئ تعريفًا خاصًا بك وتعتاد على استخدامه؟

أي.

#define SafeDelete(mem) { delete mem; mem = NULL; }
#define SafeDeleteArray(mem) { delete [] mem; mem = NULL; }

من الواضح أنه يمكنك تسميتها كما تريد.حذف Z، حذف آمن، كل ما يناسبك.

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

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