Search code examples
crpointersindexingstack-overflow

In C, initializing an array using a variable led to stack overflow error or caused R to crash when code is called in R


Okay. My original question turned out to be caused by not initializing some arrays. The original issue had to do with code crashing R. When I was trying to debug it by commenting things out, I by mistake commented out the lines that initialized the arrays. So I thought my problem had to do with passing pointers.

The actual problem is this. As I said before, I want to use outer_pos to calculate outer differences and pass both the pointers of the results and the total number of positive differences back to a function that calls outer_pos

#include <R.h>
#include <Rmath.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>


void outer_pos(double *x, double *y, int *n, double *output){
    int i, j, l=0;
    for(i=0; i<*n; i++){
        for(j=0; j<*n; j++){
            if((x[j]-x[i])>0){
                output[l+1]=(y[j]-y[i])/(x[j]-x[i]);
                output[0]=(double)(++l);
            }
        }
    } 
    Rprintf("%d\n", (int)output[0]);
}


void foo1(double *x, double *y, int *nsamp){
    int i, j, k, oper=2, l;
    double* v1v2=malloc(sizeof(double)*((*nsamp)*(*nsamp-1)/2 + 1));

    outer_pos(x, y, nsamp, &v1v2[0]);
    double v1v2b[1999000];    // <--------------HERE
    for(i=1; i<= (int)v1v2[0]; i++){
        v1v2b[i-1]=1;
    }
}

Suppose foo1 is the function that calls outer_pos. Here I specified the size of the array v1v2b using an actual number 1999000. This value corresponds to the number of positive differences. Calling foo1 from R causes no problem. It's all fine.

In the scenario above, I know the number of positive differences, so I can use the actual value to set the array size. But I would like to accommodate situations where I don't necessarily know the value. foo2 below is intended to do that. As you can see, v1v2b is initialized using the first value of the array v1v2. Recall that the first slot of the output of outer_pos stores the number of positive differences. So basically I use this value to set v1v2's size. However, calling this function in R causes R to either show a stack overflow error or causes it to crash (see screen shot below)

void foo2(double *x, double *y, int *nsamp){
    int i, j, k, oper=2, l;
    double* v1v2=malloc(sizeof(double)*((*nsamp)*(*nsamp-1)/2 + 1));

    outer_pos(x, y, nsamp, &v1v2[0]);
    double v1v2b[(int)v1v2[0]];        //<--------HERE
    for(i=1; i<= (int)v1v2[0]; i++){
        v1v2b[i-1]=1;
    }
}

So I thought, maybe it has to do with indexation. Maybe the actual size of v1v2b is too small, or something, so the loop iterates outside the bound. So I created foo2b in which I commented out the loop, and use Rprintf to print the first slot of v1v2 to see if the value stored in it is correct. But it seems that the value v1v2[0] is correct, namely 1999000. So I don't know what is happening here.

Sorry for the confusion with my previous question!!

void foo2b(double *x, double *y, int *nsamp){
        int i, j, k, oper=2, l;
        double* v1v2=malloc(sizeof(double)*((*nsamp)*(*nsamp-1)/2 + 1));

        outer_pos(x, y, nsamp, &v1v2[0]);
        double v1v2b[(int)v1v2[0]];         //<----Array size declared by a variable
       Rprintf("%d", (int)v1v2[0]);
        //for(i=1; i<= (int)v1v2[0]; i++){
            //v1v2b[i-1]=v1v2[i];
        //}
}

R code to run the code above:

x=rnorm(2000)
y=rnorm(2000)
.C("foo1", x=as.double(x), y=as.double(y), nsamp=as.integer(2000))
.C("foo2", x=as.double(x), y=as.double(y), nsamp=as.integer(2000))
.C("foo2b", x=as.double(x), y=as.double(y), nsamp=as.integer(2000))

enter image description here enter image description here

** FOLLOW UP **

I modified my code based on Martin's suggestion to check if the stack overflow issue can be resolved:

void foo2b(double *x, double *y, int *nsamp) {
    int n = *nsamp, i;
    double *v1v2, *v1v2b;

    v1v2 = (double *) R_alloc(n * (n - 1) / 2 + 1, sizeof(double));
    /* outer_pos(x, y, nsamp, v1v2); */
    v1v2b = (double *) R_alloc((size_t) v1v2[0], sizeof(int));
    for(i=0; i< (int)v1v2[0]; i++){
        v1v2b[i]=1;
    }
    //qsort(v1v2b, (size_t) v1v2[0], sizeof(double), mycompare);
    /* ... */
}

After compiling it, I ran the code:

x=rnorm(1000)
y=rnorm(1000)
.C("foo2b", x=as.double(x), y=as.double(y), nsamp=as.integer(length(x)))

And got an error message: Error: cannot allocate memory block of size 34359738368.0 Gb

** FOLLOW UP 2 **

It seems that the error message shows up every other run of the function. At least it did not crash R...So basically function alternates between running with no problem and showing an error message. (I included both headers in my script file).


Solution

  • As before, you're allocating on the stack, but should be allocating from the heap. Correct this using malloc / free as you did in your previous question (actually, I think the recommended approach is Calloc / Free or if your code returns to R simply R_alloc; R_alloc automatically recovers the memory when returning to R, even in the case of an error that R catches).

    qsort is mentioned in a comment. It takes as its final argument a user-supplied function that defines how its first argument is to be sorted. The signature of qsort (from man qsort) is

    void qsort(void *base, size_t nmemb, size_t size,
               int(*compar)(const void *, const void *));
    

    with the final argument being 'a pointer to a function that takes two constant void pointers and returns an int'. A function satisfying this signature and sorting pointers to two doubles according to the specification on the man page is

    int mycompare(const void *p1, const void *p2)
    {
        const double d1 = *(const double *) p1,
                     d2 = *(const double *) p2;
        return d1 < d2 ? -1 : (d2 > d1 ? 1 : 0);
    }
    

    So

    #include <Rdefines.h>
    #include <stdlib.h>
    
    int mycompare(const void *p1, const void *p2)
    {
        const double d1 = *(const double *) p1,
                     d2 = *(const double *) p2;
        return d1 < d2 ? -1 : (d2 > d1 ? 1 : 0);
    }
    
    void outer_pos(double *x, double *y, int *n, double *output){
        int i, j, l = 0;
        for (i = 0; i < *n; i++) {
            for (j = 0; j < *n; j++) {
                if ((x[j] - x[i]) > 0) {
                    output[l + 1] = (y[j] - y[i]) / (x[j] - x[i]);
                    output[0] = (double)(++l);
                }
            }
        } 
    }
    
    void foo2b(double *x, double *y, int *nsamp) {
        int n = *nsamp;
        double *v1v2, *v1v2b;
    
        v1v2 = (double *) R_alloc(n * (n - 1) / 2 + 1, sizeof(double));
        outer_pos(x, y, nsamp, v1v2);
        v1v2b = (double *) R_alloc((size_t) v1v2[0], sizeof(double));
        qsort(v1v2b, (size_t) v1v2[0], sizeof(double), mycompare);
        /* ... */
    }