C++ debug assertion 질문

조회수 1692회
#include <iostream>
#include <fstream>

#define FILENAME "data.txt"
#define MAXSIZE 60
using namespace std;

ifstream fin;
ofstream fout;

class Matrix {

private:
    int size;
    int numOfElements;
    int* row;
    int* col;
    int* value;

public:
    void sort();
    Matrix(); // constructor
    Matrix(int, int, int*, int*, int*);
    Matrix(const Matrix&); // copy constructor
    ~Matrix();
    void display() const;
    void getData();
    void printData();
    Matrix operator*(const Matrix&);
};
void Matrix::printData() {
    fout.open("resultMatrix.txt");
    fout << size << endl << numOfElements << endl;
    for (int i = 0; i < numOfElements; i++) {
        fout << row[i] << " " << col[i] << " " << value[i] << endl;
    }
    fout.close();
}
void Matrix::sort() {
    int tempRow, tempCol, tempValue;
    for (int i = 0; i < numOfElements; i++)
        for (int j = i + 1; j < numOfElements; j++) {
            if (row[i] > row[j]) {
                tempRow = row[i]; row[i] = row[j]; row[j] = tempRow;
                tempCol = col[i]; col[i] = col[j]; col[j] = tempCol;
                tempValue = value[i]; value[i] = value[j]; value[j] = tempValue;
            }
            else if (row[i] == row[j]) {
                if (col[i] > col[j]) {
                    tempRow = row[i]; row[i] = row[j]; row[j] = tempRow;
                    tempCol = col[i]; col[i] = col[j]; col[j] = tempCol;
                    tempValue = value[i]; value[i] = value[j]; value[j] = tempValue;
                }
            }
        }
}
Matrix::Matrix(int _size, int _numOfElements, int* _row, int* _col, int* _value) {
    this->size = _size;
    this->numOfElements = _numOfElements;
    row = new int[_numOfElements];
    col = new int[_numOfElements];
    value = new int[_numOfElements];

    for (int i = 0; i < _numOfElements; i++) {
        this->row[i] = _row[i];
        this->col[i] = _col[i];
        this->value[i] = _value[i];
    }

}
Matrix::Matrix() {
    size = 0;
    numOfElements = 0;
    row = nullptr;
    col = nullptr;
    value = nullptr;
}
void Matrix::getData() {

    fin >> this->size;
    fin >> this->numOfElements;
    row = new int[numOfElements];
    col = new int[numOfElements];
    value = new int[numOfElements];

    for (int i = 0; i < numOfElements; i++) {
        fin >> row[i] >> col[i] >> value[i];
    }
    this->sort();
}
Matrix Matrix::operator*(const Matrix& B) {
    int newNumOfElements = 0, notAdded = 1;
    int* tempRow, *tempCol, *tempValue;
    tempRow = new int[this->size * this->size];
    tempValue = new int[this->size * this->size];
    tempCol = new int[this->size * this->size];

    Matrix rhs((const Matrix&)B);

    int* temp; // transpose
    temp = rhs.row;
    rhs.row = rhs.col;
    rhs.col = temp;

    for (int i = 0; i < this->numOfElements; i++) { // multiply

        for (int j = 0; j < rhs.numOfElements; j++) {

            notAdded = 1;
            if (col[i] == rhs.col[j]) {
                for (int k = 0; k <= numOfElements - 1; k++) {
                    if (tempRow[k] == row[i] && tempCol[k] == rhs.row[j]) {
                        tempValue[k] += value[i] * rhs.value[j];
                        notAdded = 0;
                    }



                }
                if (notAdded) {
                    tempRow[newNumOfElements] = row[i];
                    tempCol[newNumOfElements] = rhs.row[j];
                    tempValue[newNumOfElements] = value[i] * rhs.value[j];
                    newNumOfElements++;
                }


            }

        }
    }
    Matrix ans(rhs.size, newNumOfElements, tempRow, tempCol, tempValue);
    //ans.display();
    delete[] tempRow;
    delete[] tempCol;
    delete[] tempValue;
    return ans;
}
void Matrix::display()const {
    cout << "The Matrix is : " << endl;
    for (int i = 0; i < this->numOfElements; i++) {
        cout << row[i] << " " << col[i] << " " << value[i] << endl;
    }
}

Matrix::~Matrix() {
    if (this->row != nullptr) {
        delete(this->row);
        delete(this->col);
        delete(this->value);
    }
}

Matrix::Matrix(const Matrix& rhs) {
    this->size = rhs.size;
    this->numOfElements = rhs.numOfElements;

    if (rhs.row != nullptr) {
        this->row = new int(numOfElements);
        this->col = new int(numOfElements);
        this->value = new int(numOfElements);

        for (int i = 0; i < rhs.numOfElements; i++) {
            this->row[i] = rhs.row[i];
            this->col[i] = rhs.col[i];
            this->value[i] = rhs.value[i];
        }
    }
    else {
        this->row = nullptr;
        this->col = nullptr;
        this->value = nullptr;
    }

}

int main() {
    fin.open(FILENAME);

    Matrix A, B, C;
    A.getData();
    B.getData();

    C = A * B;
    A.display(); B.display(); C.display();
    C.printData();

    cout << "Done !!" << endl;
    return 0;
}

코딩중에 질문이 생겨 질문드려요 (Sparse Matrix Multiplication 에 관한 소스입니다.)

내용이해를 위해 잠깐 설명해드리면, getData()함수로 파일입력을 통해 Sparse Matrix Data를 받아와서, 두 Matrix간의 곱셈을 하는 코드입니다.

Visual Studio에서만 자꾸 에러가 나서 질문드려요.. 저번에 isValidHeapPointer(Block) 에러에 관한 질문을 드렸었고, 답변을 받았었습니다.(감사합니다♥♥)

그래서 그 때 실수한점 생각하면서 코딩 했는데, 이번에도 오류가납니다. 이번에는 좀 이상한게.. 실행시키면 오류가 안나고 제가 예상하는 지점 이후로 아무런 실행이 되지않거나, 계속 다른 오류가 납니다. 위에쓴 오류 isValidHeapPointer(Block)가 날때도 있습니다

디버깅을 해보면 오류명이 안뜨고 '중단점을 트리거 했습니다' 가 뜨거나 다른 오류들이 뜹니다.

같은 실수 반복안하려 아무리 찾아봤는데 잘못한점이 제겐 보이지 않네요ㅠㅠ 한번 찾아봐주시면 감사하겠습니다

  • (•́ ✖ •̀)
    알 수 없는 사용자

1 답변

  • 일단 각각의 문제를 나눠서 코멘트 쓰기보단 한번에 쓰는 게 좋을 듯 하여, 전체적으로 코드를 수정하고, 주석으로 코멘트를 달았습니다.

    그리고 사실 new int[]를 통한 배열 할당보다는 std::vector 등의 STL을 이용하는 것이 좋지만, 거기까지 수정해버리면 원래 코드와 너무 달라질 듯 하여 알려만 드립니다.

    #include <iostream>
    #include <fstream>
    
    #define FILENAME "data.txt"
    #define MAXSIZE 60
    using namespace std;
    
    // 전역변수는 왠만하면 쓰지 않는 게 좋습니다.
    // ifstream fin;
    // ofstream fout;
    
    class Matrix
    {
    public:
        Matrix();
        ~Matrix();
        // 함수 선언시 매개변수의 이름을 생략하는 건 좋지 않습니다.
        // 그리고 변수명은 일관성 있게 짓는 게 좋습니다.
        // 어떤 함수는 매개변수에 접두사(_)를 붙이고 어떤 함수는 안 붙이면, 헷갈리게 됩니다.
        Matrix(int _size, int _numOfElements, int* _row, int* _col, int* _value);
        Matrix(const Matrix& _rhs);
        Matrix& operator=(const Matrix& _rhs);
    
        void init(const Matrix& _rhs);
        void release();
    
        void getData(ifstream& _fin);
        void printData() const;
        void display() const;
        void sort();
    
        Matrix operator*(const Matrix& _rhs);
    
    private:
        // 멤버 변수에는 적당한 접두사나 접미사를 붙이는 것이 좋습니다.
        // 함수 내에서 보기도 편하고, 지역변수와 이름이 겹치는 것을 피할 수 있습니다.
        int     mSize;
        int     mNumOfElements;
        int*    mRow;
        int*    mCol;
        int*    mValue;
    };
    
    // 메인 함수는 최대한 위쪽에 위치시키는 것이
    // 전체코드 가독성에 있어서 좋다고 생각합니다.
    int main()
    {
        Matrix A, B, C;
    
        ifstream fin(FILENAME);
        A.getData(fin);
        B.getData(fin);
        fin.close();
    
        C = A * B;
    
        // 함수 호출을 한 줄에 여러번 쓰는 것은 좋지 않습니다.
        A.display();
        B.display();
        C.display();
    
        C.printData();
    
        cout << "Done !!" << endl;
        return 0;
    }
    
    // 함수 정의 순서는 선언 순서와 동일하게 유지하는 것이 좋습니다.
    Matrix::Matrix()
        : mSize(0)
        , mNumOfElements(0)
        , mRow(nullptr)
        , mCol(nullptr)
        , mValue(nullptr)
    {
        // 기본적인 멤버 변수의 초기화는
        // 생성자 초기화 리스트를 이용하는 게 좋습니다.
    }
    
    Matrix::~Matrix()
    {
        // 문제1. 널체크를 변수 하나에 대해서만 했네요.
        // 문제2. 할당은 배열로 하고, 해제는 그냥 delete를 썼네요.
        // PS1. this->는 굳이 쓸 필요가 없습니다.
        // PS2. 널체크는 if(변수) 이런 식으로도 가능합니다.
        release();
    }
    
    Matrix::Matrix(int _size, int _numOfElements, int* _row, int* _col, int* _value)
        : mSize(_size)
        , mNumOfElements(_numOfElements)
        , mRow(_row)
        , mCol(_col)
        , mValue(_value)
    {
        // 이 생성자가 호출되는 쪽을 보니, temp변수에 new를 해서 이걸 호출하고,
        // 여기서 또 new 해서 복사한다음에 temp변수를 delete하던데,
        // 굳이 그럴필요 없이 포인터를 그대로 옮겨주면 될 듯합니다.
    }
    
    Matrix::Matrix(const Matrix& _rhs)
        : mSize(0)
        , mNumOfElements(0)
        , mRow(nullptr)
        , mCol(nullptr)
        , mValue(nullptr)
    {
        // 문제3. 여기도 널체크가 변수 하나에만 되어있었습니다.
        // 문제4. new int()는 배열 할당이 아닙니다.
        // 문제5. _rhs.mNumOfElements의 값이 0이하 라면 문제가 생깁니다.
        //        따라서, 그때는 빈 객체로 남도록 해야합니다.
        init(_rhs);
    }
    
    Matrix& Matrix::operator=(const Matrix& _rhs)
    {
        // PS3. 복사 생성자와 복사 대입 연산자를 구현할 때는,
        //      중복되는 코드를 사용하게 되니, 초기화와 메모리 해제를 함수로 추출하여
        //      해당 함수들을 호출하도록 해주는 것이 좋습니다.
        release();
        init(_rhs);
        return *this;
    }
    
    void Matrix::init(const Matrix& _rhs)
    {
        if (_rhs.mNumOfElements > 0
            && _rhs.mRow != nullptr
            && _rhs.mCol != nullptr
            && _rhs.mValue != nullptr)
        {
            mSize = _rhs.mSize;
            mNumOfElements = _rhs.mNumOfElements;
            mRow = new int[mNumOfElements];
            mCol = new int[mNumOfElements];
            mValue = new int[mNumOfElements];
    
            for (int i = 0; i < mNumOfElements; i++)
            {
                mRow[i] = _rhs.mRow[i];
                mCol[i] = _rhs.mCol[i];
                mValue[i] = _rhs.mValue[i];
            }
        }
    }
    
    void Matrix::release()
    {
        if (mRow) delete[] mRow;
        if (mCol) delete[] mCol;
        if (mValue) delete[] mValue;
    }
    
    void Matrix::getData(ifstream& _fin)
    {
        _fin >> mSize;
        _fin >> mNumOfElements; // 어차피 이 변수는 size * size 인듯 하니, 따로 읽을 필요는 없어 보입니다.
        // 위의 데이터 읽기에 실패한다면, 아래 코드들을 실행하면 안 되기 때문에
        // 예외처리를 추가했습니다.
        if (mSize < 1 || mNumOfElements < 1)
            return;
    
        mRow = new int[mNumOfElements];
        mCol = new int[mNumOfElements];
        mValue = new int[mNumOfElements];
    
        for (int i = 0; i < mNumOfElements; i++)
        {
            _fin >> mRow[i] >> mCol[i] >> mValue[i];
        }
        // 여기서 sort 함수를 호출하는 의미는 잘 모르겠습니다..
        sort();
    }
    
    void Matrix::printData() const
    {
        // 파일 오픈시 ios::ate 플래그를 주면,
        // 파일에 이미 있는 데이터에 이어서 쓰기 작업을 진행할 수 있습니다.
        ofstream fout("resultMatrix.txt", ios::ate);
        fout << mSize << endl;
        fout << mNumOfElements << endl;
        for (int i = 0; i < mNumOfElements; i++)
        {
            fout << mRow[i] << " " << mCol[i] << " " << mValue[i] << endl;
        }
        fout.close();
    }
    
    void Matrix::display() const
    {
        cout << "The Matrix is : " << endl;
        for (int i = 0; i < mNumOfElements; i++)
        {
            cout << mRow[i] << " " << mCol[i] << " " << mValue[i] << endl;
        }
    }
    
    void Matrix::sort()
    {
        // 문제6. 가장 바깥의 for문에 스코프({})가 없습니다.
        // 문제7. 한 줄에 여러 코드가 들어가 있어서 가독성이 떨어집니다.
        // PS4. 중복되는 패턴의 코드들은 함수로 추출하는 게 좋지만,
        //      추가적인 함수를 만들어드리면, 원래코드랑 너무 차이가 생길 듯 하여 주석만 남깁니다.
        int tempRow, tempCol, tempValue;
        for (int i = 0; i < mNumOfElements; i++)
        {
            for (int j = i + 1; j < mNumOfElements; j++)
            {
                if (mRow[i] > mRow[j])
                {
                    // swap(mRow[i], mRow[j]); 이런 식의 코드로 변경하는 것이 좋을 듯 합니다.
                    tempRow = mRow[i];
                    mRow[i] = mRow[j];
                    mRow[j] = tempRow;
    
                    tempCol = mCol[i];
                    mCol[i] = mCol[j];
                    mCol[j] = tempCol;
    
                    tempValue = mValue[i];
                    mValue[i] = mValue[j];
                    mValue[j] = tempValue;
                }
                else if (mRow[i] == mRow[j])
                {
                    if (mCol[i] > mCol[j])
                    {
                        tempRow = mRow[i];
                        mRow[i] = mRow[j];
                        mRow[j] = tempRow;
    
                        tempCol = mCol[i];
                        mCol[i] = mCol[j];
                        mCol[j] = tempCol;
    
                        tempValue = mValue[i];
                        mValue[i] = mValue[j];
                        mValue[j] = tempValue;
                    }
                }
            }
        }
    }
    
    Matrix Matrix::operator*(const Matrix& _rhs)
    {
        int newNumOfElements = 0;
        int notAdded = 1;
    
        // 변수 선언과 동시에 값을 대입하는 것이 좋습니다.
        int* tempRow = new int[mSize * mSize];
        int* tempValue = new int[mSize * mSize];
        int* tempCol = new int[mSize * mSize];
    
        // (const Matrix&) 캐스팅은 필요 없는 코드입니다.
        Matrix rhs(_rhs);
    
        // 여기도 swap함수를 쓰는 게 좋을 듯 합니다.
        // transpose
        int* temp = rhs.mRow;
        rhs.mRow = rhs.mCol;
        rhs.mCol = temp;
    
        // 아래 알고리즘이 제대로 동작하는 지까지는 검증하지 않았습니다.
        // 제대로 작성하셨다고 가정하고 넘어가겠습니다.
        // multiply
        for (int i = 0; i < mNumOfElements; i++)
        {
            for (int j = 0; j < rhs.mNumOfElements; j++)
            {
                notAdded = 1;
                if (mCol[i] == rhs.mCol[j])
                {
                    for (int k = 0; k <= mNumOfElements - 1; k++)
                    {
                        if (tempRow[k] == mRow[i] && tempCol[k] == rhs.mRow[j])
                        {
                            tempValue[k] += mValue[i] * rhs.mValue[j];
                            notAdded = 0;
                        }
                    }
                    if (notAdded)
                    {
                        tempRow[newNumOfElements] = mRow[i];
                        tempCol[newNumOfElements] = rhs.mRow[j];
                        tempValue[newNumOfElements] = mValue[i] * rhs.mValue[j];
                        newNumOfElements++;
                    }
                }
            }
        }
    
        // temp 변수들은 그대로 새 객체에게 넘겨줬기 때문에
        // 여기서 delete[] 해줄 필요가 없습니다.
        // 하지만, 1,2번째 인자가 문제가 없는지는 애매합니다.
        // 위의 알고리즘과 함께 다시 한번 검증해보셔야 할 듯 합니다.
        Matrix ans(_rhs.mSize, newNumOfElements, tempRow, tempCol, tempValue);
        //ans.display();
        return ans;
    }
    
    • 헐.. 감사합니다ㅠㅠㅠ 정말 많이배워가네요.. 선물이라도 드리고싶네요ㅠㅠㅠ 이렇게까지 해주실줄은.. 알 수 없는 사용자 2016.10.2 14:31
    • 하나만 더 여쭤볼게요!! Matrix::Matrix(const Matrix& _rhs)에서, 생성자 초기화 리스트를 사용하셨는데, 이 부분은 if문의 조건이 해당하지 않을 때 작동하는 건가요? 알 수 없는 사용자 2016.10.2 14:36
    • 생성자 초기화 리스트는 생성자 본문에 진입하기 전에 시행됩니다. 즉, Matrix::Matrix(const Matrix& _rhs)에서 초기화 리스트를 통해 전부 기본값(0 or nullptr)으로 초기화를 먼저 진행한 후, if문의 조건이 해당될 때 추가적으로 값을 할당하는 작업이 이루어집니다. Subin Park 2016.10.2 15:52
    • 흐음.. 감사합니다!! 습관이나 코딩스타일에관해서도 고칠게많군요 알려주신대로 수정하면서 해보니 main에서 C = A * B;를 수행할 때 대입연산자를 구현 안했더군요.. 그래서 메인에서 C의 데이터를 보면 쓰레기값이 나왔었네요 감사합니다!! 알 수 없는 사용자 2016.10.3 01:08
    • 죄송합니다 하나만더 여쭤봐도될까요..? 전역변수를 안쓸려고 수정중인데, txt파일에서 두 개의 매트릭스의 데이터를 읽어오려고 합니다. getData의 함수는 한 개의 매트릭스만 읽어오는 함수입니다. getData함수를 호출 할 때 마다 파일을 오픈하니, A매트릭스 B매트릭스 가 같은 데이터를 읽어오더라구요.. (txt파일에 보면 A매트릭스 데이터 밑에 B매트릭스 데이터가 있습니다) 그래서 다른 방법으로 메인에서 ifstream을 선언하고, getData의 parameter로 ifstream을 넘겨주려고 하니 삭제된함수? 라고하면서 컴파일에러가 뜨네요.. getData함수 자체를 두 개 매트릭스를 읽어 오는것 밖에 방법이 없을까요? 알 수 없는 사용자 2016.10.3 01:47
    • 아마 컴파일 에러가 뜬 이유는, 함수의 정의부분만 수정하고 선언부분을 수정하지 않은 게 아닐 까 싶습니다. 일단 위 코드에서 생성자부분과 파일 입출력 부분을 수정하였으니, 한번 비교해보세요. Subin Park 2016.10.3 17:40

답변을 하려면 로그인이 필요합니다.

프로그래머스 커뮤니티는 개발자들을 위한 Q&A 서비스입니다. 로그인해야 답변을 작성하실 수 있습니다.

(ಠ_ಠ)
(ಠ‿ಠ)