1

I have an array of array like this :

double **Arr = malloc(N*sizeof(double*));
for(int j = 0; j < N-1; j++)
  Arr[j] = malloc(M*sizeof(double))

Theses arrays are full of values. And I want to shift some arrays (the N-i last ones). Are there any differences between these 2 lines ?

for(int j = i; j < N-1; j++)
  for(int k = 0; k < M; k++)
    Arr[j][k] = Arr[j+1][k];

or

memmove(&(Arr[i]), &(Arr[i+1]), (N-1-i)*sizeof(double*));
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • 4
    You don't have an array of arrays, you have an array of pointers. Think about the difference copying the elements of an array and copying a pointer. – molbdnilo Nov 20 '19 at 12:43
  • 2
    memmove is basically memcpy but it's allows overlap between `src` and `dest` pointers (meaning it will also be slower). – Clonk Nov 20 '19 at 12:45
  • 2
    @molbdnilo he doesn't have an array of pointers, he has a pointer to pointers. – Fredrik Nov 20 '19 at 12:49
  • 3
    Consider using `double (*arr)[M] = malloc( sizeof(double[N][M]) );` and then `memcpy(arr[i], arr[i+1], sizeof(double[M]));`. It will be far more cache-friendly and easier to read too. – Lundin Nov 20 '19 at 12:49
  • Thanks for your quick anwsers. Indeed, I have a pointer to pointers ! @Lundin I cannot defined my array like `double (*arr)[M]` because M is a variable. If I understand well your answers, I can only remove one for loop using: `for(int j=i; j – Florian Aubin Nov 20 '19 at 12:58
  • 2
    @FlorianAubin Update your C compiler to a standard one made this millennium and you can. – Lundin Nov 20 '19 at 12:59
  • `Theses arrays are full of values` Not so full, `Arr[N-1]` is uninitialized. – KamilCuk Nov 20 '19 at 13:22

3 Answers3

1

Are there any differences between these 2 lines ?

Yes, they are very different. And the second is leaking memory.

Explanation:

After initialization like:

double **Arr = malloc(N*sizeof(double*));
for(int j = 0; j < N; j++)
  Arr[j] = malloc(M*sizeof(double))

(note: I changed the N-1 to N as I assumed it to be a mistake)

The allocated memory now looks like:

enter image description here

When you then do:

for(int j = i; j < N; j++)
  for(int k = 0; k < M; k++)
    Arr[j][k] = Arr[j+1][k];

You copy the doubles from a horizontal array to the horizontal array above. Like:

enter image description here

When you do:

memmove(&(Arr[i]), &(Arr[i+1]), (N-1-i)*sizeof(double*));

You copy the double-pointers in the vertical array one position up. Like:

enter image description here

So it's very different operations that the 2 code blocks do.

And the memmove is bad as it leaks memory.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
0

The first copies M numbers N-1-i times, and the second copies N-1-i pointers once.
That is, if double and double* are the same size, the first copies M times as much memory as the second.
(And is less cache-friendly.)

Simplifying to just two pointers:

 double *arr = malloc(M * sizeof(double));
 double *arr2 = malloc(M * sizeof(double));

the difference is the same as between

 for (int k = 0; k < M; k++) arr2[k] = arr[k];

and

arr2 = arr;

Which one you should use depends on what you're after; copying the pointers or copying the elements.

molbdnilo
  • 64,751
  • 3
  • 43
  • 82
0

Are there any differences between these 2 lines ?

Let's take:

memmove(&(Arr[i]), &(Arr[i+1]), (N-1-i)*sizeof(double*));

Basically what it does can be represented by copying byte-by-byte the specified amount of bytes from one pointer to another. So it can be written in this I hope correct code:

for (size_t j = 0; j < (N-1-i)*sizeof(double*); ++j) {
    *( ((char*)&Arr[i]) + j ) = *( ((char *)(&Arr[i + 1]) + j );

Puff, it makes a big technical difference, but let's remove sizeof(double*) and the casts:

for (size_t j = 0; j < N - 1 - i; ++j)
    *(&Arr[i] + j) = *(&Arr[i + 1] + j);

The &Arr[i] is equal to &*(Arr + i) which is equal to Arr + i, so:

for (size_t j = 0; j < N - 1 - i; ++j) {
    *(Arr + i + j) = *(Arr + i + j + 1);

The *(a + i) is equal to a[i], so we can:

for (size_t j = 0; j < N - 1 - i; ++j) {
    Arr[i + j] = Arr[i + j + 1];

As i + j is on both sides and j starts from 0, we could just start from i:

for (size_t j = i; j < N - 1; ++j) {
    Arr[j] = Arr[j + 1];

Now this copies the pointers values(!) from Arr[j + 1] into Arr[j]. This does something very different then copying the values that are pointed to by the pointers:

for(int j = i; j < N - 1; j++)
    for(int k = 0; k < M; k++)
         Arr[j][k] = Arr[j + 1][k];

On the other hand, the above code could be translated to:

for(int j = i; j < N - 1; j++)
    memcpy(Arr[j], Arr[j + 1], M * sizeof(double));
  //^^^^^^ or memove, but I guess not

Theses arrays are full of values

Your initialization routine that you have showed has this loop:

for(int j = 0; j < N-1; j++)
   Arr[j] = malloc(M*sizeof(double))

Being sane and assuming N > 1, then Arr[N - 1] is left uninitialized. So, accessing in the above loops Arr[j + 1] for the last loop when j = N - 2 is accessing uninitialized value. And the loop for(int k = 0; k < M; k++) Arr[j][k] = Arr[j + 1][k]; dereferences uninitialized pointer in Arr[j + 1][k] and is undefined behavior.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111