You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++未修改代码每次运行cout输出结果异常问题排查

问题描述

在C++代码中遍历4张GeoTIFF影像,需要提取影像的geotiepoints(地理控制点坐标),存储为std::pair结构:first元素为影像编号,second元素为存储地理控制点的double数组。所有pair最终存入全局std::vector<std::pair<short, double*>>类型变量供后续调用,初始实现代码如下:

//Global Variable 
std::vector <std::pair<short, double*>> GeoTPWithNr;

//for each file in directory
void GetGeoTiePoints(TIFF* tif)
{
    unsigned short count;
    double* Geotiepoints;
    std::pair<short, double*>TiepointsWithNumber;
    TiepointsWithNumber.first = filenumber;

    TIFFGetField(tif, TIFFTAG_GEOTIEPOINTS, &count, &Geotiepoints); // 原代码此处遗漏分号
    TiepointsWithNumber.second = Geotiepoints;
    GeoTPWithNr.push_back(TiepointsWithNumber); 

    std::cout << GeoTPWithNr[0].second[3] << std::endl;
    std::cout << GeoTPWithNr[0].second[4] << std::endl;
}

测试时出现如下现象:

  • 测试逻辑为打印vector中下标为0位置存储的控制点值,预期四次调用该函数时都能输出一致的控制点数值
  • 首次运行代码时结果符合预期,但后续完全不修改代码重复运行,每次都会得到不同的异常输出
  • 调试确认:将pair对象push到vector之前打印pair内存储的控制点值,始终能得到正确结果,初步怀疑问题出在vector写入或读取环节
问题根因

问题和vector本身的写入/读取逻辑无关,根因是存储了悬空指针,触发未定义行为:

  1. TIFFGetField获取到的Geotiepoints指针指向libtiff库内部管理的内存,这块内存的生命周期绑定在当前传入的TIFF*句柄上:当你关闭当前TIFF句柄、后续调用libtiff接口操作该句柄、或者libtiff内部做内存复用回收时,这块内存会被释放或覆写。
  2. 你存入vector的只是指针地址,没有拷贝指针指向的实际数据。刚调用完TIFFGetField立刻打印时,这块内存还没被改动,所以输出正确;一旦后续操作触发libtiff回收/覆写该内存,指针就变成野指针,读取到的是内存中随机写入的垃圾值,因此每次运行结果都不一致,属于典型的内存生命周期管理错误。
修复方案

不要直接存储libtiff返回的内部指针,应当自己申请可控生命周期的内存,将控制点数据完整拷贝后再存储,优先使用标准容器自动管理内存,从根源避免野指针和内存泄漏问题。
推荐修复代码如下:

#include <vector>
#include <tiffio.h>

// 全局变量:second改用std::vector<double>存储控制点数据,自动管理内存
std::vector<std::pair<short, std::vector<double>>> GeoTPWithNr;

// 遍历目录下每个影像文件时调用
void GetGeoTiePoints(TIFF* tif, short filenumber)
{
    unsigned short count = 0;
    double* raw_tiepoints = nullptr;

    // 读取TIFF控制点标签,增加返回值校验
    if (TIFFGetField(tif, TIFFTAG_GEOTIEPOINTS, &count, &raw_tiepoints) != 1) {
        // 此处可添加无控制点的错误处理逻辑
        return;
    }

    std::pair<short, std::vector<double>> entry;
    entry.first = filenumber;
    // 核心步骤:将libtiff内部管理的控制点数据拷贝到自有内存中
    entry.second.assign(raw_tiepoints, raw_tiepoints + count);

    GeoTPWithNr.push_back(std::move(entry));

    // 测试打印,此时无论后续对TIFF句柄做任何操作,存储的控制点值都不会异常
    std::cout << GeoTPWithNr[0].second[3] << std::endl;
    std::cout << GeoTPWithNr[0].second[4] << std::endl;
}

如果因历史代码约束必须使用double*裸指针存储,则需要手动申请内存拷贝数据,在数据使用完成后手动释放内存:

  • 存储时调用double* my_tiepoints = new double[count];申请内存,通过memcpy将Geotiepoints指向的数据拷贝到my_tiepoints指向的自有内存,再存入vector
  • 所有控制点使用完成后,遍历全局vector,对每个元素的second指针执行delete[]释放内存,避免泄漏。

内容的提问来源于stack exchange,提问作者DGIS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 08:06:19