2012-10-31 60 views
5

我有一個基本庫,我用它來繪製OpenGL文本,並且當我使用valgrind來確保它是氣密的。我不斷收到一個不尋常的錯誤,看起來好像linux C++庫存在錯誤。我想看看你們是否能夠發現我的錯誤或認證我害怕的東西,那就是我的C++庫有問題,需要更換。代碼非常簡單,但它同時使用了OpenGL和FreeImage,因此某些行將無意義。這個valgrind錯誤是什麼意思?

這裏是fontsystem.h:

#ifndef FONTSYSTEM_H 
#define FONTSYSTEM_H 

/* 
    The Font System works by loading all the font images (max image size 32px^2) into memory and storing 
    the OpenGL texture ID's into an array that can be access at all times. The DrawString() functions will 
    search through the string for the specified character requested to draw and then it will draw a quad 
    and paint the texture on it. Since all the images are pre-loaded, no loading of the images at load time 
    is necessary, this is memory consuming but efficiant for the CPU. ALL functions WILL return a character 
    string specifing errors or success. A function will work as long as it can and when an error happens, 
    unless the error is fatal, the functions will NOT rollback changes! This ensures that during testing, a 
    very certain bug can be spotted. 
*/ 

#include <cstdio> 
#include <cstdlib> 
#include <iostream> 
#include <string.h> 
#include <assert.h> 
#include <GL/gl.h> 
#include <GL/glu.h> 
#include <GL/glext.h> 
#include <FreeImage.h> 

#define REPORT(x) (std::cout<<x) 
#define TIME clock() 

class CFont 
{ 
public: 
    CFont(); 
    ~CFont(); 

    void DrawString(char *apString, int aiLetterSize, int aiX, int aiY); 
    void DrawString(long anNumber, int aiLetterSize, int aiX, int aiY); 
    void SetPath(char avPath[]); 
    void SetupFont(); // This function will load as many images as possible into memory. 

private: 
    GLuint *mpTextIDs; 
    int *mpDrawIDs; 
    char *mpPath; 

public: 
    CFont(const CFont& a): 
     mpTextIDs(a.mpTextIDs), 
     mpDrawIDs(a.mpDrawIDs), 
     mpPath(a.mpPath) 
    { 
     std::copy(a.mpTextIDs, a.mpTextIDs+128, mpTextIDs); 
     std::copy(a.mpDrawIDs, a.mpDrawIDs+128, mpDrawIDs); 
     std::copy(a.mpPath, a.mpPath + strlen(a.mpPath), mpPath); 
    } 

    CFont& operator=(const CFont& a) 
    { 
     GLuint *iTmpTex = new GLuint[128]; 
     int *iTmpDraw = new int[1024]; 
     char *vTmpPath = new char[4096]; 

     delete[] mpTextIDs; 
     delete[] mpDrawIDs; 
     delete[] mpPath; 

     std::copy(a.mpTextIDs, a.mpTextIDs+128, iTmpTex); 
     std::copy(a.mpDrawIDs, a.mpDrawIDs+128, iTmpDraw); 
     std::copy(a.mpPath, a.mpPath + strlen(a.mpPath), vTmpPath); 

     mpTextIDs = iTmpTex; 
     mpDrawIDs = iTmpDraw; 
     mpPath = vTmpPath; 

     return *this; 
    } 
}; 

#endif // FONTSYSTEM_H 

這裏是fontsystem.cpp:

#include "fontsystem.h" 

CFont::CFont() 
{ 
    mpTextIDs = new GLuint[128]; 
    mpDrawIDs = new int[1024]; 
    mpPath = new char[4096]; 
} 

CFont::~CFont() 
{ 
    delete[] mpTextIDs; 
    delete[] mpDrawIDs; 
    delete[] mpPath; 
} 

void CFont::DrawString(char *apString, int aiLetterSize, int aiX, int aiY) 
{ 
    // Sanity check! 
    if(apString == NULL) 
    { 
     REPORT("{Gfx}["<< TIME<< "]Error: Drawing string is NULL! <Font System>\n"); 
     return; 
    } 

    if(aiLetterSize <= 0) 
    { 
     REPORT("{Gfx}["<< TIME<< "]Error: Letter size is less than zero! <Font System>\n"); 
     return; 
    } 

    // Search the string from most significant character to least significant. 
    int iSelectIndex = 0; 
    int iNumOfSymb = 0; 
    for(size_t i = 0; apString[i] != '\0'; ++i) 
    { 
     iSelectIndex = apString[i] >= '0' && apString[i] <= '9' ? (apString[i] - '0') : 
         apString[i] >= 'A' && apString[i] <= 'Z' ? (apString[i] - 'A' + 10) : 
         apString[i] >= 'a' && apString[i] <= 'z' ? (apString[i] - 'a' + 10) : 
         apString[i] == ' ' ? 36 : // This is a special case, This see's if the current character is a space or not. 
         -1; 

     if(iSelectIndex == -1) 
     { 
      return; 
     } 

     // Add the current selected character to the drawing array. 
     mpDrawIDs[i] = iSelectIndex; 
     ++iNumOfSymb; 
    } 

    // Go through and draw each and every character. 
    for(size_t i = 0; apString[i] != '\0'/*static_cast<size_t>(iNumOfSymb)*/; ++i) 
    { 
     // Paint each qaud with the X,Y coordinates. After each quad has been successfully drawn, 
     // Add the size to the X coordinate. NOTE: Each character is square!!! 

     glBindTexture(GL_TEXTURE_2D, mpTextIDs[(uint)apString[i]]); 

     int yPos = apString[i] != 'q' || apString[i] != 'j' || apString[i] != 'y' ? aiY : aiY + (aiLetterSize/2); 

     glBegin(GL_QUADS); 
      glTexCoord2d(0, 0); 
      glVertex2d(aiX, yPos); 

      glTexCoord2d(1, 0); 
      glVertex2d(aiX + aiLetterSize, yPos); 

      glTexCoord2d(1, 1); 
      glVertex2d(aiX + aiLetterSize, yPos + aiLetterSize); 

      glTexCoord2d(0, 1); 
      glVertex2d(aiX, yPos + aiLetterSize); 
     glEnd(); 

     // Now, increase the X position by the size. 
     aiX += aiLetterSize; 
    } 
} 

void CFont::DrawString(long anNumber, int aiLetterSize, int aiX, int aiY) 
{ 
    // Sanity Check! 
    if(aiLetterSize <= 0) 
    { 
     REPORT("{Gfx}["<< TIME<< "]Error: Letter size is less than zero! <Font System>\n"); 
     return; 
    } 

    // Convert the supplied number to a character string via snprintf(). 
    char *vTempString = new char[1024]; 
    snprintf(vTempString, 1024, "%ld", anNumber); 

    // Next, run DrawString(). 
    DrawString(vTempString, aiLetterSize, aiX, aiY); 
} 

void CFont::SetupFont() 
{ 
    // First Load The PNG file holding the font. 
    FreeImage_Initialise(false); 

    FIBITMAP *spBitmap = FreeImage_Load(FIF_PNG, mpPath, BMP_DEFAULT); 

    if(!spBitmap) 
    { 
     REPORT("{Gfx}["<< TIME<< "]Error: Was Unable to opne/decode font bitmap! <FreeImage>\n"); 
     return; 
    } 

    // Do an image sanity check. 
    if(!FreeImage_HasPixels(spBitmap)) 
    { 
     REPORT("{Gfx}["<< TIME<< "]Error: The font bitmap contains nothing! <FreeImage>\n"); 
     return; 
    } 

    // Retrieve all the image data from FreeImage. 
    unsigned char *pData = FreeImage_GetBits(spBitmap); 
    int iWidth = FreeImage_GetWidth(spBitmap); 
    int iHeight = FreeImage_GetHeight(spBitmap); 
    size_t const ciCharWidth = iHeight; 
    size_t const ciCharHeight = iHeight; 

    // Cutup the PNG. 
    int iFontElementSize = (ciCharWidth*ciCharHeight)*4; // The first two numbers, are the dimensions fo the element, the last number (4) is the number of color channels (Red Green Blue and Alpha) 
    unsigned char *pElemBuff = new unsigned char[iFontElementSize]; // The temporary element buffer. 

    // Create all 37 OpenGL textures. 0-9 and A-Z and finally space (' ') 
    glGenTextures(128, mpTextIDs); 

    for (size_t iCharIdx = 0; 128 > iCharIdx; ++iCharIdx) 
    { 
     // Create character texture. 
     size_t const ciScanOfst = ciCharWidth * iCharIdx * 4; 
     for (size_t iScanLineIdx = 0; ciCharHeight > iScanLineIdx; ++iScanLineIdx) 
     { 
      memcpy(pElemBuff + ciCharWidth * iScanLineIdx * 4, 
        pData + ciScanOfst + iWidth * (ciCharHeight - iScanLineIdx - 1) * 4, 
        ciCharWidth * 4); 
     } 

     // Create The OpenGL Texture with the current Element. 
     glBindTexture(GL_TEXTURE_2D, mpTextIDs[iCharIdx]); 
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, pElemBuff); 

     // Create the correct texture environment to the current texture. 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); 
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
     glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); 
    } 

    // Do a little house cleaning! 
    delete[] pElemBuff; 
    FreeImage_Unload(spBitmap); 
    FreeImage_DeInitialise(); 

    REPORT("{Gfx}["<< TIME<< "]Information: Font was created succesfully! <Font System>\n"); 
} 

void CFont::SetPath(char avPath[]) 
{ 
    mpPath = avPath; 
} 

Valgrind的報告如下:

Starting the FontSystem... 
FontSystem Started! 
==5058== Invalid free()/delete/delete[]/realloc() 
==5058== at 0x402A8DC: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==5058== by 0x8048F03: CFont::~CFont() (fontsystem.cpp:14) 
==5058== by 0x8048DE0: main (main.cpp:13) 
==5058== Address 0x804972f is not stack'd, malloc'd or (recently) free'd 
==5058== 
==5058== 
==5058== HEAP SUMMARY: 
==5058==  in use at exit: 4,172 bytes in 3 blocks 
==5058== total heap usage: 153 allocs, 151 frees, 135,457 bytes allocated 
==5058== 
==5058== 20 bytes in 1 blocks are still reachable in loss record 1 of 3 
==5058== at 0x402A5E6: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==5058== by 0x64DB3AD: _dlerror_run (dlerror.c:142) 
==5058== by 0x444EC64: ??? (in /usr/lib/libGL.so.295.59) 
==5058== 
==5058== 56 bytes in 1 blocks are still reachable in loss record 2 of 3 
==5058== at 0x402BE68: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==5058== by 0x442860E: ??? (in /usr/lib/libGL.so.295.59) 
==5058== by 0xBE872481: ??? 
==5058== by 0x4E45504E: ??? 
==5058== 
==5058== 4,096 bytes in 1 blocks are definitely lost in loss record 3 of 3 
==5058== at 0x402B454: operator new[](unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==5058== by 0x8048EAC: CFont::CFont() (fontsystem.cpp:7) 
==5058== by 0x8048D68: main (main.cpp:7) 
==5058== 
==5058== LEAK SUMMARY: 
==5058== definitely lost: 4,096 bytes in 1 blocks 
==5058== indirectly lost: 0 bytes in 0 blocks 
==5058==  possibly lost: 0 bytes in 0 blocks 
==5058== still reachable: 76 bytes in 2 blocks 
==5058==   suppressed: 0 bytes in 0 blocks 
==5058== 
==5058== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 0 from 0) 

我希望你們能幫助我這是因爲這些似乎是我所有程序中的常見錯誤。作爲一名編碼員,我不太喜歡微妙但令人討厭的錯誤。

回答

7

最突出的問題是這個:

==5058== Invalid free()/delete/delete[]/realloc() 
==5058== at 0x402A8DC: operator delete[](void*) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==5058== by 0x8048F03: CFont::~CFont() (fontsystem.cpp:14) 
==5058== by 0x8048DE0: main (main.cpp:13) 
==5058== Address 0x804972f is not stack'd, malloc'd or (recently) free'd 
==5058== 

正如你所看到的,它指的是線fontsystem.cpp 14,在CFont析構函數,在這裏:

delete[] mpPath; 

顯然你嘗試delete []那裏從來沒有動態分配,或沒有以正確的方式。怎麼可能?在構造相應的聲明看起來不錯:

mpPath = new char[4096]; 

所以mpPath值必須被改變的地方的構造被稱爲後。事實上,有此功能:

void CFont::SetPath(char avPath[]) 
{ 
    mpPath = avPath; 
} 

你可能把這種地方,試圖將堆棧分配的陣列轉移到它。你不應該那樣做。如果CFont負責分配和解除分配,則不應該有一個函數試圖獲取外部數組併爲其設置成員指針。考慮替代方法,例如將avPath數組的副本複製到堆上新分配的數組中。並取消分配先前分配的一個。甚至更好,使用std::vector或智能指針。

另一個問題是相關的:

==5058== 4,096 bytes in 1 blocks are definitely lost in loss record 3 of 3 
==5058== at 0x402B454: operator new[](unsigned int) (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==5058== by 0x8048EAC: CFont::CFont() (fontsystem.cpp:7) 
==5058== by 0x8048D68: main (main.cpp:7) 

這是關係到同一成員,mpPath內存泄漏。當然,這種泄漏發生的原因是setPath重置指針而不取消分配現有數組。與上面相同的問題。

其餘消息與各種外部庫調用有關。微小的內存泄漏你可能不會做太多的事情。

+0

事實證明,函數'void CFont :: SetPath(char *)'導致錯誤。很有趣的是,這樣的小事可能會導致這種奇怪的和隱窩的錯誤。我現在已將其更改爲:void CFont :: SetPath(char * avPath) assert(avPath!= NULL);如果(mpPath == NULL) mpPath = new char [4096]; } std :: copy(avPath,avPath + strlen(avPath),mpPath); }' – user1787379

+0

是的,這聽起來是正確的(假設'avPath'字符串不超過4096個字符,包括終端'\ x0')。也許不用說,最好的解決方案是取消字符數組,而是使用'std :: string'。 – jogojapan

2

你失去4096個字節可能是從這裏開始:

void CFont::SetPath(char avPath[]) 
{ 
    mpPath = avPath; 
} 

你只是複製指針(並覆蓋舊的一個);你需要strcpy或像在代碼的其他部分一樣使用std :: copy。