سؤال

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

أنا أستخدم الكود التالي:

char *msg = (char*)malloc(sizeof(char)*2048);
char *temp = (char*)malloc(sizeof(char)*1024);
memset(msg, 0, 2048);
memset(temp, 0, 1024);
char *tempstr = (char*)malloc(sizeof(char)*128);

sprintf(temp, "%s %s/%s %s%s", EZMPPOST, EZMPTAG, EZMPVER, TYPETXT, EOL);
strcat(msg, temp);

//Add Data
memset(tempstr, '\0', 128);
wcstombs(tempstr, gdevID, wcslen(gdevID));
sprintf(temp, "%s: %s%s", "DeviceID", tempstr, EOL);
strcat(msg, temp);

كما ترون، أنا لا أحاول استخدام memset بحجم أكبر مما تم تخصيصه في الأصل malloc()

هل يرى أحد ما قد يكون الخطأ في هذا؟

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

المحلول

malloc يمكن العودة NULL إذا لم تتوفر الذاكرة.أنت لا تتحقق من ذلك.

نصائح أخرى

هناك بضعة أشياء.أنت تستخدم sprintf وهو أمر غير آمن بطبيعته؛إلا إذا كنت متأكدًا بنسبة 100% من أنك لن تتجاوز حجم المخزن المؤقت، فيجب عليك فعل ذلك تقريبًا دائماً يفضل snprintf.الأمر نفسه ينطبق على strcat;تفضل البديل الأكثر أمانا strncat.

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

يمكن أن يعود Malloc NULL إذا لم تتوفر ذاكرة.أنت لا تتحقق من ذلك.

صحيح أنت...لم أفكر في ذلك لأنني كنت أراقب الذاكرة وكان هناك ما يكفي من الحرية.هل هناك أي طريقة لتوفير ذاكرة متاحة على النظام ولكن فشل malloc؟

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

وفي كلتا الحالتين...سأضيف هذا الاختيار :)

wcstombs لا يحصل على حجم الوجهة، لذلك يمكنه، من الناحية النظرية، تجاوز سعة المخزن المؤقت.

ولماذا تستخدم sprintf مع ما أفترض هي الثوابت؟مجرد استخدام:

EZMPPOST" " EZMPTAG "/" EZMPVER " " TYPETXT EOL

يجمع C وC++ بين التصريحات الحرفية للسلسلة في سلسلة واحدة.

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

أنت تستخدم Sprintf الذي لا هو آمن بطبيعته ؛ما لم تكن إيجابيًا بنسبة 100 ٪ أنك لن تتجاوز حجم المخزن المؤقت ، يجب أن تفضل دائمًا Snprintf.الأمر نفسه ينطبق على strcat؛تفضل البديل الأكثر أمانًا strncat.

نعم.....غالبًا ما أستخدم .NET مؤخرًا والعادات القديمة تموت بصعوبة.من المحتمل أنني قمت بسحب هذا الرمز من شيء آخر تمت كتابته قبل وقتي ...

لكن سأحاول عدم استخدامها في المستقبل ;)

أنت تعلم أنه قد لا يكون حتى الرمز الخاص بك ...هل هناك أي برامج أخرى قيد التشغيل يمكن أن يكون بها تسرب للذاكرة؟

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

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

لن يحدث ذلك إلا إذا كنت تستخدم وحدة معالجة مركزية غير عادية.على سبيل المثال، سيفعل ARM9 ذلك، لكن i686 لن يفعل ذلك.أرى أنه يحتوي على علامة Windows Mobile، لذا ربما تكون لديك مشكلة في وحدة المعالجة المركزية (CPU).

بدلا من القيام malloc تليها memset, ، يجب أن تستخدم calloc مما سيؤدي إلى مسح الذاكرة المخصصة لك حديثًا.بخلاف ذلك، افعل ما قاله جويل.

استعارت NB بعض التعليقات من الإجابات الأخرى وتم دمجها في الكل.الكود كله عندي ...

  • تحقق من رموز الخطأ الخاصة بك.على سبيل المثاليمكن لـ malloc إرجاع NULL في حالة عدم توفر ذاكرة.قد يتسبب هذا في إحباط بياناتك.
  • sizeof(char) هو 1 حسب التعريف
  • استخدم snprintf وليس sprintf لتجنب تجاوز سعة المخزن المؤقت
    • إذا كانت EZMPPOST وما إلى ذلك ثوابت، فلن تحتاج إلى سلسلة تنسيق، يمكنك فقط دمج عدة سلاسل حرفية مثل STRING1 " " STRING2 " " STRING3 وstrcat المجموعة بأكملها.
  • أنت تستخدم ذاكرة أكثر بكثير مما تحتاج إليه.
  • مع تغيير بسيط واحد، لن تحتاج إلى الاتصال بـ memset في المقام الأول.لا شيء يتطلب حقًا تهيئة صفر هنا.

هذا الرمز يفعل نفس الشيء، بأمان، ويعمل بشكل أسرع، ويستخدم ذاكرة أقل.

    // sizeof(char) is 1 by definition. This memory does not require zero
    // initialisation. If it did, I'd use calloc.
    const int max_msg = 2048;
    char *msg     = (char*)malloc(max_msg);
    if(!msg)
    {
       // Allocaton failure
       return;
    }
    // Use snprintf instead of sprintf to avoid buffer overruns
    // we write directly to msg, instead of using a temporary buffer and then calling
    // strcat. This saves CPU time, saves the temporary buffer, and removes the need
    // to zero initialise msg.
    snprintf(msg, max_msg, "%s %s/%s %s%s", EZMPPOST, EZMPTAG, EZMPVER, TYPETXT, EOL);

   //Add Data
   size_t len = wcslen(gdevID);
   // No need to zero init this
   char* temp = (char*)malloc(len);
   if(!temp)
   {
      free(msg);
      return;
   }
   wcstombs(temp, gdevID, len);
   // No need to use a temporary buffer - just append directly to the msg, protecting 
   // against buffer overruns.
   snprintf(msg + strlen(msg), 
           max_msg - strlen(msg), "%s: %s%s", "DeviceID", temp, EOL);
   free(temp);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top