草庐IT

c++ - 这个 valgrind 错误是什么意思?

coder 2023-06-19 原文

我有一个基本的库,我用它来绘制 OpenGL 文本,每当我使用 valgrind 来确保它是气密的。我一直收到一个不寻常的错误,在我看来好像 linux c++ 库有问题。我想看看你们是否能发现我的错误或验证我所担心的,那就是我的 c++ 库有问题,需要更换。代码非常简单,但它同时使用了 OpenGL 和 FreeImage,因此某些行没有意义。

这里是 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)

我希望你们能帮我解决这个问题,因为这些似乎是我所有程序中的常见错误。作为一名编码员,我不太喜欢细微但令人讨厌的错误。

最佳答案

最突出的问题是这个:

==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 重置了指针而没有取消分配现有数组。同上问题。

其余消息与各种外部库调用有关。您可能无能为力的轻微内存泄漏。

关于c++ - 这个 valgrind 错误是什么意思?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13150955/

有关c++ - 这个 valgrind 错误是什么意思?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby-on-rails - Rails 常用字符串(用于通知和错误信息等) - 2

    大约一年前,我决定确保每个包含非唯一文本的Flash通知都将从模块中的方法中获取文本。我这样做的最初原因是为了避免一遍又一遍地输入相同的字符串。如果我想更改措辞,我可以在一个地方轻松完成,而且一遍又一遍地重复同一件事而出现拼写错误的可能性也会降低。我最终得到的是这样的:moduleMessagesdefformat_error_messages(errors)errors.map{|attribute,message|"Error:#{attribute.to_s.titleize}#{message}."}enddeferror_message_could_not_find(obje

  4. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  5. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  6. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  7. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  8. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

  9. ruby - Infinity 和 NaN 的类型是什么? - 2

    我可以得到Infinity和NaNn=9.0/0#=>Infinityn.class#=>Floatm=0/0.0#=>NaNm.class#=>Float但是当我想直接访问Infinity或NaN时:Infinity#=>uninitializedconstantInfinity(NameError)NaN#=>uninitializedconstantNaN(NameError)什么是Infinity和NaN?它们是对象、关键字还是其他东西? 最佳答案 您看到打印为Infinity和NaN的只是Float类的两个特殊实例的字符串

  10. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

随机推荐