الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

مشكله في الذاكرة

بدأه OsamaHosamEldeen في 15 ديسمبر 2009 · 14 رد · 1,411 مشاهدة · في الأسئلة المجابة
مشاركة: واتساب X فيسبوك تيليجرام
#1 صاحب الموضوع

السلام عليكم جميعا ،

انا جديد في المنتدى وارجو من الجميع مساعدتي، اعمل برنامج حاليا يقوم بعرض الملفات التي تم صنعها ببرنامج ثري دي ماكس، والبرنامج يستخدم مكتبه MFC وفي خلال عملي اجد مشكله عند اضافة متغيرمن نوع string الى الكلاس سواء public or private or protected في كل الاحوال اجد ان Debugger يعطيني هذه الرسالة

"Unhandled Exception ,,Access violation

البرنامج يأخذ من الذاكرة لانه يقرأ الملف الذي له الامتداد .3ds ثم يقوم بقراءة هذا الملف في صورة struct وبذلك اقوم باستخدام هذه structs في برنامجي لعمل بعض التعديلات على الشكل الثلاثي الابعاد

1
#2

السلام عليكم ...

هذا الخطأ عبارة عن Exception من نظام التشغيل الذي تعمل عليه, يخبرك بأنك تحاول الوصول إلى ذاكرة لا تملكها. لو وضعت الكود الذي يحدث المشكلة أخي العزيزي لننظر فيه,

تحياتي,

#3

أخي العزيز

الكود ببساطة يقرأ ملف من نوع ثري دي اس ويضعه في struct واليك كود بناء struct

#define MAX_VERTICES 80000 // Max number of vertices (for each object)
#define MAX_POLYGONS 80000 // Max number of polygons (for each object)

// Our vertex type
typedef struct{
    float x,y,z;
}vertex_type;

// The polygon (triangle), 3 numbers that aim 3 vertices
typedef struct{
    int a,b,c;
}polygon_type;

// The object type
typedef struct {
	char name[20];

	int vertices_qty;
    int polygons_qty;

    vertex_type vertex[MAX_VERTICES]; 
    polygon_type polygon[MAX_POLYGONS];

} obj_type, *obj_type_ptr;

وأما كود قراءة الملف فهو عبارة عن دالة عامة يمكن استدعاؤها من اي مكان في البرنامج وتعمل بالشكل الاتي

Load3DS(&object,charItemName);

بالنسبة لل class التي استخدمها في العمل على object هي دوال بسيطة وعادية جدا غير معقدة ولا تحتاج الى تعامل معقد مع الداكرة ولكن المشكلة في انني عند اضافة متغير الى class من نوع string تحدث المشكلة التي قلتها سالفا

#4

أعتقد أني أمسكت بطرف الخيط أخي العزيز, و لكن أحتاج إلى قراءة كود الدالة, إن لم يكن هناك مانع بالطبع:

Load3DS(&object,charItemName);

هل المقصود حينما تستبدل:

char name[20];

بـ:

std::string name;

أو CString من MFC, تحدث المشكلة؟

الذي يظهر لي أنك تحاول القراءة من ملف binary, و ربما هذا هو سبب المشكلة,

تحياتي,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 15 ديسمبر 2009 في 18:09

#5

سيدي

لا توجد اي مشكلة في الدالة التي تقرا الملف، لاني استخدمها في مشروعات اخرى دون ادني مشكلة، والبرنامج لاتوجد به ايضا اي اخطاء عند Run ولكن المشكلة تحدث فقط في مرحلة ال Debug وهي عند انهاء البرنامج او انهاء الفورم الحالية، وما يزعجني من ها الخطأ هو انني عند غلق الفورم الفرعية يقوم بغلق البرنامج بالكامل ( هدا بالطبع بعد اضافة المتغير من نوع string في الكلاس )

بعد انهاء مرحلة ال debug تظهر الرسالة ( unhandled exception ...0xc0000005 access violation( ، لا تنسي البرنامج يعمل ولا يعطي اي اخطاء والرسالة السالفة تظهر في مرحلة الDebug فقط.

مثلا اضافة المتغير كما في السطور الاتية ( ادرجت تعريف الكلاس كاملا ملف .h ) ،

#if !defined(AFX_EMBEDDINGOPTIONSDLG_H__6582BD81_A7DE_4D1B_B7FB_79E7FAB8D8E9__INCLUDED_)
#define AFX_EMBEDDINGOPTIONSDLG_H__6582BD81_A7DE_4D1B_B7FB_79E7FAB8D8E9__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// EmbeddingOptionsDlg.h : header file
//

#include "3dsloader.h"
#include "ProgressDlg.h"

#include <io.h>
#include <string>
#include <sstream>
#include <cmath>
#include <bitset>
using namespace std;
/////////////////////////////////////////////////////////////////////////////
// CEmbeddingOptionsDlg dialog

class CEmbeddingOptionsDlg : public CDialog
{
private:
	string fileName;
	string ss; // the extra variable
// Construction
public:
	CEmbeddingOptionsDlg(CWnd* pParent = NULL);   // standard constructor
	void ReadTheFileStream();

// Dialog Data
	//{{AFX_DATA(CEmbeddingOptionsDlg)
	enum { IDD = IDD_DIALOG_EMBEDDING_OPTIONS };
		// NOTE: the ClassWizard will add data members here
	//}}AFX_DATA


// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CEmbeddingOptionsDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:

	// Generated message map functions
	//{{AFX_MSG(CEmbeddingOptionsDlg)
	afx_msg void OnButtonBrowse();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_EMBEDDINGOPTIONSDLG_H__6582BD81_A7DE_4D1B_B7FB_79E7FAB8D8E9__INCLUDED_)
#6

أخي العزيز, اعذرني, فأنا خبرتي ضحلة جداً في برمجة الـ Windows و خصوصاً عند الحديث عن MFC :)

من المستحيل أن يكون السبب هو إضافة هذا المتغير الإضافة المسمى ss. باختصار السبب هو أمر آخر, لم يكن ظاهراً, و ربما عند إضافة هذا المتغير ظهرت الـ bug بكل بساطة.

حسبما تقول, فأنت تحصل على Access Violation, إذاً هناك bug :)

اقتباس
بعد انهاء مرحلة ال debug تظهر الرسالة ( unhandled exception ...0xc0000005 access violation( ، لا تنسي البرنامج يعمل ولا يعطي اي اخطاء والرسالة السالفة تظهر في مرحلة الDebug فقط.

كما قلت لك, معلوماتي متواضعة في عالم الـ Windows. و لكن عند تشغيل البرنامج تحت الـ Debugger المرفق مع VC, فإن برنامجك سيستخدم مايسمى بالـ Debug Heap,

بينما عند تشغيل البرنامج بشكل مستقل, فإن البرنامج سيستخدم الـ heap الافتراضية في النظام.

الـ Debug Heap توفر الكثير من عمليات التحقق الإضافية.و لهذا يظهر هذا الخطأ في حالة الـ Debugging, بينما في حالة تشغيل البرنامج بشكل مستقل, فإن البرنامج يقوم بتعديل قطعة عشوائية من الذاكرة,

دون حصول أي خطأ. غير ذلك, فالبرنامج سيقوم بتعديل محتوى الذاكرة دون أن يدري المبرمج, إلا في حالة عناوين خاصة كالعنوان 0.

عموماً, أنت أدرى ببرنامجك, و لكن في "الغالب", هناك قطعة في برنامجك, تقوم بتعديل الذاكرة عن طريق مؤشر, و هذا المؤشر لا يشير إلى ذاكرة يملكها برنامجك, لأي سبب من الأسباب,

أين هو ذلك المؤشر في برنامجك؟ لا أدري, لأنك لم تعطنا معلومات كافية أخي العزيز,

أخيراً,

مرحباً بك معنا, أعتقد أنا كسبنا مبرمج ++C جديد في المنتدى :)

تحياتي,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 15 ديسمبر 2009 في 20:58

#7

شكرا زميلي العزيز،

واليك اخر اخباري

المشكلة مازالت موجودة ، ولكن بعد كثير من الاطلاع اتضح لي كما قلت انت ، انها مشكله مؤشر يشير الى مكان في الذاكرة لا يملكه برنامجي، ولكن للأسف لا اعرف كيف اجد هذا المؤشر فالبرنامج كبير ويستخدم المؤشرات (لا أستخدم المؤشرات بكثرة) ، قمت بالبحث على شبكة الانترنت فوجدت ان هناك برامج تساعد على اكتشاف leaks والاخطاء الخاصة بالذاكرة مثل IBM Rational Purify ولكن للأسف لا اعرف كيف احصل على هذه النوعية من البرامج .

وجدت حل مؤقت لمشكلة المتغير الذي من النوع string وهو تعريفه global داخل ملف cpp ولكن هذا الحل لا يعجبني حيث انه يخالف مبادئ OOP وانا احب ان اقوم بعمل برنامج متماسك،

#8

أهلاً أخ أسامة,

اقتباس
فوجدت ان هناك برامج تساعد على اكتشاف leaks والاخطاء الخاصة بالذاكرة مثل IBM Rational Purify ولكن للأسف لا اعرف كيف احصل على هذه النوعية من البرامج .

للأسف برامج الـ Static Analysis نادرة للغة ++C, و إن وجدت فهي مرتفعة الثمن بشكل كبير,

البرنامج الوحيد الذي أعرفه و له سمعة طيبة, و لكني لم أجربه, هو VLD, هو برنامج حر بالمناسبة يعمل مع Visual Cpp.

تحياتي,,,

#9

عذراً على التدخل بين مبرمجي السي++ :)

لو كان الكومبايلر المستخدم هو MSVS 2008 مثلاً ففيه الـ debugger جيد، وعند الاستثناء يكفي عرض الشفرة المصدرية كـ assembly فيه وسترى فيه تعليقات تحدد السطر البرمجي بالـ C++ الخاص بكل كتلة تعليمات أسمبلي، سيكون بإمكانك معرفة السطر الذي فيه الخطأ بالأسمبلي ورؤية السطر البرمجي المقابل بالـ C++.

والله أعلم.

Do as I say, not as I do

We are Anonymous. We are Legion. We don't forgive. We don't forget

#10

وجدتها ،،،،،،،،

المثال الاتي يعطي نفس الخطأ

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

class ICantHaveString
{
	string str;
public:
	void DoSomeThing()
	{
		str="I'm the string";
		cout<<"I don't know where is my error: "<<str<<"\n";
	}

};

int main(int argc, char* argv[])
{
	ICantHaveString *istr;
	istr=(ICantHaveString*)malloc(sizeof(ICantHaveString));
	istr->DoSomeThing();

	return 0;
}
1
#11

السلام عليكم ...

أخيراً, جميل أنك استعطت اكتشاف الخطأ,

الخطأ بكل بساطة أنك تستخدم malloc في حجز الذاكرة لكائن ليس POD :cool:

الطريقة الصحيحة هي أن تستخدم new و delete, و من الخطأ أن تستخدم malloc في هذه الحالة, لأن كائن النص string في ICantHaveString لن يتم استدعاء الـ constructor الخاص به,

سأعرض الموضوع بتفصيل أكبر في الرد القادم بإذن الله,

تحياتي,

1
#12

السلام عليكم ،

جرب هذا الكود

#include "stdafx.h"
#include <iostream>
#include <string>
using namespace std;
class ICantHaveString
{string str;
public:
	ICantHaveString() { } 
	void DoSomeThing()
	{str="I'm the string";
	cout<<"I don't know where is my error: "<<str<<"\n";      
	}       
};

int main(int argc, char* argv[])
{
ICantHaveString *istr;        
istr= new ICantHaveString();   
istr->DoSomeThing();       
return 0;
}

:cool:

لحقت تسبقني ياخالد بزمن 50 بيكو ثانية

تم تعديل هذه المشاركة بواسطة احمد صالح في 3 يناير 2010 في 19:33

1

BEng , Electronics and communications.

Embedded systems engineer.

Graphics Programmer

عالم الكومبيوتر هو من لديه المعرفة في علوم الكومبيوتر ، الرياضيات ، هندسة الالكترونيات -احمد صالح

#13

لنتعرض للمشكلة بقليل من التفصيل,

ما يحصل هو أنك تقوم بحجز الذاكرة لكائن من النوع ICantHaveString, بالطبع هذا الكائن يحتوي على كائن آخر هو كائن النص string.

في ++C, كما تعرف, الكائنات لها constructors و أي كائن لم يتم استدعاء الـ constructor الخاص به, يقال أنه في حالة غير صالحة أو ما يسمى undefined behavior. فمثلاً, كائن النص -على سبيل التوضيح- يحتوي على مؤشر من نوع char* و حجم النص.

class string
{
    char* str;
    size_t size;
public:
    ....
};

الذي يحصل هو أن المؤشر str و المتغير size يأخذان القيمة الـ garbage التي كانت في ذلك المكان الذي تم حجزه لكائنك و من ضمنه كائن النص طبعاً, و المفترض أن يتم استدعاء الـ constructor الخاص بالكائن ICantHaveString, و الذي بدوره يقوم باستدعاء الـ constructor الخاص بالنص, و افتراضياً فإن حجم النص يصبح صفر و str تساوي NULL.

و لكن! malloc لا تقوم بغير حجز قطعة من الذاكرة و إرجاع مؤشر لها. و بالتالي كائن النص في حالة undefined و ذلك المؤشر, و size يحتويان على قيم, و لكنها قيم غير صالحة أصلاً,

و بهذا عندما يتم تعديل النص, فإن كائن النص يعتقد أنه يحتوي على نص أساساً فيطلب حذف الذاكرة التي يشير إليها str!

الحل هو أن تقوم باستخدام new و delete, دائماً في ++C و لن تواجه هذا النوع من المشاكل, أو ربما إذا اتيحت لك الفرصة أن تطلع على الـ smart pointers و التي ستصبح جزءاً من Cpp0x و لن تستغني عنها :)

هناك حل آخر بالطبع, و لكن لا تحتاجه أصلاً, في تلك الحالة نقوم بحجز الذاكرة "الخام" باستخدام malloc ثم نستدعي الـ constructor الخاص بالكائن على تلك المنطقة بالتحديد, و بالتالي يصبح لدينا كائن صالح للاستخدام, و لكن لا داعي لتلك الطريقة هنا برأيي.

=================== تعديل:

اقتباس
لحقت تسبقني ياخالد بزمن 50 بيكو ثانية

ماشي يا عم احمد :cool:

تحياتي,

تم تعديل هذه المشاركة بواسطة Khaled.Alshaya في 3 يناير 2010 في 20:32

1
#14

جميل ...

من الوهلة الأولى عرفت المشكلة لكن كنت أريد كتابة الرد لكن ذهب للصلاة ولم أرجع إلا الآن ...

عندما تبرمج بال++C فيجب أن تنسى الC ... توجد تفاصيل دقيقة يخفيها الكومبايلر عنك فإلزم ال standard بارك الله فيك ...

يالله زمان عن لغة ال++C ... هذه اللغة لا يمكنك أن تفارقها ... حتى لو إكتويت بنيرانها ... :cool:

1
#15

السلام عليكم

شكرا للجميع على مساعدتي ، ادعو الله ان يجعله في ميزان حسناتكم

بالفعل قمت بتغيير كل malloc ب new في برنامجي فلم يعد للخطأ وجود

واعدكم اني سأحاول "الانسلاخ" تدريجيا من السي الى سي بلس بلس

تحياتي

مواضيع مشابهة