سؤال

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

Unhandled exception at 0x012c240f in Project 5.exe: 0xC0000005: Access violation reading location 0x002b0000.

يحتفظ البرنامج حاليا بمجموعة Char، ولكن بعد ذلك يتوقف وينبثق هذا الخطأ. لدي شعور يجب القيام به مع NULL التحقق من أنني أفعل في بلدي Next() طريقة.

فكيف يمكنني إصلاح هذا؟

أيضا، إذا لاحظت أي شيء يمكنني القيام به أكثر كفاءة أو مع ممارسة أفضل، واسمحوا لي أن أعرف.

شكرًا!!


stringtokenizer.h:

#pragma once

class StringTokenizer
{
public:
StringTokenizer(void);
StringTokenizer(char* const, char);
char* Next(void);
~StringTokenizer(void);
private:
char* pStart;
char* pNextWord;
char delim;
};

stringtokenizer.cpp:

#include "stringtokenizer.h"
#include <iostream>
using namespace std;

StringTokenizer::StringTokenizer(void)
{
pStart = NULL;
pNextWord = NULL;
delim = 'n';
}

StringTokenizer::StringTokenizer(char* const pArray, char d)
{
pStart = pArray;
delim = d;
}

char* StringTokenizer::Next(void)
{
pNextWord = pStart;
if (pStart == NULL) { return NULL; }

while (*pStart != delim) // access violation error here
{
    pStart++;
}

if (pStart == NULL) { return NULL; }

*pStart = '\0'; // sometimes the access violation error occurs here
pStart++;

return pNextWord;
}

StringTokenizer::~StringTokenizer(void)
{
delete pStart;
delete pNextWord;
}

main.cpp:

// The PrintHeader function prints out my
// student info in header form
// Parameters - none
// Pre-conditions - none
// Post-conditions - none
// Returns - void
void PrintHeader();

int main ( )
{
const int CHAR_ARRAY_CAPACITY = 128;
const int CHAR_ARRAY_CAPCITY_MINUS_ONE = 127;

// create a place to hold the user's input
// and a char pointer to use with the next( ) function
char words[CHAR_ARRAY_CAPACITY];
char* nextWord;

PrintHeader();

cout << "\nString Tokenizer Project";
cout << "\nyour name\n\n";
cout << "Enter in a short string of words:";
cin.getline ( words, CHAR_ARRAY_CAPCITY_MINUS_ONE );

// create a tokenizer object, pass in the char array
// and a space character for the delimiter
StringTokenizer tk( words, ' ' );

// this loop will display the tokens
while ( ( nextWord = tk.Next ( ) ) != NULL )
{
    cout << nextWord << endl;
}


system("PAUSE");
return 0;
}


تعديل:

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

الوظيفة التي تعمل مع المساحات:

char* StringTokenizer::Next(void)
{
pNextWord = pStart;

if (*pStart == '\0') { return NULL; }

while (*pStart != delim)
{
    pStart++;
}

if (*pStart = '\0') { return NULL; }

*pStart = '\0';
pStart++;

return pNextWord;
}
هل كانت مفيدة؟

المحلول

يتم توفير هذه الإجابة بناء على السؤال المحرر والتعليقات / الملاحظات المختلفة في إجابات أخرى ...

أولا، ما هي الدول الممكنة ل PSTArt عند التالي () تسمى؟

  1. PSTart هو NULL (منشئ افتراضي أو تعيين خلاف ذلك إلى NULL)
  2. * pstart هو " 0" (سلسلة فارغة في نهاية السلسلة)
  3. * pstart هو delim (سلسلة فارغة في محدد مجاور)
  4. * PSTart هو أي شيء آخر (الرمز المميز غير فارغ)

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

if (pStart == NULL) { return NULL; }

لماذا لا نحسن أن تقلق بشأن الحالات 2 أو 3 حتى الآن؟ ربما تريد معالجة المحددات المجاورة بأنها رمزية سلسلة فارغة بينهما، بما في ذلك في بداية ونهاية السلسلة. (إن لم يكن، ضبط حسب الذوق.) ستتعامل الحلقة أثناء التعامل مع ذلك بالنسبة لنا، شريطة أن تضيف أيضا التحقق من " 0" (المطلوب بغض النظر):

while (*pStart != delim && *pStart != '\0')

بعد حلقة بينما تحتاج إلى أن تكون حذرا. ما هي الدول الممكنة الآن؟

  1. * pstart هو " 0" (ينتهي الرمز المميز في نهاية السلسلة)
  2. * PSTArt هو DELIM (Token ينتهي في Delimiter التالي)

لاحظ أن PSTart نفسها لا يمكن أن تكون فارغة هنا.

تحتاج إلى إرجاع pnextword (الرمز المميز الحالي) ل على حد سواء من هذه الشروط حتى لا تقوم بإسقاط الرمز المميز الأخير (أي متى * pstart هو " 0"). رمز مقابض الرمز 2 بشكل صحيح ولكن ليس حالة 1 (التعليمات البرمجية الأصلية تدريج خطير pstart الماضي " 0"، إرجاع الرمز الجديد فارغة). بالإضافة إلى ذلك، من المهم إعادة تعيين PStart للحالة 1 بشكل صحيح، بحيث تتم إرجاع المكالمة التالية إلى Next () NULL. سأترك الرمز الدقيق كممارحة للقارئ، لأنه واجباته المنزلي بعد كل شيء؛)

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

أخيرا، لاحظت أن لديك حذف المكالمات على كلا من PSTART و PnextWord في Destructor الخاص بك. أولا، لحذف المصفوفات، تحتاج إلى استخدام delete [] ptr; (أي مجموعة مجموعة). ثانيا، لن تحذف كلا من pstart و pnextword لأن نقاط pnextword في صفيف pstart. ثالثا، بحلول النهاية، لم يعد PSTART يشير إلى بداية الذاكرة، لذلك ستحتاج إلى عضو منفصل لتخزين البداية الأصلية ل delete [] يتصل. أخيرا، يتم تخصيص هذه الصفائف على المكدس وليس الكومة (أي باستخدام char var[], ، ليس char* var = new char[])، وبالتالي لا ينبغي حذفها. لذلك، يجب عليك ببساطة استخدام مدمر فارغ.

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

نصائح أخرى

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

النظر في الحلقة أثناء التالي ():

while (*pStart != delim) // access violation error here
{
    pStart++;
}

دعنا نقول السلسلة هي "blah\0". وبعد لاحظ أنني تضمنت إنهاء NULL. الآن، اسأل نفسك: كيف تعرف هذه الحلقة أن تتوقف عند وصولها إلى نهاية السلسلة؟

الأهم من ذلك: ماذا يحدث مع *pStart إذا حلقة فشل للتوقف في نهاية السلسلة؟

داخل :: التالي تحتاج إلى التحقق من حرف Delim، لكنك تحتاج أيضا إلى التحقق من نهاية المخزن المؤقت، (الذي أظن أنه يشار إليه 0).

while (*pStart != '\0' && *pStart != delim) // access violation error here
{
    pStart++;
}

وأعتقد أن هذه الاختبارات في :: التالي

if (pStart == NULL) { return NULL; }

يجب أن يكون هذا بدلا من ذلك.

if (*pStart == '\0') { return NULL; }

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

انتهاك وصول يعني عادة مؤشر سيء.

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

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