繁体   English   中英

如何在我的C程序中纠正分段错误

[英]How to correct segmentation fault in my C program

我很难调试下面为knapSack编写的程序

#include <stdio.h>
#include <stdlib.h>
#include "timer.h"


#define    MAX(x,y)   ((x)>(y) ? (x) : (y))
#define    table(i,j)    table[(i)*(C+1)+(j)]

int main(int argc, char **argv) {

   FILE   *fp;
   long    N, C, opt;                   // # of objects, capacity 
   int    *weights, *profits, *table, *solution;     // weights and profits
   int    verbose;

  // Temp variables
   long    i, j, count, size, size1, ii, jj;

   // Time
   double time;

   // Read input file:
   //  first line:  # of objects, knapsack capacity, 
   //  next lines:   weight and profit  of next object (1 object per line)
   if ( argc > 1 ) {
      fp = fopen(argv[1], "r"); 
      if ( fp == NULL) {


        printf("[ERROR] : Failed to read file named '%s'.\n", argv[1]);
         exit(1);
      }
   } else {
      printf("USAGE : %s [filename].\n", argv[0]);
      exit(1);
   }

   if (argc > 2) verbose = 1; else verbose = 0;

   fscanf(fp, "%ld %ld", &N, &C);
   printf("The number of objects is %ld, and the capacity is %ld.\n", N, C);

   size    = N * sizeof(int);
   size1   = C * sizeof(int);
   weights = (int *)malloc(size);
   profits = (int *)malloc(size);
   table   = (int *)malloc(size*size1);
   solution= (int *)malloc(size);

   if ( weights == NULL || profits == NULL ) {
      printf("[ERROR] : Failed to allocate memory for weights/profits.\n");
      exit(1);
   }

  for ( i=0 ; i < N ; i++ ) {
      count = fscanf(fp, "%d %d", &(weights[i]), &(profits[i]));
      if ( count != 2 ) {
         printf("[ERROR] : Input file is not well formatted.\n");
         exit(1);
      }
   }

   fclose(fp);



   initialize_timer ();
   start_timer();
   // Solve for the optimal profit (create the table)
    for(j=0; j<=C; j++) {
        table(0,j)=0;
    }

    for(ii=1;ii<=N;ii++) {
        for(jj=0; jj<=C; jj++) {    
            if(weights[ii-1]>jj) {
                table(ii,jj)=table(ii-1,jj);
            }
            else {
                table(ii,jj)=MAX(table(ii-1,jj),(profits[ii-1]+table(ii-1,jj-weights[ii-1])));
            }
            }
        }
opt=table(N,C);
   // We only time the creation of the table

  stop_timer();
   time = elapsed_time ();

   printf("The optimal profit is %ld Time taken : %lf.\n",opt,time);


 // End of "Solve for the optimal profit"


// Find the solution (choice vector) by backtracking through the table


      printf("Solution vector is: \n");
j=C;
      for(i=N;i>0;i--) {
    if(table(i,j)==table(i-1,j)) {
        //printf("Object %d not picked", i);
        solution[i-1]=0;
    }   
    else {
        //printf("Object %d picked", i);
        j=j-weights[i-1];
        solution[i-1]=1;
    }
      }
    for(i=0; i<N; i++) {
        printf("%d ",solution[i]);
    }
      if (verbose) {

    // print the solution vector 
      }

   return 0;
}

对于较小的输入,代码运行良好。 但是对于N = 1200和C = 38400000或C的任何其他大输入,该代码将显示分段错误。 以下是Valgrind的输出:

The number of objects is 1200, and the capacity is 38400000.
==2297== Invalid write of size 4
==2297==    at 0x400A4E: main (knap1.c:73)
==2297==  Address 0x8 is not stack'd, malloc'd or (recently) free'd
==2297== 
==2297== 
==2297== Process terminating with default action of signal 11 (SIGSEGV)
==2297==  Access not within mapped region at address 0x8
==2297==    at 0x400A4E: main (knap1.c:73)
==2297==  If you believe this happened as a result of a stack
==2297==  overflow in your program's main thread (unlikely but
==2297==  possible), you can try to increase the size of the
==2297==  main thread stack using the --main-stacksize= flag.
==2297==  The main thread stack size used in this run was 8388608.
==2297== 
==2297== HEAP SUMMARY:
==2297==     in use at exit: 14,400 bytes in 3 blocks
==2297==   total heap usage: 4 allocs, 1 frees, 14,968 bytes allocated
==2297== 
==2297== LEAK SUMMARY:
==2297==    definitely lost: 0 bytes in 0 blocks
==2297==    indirectly lost: 0 bytes in 0 blocks
==2297==      possibly lost: 0 bytes in 0 blocks
==2297==    still reachable: 14,400 bytes in 3 blocks
==2297==         suppressed: 0 bytes in 0 blocks
==2297== Rerun with --leak-check=full to see details of leaked memory
==2297== 
==2297== For counts of detected and suppressed errors, rerun with: -v
==2297== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 2 from 2)
Segmentation fault

这是有关使用不同输入文件(k10.txt至k1200.txt)运行时的本地值(从gdb获取)的信息:

  for files with which I got correct output until it exceeded that fix. no. of byte value 
  fp = 0x0 N = 4131212846 C = 140737488347792 opt = 4294967295 weights =

  0x0 profits = 0x36dd221168 table = 0x7ffff7ffc6b8 solution =
  0x36dd8101c0 verbose = 0 i = 0 j = 140737488348128 count = 2 size =
  4198301 size1 = 1 ii = 4196160 jj = 4198224 time =
  2.0728237613194911e-317

  for k1200.txt 
  k1200.txt fp = 0x177b010 N = 1200 C = 38400000 opt = 4294967295
  weights = 0x177b250 profits = 0x177c520 table = 0x8 solution =
  0x7f3cd40008c0 verbose = 0 i = 1200 j = 0 count = 2 size = 4800 size1
  = 153600000 ii = 4196160 jj = 4198224 time = 2.0728237613194911e-317

关于我的代码有什么问题的任何输入吗? 以及如何纠正该程序,使其永远不会显示分段错误?

您在这里要求太多的内存:

table   = (int *)malloc(size*size1);

正好为1200 * 38400000 * sizeof (int) * sizeof (int) ,这大约是74GB的内存(假设sizeof (int) == 4 )。 您的计算机无法彻底处理这么大的块,因此分配失败,并且失败时,将返回NULL指针。 您应该检查以下情况:

if (table == NULL) {
    fprintf(stderr, "Memory allocation failed :(");
    exit(1);
}

您没有使用NULL指针,从而产生了分段错误。

不幸的是,这里不容易解决。 您应该重新考虑算法,看看是否真的需要一次如此大的块,或者可以重用较小的块。

一个较小的问题是,您需要的内存是您所需内存的4倍(仍然假设sizeof (int) == 4 )。 实际上,当您malloc size * size1字节时,您将sizeof (int)考虑在内两次,一次是size = N * sizeof (int) ,一次是size1 = D * sizeof (int) ,而您显然想要一个N * C * sizeof(int)矩阵。

74GB / 4意味着18.5GB的空间仍然太大:您的OS也许可以在虚拟内存中处理它,但是当启动交换时,它将变得非常缓慢。当然,除非您安装了18 + GB的RAM。

无论如何,我想您正在使用table作为true / false布尔矩阵。 每个元素可能都是32位int ,而您只使用了1位。 如果使用按位运算将32个单元格打包为一个整数,则可以将分配的大小减少32倍。 它可能会影响性能,但是肯定会将内存占用空间减小到计算机可以处理的大小。

如注释中所建议,您也可以使用charbool代替int ,因为它们通常较小。

在N = 1200和C = 38400000时,N * C为46,080,000,000。 您使用的是32位还是64位操作系统? 在32位上,您的买单可能溢出。 此外,您可能没有足够的内存来进行此计算。

查看您的算法,我发现您可能不需要将表分配为N * C,而只需分配2 * C。

for循环仅使用第ii-1行更新第ii行。 因此,一旦计算完ii,就不再需要ii-1。 这意味着您可以重用第ii-1行中的内存来存储ii + 1等。因此,您实际上只需要两行。

像这样:

table = malloc(2*size1);

...

for(ii=1;ii<=N;ii++) {
    iiOut = ii%2;
    iiIn = (ii-1)%2;
    for(jj=0; jj<=C; jj++) {    
        if(weights[ii-1]>jj) {
            table(iiOut,jj)=table(iiIn,jj);
        }
        else {
            table(iiOut,jj)=MAX(table(iiIn,jj),(profits[ii-1]+table(iiIn,jj-weights[ii-1])));
        }
    }
}
opt=table(iiOut,C);

好的,除了dohashi的问题外,还有几件事。

您应该添加检查以查看以下内存分配是否失败:

if ( table == NULL ) {
    printf("[ERROR] : Failed to allocate memory for calculation table.\n");
    exit(1);
}
if ( solution == NULL) {
    printf("[ERROR] : Failed to allocate memory for solution.\n");
    exit(1);
}

如果您没有足够的内存来分配这些,现在您将知道。

接下来,我注意到您用于在2d表中建立索引的宏会神秘地添加一个未分配的额外列:

#define    table(i,j)    table[(i)*(C+1)+(j)]

看到那里的“(C + 1)”吗? 它说该表的大小实际上为N *(C + 1)。 接下来,您稍后在表中从1到N以及从1到C进行索引

for(ii=1;ii<=N;ii++) {
    for(jj=0; jj<=C; jj++) {    
        if(weights[ii-1]>jj) {
            table(ii,jj)=table(ii-1,jj);
        }
        else {
            table(ii,jj)=MAX(table(ii-1,jj),(profits[ii-1]+table(ii-1,jj-weights[ii-1])));
        }
    }
}
opt=table(N,C);

宏将table的大小设置为N *(C + 1),实际上这要求表的大小为(N + 1)*(C + 2)。

我认为这里至少有一个问题,就是有人从FORTRAN转换了此代码,而没有考虑到C中的数组是基于零而不是基于一的事实。 例如参见这里

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM