0

I was writing a C program to calculate the Spearman Coefficient. It works fine but when I use

*spearmanCoefficient = coeff; I get this output: Spearman: 0.01

and when I use

spearmanCoefficient = &coeff; I get this output: Spearman: 15707109983512927750860237824432537600.00

However, correlationFlag = &corrFlag; gives me the correct output.

If I understand correctly, the first statement assigns the value of the variable coeff to the value of the pointer spearmanCoefficient and the second statement assigns the address of the variable coeff to the pointer spearmanCoefficient which should result in the same output but the output suggests that the second statement is referencing the address of the variable and not its value.

Can anyone explain why this is happening? Thank you.

Source code:

#include <stdio.h>
#include <math.h>

_Bool Correlate (int size, float arrayOne[], float arrayTwo[], float *spearmanCoefficient, float *correlationFlag)
{
    if (size > 0) {
        float sumOne = 0.0f;
        float sumTwo = 0.0f;
        for (int i = 0; i < size; i++) {
            sumOne = sumOne + arrayOne[i];
            sumTwo = sumTwo + arrayTwo[i];
        }
        float meanOne = sumOne / (float)size;
        float meanTwo = sumTwo / (float)size;

        float varianceOne = 0.0f, varianceTwo = 0.0f;
        for (int i = 0; i < size; i++) {
            sumOne = sumOne + pow((arrayOne[i] - meanOne), 2);
            sumTwo = sumTwo + pow((arrayTwo[i] - meanTwo), 2); 
        }
        varianceOne = sumOne / (float)size;
        varianceTwo = sumTwo / (float)size;
        float coeff = 0.0f;
        float corrFlag = 0.0f;

        for (int i = 0; i < size; i++) {
            coeff = coeff + (((arrayOne[i] - meanOne) * (arrayTwo[i] - meanTwo)) / (size * sqrt(varianceOne * varianceTwo)));
        }
        spearmanCoefficient = &coeff;

        if (coeff >= 0.9 && coeff <= 1.0) {
            corrFlag = 1.0;
        } else if (coeff >= -1.0 && coeff <= -0.9) {
            corrFlag = -1.0;
        } else {
            corrFlag = 0.0;
        }
        correlationFlag = &corrFlag;
        return 1;
    }
    else {
        return 0;
    }
}

int main() {
    float arrayOne[10] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};
    float arrayTwo[10] = {4.0, 6.0, 9.0, 10.0, 2.0, 3.0, 5.0, 5.0, 6.0, 8.0};
    int size = 10;
    float *spearmanCoefficient;
    float *correlationFlag;
    _Bool var = Correlate(size, arrayOne, arrayTwo, spearmanCoefficient, correlationFlag);
    printf("Spearman: %.2f\n", *spearmanCoefficient);
    printf("Flag: %.2f\n", *correlationFlag);
    return 0;
}
vkainth
  • 13
  • 4
  • 2
    Those pointers in `main` aren't pointing anywhere. – Kevin Oct 17 '17 at 19:46
  • 1
    Your function isn't updating the pointers because they are passed by value. And if the pointers were updated, it still wouldn't work because they'd be pointing to local variables that no longer exist. – Fred Larson Oct 17 '17 at 19:47

2 Answers2

1

If I understand correctly, the first statement assigns the value of the variable coeff to the value of the pointer spearmanCoefficient

The first statement, *spearmanCoefficient = coeff;, assigns the value of variable coeff to the object to which pointer spearmanCoefficient points. Doing this is predicated on the pointer in fact pointing to some object; if it does not, then the behavior is undefined.

and the second statement assigns the address of the variable coeff to the pointer spearmanCoefficient

The second statement, spearmanCoefficient = &coeff;, does assign the address of variable coeff to the pointer variable spearmanCoefficient.

which should result in the same output but the output suggests that the second statement is referencing the address of the variable and not its value.

The two statements perform distinct operations. They have related, but different effects, so whether they are interchangeable in your program depends on the rest of the program. Usually in such situations, which variation you use does matter.

As it turns out, neither of those alternatives is correct for the rest of the program as written. You appear to be trying to return the Spearman coefficient and correlation flag from your function via pointer arguments. In that case, you need to understand that in C, all program arguments are passed by value. This includes arguments of pointer type, and it is the reason that if we want to return a value via an argument, that argument needs to be a pointer.

Your program provides a good illustration. Your Correlate() function receives arguments spearmanCoefficient and correlationFlag, both of type float *. These are copies of the caller's pointers. Changes you make to the function's copies are not reflected by the caller's originals. Thus, sure, you can assign spearmanCoeff = &coeff, and you can use that assigned value withing the function, but the caller won't see the result.

HOWEVER, the pointer copies in the function point to the same thing (if anything) that the caller's original pointers point to. You can modify that thing via the pointers: *spearmanCoeff = coeff. The caller will see the effect of that, but there's a problem in your case: the main program doesn't set its original spearmanCoefficient or correlationFlag pointers to point to anything! Therefore, when Correlate() dereferences those pointers, undefined behavior results. That can seem to be behavior you want or expect, as you claim is the case with correlationFlag, but you cannot rely on that.

You appear to have fallen into a common trap. Inexperienced C programmers seem often to think that when a function wants an argument of pointer type, the right thing to do is to declare a variable of that type, and pass its value. That sometimes is the right thing to do, though you must in that case actually assign a value first. It is quite common, however, that the right thing to do is to declare an object of the pointed-to type, and pass its address:

int main() {
    float arrayOne[10] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};
    float arrayTwo[10] = {4.0, 6.0, 9.0, 10.0, 2.0, 3.0, 5.0, 5.0, 6.0, 8.0};
    int size = 10;
    float spearmanCoefficient;
    float correlationFlag;
    _Bool var = Correlate(size, arrayOne, arrayTwo, &spearmanCoefficient,
            &correlationFlag);
    printf("Spearman: %.2f\n", spearmanCoefficient);
    printf("Flag: %.2f\n", correlationFlag);
    return 0;
}

That goes with the function performing assignments of your first kind, not your second kind. I observe that I still do not initialize the spearmanCoefficient and correlationFlag variables. This is acceptable because, with the arguments I am passing, I can rely on Correlate() to assign values to those variables (via the pointers I pass) before main() uses those variables' values. If I could not rely on that or did not want to do, then it would be prudent to initialize those variables, or otherwise to assign values to them before the call.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
0

Use this code:

#include <stdio.h>
#include <math.h>

_Bool Correlate (int size, float arrayOne[], float arrayTwo[], float *spearmanCoefficient, float *correlationFlag)
{
    if (size > 0) {
        float sumOne = 0.0f;
        float sumTwo = 0.0f;
        for (int i = 0; i < size; i++) {
            sumOne = sumOne + arrayOne[i];
            sumTwo = sumTwo + arrayTwo[i];
        }
        float meanOne = sumOne / (float)size;
        float meanTwo = sumTwo / (float)size;

        float varianceOne = 0.0f, varianceTwo = 0.0f;
        for (int i = 0; i < size; i++) {
            sumOne = sumOne + pow((arrayOne[i] - meanOne), 2);
            sumTwo = sumTwo + pow((arrayTwo[i] - meanTwo), 2); 
        }
        varianceOne = sumOne / (float)size;
        varianceTwo = sumTwo / (float)size;
        float coeff = 0.0f;
        float corrFlag = 0.0f;

        for (int i = 0; i < size; i++) {
            coeff = coeff + (((arrayOne[i] - meanOne) * (arrayTwo[i] - meanTwo)) / (size * sqrt(varianceOne * varianceTwo)));
        }
        *spearmanCoefficient = coeff;

        if (coeff >= 0.9 && coeff <= 1.0) {
            corrFlag = 1.0;
        } else if (coeff >= -1.0 && coeff <= -0.9) {
            corrFlag = -1.0;
        } else {
            corrFlag = 0.0;
        }
        *correlationFlag = corrFlag;
        return 1;
    }
    else {
        return 0;
    }
}

int main() {
    float arrayOne[10] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0};
    float arrayTwo[10] = {4.0, 6.0, 9.0, 10.0, 2.0, 3.0, 5.0, 5.0, 6.0, 8.0};
    int size = 10;
    float spearmanCoefficient;
    float correlationFlag;
    _Bool var = Correlate(size, arrayOne, arrayTwo, &spearmanCoefficient, &correlationFlag);
    printf("Spearman: %.2f\n", spearmanCoefficient);
    printf("Flag: %.2f\n", correlationFlag);
    return 0;
}
farbiondriven
  • 2,450
  • 2
  • 15
  • 31