views:

660

answers:

5

I'm trying to write a function to swap 2 elements in a 2D array:

void swap(int surface[][], int x1, int y1, int x2, int y2) {
    int temp = surface[x1][y1];
    surface[x1][y1] = surface[x2][y2];
    surface[x2][y2] = temp;
}

however when I try to compile it (gcc), I get this error message:

Sim_Annealing.c: In function `swap': 
Sim_Annealing.c:7: error: invalid use of array with unspecified bounds
Sim_Annealing.c:8: error: invalid use of array with unspecified bounds
Sim_Annealing.c:8: error: invalid use of array with unspecified bounds
Sim_Annealing.c:9: error: invalid use of array with unspecified bounds

Is there some special magic I have to do in order to have a 2D array as a function parameter?

Thanks for your help. If you know of any good references for arrays as function parameters send them my way :)

+6  A: 

Just declare the array parameters. Better yet, use a typedef for both the initial declaration and the function's formal parameter.

The problem is that without knowing the row size, i.e., the number of columns, it has no way to compute the pointer adjustment to get subsequent rows. Interestingly, it does not need to know how many rows you have.

For example, this works:

void swap(int surface[][20], int x1, int y1, int x2, int y2) {
  int temp = surface[x1][y1];
    surface[x1][y1] = surface[x2][y2];
    surface[x2][y2] = temp;
}

But it would be better to tie the caller's types and the function's type together.


Every subscript access will require a multiply, but this works (only C99-conforming compilers) ...

int f(int, int, int a[*][*]);

int f(int r, int c, int a[r][c])
{
  return a[99][100];
}

Another example, which would work in even pre-C89 environments:

typedef int surface_t[][20];

surface_t therealthing = {
  { 1, 2, 3},
  { 4, 5, 6}
};

void swap(surface_t x) {
  x[0][2] = 'q';
}

void f1(void) {
  swap(therealthing);
}

And finally, because variable length arrays are something quite recent, the traditional and still the fastest technique is to pass int *a[]. This doesn't require any knowledge of either row or column lengths, but you do need to construct the pointer vector.

DigitalRoss
What if I want the function to handle variable sized 2D arrays?
Casey
@Casey, Pass the dimensions of the array as parameters, or use e.g. `std::vector`.
strager
@Casey use Type**
AraK
+1  A: 

If the array is a "real" 2D array, you need to specify the size of all but the first dimension:

void swap(int surface[][NUMBER_OF_COLUMNS], int x1, int y1, int x2, int y2) {
    ...
}

There are a few potential problems with this. If your 2D arrays are really arrays of pointers (int *surface[]), that won't work, and you need to change the surface parameter to a pointer to a pointer:

void swap(int **surface, int x1, int y1, int x2, int y2) {
    ...
}

Or, to make the function more general, you could change it to accept two int pointers (that could point anywhere) and swap them:

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

and you would call it like this:

swap(&surface[x1][y1], &surface[x2][y2]);
Matthew Crumley
You have to specify the 2nd dimension, which is the number of columns.
Falaina
Yeah, I just noticed that and fixed it. Thanks.
Matthew Crumley
+1  A: 

In C only the first dimension of the array is allowed to be left unspecified, because it needs to know how to calculate the offsets. If you need to work with a variable sized 2D array pass in our array as an int*, pass in the size of your second dimensions and to the pointer math yourself:

void swap(int *surface, int ySize, int x1, int y1, int x2, int y2) {
    int temp = *(surface + ySize * x1 + y1) ;
    *(surface + ySize * x1 + y1) = *(surface + ySize * x2 + y2);
    *(surface + ySize * x2 + y2) = temp;
}

This is the same thing that the [][] syntax is doing, since arrays in C are really just pointers.

shf301
+1  A: 

GCC allows variable-length arrays as arguments to functions:

#include <stdio.h> 

void swap(int size; int surface[][size], int size, int x1, int y1, int x2, int y2) {
  int temp = surface[x1][y1];
  surface[x1][y1] = surface[x2][y2];
  surface[x2][y2] = temp;
}

int s[10][10];

int main(){
  s[1][1] = 11;
  s[2][2] = 22;

  printf("s[1][1]: %i   s[2][2]: %i\n", s[1][1], s[2][2] );
  swap( s, 10, 1, 1, 2, 2 );
  printf("s[1][1]: %i   s[2][2]: %i\n", s[1][1], s[2][2] );

  return 0;
}
sambowry
+1; I don't think I'd use this in real code, but it is a neat trick :)
Whisty
+1  A: 

Passing multidimensional arrays as function arguments introduces some headaches. Remember that in most contexts, an expression of array type will be implicitly converted to a pointer type, and its value will be the address of the first element of the array. So for example, a 10x20 array of int will be converted to a pointer to a 20-element array of int:

void swap(int (*surface)[20], size_t rows, size_t x1, size_t x2, 
          size_t y1, size_t y2)
{
  int temp;
  assert(x1 < rows && x2 < rows);
  temp = surface[x1][y1];
  surface[x1][y1] = surface[x2][y2];
  surface[x2][y2] = temp;
}
int main(void)
{
  int surface[10][20];
  ...
  swap(surface, 10, 1, 1, 2, 2);
  ...
}

Here's where a big problem shows up. Based on its prototype, swap() can only deal with Nx20 arrays of int; the number of rows can vary, but the number of columns cannot, because T (*)[N] is a different type from T (*)[M] where N != M. Ideally you'd like a function that can deal with an arbitrary number of rows and columns. One way to accomplish that is to treat the array as a 1D array of T, and compute row and column offsets manually:

void swap(int *surface, size_t rows, size_t cols, size_t x1, 
          size_t x2, size_t y1, size_t y2)
{
  int temp;
  assert(x1 < rows && x2 < rows && y1 < cols && y2 < cols);
  temp = surface[x1*cols+y1];
  surface[x1*cols+y1] = surface[x2*cols+y2];
  surface[x2*cols+y2] = temp;
}
int main(void)
{
  int surface[10][20];
  ...
  swap(&surface[0][0], 10, 20, 1, 1, 2, 2);
}

Here we pass the address of the first element (&surface[0][0]) and treat it as a simple pointer to int. That way we can deal with any number of rows and columns. Note that this will only work for actual 2D arrays (not arrays of pointers), since swap assumes that the array elements are laid out contiguously.

John Bode