访问违例与指针? - C ++ [英] Access Violation With Pointers? - C++

查看:137
本文介绍了访问违例与指针? - C ++的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个简单的字符串tokenizing程序使用指针为最近的一个学校项目。但是,我遇到了麻烦与我的 StringTokenizer :: Next()方法,当被调用时,应该返回一个指针下一个字的第一个字母char数组。我没有编译时错误,但我得到一个运行时错误,说明:

 未处理的异常在0x012c240f在Project 5.exe :0xC0000005:访问冲突读取位置0x002b0000。 

程序当前对char数组进行标记,但随后停止,我有一个感觉,它与 NULL 检查我在我的 Next()方法。 / p>

那么我该如何解决这个问题呢?



此外,如果你注意到我能做的更有效或更好






b


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:

  #includestringtokenizer.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)//访问冲突错误这里
{
pStart ++;
}

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

* pStart ='\0'; //有时会出现访问冲突错误
pStart ++;

return pNextWord;
}

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






cpp:

  // PrintHeader函数以标题格式打印出我的
//学生信息b $ b //参数 - 无
//前提条件 - 无
//后置条件 - 无
//返回 - void
void PrintHeader();

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

//创建一个用于保存用户输入的地方
//和一个用于next()函数的char指针
char words [CHAR_ARRAY_CAPACITY];
char * nextWord;

PrintHeader();

cout<< \\\
String Tokenizer Project;
cout<< \\\
your name\\\
\\\
;
cout<< 输入一串短语:;
cin.getline(words,CHAR_ARRAY_CAPCITY_MINUS_ONE);

//创建一个tokenizer对象,传入char数组
//和分隔符的空格字符
StringTokenizer tk(words,'');

//这个循环将显示标记
while((nextWord = tk.Next())!= NULL)
{
cout< nextWord<< endl
}


系统(PAUSE);
return 0;
}








b $ b

EDIT:



好了,现在我的程序运行正常,只要分隔符是空格。但是如果我把一个`/'作为一个分隔符,它再次出现访问冲突错误。任何想法?



适用于空格的函数:

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

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

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

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

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

return pNextWord;
}


解决方案

根据编辑的问题和其他答案中的各种注释/观察提供...



首先,当Next




  1. pStart为NULL(默认构造函数或以其他方式设置为NULL) '\0'(字符串末尾的空字符串)

  2. * pStart是delim(相邻分隔符处的空字符串)

  3. * pStart是任何其他(非空字符串令牌)

此时,我们只需要担心第一个选项。因此,我将使用原始的if检查:

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

为什么我们不需要担心案例2或3?您可能希望将相邻定界符视为在它们之间具有空字符串标记,包括在字符串的开头和结尾。 (如果没有,调整味道。)while循环将处理我们,如果你还添加'\0'检查(不需要):

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

在while循环之后,你需要小心。


  1. * pStart是'\0'(标记结束于字符串结尾)
  2. b $ b
  3. * pStart是delim(令牌结束于下一个分隔符)

请注意,pStart本身不能为NULL。 / p>

您需要为这些条件的两者返回pNextWord(当前令牌),以便不丢弃最后一个令牌(即,当* pStart是'\0')。代码正确处理case 2,但不是case 1(原始代码危险地增加pStart超过'\0',新代码返回NULL)。此外,重要的是正确地重置情况1的pStart,以便下一次调用Next()返回NULL。我将把确切的代码作为练习留给读者,因为它毕竟是作业;)



这是一个很好的练习,概述整个函数中可能的数据状态以便为每个状态确定正确的操作,类似于正式定义基本案例与递归函数的递归案例。



最后,我注意到你有两个删除调用pStart和pNextWord在你的析构函数中。首先,要删除数组,需要使用 delete [] ptr; (即数组删除)。第二,你不会同时删除pStart和pNextWord,因为pNextWord指向pStart数组。第三,到最后,pStart不再指向内存的开始,因此您需要一个单独的成员来存储 delete [] 调用的原始开始。最后,这些数组分配在堆栈而不是堆上(即使用 char var [] ,而不是 char * var = new char [] ),因此不应删除它们。因此,你应该使用一个空析构函数。



另一个有用的提示是计算 new code> delete 调用;应该有相同的数量。在这种情况下,您有零个呼叫和两个删除呼叫,表示严重的问题。如果是相反的,它将指示内存泄漏。


I've written a simple string tokenizing program using pointers for a recent school project. However, I'm having trouble with my StringTokenizer::Next() method, which, when called, is supposed to return a pointer to the first letter of the next word in the char array. I get no compile-time errors, but I get a runtime error which states:

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

The program currently tokenizes the char array, but then stops and this error pops up. I have a feeling it has to do with the NULL checking I'm doing in my Next() method.

So how can I fix this?

Also, if you notice anything I could do more efficiently or with better practice, please let me know.

Thanks!!


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;
}



EDIT:

Okay, I've got the program working fine now, as long as the delimiter is a space. But if I pass it a `/' as a delim, it comes up with the access violation error again. Any ideas?

Function that works with spaces:

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;
}

解决方案

This answer is provided based on the edited question and various comments/observations in other answers...

First, what are the possible states for pStart when Next() is called?

  1. pStart is NULL (default constructor or otherwise set to NULL)
  2. *pStart is '\0' (empty string at end of string)
  3. *pStart is delim (empty string at an adjacent delimiter)
  4. *pStart is anything else (non-empty-string token)

At this point we only need to worry about the first option. Therefore, I would use the original "if" check here:

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

Why don't we need to worry about cases 2 or 3 yet? You probably want to treat adjacent delimiters as having an empty-string token between them, including at the start and end of the string. (If not, adjust to taste.) The while loop will handle that for us, provided you also add the '\0' check (needed regardless):

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

After the while loop is where you need to be careful. What are the possible states now?

  1. *pStart is '\0' (token ends at end of string)
  2. *pStart is delim (token ends at next delimiter)

Note that pStart itself cannot be NULL here.

You need to return pNextWord (current token) for both of these conditions so you don't drop the last token (i.e., when *pStart is '\0'). The code handles case 2 correctly but not case 1 (original code dangerously incremented pStart past '\0', the new code returned NULL). In addition, it is important to reset pStart for case 1 correctly, such that the next call to Next() returns NULL. I'll leave the exact code as an exercise to reader, since it is homework after all ;)

It's a good exercise to outline the possible states of data throughout a function in order to determine the correct action for each state, similar to formally defining base cases vs. recursive cases for recursive functions.

Finally, I noticed you have delete calls on both pStart and pNextWord in your destructor. First, to delete arrays, you need to use delete [] ptr; (i.e., array delete). Second, you wouldn't delete both pStart and pNextWord because pNextWord points into the pStart array. Third, by the end, pStart no longer points to the start of the memory, so you would need a separate member to store the original start for the delete [] call. Lastly, these arrays are allocated on the stack and not the heap (i.e., using char var[], not char* var = new char[]), and therefore they shouldn't be deleted. Therefore, you should simply use an empty destructor.

Another useful tip is to count the number of new and delete calls; there should be the same number of each. In this case, you have zero new calls, and two delete calls, indicating a serious issue. If it was the opposite, it would indicate a memory leak.

这篇关于访问违例与指针? - C ++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆