1

Lets say I have a 2d array:

int array[3][3];

with

000
111
222

and I want to remove 000.

I wrote a function:

void remove(int (*array)[3], int index, int array_length)
{
   int i;
   for(i = index; i < array_length - 1; i++)
   {
       array[i] = array[i + 1];
   } 
}

which receives pointer to the first element of the 2d array, index which I want to remove and a length of the array.

In the for loop, I move array at the position index to the next element.

But I receive this error message:

error: assignment to expression with array type

array[i] = array[i + 1];

Why?

How can I remove element and get the 2d array without array at the index? Should I maybe make new 2d array and return it instead of passing pointer of 2d array to the function?

Community
  • 1
  • 1
cheshire
  • 1,109
  • 3
  • 15
  • 37
  • You can't change the dimensions of a static or auto array after declaring it. – kaylum Jan 02 '20 at 23:50
  • So the only way is to use `malloc` or to declare a new array with reduced size? – cheshire Jan 03 '20 at 00:07
  • Technically what you're doing is a valid strategy. You're treating the last line of the matrix as "trash" values. You're just ignoring them. If you wanted to create a bigger array than you'd need to allocate a new one, but as long as you have control of the size of the memory you already allocated, it's fine. – lzbernardo Jan 03 '20 at 00:20
  • It depends on which level of optimization you're trying to achieve. If you're gonna work with relatively small array sizes, reallocating them every time you need to hide a line is not gonna be cheap. But the payoff is having to keep track of the "full" matrix / the allocated memory. – lzbernardo Jan 03 '20 at 00:21

4 Answers4

2

You can do what you originally wanted if you use an array of structs instead of a 2D array.

The C language treats structs as value types so you can copy them with simple and safe assignment (no memcpy needed).

Here's an example with a few tweaks.

//try online here: https://rextester.com/DDHBC95444
#include  <stdio.h>

//from Chromium code https://stackoverflow.com/a/1598827/7331858
#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

typedef struct row_t {
    int columns[3];
} row_t;

void remove_row(row_t * const rows, const size_t row_index, const size_t row_count)
{
   for (size_t i = row_index; i < row_count - 1; i++) {
       rows[i] = rows[i + 1];
   }
}

void print_rows(row_t const * const rows, const size_t row_count)
{
   const size_t column_count = COUNT_OF(rows[0].columns);

   for (size_t r = 0; r < row_count; r++) {
       for (size_t c = 0; c < column_count; c++) {
           printf("%02i ", rows[r].columns[c]);
       }
       printf("\n");
   }
}

int main(void)
{
    row_t rows[] = { {{0,1,2}}, {{10,11,12}}, {{20,21,22}} };
    print_rows(rows, COUNT_OF(rows));

    printf("\n'removing' row index 1\n");
    remove_row(rows, 1, COUNT_OF(rows));
    print_rows(rows, COUNT_OF(rows));

    return 0;
}

outputs:

00 01 02 
10 11 12 
20 21 22 

'removing' row index 1
00 01 02 
20 21 22 
20 21 22 
afk
  • 358
  • 2
  • 11
  • Any reason not to use `memmove` to move each row and then `memset` to overwrite the last with all zero? – David C. Rankin Jan 03 '20 at 06:28
  • @DavidC.Rankin mainly because I want to keep the beginner example simple and type safe. Even in my own programming I prefer simple & robust over performance unless I have a hot spot identified by a profiler. Do you think `memmove` would improve performance? I figured that a simple struct copy or `memcpy` would be faster as `memmove` is meant to handle overlapping memory and potentially uses an intermediate buffer. – afk Jan 03 '20 at 06:41
  • That's a good point. The only reason I suggested was that it would eliminate the nesting of the loops, but I see your point that it would add a requirement of understanding the correct usage of each of the functions to the answer. – David C. Rankin Jan 03 '20 at 07:20
  • @DavidC.Rankin I'm not sure I understand what you mean. The nested loops are only for printing. The actual memory moving happens in a single for loop. Maybe I'm missing something? – afk Jan 03 '20 at 17:55
  • Using your struct, you already accomplish eliminating the nested loop for the move of each individual element up by one. That comment was aimed at the general 2D case. Sorry for the confusion. – David C. Rankin Jan 03 '20 at 19:53
  • @DavidC.Rankin gotcha. Thanks – afk Jan 03 '20 at 20:34
1

The parameter array is declared as int (*array)[3], so its type is pointer to an array of 3 ints. The type of array[i] is array of int and an array cannot be assigned to. A function to shift the rows of your matrix can be implemented like this:

void remove(int (*array)[3], int index, int array_length)
{
   int i, j;
   for(i = index; i < array_length - 1; i++) {
       for (j = 0; j < 3; ++j)
           array[i][j] = array[i + 1][j];
   } 
}

Note that the function does not really remove the row, just shifts the rows up. So its name is misleading. You must create a new array and copy the old array to the new array except the indicated row to really remove a row and to change the dimensions of the array.

Edit: The name of that function should be changed because the standard library already has a function named remove.

If the array is obtained like that:

int (*array)[3] = malloc(array_length * sizeof *array);

You can shrink the array by calling the realloc:

int (*p)[3] = realloc(array, (array_length - 1) * sizeof *p);
if (p != NULL)
    array = p;
Lxer Lx
  • 292
  • 1
  • 6
1

int (*array)[3] -> Here, array is a pointer to 3 element integer array.

error: assignment to expression with array type

This is because an array variable is not modifiable/re-assignable like a pointer. Operations that can be performed on an lvalue of array type are: sizeof, unary & and implicit conversion to pointer type.

dev7060
  • 120
  • 8
0

The error is telling you that C doesn't support array assigning. Your code tries to copy the whole line i + 1 into the line i of the matrix, but C doesn't know how to operate this.

The solution is to iterate through each element of your array, copying one by one.

void remove(int (*array)[3], int index, int array_length)
{
   int i, j;
   for(i = index; i < array_length - 1; i++)
   {
       for(j = 0; j < array_length; ++)
       {
           array[i][j] = array[i + 1][j];
       }
   } 
}

The code above assumes that your matrix is always squared, but you could change its dimension inside j's for loop.

lzbernardo
  • 196
  • 11
  • also, the answer assumes that outside the remove function you're keeping track of the full size of your array – lzbernardo Jan 03 '20 at 00:22
  • I didn't down vote, but I think your for j loop end condition is wrong. Shouldn't it be `j < 3`? Also missing `j++`. – afk Jan 03 '20 at 02:58