32

I am a Ruby programmer who has ended up developing a code generate for C. Its like asking a Limo to tow a 1960s truck. Any way.

Here is what I thought should work but doesnt work.

float[][] pixels()
{
  float x[][]= { {1,1},{2,2} };
  return x
}

void drawLine(float x[][2])
{
  //drawing the line
}

//inside main
drawLine(pixels());

I have banged my head on my desk trying to get this thing work. Please help.

klutt
  • 30,332
  • 17
  • 55
  • 95
Eastern Monk
  • 6,395
  • 8
  • 46
  • 61

5 Answers5

39

In C, pointers and arrays are closely related. Also, you usually need to pass the size of an array as a separate variable. Let's start you with:

#include <stdio.h>

float** createArray(int m, int n)
{
    float* values = calloc(m*n, sizeof(float));
    float** rows = malloc(m*sizeof(float*));
    for (int i=0; i<m; ++i)
    {
        rows[i] = values + i*n;
    }
    return rows;
}

void destroyArray(float** arr)
{
    free(*arr);
    free(arr);
}

void drawLine(const float** coords, int m, int n);

int main(void)
{
    float** arr = createArray(2,2);
    arr[0][0] = 1;
    arr[0][1] = 1;
    arr[1][0] = 2;
    arr[1][1] = 2;
    drawLine(arr, 2, 2); 
    destroyArray(arr);
}
user438383
  • 5,716
  • 8
  • 28
  • 43
aschepler
  • 70,891
  • 9
  • 107
  • 161
  • I just took a try for this code-snippetand got: "error: invalid conversion from ‘void*’ to ‘float*’" in createArray.You need to add a cast: "float* values = (float*)calloc(m*n, sizeof(float));" .. same for the float** – Alex Oct 15 '13 at 13:26
  • 2
    @Alex Are you possibly compiling it as though it were C++ code? It is valid C and invalid C++. – aschepler Oct 15 '13 at 21:17
  • so is `float**` the "type" for 2D float arrays? – Emil S. May 28 '21 at 18:53
8

Thank you all for your answers and more specifically for the detailed explanation of the array-pointer relationship.

I encapsulated the array in a structure

 struct point_group1 {
        float x[3];
        float y[3];
};

struct point_group1 pixels(){
    struct point_group1 temp;

    temp.x[0] = 0.0;
    temp.x[1] = 1.0;
    temp.x[2] = -1.0;

    temp.y[0] = 0.0;
    temp.y[1] = 1.0;
    temp.y[2] = 1.0;

    return temp;    
}



struct point_group1 points1  = pixels();
axPoly(points1.x, points1.y ,3, 0.0);
Eastern Monk
  • 6,395
  • 8
  • 46
  • 61
  • This is a pretty good solution, just be aware that if these are large arrays and your compiler isn't good at optimizing you may end up creating needless copies. – user470379 Mar 05 '11 at 07:34
6
float (*pixels(void))[2] 
{
  static float x[2][2]= { {1,1},{2,2} };
  return x;
}

void drawLine(float (*x)[2])
{
  //drawing the line
  //x[0][0];
}

//inside main
drawLine(pixels());
Eunice
  • 137
  • 1
  • 6
  • 1
    You have the only correct answer here! Too bad you did not explain anything. If you have added some explanation, feel free to drop a comment to me, I'll check and upvote if I think it is good. – too honest for this site Jun 20 '16 at 12:37
6

In C/C++, when you pass an array to a function, it decays to be a pointer pointing to first element of the array. So, in pixels() function, you are returning the address of a stack allocated variable. The returning variable's address is no longer valid because on pixels() return, the stack allocated variable goes out of scope. So, instead you should for a variable whose storage is dynamic ( i.e., using malloc, calloc ).

So, for a two dimensional array, you may use float** arrayVariable;. Also, if you passing this to a function, you should be wary of how many rows & columns it has.

int rows, columns;

float** pixels()
{
    // take input for rows, columns
    // allocate memory from free store for the 2D array accordingly
    // return the array
}

void drawLine( float** returnedArrayVariable )
{
  //drawing the line
}

Since, 2D array is managing resources it self, it should return the resources back to the free store using free.

Mahesh
  • 34,573
  • 20
  • 89
  • 115
1

The easiest way is probably going to be declaring the float array in main and having pixels just fill it:

#define PIXEL_X_SIZE 2
#define PIXEL_Y_SIZE 2

int pixels(float x[][PIXEL_X_SIZE], int len) {
    /* I don't know if you want the logic of this method to ever change,
       but this will be roughly equivalent to what you do above */
    if (len < PIXEL_Y_SIZE) {
        /* the length of the passed array is too small, abort */
        return -1;
    }

    x[0][0] = x[0][1] = 1;
    x[1][0] = x[1][1] = 2;
    return 0;
}

void drawLine(float x[][PIXEL_X_SIZE]) {
    /* this will work fine */
}

int main() {
    float pixel_array[PIXEL_Y_SIZE][PIXEL_X_SIZE];
    pixels(pixel_array, PIXEL_Y_SIZE);
    drawLine(pixel_array);
}

You can also use malloc and free and store your pixels on the heap, but if this is all the bigger the pixels array is going to be, there's really no need and it just adds additional complexity to make sure your memory always get properly allocated and freed.

user470379
  • 4,879
  • 16
  • 21
  • You approach is fine. The only concern I feel is that since our application is expected to run on an embedded device the code review check-list asks to minimise the global vars. The code will have hundreds of pixel like functions. – Eastern Monk Mar 05 '11 at 07:08
  • @Akshar I don't use any global vars... I #define a couple things, but if you really wanted to you could just repeat the 2s everywhere, but you're setting yourself up for a big maintenance headache any time any of those values change and/or very hard to debug bugs if somebody gets a new size wrong. – user470379 Mar 05 '11 at 08:25