C# & C++:“试图读取或写入受保护的内存”错误

Posted

技术标签:

【中文标题】C# & C++:“试图读取或写入受保护的内存”错误【英文标题】:C# & C++: "Attempted to read or write protected memory" Error 【发布时间】:2009-11-29 13:01:36 【问题描述】:

以下代码编译没有错误。基本上,C#2005 控制台应用程序调用 VC++2005 类库,后者又调用本机 VC++6 代码。运行 C#2005 应用程序时出现以下错误:

未处理的异常:System.AccessViolationException:试图读取或写入受保护的内存。这通常表明其他内存已损坏。

这个错误的原因是什么?以及如何去纠正它?

Edit1:它在 StdStringWrapper ssw = w.GetNext();

行崩溃

Edit2:我听从了 Naveen 的建议,使用整数索引而不是迭代器,现在没有更多错误了。非常感谢所有发表评论的人!

用 C#2005 编写的代码作为控制台应用程序:

class Program

    static void Main(string[] args)
    
        Class1 test= new Class1();
        test.PerformAction();
        test.PerformAction();
        test.PerformAction();
        test.PerformAction();
    

在 VC++2005 中作为类库编写的代码:

public ref class Class1

    public:
        void PerformAction();       
;

void Class1::PerformAction()

    DoSomethingClass d;
    StdStringContainer w;
    d.PerformAction(w);

    for(int i=0; i<w.GetSize(); i++)
    
        StdStringWrapper ssw = w.GetNext();
        std::cout << ssw.CStr() << std::endl;
    

在 VC++6 中作为动态链接库编写的代码:

#ifdef NATIVECODE_EXPORTS
    #define NATIVECODE_API __declspec(dllexport)
#else
    #define NATIVECODE_API __declspec(dllimport)
#endif

class NATIVECODE_API StdStringWrapper

    private:
        std::string _s;

    public:     
        StdStringWrapper();
        StdStringWrapper(const char *s);
        void Append(const char *s);
        const char* CStr() const;       
;

StdStringWrapper::StdStringWrapper()



StdStringWrapper::StdStringWrapper(const char *s)

    _s.append(s);


void StdStringWrapper::Append(const char *s)

    _s.append(s);


const char* StdStringWrapper::CStr() const

    return _s.c_str();

//

class NATIVECODE_API StdStringContainer

    private:
        std::vector<StdStringWrapper> _items;
        std::vector<StdStringWrapper>::iterator _it;

    public: 
        void Add(const StdStringWrapper& item);
        int GetSize() const;        
        StdStringWrapper& GetNext();  
;

void StdStringContainer::Add(const StdStringWrapper &item)

    _items.insert(_items.end(),item);


int StdStringContainer::GetSize() const

    return _items.size();


StdStringWrapper& StdStringContainer::GetNext()

    std::vector<StdStringWrapper>::iterator it = _it;
    _it++;

    return *it;

//

class NATIVECODE_API DoSomethingClass

    public:
        void PerformAction(StdStringContainer &s);
;

void DoSomethingClass::PerformAction(StdStringContainer &s)

    StdStringWrapper w1;
    w1.Append("This is string one");
    s.Add(w1);

    StdStringWrapper w2;
    w2.Append("This is string two");
    s.Add(w2);

【问题讨论】:

它在 StdStringContainer::GetNext() 中崩溃了吗? 是的,我检查了它,它在 StdStringContainer::GetNext() 处崩溃。知道为什么吗? 【参考方案1】:

StdStringContainer 中的成员 _it 从未初始化为指向 _items 向量。这意味着它是一个无效的迭代器。当您在GetNext() 中将_it 分配给it 时,您已经给it 赋予了_it 中存在的无效、未初始化的值。然后,您通过_it++ 增加未初始化的_it,这就是触发您的错误的原因。

正如 Stroustrup 在 19.2 中所说,未初始化的迭代器是无效的迭代器。这意味着您未初始化的 _it 是无效的,并且使用它执行的操作是未定义的,并且可能会导致严重的失败。

但是,您的问题更深层次。迭代器的生命周期与其枚举的容器有着根本不同的生命周期。除非容器是不可变的并且在构造函数中初始化,否则实际上没有任何“好”的方法可以用这样的单个迭代器成员来做你想做的事情。

如果您不能公开 std:: 命名空间名称,您是否考虑过通过 typedef 给它们起别名,例如?您的组织或项目导致无法公开模板类的原因是什么?

【讨论】:

【参考方案2】:

在我看来,主要问题是您在 stdStringContainer 类中将 iterator 存储到 vector 中。请记住,每当向量调整大小时,所有现有的迭代器都会失效。因此,每当您对向量进行插入操作时,它可能会调整大小并且您现有的迭代器变得无效。如果您尝试在GetNext() 中取消引用它,那么它将访问无效的内存位置。为了检查这是否真的是这种情况,请尝试将初始向量大小保留为某个相对较大的数字,以便不会发生调整大小。您可以使用reserve()方法保留大小,这种情况下保证向量的capacity()大于或等于保留值。

【讨论】:

我想出了 StdStringContainer 类,因为我需要使用 std::vector 并能够对其进行迭代。但是,我不能将 std::vector、std::string 等 STL 变量作为函数参数公开。我该如何纠正这个错误? 当向量重新分配时迭代器不会失效——指针会失效。这就是使用迭代器而不是向量上的指针的目标。从向量中删除元素时,迭代器可能会失效。 @KK:为什么不存储整数索引而不是迭代器? 区分重新分配(一个实现可以根据需要在内存中随机移动一个向量)和失效(当向量改变时发生)。 Sgi 的文档似乎没有做出同样的区分。具体来说,通过 Stroustrup (19.2):“迭代器可能无效......因为它 [未] 初始化,它指向一个已调整大小的容器,该容器已被销毁,或者它表示序列的结束。”没有关于迭代器失效的“重新分配”概念。 可以用整数索引访问std::vector中的项目吗?【参考方案3】:

听起来你有内存泄漏。我建议查看有指针算术、写入内存或数组使用的任何地方。检查数组访问中的边界条件。

另一个问题:很多漏洞甚至不在您的代码中。如果是这种情况,您必须从项目中排除该库。

【讨论】:

【参考方案4】:

我的猜测是,您有崩溃是因为两个 C++ 模块之间的接口中的 std::stringstd::vector 是使用不同的编译器和运行时库编译的。

向量和字符串的内存布局在VC6和2005之间可能会发生变化。

当 2005 DLL 分配 StdStringContainerStdStringWrapper 类型的对象时,它会根据 2005 标头中的 stringvector 声明进行分配。

当对这些对象(已使用 VC6 编译器和库编译)调用成员函数时,它们会采用不同的内存布局并因访问冲突而失败。

【讨论】:

以上是关于C# & C++:“试图读取或写入受保护的内存”错误的主要内容,如果未能解决你的问题,请参考以下文章

PInvoke“试图读取或写入受保护的内存”

“试图读取或写入受保护的内存。这通常表明其他内存已损坏” DllImporting C#

c# emgu/opencv 使用抛出异常 - 试图读取或写入受保护的内存

Visual Studio C# 试图读取或写入受保护的内存。这通常表明其他内存已损坏

C 和 System.AccessViolationException 中的 allocVector():试图读取或写入受保护的内存?

尝试读取或写入受保护的内存。这通常表明其他内存已损坏。在 C++ DLL 中