Pi插件,用C实现,当数字位数过高时会出现段错误

时间:2013-11-28 23:45:24

标签: c segmentation-fault

我已经在C中实现了一个spigot算法,但是,当要计算的小数位数太高时,会出现segfaults(SIGSEGV)。错误发生的位数在我拥有的几台不同的Windows计算机上略有不同,但它发生在156210左右。我只会给出相关的代码,但老实说我真的不明白错误,所以我会给你我的完整代码。

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

int held[20]; //I wont have 19 consecutive 9's in pi, right?
int held_length = sizeof(held)/sizeof(int);

FILE *f;

void releaseDigits() {
    int c;
    for(c = held_length-1; c >= 0; c--) {
        if(held[c] != -1) {
           //printf("release: %i\n", held[c]); //debugging output
           fprintf(f, "%i", held[c]);
        }
    }
}

void incHeld() {
    int c;
    for(c = held_length-1; c >= 0; c--) {
        if(held[c] != -1) {
           held[c]++;
        }
    }
}

void blankHeld() {
    int c;
    for(c = held_length-1; c >= 0; c--) {
        held[c] = -1;
    }
    /*for(c = 0; c < held_length; c++) {
        printf("BLANK_%i:%i\n", c, held[c]);
    }*/ //debugging output
}

void deleteLast() {
    int c = held_length-1;
    while(held[c] != -1) {
        c--;
    }
    held[c+1] = -1;
}

void holdDigit(int hold) {
    int c = held_length-1;
    while(held[c] != -1) {
        c--;
    }
    held[c] = hold;
    for(c = 0; c < held_length; c++) {
        //printf("held_%i:%i\n", c, held[c]); //debugging output
    }
}

void main() {
    time_t start, end;
    int n; //decimals of pi, 156207 max if printf, 156210 if fprintf, higher = sivsegv
    printf("Decimal places of pi to calculate (max 156210 for now): ");
    scanf("%i", &n);
    start = clock();
    f = fopen("pi.txt", "w"); //open file
    if (f == NULL) {
        printf("Error opening file!\n");
        exit(1);
    }
    n++; //overcompensate for odd ending digit error
    //initial array of 2,2,2,...2
    int rem[((10*n)/3)+2]; //sizeof(one)/sizeof(int);
    int init_count;
    for(init_count = 0; init_count < ((10*n)/3)+2; init_count++) {
        rem[init_count] = 2;
    }
    //main digit loop
    int carry;
    int decimal;
    int pi_digit;
    for(decimal = 0; decimal <= n; decimal++) {
        carry = 0;
        int sum;
        int i;
        for(i = (10*n)/3 + 1; i >= 1; i--) {
            sum = (rem[i]*10)+carry;
            rem[i] = sum % ((2*i)+1);
            carry = ((sum-rem[i])/((2*i)+1))* i;
            //printf("decimal:%i i:%i B:%i carry:%i sum:%i rem:%i\n", decimal, i, (2*i)+1, carry, sum, rem[i]); //debugging output
        }
        sum = (rem[0]*10)+carry;
        rem[0] = sum % 10;
        pi_digit = (sum - rem[0])/10;
        //printf("sum:%i rem:%i\n",sum, rem[i]); //debugging output
        if(pi_digit != 10) {
            if(pi_digit != 9) {
                if(decimal > 0) {
                    releaseDigits();
                }
                if(decimal == 1) {
                    fprintf(f, "."); //shove a point up in that shit
                }
                blankHeld();
                holdDigit(pi_digit);
            }
            else {
                holdDigit(pi_digit);
            }
        }
        else {
           incHeld();
           releaseDigits();
           blankHeld();
           holdDigit(0);
        }
        printf("\r%i/%i decimal places done...                  ", decimal-1, n-1);
    }
    deleteLast(); //hide overcompensation
    releaseDigits();
    fclose(f);
    end = clock();
    int raw_seconds = (end - start)/1000.;
    int seconds = raw_seconds % 60;
    int minutes = (raw_seconds - seconds)/60;
    printf("\n\nSuccessfully calculated %i decimal places of pi in %i minutes and %i seconds!\nSaved to pi.txt\nPress ENTER to exit the program.\n", n-1, minutes, seconds);
    while(getch()!=0x0d);
}

这里发生了什么?

1 个答案:

答案 0 :(得分:2)

我在你的代码上使用了着名的内存检查工具Valgrind。相对较小的值(即10,100,1000,甚至10000)都没有问题。尝试156210立即让Valgrind抱怨。看起来这个问题始于这一行:

int rem[((10*n)/3)+2];

问题是以这种方式分配存储要求来自堆栈的内存和此计算值((10 * 156210/3 + 2)= 520702,sizeof(int)= 4在Windows上,所以520702 * 4 = about 2MB)远远超过机器准备从堆栈中提供的。

如果你需要这么多内存,最好使用malloc()从堆中分配它(之后不要忘记free())。