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

C++ KNN开发:注释cout后数组元素值异常是否与内存泄漏有关

KNN算法位图数据计算异常问题

问题现象

基于二维数组实现KNN算法时,最初节点仅存储X、Y坐标程序运行完全正常;改为使用16*16二维数组存储位图数据重构逻辑后出现反常问题:

  • 问题出在classify()函数内计算各节点与测试样本距离的for循环中
  • 保留循环内的cout打印距离语句时,距离计算、排序输出完全正常
  • 注释该条cout语句后,会出现距离值丢失小数精度、出现随机无意义值、零值消失等异常,最终排序结果全部为无效垃圾数据
  • 注释其他位置打印语句不会触发该异常,仅该条cout的存在与否决定程序运行结果是否正确

问题复现代码

#include <iostream>
#include <math.h>

using namespace std;

//A node
struct Node
{
    int groupValue;
    int col = 16; //We only have to update these to reflect the bitmap dimensions
    int row = 16;
    int bitmap[16][16];
    float distance = -1;
    //distance is the distance this node is from another

    //Overloading this operator to make swapping them with insertion sort easier
    Node& operator =(const Node& n)
    {
        for(int a = 0; a < row; a++)
        {
            for(int b = 0; b < col; b++)
            {
                bitmap[a][b] = n.bitmap[a][b];
            }
        }
        distance = n.distance;
        groupValue = n.groupValue;
        return *this;
    }
};

void printNode(Node n)
{
    for(int a = 0; a < 16; a++)
    {
        for(int b = 0; b < 16; b++)
            cout << n.bitmap[a][b] << ".";
        cout << endl;
    }
}

struct Group
{
    int frequency = 0;
    int id;
};

void insertionSort(Node arr[], int n)
{
    int i, key, j;
    for (i = 1; i < n; i++)
    {
        key = arr[i].distance;
        j = i - 1;

        while (j >= 0 && arr[j].distance > key)
        {
            Node temp = arr[j+1];
            arr[j + 1] = arr[j];
            arr[j] = temp;
            j = j - 1;
        }
        arr[j + 1].distance = key;
    }
}

//Takes two bitmaps and find the distance between each pixel
//using the formula d=sqrt(sum((pixel_1-pixel_2)^2))
float bitmapDistance(Node n1, Node n2)
{
    Node differences; //Node's bitmap data will be the differences
                        //squared of each pixel from n1 and n2.
    float diff;
    for(int a = 0; a < n1.row; a++)
    {
        for(int b = 0; b < n1.col; b++)
        {
            diff = n1.bitmap[a][b] - n2.bitmap[a][b];
            differences.bitmap[a][b] = diff * diff; //Squared difference
        }
    }

    //Now we need to sum up the squared differences that we stored earlier
    float sum;
    for(int a = 0; a < n1.row; a++)
    {
        for(int b = 0; b < n1.col; b++)
        {
            sum += differences.bitmap[a][b];
        }
    }

    //Now just take the square root of the sum and return it
    if(sum < 0.01) //To fix some weird rounding issues I added this. 0.01 is arbitrary.
        return 0;
    return sqrt(sum);
}

/*
 *Classify the node using KNN algorithm
 *Multiple groups can be used, and we count how many neighbors
 *belong to each group.
 *n is the total amount of nodes
 *k is the number of nearby neighbors
*/
int classify(Node arr[], int n, int k, Node node)
{
    //Two switches for sampling, recorded as binary (0-3)
    Group g0;
    g0.id = 0;
    Group g1;
    g1.id = 1;
    Group g2;
    g2.id = 2;
    Group g3;
    g3.id = 3;
    Group groupFrequency[4] = {g0, g1, g2, g3};

    //For each neighbor of the given node (node) we use the distance formula
    //and set it's distance member variable to this measurement
    for(int a = 0; a < n; a++)
    {
        arr[a].distance = bitmapDistance(node, arr[a]);
        //cout << arr[a].distance << ","; //<===============PROBLEM AREA===============
    }
    cout << endl << "----------" << endl;
    insertionSort(arr, n-1);
    for(int a = 0; a < n; a++)
        cout << arr[a].distance << ",";
    cout << endl << "----------" << endl;
    //Look at all the nearest neighbors and see which group they belong to
    //Increase a counter for each group the neighbor belongs to
    for(int i = 0; i < k; i++)
    {
        if(arr[i].groupValue == 0)
            groupFrequency[0].frequency++;
        else if(arr[i].groupValue == 1)
            groupFrequency[1].frequency++;
        else if(arr[i].groupValue == 2)
            groupFrequency[2].frequency++;
        else if(arr[i].groupValue == 3)
            groupFrequency[3].frequency++;
    }

    cout << "Neighboring group frequencies:" << endl;
    cout << "Group Zero: " << groupFrequency[0].frequency << endl;
    cout << "Group One: " << groupFrequency[1].frequency << endl;
    cout << "Group Two: " << groupFrequency[2].frequency << endl;
    cout << "Group Three: " << groupFrequency[3].frequency << endl;
    cout << "-------------------------------------------------" << endl;

    //Now we just need to look at the most common frequency and classify our node
    int highestFrequency = 0;
    int groupNumber = -1; //If -1 is returned, something went wrong.
    for(int a = 0; a < 4; a++)
    {
        if(groupFrequency[a].frequency >= highestFrequency)
        {
            highestFrequency = groupFrequency[a].frequency;
            groupNumber = groupFrequency[a].id;
        }
    }

    return groupNumber;
}

int main()
{
    Node nodeArray[200];
    for(int c = 0; c < 100; c++)
    {
        for(int a = 0; a < 16; a++)
        {
            for(int b = 0; b < 16; b++)
            {
                nodeArray[c].groupValue = 0;
                nodeArray[c].bitmap[a][b] = rand()%10;
            }
        }
    }
    for(int c = 100; c < 200; c++)
    {
        for(int a = 0; a < 16; a++)
        {
            for(int b = 0; b < 16; b++)
            {
                nodeArray[c].groupValue = 1;
                nodeArray[c].bitmap[a][b] = 50;//rand()%10+50;
            }
        }
    }
    Node testNode;
    for(int a = 0; a < 16; a++)
    {
        for(int b = 0; b < 16; b++)
        {
            testNode.bitmap[a][b] = 50;//rand()%10+50;
        }
    }

    int num = classify(nodeArray, 200, 189, testNode);
    cout << "Test node has a group classification of: " << num << endl << endl;

    printNode(testNode);
    cout << endl << "last node in array:" << endl;
    printNode(nodeArray[199]);
    cout << endl << bitmapDistance(testNode, nodeArray[199]) << endl << "first node in array:" << endl;
    printNode(nodeArray[0]);
    cout << bitmapDistance(testNode, nodeArray[0]);
}

问题根因

该问题和内存泄漏完全无关,属于C++典型的未定义行为,共有3个核心bug:

  1. 未初始化局部变量:bitmapDistance函数中用于累加像素差平方和的float sum;没有显式初始化,局部变量存储在栈上,初始值为栈内存中残留的随机垃圾值,累加计算的结果完全不确定。添加cout语句时会改变栈内存布局,凑巧让sum所在栈位置的残留值接近0,看起来结果正常;去掉cout后栈布局变化,sum初始值为随机数,直接导致计算结果错误。
  2. 插入排序类型错误:插入排序中用于暂存距离值的key被定义为int类型,而实际距离是float浮点值,赋值时会直接截断小数部分,最后又将整型的key写回节点的distance成员,必然导致精度丢失。
  3. 插入排序范围错误:调用插入排序时传入的长度参数是n-1,200个节点仅排序前199个,最后一个节点的距离值不会参与排序,直接输出会出现异常值。

另外自定义的赋值运算符重载没有拷贝col和row成员,虽然当前固定尺寸下不触发问题,但后续修改位图尺寸时会出现拷贝错误。

修复方案

按以下步骤修改代码即可解决问题:

  1. 初始化所有局部变量,将bitmapDistance中的float sum;改为float sum = 0.0f;
  2. 修正插入排序的key类型,将int i, key, j;改为int i, j; float key;,避免浮点值被截断
  3. 修正插入排序调用参数,将insertionSort(arr, n-1);改为insertionSort(arr, n);,确保所有节点参与排序
  4. 补全赋值运算符逻辑,在operator=中添加col = n.col; row = n.row;,保证节点拷贝完整

这类"加打印就正常、删打印就出错"的现象,绝大多数都是未初始化变量、数组越界写入导致的未定义行为,和打印语句本身没有关系,打印语句只是改变了内存布局凑巧掩盖了问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 10:36:17