c++structfile-iofwrite

On Heap double pointer Array is coded like single pointer to be written to a file


I have an array that is already in heap (dynamic) memory. That's not very difficult to understand, but writing it to a file by means of the fwrite() function can be a bit tricky to understand if it's also a double pointer by a way I see online, which I don't understand logic-wise, which is what I'm trying to clear up here.

int ** A;
A = new int* [randomSize];
A[0] = new int [randomSize];

For example, a single pointer by fwrite() would look like:

fwrite(&A[0], sizeof(teststruct), 54, dogfile);

So logic-wise, to what I understand, A[0] is holding a pointer pointing to a de-referenced address. But with fwrite(), what you want is the address itself, so you put the & in front of the A[0]. The contents at that address is then written to the file (correct me if I'm wrong).

So, if that's how a single pointer array looks in the fwrite() function, I think the logic of a double pointer array should look something like:

fwrite(&A[0][0], sizeof(teststruct), 54, dogfile);

With the 1st [0] at A[0][0] as a pointer pointing to another address containing a pointer (the 2nd [0] in &A[0][0]), which holds the address for an actual value.

fwrite() uses that 2nd [0] which is being de-referenced to print the actual values located at that address out to a file 54 times. So, basically, the & in front of A[0][0] should give the address of whatever is being de-referenced so stuff can be printed to the file.

This is how I understand it, based on the logic of how I think the fwrite() function works.

However, I saw on the Internet a double pointer being used with fwrite() to print to a file with a single box array (A[0]) not a double box array (A[0][0]). It looked like:

fwrite(A[0], sizeof(teststruct), 54, dogfile);

It was explained that this way is how to print all the values to a file from the double pointer array row by row, which makes no sense to me at all because it's a single box (A[0]). Also, where's the & symbol that's suppose to be in front of the array?

Could someone who understand this stuff explain the logics of this to me, please?


Solution

  • fwrite() wants a pointer to (the address of) the data to be written.

    In this code:

    int A[size];
    

    A is an array of int values:

         [0] [1] [2] [...]
        +---+---+---+-----+
    A = | x | x | x | ... |
        +---+---+---+-----+
    

    Where A[0] is the 1st int in that array, and &A[0] is a pointer to that 1st int.

    So, this code would work:

    fwrite(&A[0], sizeof(int), size, dogfile);
    

    As would this code:

    fwrite(A, sizeof(int), size, dogfile);
    

    Because referring to a fixed array by its name alone will yield (ie it decays into) a pointer to its 1st element in various contexts, such as in a function parameter, or a variable assignment.

           [0] [1] [2] [...]
          +---+---+---+-----+
      A = | x | x | x | ... |
          +---+---+---+-----+
            ^
            |
    fwrite(buffer, ...)
    

    Now, in your code:

    int ** A;
    A = new int* [randomSize];
    A[0] = new int [randomSize];
    

    This is creating an array of int* pointers, where each int* points at its own array of int values:

         [0] [1] [2] [...]
        +---+---+---+-----+
    A = | x | x | x | ... |
        +---+---+---+-----+
          |   |   |           [0] [1] [2] [...]
          |   |   |          +---+---+---+-----+
          +---|---|--------> | x | x | x | ... |
              |   |          +---+---+---+-----+
              |   |
              |   |           [0] [1] [2] [...] 
              |   |          +---+---+---+-----+
              +---|--------> | x | x | x | ... |
                  |          +---+---+---+-----+
                  |
                  |           [0] [1] [2] [...]
                  |          +---+---+---+-----+
                  +--------> | x | x | x | ... |
                             +---+---+---+-----+
    

    Where:

    So, if you try to write &A[0] using this code:

    fwrite(&A[0], sizeof(int), randomSize, dogfile);
    

    You will write garbage to the file, because A is an array of int* pointers, and you are writing the values of those pointers themselves, not the values of the ints that they point to.

         [0] [1] [2] [...]
        +---+---+---+-----+
    A = | x | x | x | ... |
        +---+---+---+-----+
         ^ |                  [0] [1] [2] [...]
         | |                 +---+---+---+-----+
         | +---------------> | x | x | x | ... |
         |                   +---+---+---+-----+
         +---+
             |
    fwrite(buffer, ...)
    

    This code works, though:

    fwrite(A[0], sizeof(int), randomSize, dogfile);
    

    Because A[0] is a pointer to the 1st int in another array.

         [0] [1] [2] [...]
        +---+---+---+-----+
    A = | x | x | x | ... |
        +---+---+---+-----+
           |                  [0] [1] [2] [...]
           |                 +---+---+---+-----+
           +---------------> | x | x | x | ... |
                             +---+---+---+-----+
                               ^
                               |
             +-----------------+
             |
    fwrite(buffer, ...)
    

    Using &A[0][0] will also work, because it is also a pointer to that same int.


    In general, when accessing array elements, brace syntax is just syntax-sugar for pointer arithmetic. Array[Index] is the same as *(Array+Index) 1, and so &Array[Index] is the same as &*(Array+Index) 2, which simplifies to just Array+Index.

    1: meaning, take the starting address of Array, increment it by Index number of elements (ie by Index multiplied by sizeof(element type) number of bytes), and then dereference it to access the element at that address.

    2: meaning take the starting address of Array, increment it by Index number of elements , dereference it to access the element, and then take the address of that element.