0

I am new to using malloc in C. I was trying to declare a dynamic array of structures and later free it, similary a 2D aray and free it. I am using gcc to compile the code.

  1. First question is regarding using array of structures,

    struct OPinfo {
    
            long NLocal;
    
            double ReFrame,ImFrame,lcl_ReFrame,lcl_ImFrame,lcl_SqFrame;
    
    };
    
    struct OPinfo *OPSTR;
    
    
    void declare_local_structure() {
            OPSTR = (struct OPinfo*) malloc(NBinsR * sizeof(struct OPinfo));
            int i =0;
            while(i<NBinsR) {
                    OPSTR[i].NLocal = 0;
                    OPSTR[i].ReFrame = 0.0;
                    OPSTR[i].ImFrame = 0.0;
                    OPSTR[i].lcl_ReFrame = 0.0;
                    OPSTR[i].lcl_ImFrame = 0.0;
                    OPSTR[i].lcl_SqFrame = 0.0;
                    i++;
            }
    
    }
    void free_local_structure() {
            fprintf(stderr,"%d %d\n",*OPSTR,OPSTR);
            free(OPSTR);
    }
    

The fprintf(..OPSTR) part is giving same address in declare_local_structure() and free_local_structure() indicating no pointer mishap in the mean time. If I comment out the free(OPSTR) part, the program works well. Otherwise, it runs and at the end, where free_local_structure() is called, it is giving the following error. * Error in `./a.out': free(): invalid size: 0x0000000002d2fd40 *

  1. In the same program in another part I am using a 2D array of pointers, which I declared by,

    double **Gxy, **GxyRot;
    Gxy = (double**) malloc((NBinsXY) * sizeof(double *));
    GxyRot = (double**) malloc((NBinsXY) * sizeof(double *));
    
    for(j=0; j<NBinsXY; j++) {
            Gxy[j] = malloc(NBinsXY *   sizeof(double));
            GxyRot[j] =  malloc(NBinsXY *   sizeof(double));
    }
    

and free by,

for(l=0;l<NBinsXY;l++) {   
        free(Gxy[l]);
        free(GxyRot[l]);
}

free(Gxy);
free(GxyRot);

This again gives the same error above but if I free by,

free(*Gxy);
free(*GxyRot);
free(Gxy);
free(GxyRot);

the program runs without error.

Where are the errors? I tried "valgrind", but could not understand the output. Another point is the program gave error while compiling with C99. gcc -std=c99 *.c headers/*.h -lm

Daniel H
  • 7,223
  • 2
  • 26
  • 41
haphaZard
  • 43
  • 1
  • 6
  • 2
    What are you doing *between* the calls to `declare_local_structure` and `free_local_structure`? Perhaps you write out of bounds in the code you don't show us? As for Valgrind, build with debug information (add the `-g` flag when building) and re-run, then Valgrind will be able to show source-file and line-number information about problems it finds. If you can't figure it out anyway, then edit your question to include the complete copy-pasted (as text) output of Valgrind. – Some programmer dude Oct 06 '17 at 20:37
  • 2
    What warnings do you get when you add `-Wall -Wextra` to the GCC command line? There should be at least one, because your `fprintf` call is invalid (you can’t pass `*OPSTR` to `fprintf` and expect it to handle things correctly, especially when you told it you were passing a number), but I doubt it’s what’s causing the issue. – Daniel H Oct 06 '17 at 20:41
  • Thanks for the "-g" suggestion. Valarind error in the 2D array declaration. "initialize_declare.c:32" is array Gxy and GxyRot, Invalid read of size 8 ==2138== at 0x401864: calculate_bin (calculation.c:125) ==2138== by 0x401E36: calculate_for_frame (calculation.c:168) ==2138== by 0x40359E: main (main_function.c:72) ==2138== Address 0x652c710 is 16 bytes after a block of size 8,000 alloc'd ==2138== at 0x4C2DB9D: malloc (vg_replace_malloc.c:299) ==2138== by 0x402CCA: declare_arrays (initialize_declare.c:32) ==2138== by 0x403516: main (main_function.c:58) – haphaZard Oct 06 '17 at 20:46
  • If you ran Valgrind but could not understand the output, that implies that valgrind in fact noticed some memory-usage problems (I have trouble believing that you wouldn't understand its output when there aren't any problems). Figuring out what Valgrind is trying to tell you and fixing those problems should be high among your priorities. – John Bollinger Oct 06 '17 at 20:47
  • Note well that `double **Gxy` does *not* declare a 2D array of pointers, nor a 2D array of `double`, nor anything assignment-compatible with either of those or that can point to either of those. It declares a pointer to a pointer to `double`, which is related to (but not the same as) a 1D array of pointers to `double`. – John Bollinger Oct 06 '17 at 20:52
  • @Daniel , except for the error you mentioned it gives just some "unused variable"s. – haphaZard Oct 06 '17 at 20:53
  • @JohnBollinger, But I again malloc'd it, is there something wrong in the way I malloc'd? – haphaZard Oct 06 '17 at 20:56
  • @JohnBollinger It does declare something that can be used almost like a 2d array of `double`, if it points to the start of a (static or dynamic) array of pointers to starts of (static or dynamic) arrays of `double`. – Daniel H Oct 06 '17 at 20:57
  • @DanielH, yes, it is possible to use a double index with such a pointer -- the same syntax that one can use for a *bona fide* 2D array. But it is really important to understand C data types, and this particular distinction is one that *frequently* is not appreciated. Moreover, if one does appreciate it then one often can write clearer and more efficient code. – John Bollinger Oct 06 '17 at 21:04
  • @haphaZard, given the way you declared those pointers, the way you allocated memory for them to point to looks reasonable. Just don't call the result a 2D array, because getting that in your head will cause you grief when you use *real* 2D arrays. – John Bollinger Oct 06 '17 at 21:09
  • Please *edit your question* to include the Valgrind output. It's almost impossible to read in a comment. – Some programmer dude Oct 07 '17 at 04:34
  • @Daniel but I can access the elements of the array by Gxy[i] [j] and Gxy[i] [j] =45 means the (i, j) th element of Gxy is 45. Is this correct? – haphaZard Oct 07 '17 at 06:13
  • @haphaZard If instead of dynamically allocating the array, you had declared it as `double Gxy[NBinsXY][NBinsXY]`, it would be a single contiguous object, and that object would be a 2-d array. Now `Gxy` is a pointer, and each element is a pointer. Because arrays decay to pointers, and because both can be indexed, they behave similarly a lot of the time, but they are different. The sub-arrays of `Gxy` might be stored in many different memory locations, and you can swap them around with pointer changes instead of by changing each element. – Daniel H Oct 07 '17 at 15:33
  • @haphaZard The C language doesn't really define what "the (`i`, `j`)th element of `Gxy`" means; that's for you to decide. "Whatever value `Gxy[i][j]` has" is a reasonable definition as long as that value exists and is accessible without undefined behavior. Other common options are `Gxy[j][i]`, or make `Gxy` a 1-d array and have it be `Gxy[i * NBinsXY + j]`. For simplicity I recommend sticking with what you already have. – Daniel H Oct 07 '17 at 15:47

1 Answers1

0

I agree with the comments above, about actively using a debugger as it will provide detailed information about the nature and location of your issues.

Regarding allocating memory in C, (not applicable to C++) a general rule, when using [m][c]alloc in C, is that (although still rigorously debated) it not necessary (or recommended) that you cast the return. For example, the following:

Gxy = (double**) malloc((NBinsXY) * sizeof(double *)); 

should be written:

Gxy = malloc((NBinsXY) * sizeof(double *));
if(Gxy)//and always test for success before using pointers created using malloc/calloc
{
   ... 

Regarding freeing, the rule is for every call to [m][c]alloc() there must be a corresponding equal number of calls to free(...). Because you are using a loop to allocate memory, it makes sense to use one during free as well:

for(j=0; j<NBinsXY; j++) {
    free(Gxy[j]);
    free(GxyRot[j]); 
}
free(Gxy);
free(GxyRot);
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • Casting the return might, or might not, be better style depending on who you ask. It won’t, however, cause this sort of bug. – Daniel H Oct 06 '17 at 20:55
  • @DanielH - Although still debated often, the question of casting or not casting the return of calloc in C is not a matter of style. It is more a matter of masking problems that can occur when attempting to create memory. This only applies to `C`, not `C++`, where it is recommended to cast the return. – ryyker Oct 06 '17 at 20:58
  • In your last code sample you check `Gxy` and `GxyRot` after dereferencing pointers based on them. If you aren’t going to check them until later, it isn’t worth checking them at all. – Daniel H Oct 06 '17 at 20:58
  • @DanielH - Yes, agree. :) – ryyker Oct 06 '17 at 21:01
  • Assuming you have any warnings enabled or are compiling with C99 or later, implicit declaration of `malloc` isn’t an issue. Otherwise, `malloc` will either work or fail. If it fails, it gives `NULL` whether or not you cast, and if it works it gives the same value either way. Other than not compiling if you cast the result wrong, how can either choice mask problems? – Daniel H Oct 06 '17 at 21:02
  • I also just checked to make sure, and `free` explicitly does nothing if passed `NULL`. I don’t think you need to check then at all, except possibly to check `Gxy` and `GxyRot` before indexing into them. – Daniel H Oct 06 '17 at 21:03
  • @DanielH - Its an old habit that I should re-examine. Edited to remove checks. Thanks. – ryyker Oct 06 '17 at 21:05
  • @DanielH - regarding _how can either choice mask problems_, have you read the link in my answer to that issue? In particular, I subscribe to _[this line of thinking](https://stackoverflow.com/a/605858/645128)_ (from the same link included in my answer) – ryyker Oct 06 '17 at 21:06
  • I read all the positively-voted answers, which is why I even thought about implicit declaration of `malloc`. That was the only case I saw where casting could hide problems, and as I mentioned it doesn’t apply here because this question uses C99. That answer says there are “a bunch” of potential problems, but only mentions that one that doesn’t cause a compile error. – Daniel H Oct 06 '17 at 21:33