4

I have to port some Java methods in C, have a Java background but I'm a total noob in C programming

In java

float[][] traspose(float Xy[][]) {
    float result[][]=new float[5000][3000];
    for(int i = 0; i < m; i++) {
        for(int j = 0; j < n; j++) {
            result[i][j] = Xy[j][i];
        }
    }
    return result;
}

My C porting attempt

float traspose(int m, int n, float Xy[m][n]) {
    int i,j;
    float result[5000][3000];
    for(i = 0; i < m; i++) {
        for(j = 0; j < n; j++) {
            result[i][j] = Xy[j][i];
        }
    }
    return result;
}

This doesn't work and get incompatible types error.

My 2 questions

1) How should I fix my code? Googling I have seen some questions about returning matrix in C but not very clear and in most case was suggested to use an approach that doesn't imply the use of return.

2) I have seen that usually this kind of operations in C are written without return type approach e.g. void methods that operate on constants or the code is written directly in main. Why?

EDIT

Following the suggestions I have tried to code this

float **transpose(int m, int n, float Xy[]) {
    int i,j;
    float **result = allocate_mem_m(m,n);
    for(i = 0; i < m; i++) {
        for(j = 0; j < n; j++) {
            result[i][j] = Xy[j*n+i];
        }
    }
    return result;
}


int main(int argc, char **argv) {
    printf("Hello World!");
    float matrix[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
    printf("Matrix created\n");
    int size=3;
    print(size, size, matrix);
    float **transposedmat = transpose(size, size, &matrix[0][0]);
    printMat(size, size, transposedmat);
    return 0;
}

But unfortunately the program crashes when I invoke trasposition method.

PS I want to compile the code in standard C (not in C99)

AndreaF
  • 11,975
  • 27
  • 102
  • 168
  • In Java, you tried returning a 2D array of type `float[][]`. But you're returning a single `float` value in C. While at the same time, `result` is a 2D array of type `float[][]` – Alex May 19 '14 at 18:06
  • @AlexYan right... but in c I cannot write `float[][]` in the method return type, so what is the correct syntax? – AndreaF May 19 '14 at 18:09
  • I don't have enough experience in C/C++ at the moment to say for sure, but I think you would have to return pointers instead, as pointers and arrays in C/C++ are the same things if I remember correctly. But the memory would have to be allocated on the heap using `malloc()` so that the pointer do not become invalid when the function returns – Alex May 19 '14 at 18:15
  • More on returning arrays in C: http://stackoverflow.com/questions/11656532/returning-an-array-using-c – Alex May 19 '14 at 18:15
  • 1
    @AlexYan a pointer is a pointer and an array is an array. However, array names are *converted* to pointers in a lot of cases. – aglasser May 19 '14 at 18:20
  • 1
    Aah, right. That's what I meant :) – Alex May 19 '14 at 18:22

3 Answers3

6

Strictly speaking, the answers about using pointer to pointer to float are not technically correct, because a 2D array of floats is not the same as a pointer to pointer to float.

This would be the equivalent C code:

#include <stdlib.h>

float (*transpose(int m, int n, float Xy[m][n]))[5000][3000] {
    float (*result)[5000][3000] = malloc(sizeof(*result));
    for(int i = 0; i < m; i++) {
        for(int j = 0; j < n; j++) {
            (*result)[i][j] = Xy[j][i];
        }
    }
    return result;
}

This works with pointers: the first line inside the function allocates space for a 2D array of 5000 by 3000, makes the copy as you have in your Java code, and returns the pointer to the new array. Note that you have to free the memory once you are done with it (by calling free()).

The function returns a pointer to an array, which means you'll have to use it like this:

float (*array)[5000][3000] = transpose(m, n, Xy);

And then you can access element i,j from the new array using (*array)[i][j].

To free, you do this:

free(array);

Finally, remember to compile this with C99 support - you need it because of the variable length array Xy in the parameters list. If you're using gcc, this can be achieved with -std=c99

The solution proposed in the other answers may be enough for your purposes, but keep in mind that using a float ** as a 2D array has a few caveats and 'gotchas'. For example, with the float ** solution, you'll have to manually free every position in array[i] before freeing array itself; sizeof will not tell you the true size of the "simulated" 2D array, and floats will not be stored in memory contiguously.

2) I have seen that usually this kind of operations in C are written without return type approach e.g. void methods that operate on constants or the code is written directly in main. Why?

The part about code being written directly in main() is not very common. Maybe you just saw some tutorial examples. In bigger programs, and in general, this is, of course, not inside main().

Writing it without a return type can be useful if you don't want to have to allocate memory: you leave that up to the caller. This is important, because you don't place the burden of freeing memory you allocated on the caller. Instead, you receive a pointer to an already allocated memory buffer, given to you by the caller, and write the results in there. This is generally a superior approach when it comes to memory management, but of course, lots of design choices and little details can quickly change this.

UPDATE (how to compile it without C99 support):

Well, the problem here is that Xy may be a 2D array of arbitrary length, i.e., you want to call transpose() with any 2D array (that's why you're giving it m and n for the dimensions).

C has no direct way of passing arbitrarily sized 2D arrays to a function. Such support was added in C99. If you want to do this in C89, a known workaround is to use the fact that arrays are linearly layed out in contiguous memory positions, and thus use it as if it were a 1D array of m*n floats. In other words, you roll your own indexing. Since C arrays are stored in row-major order, Xy[i][j] is the same as Xy_flat[i*n+j]. So, in effect, transpose() receives a pointer to the first element in Xy, and treats Xy as a 1D array. We just have to replace Xy[i][j] with Xy[i*n+j]:

/* C89 version */
#include <stdlib.h>

float (*transpose2(int m, int n, float Xy[]))[5000][3000] {
    float (*result)[5000][3000] = malloc(sizeof(*result));
    int i, j;
    for(i = 0; i < m; i++) {
        for(j = 0; j < n; j++) {
            (*result)[i][j] = Xy[j*n+i];
        }
    }
    return result;
}

This may seem weird and non trivial for someone coming from Java, but C usually works at a lower level.

To use this function, you have to give it a pointer to the first element of Xy. Here's an example:

float matrix[3][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}};
float (*transposed)[5000][3000] = transpose(3, 3, &matrix[0][0]);
/* Use (*transposed)[i][j]... */
free(transposed);

If you don't want to hang around with (*array)[5000][3000] all over the code because of the fixed hardcoded dimensions, you can certainly use the solutions in the other answers, but always keeping in mind the differences between a pointer to pointer to float and a 2D array of floats. Since you seem to prefer this approach, here's how the code would look like:

float **allocate_mem_m(int m, int n)
{
 int i;
 float **arr = malloc(n*sizeof(*arr));
 for(i=0;i<n;i++)
   {
     arr[i]=malloc(m*sizeof(**arr));
   }
 return arr;
} 


float **transpose(int m, int n, float Xy[]) {
  int i,j;
  float **result = allocate_mem_m(m,n);
  for(i = 0; i < m; i++) {
    for(j = 0; j < n; j++) {
      result[i][j] = Xy[j*n+i];
    }
  }
  return result;
}

I took the liberty to change your allocate_mem_m() to receive the dimensions only, and return the pointer with the allocated memory. I think it starts to get a little overly complex when you use float ***. That's not necessary.

As a word of advice, I would equally add a free_mem_m() to ease the process of freeing the allocated memory:

void free_mem_m(int m, float **array) {
  int i;
  for (i = 0; i < m; i++) {
    free(array[i]);
  }
  free(array);
}

Here's the full code listing:

#include <stdlib.h>

float **allocate_mem_m(int m, int n)
{
 int i;
 float **arr = malloc(n*sizeof(*arr));
 for(i=0;i<n;i++)
   {
     arr[i]=malloc(m*sizeof(**arr));
   }
 return arr;
} 

void free_mem_m(int m, float **array) {
  int i;
  for (i = 0; i < m; i++) {
    free(array[i]);
  }
  free(array);
}

float **transpose(int m, int n, float Xy[]) {
  int i,j;
  float **result = allocate_mem_m(m,n);
  for(i = 0; i < m; i++) {
    for(j = 0; j < n; j++) {
      result[i][j] = Xy[j*n+i];
    }
  }
  return result;
}

And an example usage:

int main(void) {
  float Xy[3][3] = { { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 } };
  float **transposed = transpose(3, 3, &Xy[0][0]);
  int i, j;
  for (i = 0; i < 3; i++)
    for (j = 0; j < 3; j++)
      printf("%f ", transposed[i][j]);
  printf("\n");
  free_mem_m(3, transposed);
  return 0;
}

Note that transpose() assumes a square matrix (because you allocate a block of n positions for m floats). If you want to use this with rectangular matrixes, you would have to allocate m blocks to hold n floats instead.

Live demo working: http://ideone.com/CyNdpn

Final note: The hack to treat a 2D array as 1D array is old and tricky; it is generally considered bad practice (in my opinion), and you should avoid this kind of clever code. But if you don't want to use C99 features, this is all you're left with.

For interested readers, at the risk of being a bit of a spammer, I have written 2 articles in my blog about generalizing this idea to arbitrary arrays with N dimensions. It goes a little bit deeper in the technical explanation of why you need to do it, and how to do it: http://codinghighway.com/?p=1159 and http://codinghighway.com/?p=1206

Filipe Gonçalves
  • 20,783
  • 6
  • 53
  • 70
  • +1 for the clarification, however the posted code doesn't compile, the Kdev IDE marks red the everything – AndreaF May 19 '14 at 21:58
  • @AndreaF Hum, I compiled it before posting my answer. Indeed, it compiles as long as your compiler supports C99. That's why I mentioned you need to use C99 flags - gcc, for example, doesn't compile on C99 mode by default. It's a matter of configuring Kdev IDE's compiler flags, but if the accepted answer is enough for your purposes, I understand you don't want to dive into such details :) – Filipe Gonçalves May 19 '14 at 23:10
  • I use gcc 4.8 and Kdevelop IDE. are you sure that you haven't do mistake re-writing the code here? Otherwise without using C99 notation what should I have to change – AndreaF May 21 '14 at 17:48
  • @AndreaF See http://ideone.com/ERYm80 - copied from my answer (with a `main()` added for a minimal test). It compiles and runs. – Filipe Gonçalves May 21 '14 at 19:02
  • mmm... Probably kdev doesn't use c99 by default. How should I convert in standard (non-C99) C? – AndreaF May 22 '14 at 09:01
  • @AndreaF See my update added to the end of my answer. – Filipe Gonçalves May 22 '14 at 11:12
  • Still I cannot fix the program crashes at runtime. I get these warnings during compilation `main.c:69:5: warning: incompatible implicit declaration of built-in function ‘free’ [enabled by default]` `main.c:99:5: warning: passing argument 2 of ‘print’ from incompatible pointer type [enabled by default] ` I invoke the method in main in this way `float **transposedmat = transpose(size, size, &matrix[0][0]); print(size, transposedmat);` My `print` method is in the edited question – AndreaF May 22 '14 at 11:50
  • @AndreaF Add `#include ` to the beginning of the file (that's where `free()` is declared), and change your `print()` function to receive `float **matrix` instead of `float matrix[n][n]`. – Filipe Gonçalves May 22 '14 at 12:11
  • @AndreaF Please update the question with your code as is right now. – Filipe Gonçalves May 22 '14 at 12:27
  • @AndreaF it's not `float **matrix[n][n]` in print, it's `float **matrix` – Filipe Gonçalves May 22 '14 at 12:52
2

Read up on pointers in C. The array name result is the same thing as &result[0] (address of the first element of the result array) so when you return result, you are returning a pointer to the array.

However, you are declaring result on the stack of the traspose function. This local variable will be destroyed when the function returns. Even if result was returned as a pointer, it would not be a valid pointer because the memory is gone once the function returns.

Try using malloc to put the result array on the heap (dynamic memory), then return a pointer to that array. You will need to change the return type of the function to be a pointer, as well.

Edit for example: Create a pointer to a pointer to a float. For each element of that array, malloc the space for each float.

float **array;
array = malloc(rows * sizeof(float *));
for (i = 0; i < rows; i++)
{
  array[i] = malloc(cols * sizeof(float));
}

Fill the array with whatever you want from the original array.

for (i = 0; i < rows; i++) 
{
  for (j = 0; j < cols; j++)
  {
    array[i][j] = 0; //VALUE
  }
}
aglasser
  • 3,059
  • 3
  • 13
  • 10
  • could you give me a sample based on my code, I have many problems with c syntax. thanks – AndreaF May 19 '14 at 18:07
  • Edited with small example – aglasser May 19 '14 at 18:13
  • @AndreaF you will also need to change the return type of the function to be `float **`. You are returning a pointer to a pointer to a float (well, a 2D array of floats ;) ) instead of just a float. – aglasser May 19 '14 at 18:19
  • *a pointer to a pointer to a float (well, a 2D array of floats)* This is misleading. A pointer to pointer to float is definitely not a 2D array. – Filipe Gonçalves May 19 '14 at 18:20
  • @FilipeGonçalves you are correct. But in this case, a 2D array is *effectively* being returned, which is why I put a winky face. Of course a pointer to a pointer to something else is not the same as a 2D array in all cases, but in THIS case it is. – aglasser May 19 '14 at 18:21
  • 1
    I tend to disagree. There are a lot of differences, even in this case. For example, `array[0][1]` is not immediately before `array[0][2]` in memory, because each `array[i]` has a pointer to heap-allocated space. Moreover, `sizeof array` will be the same as `sizeof(float **)`. Nit-picking? Maybe, but be careful with such assumptions. Maybe the code is enough for the OP's purposes, but it never hurts to be a little bit more precise ;) – Filipe Gonçalves May 19 '14 at 18:27
  • Okay, I see your point now. I'll clarify more in my future explanations. – aglasser May 19 '14 at 18:29
  • Unfortunately still I cannot run the method correctly. :( see the edit – AndreaF May 22 '14 at 10:02
1

The easiest way to do this is to pass the destination array (the one which holds the transposed version) as a parameter to your function. This leaves the responsibility for memory management to the caller (where it should be), gives you some type safety, and is much cleaner:

#include <stdio.h>

// Re-written method
void transpose(int m, int n, float srcXy[m][n], float dstYx[n][m]) {
    int i,j;
    for(i = 0; i < m; i++) {
        for(j = 0; j < n; j++) {
            dstYx[j][i] = srcXy[i][j];
        }
    }
}

// (just for demo purposes)
void print_matrix(int m, int n, float matrix[m][n]) {
  int i,j;
  for (i=0; i<m; i++ ) {
    printf("[");
    for (j=0; j<n-1; j++ ) {
      printf(" %f, ", matrix[i][j]);
    }
    printf("%f ]\n", matrix[i][j]);
  }
}

// (test the function)
int main() {
  int m = 2;
  int n = 3;
  float original[2][3] = { {1, 2, 3}, {4, 5, 6} };  
  float transposed[3][2];

  transpose(2, 3, original, transposed);

  printf("Original matrix\n\n");
  print_matrix(2, 3, original);

  printf("Transposed matrix\n\n");
  print_matrix(3, 2, transposed);
}
Anna Dickinson
  • 3,307
  • 24
  • 43