为什么我在这里获得EXC_BAD_ACCESS以及如何使其工作?

时间:2009-11-25 01:16:22

标签: c++ string strtok

我正在努力让我的代码能够将a file分成一个客户数据库(它由许多空格而不是制表符分隔)。我尝试使用strtok,但是我收到了EXC_BAD_ACCESS错误。这是我的main.cpp代码:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include "Cust.h"
using namespace std;

int main (int argc, char * const argv[]) {
    Cust customers[500];
 int idx = 0;
 string tmpString = "";
 string tmpAcctFN = "";
 string tmpAcctLN = "";
 ifstream input("P3_custData.txt");
 while (!input.eof()){
  getline(input,tmpString);
  tmpString.insert(0,"");
  customers[idx].setAcctNum(atoi(strtok((char *)tmpString.c_str()," ")));
  customers[idx].setAcctFN(strtok(NULL," "));
  customers[idx].setAcctLN(strtok(NULL," "));
  //customers[idx].setCurrBalance(atof(strtok((char *) tmpString.c_str()," ")));
 }
 cout << "return 0;";
    return 0;
}

根据评论进行更改后,我仍然获得了EXC_BAD_ACCESS:

#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
#include "Cust.h"
using namespace std;

int main (int argc, char * const argv[]) {
    Cust customers[500];
    int idx = 0;
    string tmpString = "";
    string tmpAcctFN = "";
    string tmpAcctLN = "";
    char * s;
    ifstream input("P3_custData.txt");
    while (!input.eof()){
        getline(input,tmpString);
        s = strdup (tmpString.c_str());
        customers[idx].setAcctNum(atoi(strtok(s," ")));
        customers[idx].setAcctFN(strtok(NULL," "));
        customers[idx].setAcctLN(strtok(NULL," "));
        //customers[idx].setCurrBalance(atof(strtok((char *) tmpString.c_str()," ")));
    }
    cout << "return 0;";
    return 0;
}

3 个答案:

答案 0 :(得分:2)

尝试修改std::string::c_str()方法返回的字符串是非法的。 strtok将进行这样的尝试(事实上你必须抛弃返回的字符串的常量是一个死的赠品)。换句话说,您无法对strtok的结果使用std::string::c_str()

要么摆脱strtok(更好),要么创建一个独立的可修改字符串副本并在其上使用strtok(更糟糕)。

答案 1 :(得分:0)

c_str方法将返回指向const数据的指针。一种方法是替换:

customers[idx].setAcctNum(atoi(strtok((char *)tmpString.c_str()," ")));
customers[idx].setAcctFN(strtok(NULL," "));
customers[idx].setAcctLN(strtok(NULL," "));

使用:

char *s = strdup (tmpString.c_str());
// check if s is NULL
customers[idx].setAcctNum(atoi(strtok(s," ")));
customers[idx].setAcctFN(strtok(NULL," "));
customers[idx].setAcctLN(strtok(NULL," "));
free (s);

这为您提供了一个重复的可写字符串,您可以在其上运行strtok

我通常不是在C ++代码中使用旧式C语言的粉丝,但我在实现代码运行方面也很务实。可能有一种更“C ++”的做事方式。

并且,如果您的实现没有strdup(我认为它不是标准的一部分):

char *strdup (const char *s) {
    char *d = (char *)(malloc (strlen (s) + 1));
    if (d == NULL) return NULL;
    strcpy (d,s);
    return d;
}

更新

Anthony,基于您的问题的更新,如果您仍然收到错误,那么可能是因为字符串不是您所期望的。考虑一下,你应该EXC_BAD_ACCESS写入c_str的结果,因为内存在技术上不是只读的。 EXC_BAD_ACCESS只应在您尝试写入只读内存(或在地址空间之外)时才会出现。正如一位评论者指出的那样,c_str 可以为某些简单的情况返回指向只读内存的指针,但如果您的文件有复杂的行,则不太可能出现这种情况。

在任何一种情况下,复制到可变字符串都会修复它。

我认为可能发生的是你的strtok之一正在返回NULL。如果您正在处理少于三个字段的行(例如,“hello there”,其中第三个strtok将返回NULL),则会出现这种情况。

strtok来电之前打印代码中的字符串:

std::cerr << "[" << s << "]" << std::endl;

(并确保在strtok调用后释放字符串,以免最终导致内存泄漏。

如果结果是问题,解决方案取决于您的需求。您可以通过在调用set...()之前检查并复制单个字符串来完全忽略这些行 - 换句话说,只有在所有三个字符串都为非null时才调用它们。

您可以事先检查字符串,以计算分隔符的数量,确保至少有两个。

我确信还有其他可能性,但直到我才能真正提供帮助:

  1. 我们知道问题是可疑的;和
  2. 你告诉我们你想要发生什么。
  3. 希望有所帮助。

答案 2 :(得分:0)

如果没有剩余的令牌,

strtok()将返回NULL。你必须检查那个指针,例如到std::string的构造函数或赋值运算符。

char* token = ::strtok(0, " ");
if(!token) {
    // handle error
}

不过,考虑使用字符串流或其他更多类似C ++的功能,以避免首先陷入此类陷阱。