【问题标题】:Access violation when using free() when running but not when debugging... slowly运行时使用 free() 时访问冲突,但在调试时不使用...慢慢地
【发布时间】:2012-11-05 03:18:09
【问题描述】:

根据我之前的question(非常感谢 Jonathan Leffler),我编辑了我的代码(后两个代码块),但遇到了一个相当奇怪的问题。

以下一个意外中断...

void free_array(array_info *A)
{
    int i;
    for(i = 0; i < (A->height); ++i)
    {
        printf("About to free: %x\n", A->dat_ptr[i]);//for debugging purposes
        free(A->dat_ptr[i]);
        printf("Freed row %i\n", i);//for debugging purposes
    }
    free(A->dat_ptr);
}

我最初测试了create_array,然后直接测试了free_array,它在相当大的数组 (10^8) 上完美运行。但是,当我在两者之间进行计算然后尝试free() 数组时,我得到一个访问冲突异常(c00000005)。当我调试它时,我注意到如果我在“free_array”循环中有一个断点并单独执行每一行,程序每次都会完美执行。但是,编译后的代码永远不会自己运行超过我的第二个数组的第 6 行。我关闭了编译器中的所有优化,执行时仍然报错。

附加信息

typedef struct {
    int    height;
    int    width;
    int    bottom;//position of the bottom tube/slice boundary
    unsigned int**  dat_ptr;//a pointer to a 2d array
    } array_info;

dat_ptr 现在是一个正确的 2D 指针。创建要放入结构中的数组的create_array 函数是(我已剥离 NULL 检查以确保可读性):

int create_array(array_info *A)
{
int i;
unsigned int **array = malloc(sizeof(*array) * A->height);

for (i = 0; i < A->height; ++i)
{
    array[i] = malloc(sizeof(**array) * A->width);
}

A->dat_ptr = array;
return 0;
}

此功能完全按预期工作。

更多附加信息

在 Jonathan、Chris 和 rharrison33 的回复之后添加

非常感谢乔纳森,通过你的每一篇文章,我发现了很多关于编程的知识:) 我终于找到了罪魁祸首。导致异常的代码如下:

void fill_number(array_info* array,  int value,  int x1,  int y1,  int x2,  int y2)//fills a rectangular part of the array with `value`
    {
    int i, j;
    for(i=y1 ; ((i<=y2)&&(i<array->height)) ; i++)//start seeding the values by row (as in vertically)
    {
        for(j=x1 ; ((i<=x2)&&(i<array->width)) ; j++)//seed the values by columns (as in horizontally)
        {
        array->dat_ptr[i][j]=value;
        }
    }
}

((i&lt;=x2)&amp;&amp;(i&lt;=array-&gt;width)) 并没有像我预期的那样被评估(克里斯·多德,你是对的)。我认为它会按该顺序评估这两个条件,或者如果其中一个为“FALSE”则停止,而与它们的顺序无关。然而,事实证明它不是那样工作的,它只是拒绝正确评估(i&lt;array-&gt;width) 部分。另外,我认为它会在尝试访问数组范围之外的内存时触发异常,但事实并非如此。总之,

我把代码改成:

void fill_number(array_info* array,  int value,  int x1,  int y1,
                                                    int x2,  int y2)
{
    int i, j;
    if(y1>=array->height){ y1=array->height-1;}
    if(y2>=array->height){ y1=array->height-1;}
    if(x1>=array->width) { x2=array->width-1;}
    if(x2>=array->width) { x2=array->width-1;}
    for(i=y1 ; i<=y2 ; i++)//start seeding the values by row
    {
        for(j=x1 ; j<=x2 ; j++)//seed the values by column
        {
        array->dat_ptr[i][j]=value;
        }
    }
}

现在它可以工作了。 if()s 块在那里,因为与其他代码相比,我不会经常调用该函数,并且我需要一种视觉方式来提醒我检查在那里。

再次感谢 Jonathan Leffler、Chris Dodd 和 rharrison33 :)

【问题讨论】:

  • 听起来像您在 create_arrayfree_array 调用之间执行的“计算”会注销其中一个数组的末尾(因此当 y &gt;= A-&gt;width 或 @987654339 时存储在 A-&gt;dat_ptr[x][y] 中@.) 要么使用内存检查器 (valgrind),要么手动为每个数组访问添加边界检查。

标签: c free access-violation


【解决方案1】:

这段代码完全基于您从我这里得到的内容以及您在上面写的内容,似乎按预期工作。注意&lt;inttypes.h&gt;PRIXPTR 的使用(以及(uintptr_t) 的转换)。它避免对指针的大小做出假设,并且在 32 位和 64 位系统上同样有效(尽管 %.8 意味着您在 32 位编译中获得完整的 8 位十六进制值,并且 12(超出最大值) 16) 在这个特定的 64 位平台上)。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

typedef struct
{
    int    height;
    int    width;
    int    bottom;
    unsigned int **dat_ptr;  // Double pointer, not triple pointer
} array_info;

static void create_array(array_info *A)
{
    unsigned int **array = malloc(sizeof(*array) * A->height);
    printf("array (%zu) = 0x%.8" PRIXPTR "\n",
           sizeof(*array) * A->height, (uintptr_t)array);
    for (int i = 0; i < A->height; ++i)
    {
        array[i] = malloc(sizeof(**array) * A->width);
        printf("array[%d] (%zu) = 0x%.8" PRIXPTR "\n",
               i, sizeof(**array) * A->width, (uintptr_t)array[i]);
    }
    A->dat_ptr = array;
}

static void free_array(array_info *A)
{
    int i;
    for(i = 0; i < (A->height); ++i)
    {
        printf("About to free %d: 0x%.8" PRIXPTR "\n",
               i, (uintptr_t)A->dat_ptr[i]);
        free(A->dat_ptr[i]);
    }
    printf("About to free: 0x%.8" PRIXPTR "\n", (uintptr_t)A->dat_ptr);
    free(A->dat_ptr);
}

int main(void)
{
    array_info array = { .height = 5, .width = 10, .dat_ptr = 0 };
    create_array(&array);
    if (array.dat_ptr == 0)
    {
        fprintf(stderr, "Out of memory\n");
        exit(1);
    }
    free_array(&array);
    puts("OK");
    return(0);
}

样本输出

array (40) = 0x7FAFB3C03980
array[0] (40) = 0x7FAFB3C039B0
array[1] (40) = 0x7FAFB3C039E0
array[2] (40) = 0x7FAFB3C03A10
array[3] (40) = 0x7FAFB3C03A40
array[4] (40) = 0x7FAFB3C03A70
About to free 0: 0x7FAFB3C039B0
About to free 1: 0x7FAFB3C039E0
About to free 2: 0x7FAFB3C03A10
About to free 3: 0x7FAFB3C03A40
About to free 4: 0x7FAFB3C03A70
About to free: 0x7FAFB3C03980
OK

我在这台机器上没有valgrind,但是可以目测分配和释放的地址,表明那里没有明显的问题。巧合的是,我调整了数组的大小,使它们都是 40 字节(在 64 位机器上)。

后续问题

  • 您还用您的数据做什么?
  • 您要分配的数组有多大?
  • 您确定没有遇到算术溢出吗?

在 Mac OS X 10.8.2 和 GCC/Clang 的 XCode 版本上测试:

i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1(基于 Apple Inc. build 5658)(LLVM build 2336.11.00)


数组设置和打印功能

static void init_array(array_info *A)
{
    unsigned int ctr = 0;
    printf("D       = 0x%.8" PRIXPTR "\n", (uintptr_t)A->dat_ptr);
    for (int i = 0; i < A->height; i++)
    {
        printf("D[%d]    = 0x%.8" PRIXPTR "\n",i, (uintptr_t)A->dat_ptr[i]);
        for (int j = 0; j < A->width; j++)
        {
            printf("D[%d][%d] = 0x%.8" PRIXPTR " (%u)\n",
                   i, j, (uintptr_t)&A->dat_ptr[i][j], ctr);
            A->dat_ptr[i][j] = ctr;
            ctr += 7;
        }
    }
}

static void print_array(array_info *A)
{
    printf("D       = 0x%.8" PRIXPTR "\n", (uintptr_t)A->dat_ptr);
    for (int i = 0; i < A->height; i++)
    {
        printf("D[%d]    = 0x%.8" PRIXPTR "\n",i, (uintptr_t)A->dat_ptr[i]);
        for (int j = 0; j < A->width; j++)
        {
            printf("D[%d][%d] = 0x%.8" PRIXPTR " (%u)\n",
                   i, j, (uintptr_t)&A->dat_ptr[i][j], A->dat_ptr[i][j]);
        }
    }
}

在成功create_array() 之后在main() 中调用init_array(&amp;array); 并在此之后调用print_array(&amp;array);,我得到了预期的输出。在这里展示太无聊了。

【讨论】:

  • 你还用你的数据做什么?我正在对一组偏微分方程做一个简单的四点法求解。本质上,当前数组中的每个点都等于前一个数组中四个邻居的平均值f(i, j)=0/25*(f(i+1, j)+f(i-1, j)+f(i, j+1)+f(i, j-1))。有了这种数学,我现在离溢出任何最大值都很远。
  • 您要分配的数组有多大? 现在,当我在调试时,它们大约是 10x10。但是,该程序的想法是找到 10x10 网格的解,然后通过在已建立的节点之间使用线性插值将其应用于 100x100,然后使用这些解对 1000x1000 网格进行线性插值等等,直到最大(可能永远不会)10,000x10,000。这种方法大大减少了每个解决方案的迭代次数。
  • 我以前使用数组,但是当我尝试在我的计算机上寻址 1000x1000 数组时遇到了限制,所以我决定切换到 mallocs。另外,它允许我使用单独的内存块,这样我就不必找到超过10kB的连续字符串程序的最终输出是某种结构的电容和内部场分布的漂亮图片:)
  • 1000 x 1000 x sizeof(unsigned int) 只有 4 MB;这并不离谱。在大多数机器上,100 次这不是大问题。我假设你的计算不是0/25 * ...,而是0.25 * ...;否则,您将以(非常)困难的方式计算零。我还假设您要小心确保您既不阅读也不(尤其是)写入超出范围的索引(在处理矩阵边缘的单元格时)。越界阅读通常不会很好,但通常不会让你崩溃。越界写是非常糟糕的。角单元平均有 2 个节点,边缘单元平均有 3 个节点吗?
  • (我现在也看到了您对问题的更新,它提供了解决方案,并且确实与访问“超出范围”的元素有关。做得好。我很高兴你找到了您的问题,并且在此过程中为您提供了帮助。)
【解决方案2】:

我相信你的 malloc'ing 不正确。尝试将您的 create_array 函数修改为:

int create_array(array_info *A)
{
  int i;
  unsigned int **array = malloc(sizeof(unsigned int*) * A->height);

  for (i = 0; i < A->height; ++i)
  {
    array[i] = malloc(sizeof(unsigned int) * A->width);
  }

  A->dat_ptr = array;
  return 0;
}

【讨论】:

  • 在调试时尝试过,但它的行为方式相同:(
  • 好吧,如果您更改了 malloc 函数,我在您的代码段中看不到任何其他错误。这个错误通常是由某种数组越界异常产生的。也许错误不在此代码段中。我建议发布您的计算代码。
  • 我昨天尝试更改malloc() 部分,但它没有做任何事情。现在我知道为什么了 :) 再次感谢您建议在第一次访问未分配的内存时可能不会生成异常 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-19
  • 2016-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-07
  • 1970-01-01
相关资源
最近更新 更多