General c query

Discussion in 'Programmer's Corner' started by aamirali, Oct 15, 2012.

  1. aamirali

    Thread Starter Member

    Feb 2, 2012
    415
    1
    Hi,

    I have to use a 2D array with diff length. one is temp1[10][33] & other is temp2[10][97] & have to pass it to same function depending upon condition.

    Now when I pass array by reference in C & made a function in C, it ask for constant 2nd dimension. Like if I have to use temp1 array then in function which is to be called I have to tell that array which I am going to pass has 2nd dimension 33. Due to which I am not able to use it for temp2 array.

    Any suggestion how to do it.
     
  2. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    One way is to get rid of the array notation, and compute the offset yourself.

    1. The order of the dimensions is backwards.
    2. You have to use different offsets for different array arrangement.
    If you use 2 dimensions, and the number of columns is different, you need different computation. But you can pass one and the same char* pointer to the function!

    3. I made two screenshots using the integrated simulator which demonstrate this actually works.

    Here the source code:

    Code ( (Unknown Language)):
    1. /******************************************************************************/
    2. /* Main Program                                                               */
    3. /******************************************************************************/
    4. char arr_1[5][8];
    5. char arr_2[5][3];
    6.  
    7. void f1(char* data)
    8. {unsigned char dim0,dim1,offset;
    9. dim0=1;
    10. dim1=3;
    11. offset=(dim0*8)+dim1;
    12.     *(data+offset)=12;
    13. }
    14.  
    15. uint8_t main(void)
    16. {
    17.     /* Configure the oscillator for the device */
    18.     ConfigureOscillator();
    19.  
    20.     /* Initialize I/O and Peripherals for application */
    21.     InitApp();
    22.     arr_1[1][3]=13;
    23.     f1(&arr_1);
    24.     f1(&arr_2);
    25.  
    26.     unsigned char new_value;
    27.     new_value=arr_1[1][3];
    28.     /* TODO <INSERT USER APPLICATION CODE HERE> */
    29.  
    30.     while(1)
    31.     {
    32.  
    33.     }
    There are some drawbacks, for instance you need to take care the element size.

    If someone knows a better way, feel free to correct me.
     
  3. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    Another solution is to add dummy elements so the sizes are the same.
     
  4. aamirali

    Thread Starter Member

    Feb 2, 2012
    415
    1
    HI,
    I tried this & it shows error (Keil 4.54.0.0 + C)

    error: #167: argument of type "uint8_t (*)[4]" is incompatible with parameter of type "uint8_t *"

     
  5. takao21203

    Distinguished Member

    Apr 28, 2012
    3,577
    463
    1. You need to use the & operator.
    2. If this still can not work, you need to use a type cast: (char*)
    Which you need to enclose in brackets.
     
  6. WBahn

    Moderator

    Mar 31, 2012
    17,757
    4,800
    If you learn a bit about what arrays actually are, then you will be able to work through these kinds of problems, now and in the future, pretty easily.

    So let's step back and talk about some fundamentals for a bit.

    In general, an array is nothing more than a bunch of things of the same type stored one after another in memory starting from some base address. That base address is called a 'pointer' since it 'points' to the start of the array.

    Code ( (Unknown Language)):
    1.  
    2. double array_1D[50];
    3.  
    Let's take the second one first. By making this declaration, several things happen. A chunk of memory that is large enough to hold 50 values of type double is allocated and the value of the starting address is stored in a constant called array_1D. So array_1D is a pointer (and one that is constant, meaning it can't be altered) that refers to the block of memory.

    Now let's say that you have a function that you want to pass this array to. That function might be defined as follows:

    Code ( (Unknown Language)):
    1.  
    2. double sum(double x[])
    3. {
    4.     double total;
    5.     int i;
    6.  
    7.     for (i = 0, total = 0.0; i < 50; i++)
    8.         total += x[i];
    9.  
    10.    return total;
    11. }
    12. [/i]


    You would call this with something like the following:

    Code ( (Unknown Language)):
    1.  
    2. the_total = sum(array_1D);
    3.  
    This is fine, provided that the array being passed is big enough to include the highest index value that will be used. In this case, that is 49 and everything is okay (at least for our present needs).

    But what is really happening here?

    When you declare " double x[] " in the argument list, you are really using a shorthand notation that is equivalent to the following:

    Code ( (Unknown Language)):
    1.  
    2. double sum(const double *x)
    3.  
    Don't worry about the 'const' key word, it doesn't affect us here (I'm not even positive, off the top of my head, that it is needed to make them equivalent, but I think it is). The important thing is that this 'x' is a variable that is not of type double, but rather is a pointer to a variable of type double, meaning the value that gets passed into it is an address in memory where a value of type double resides.

    Notice that the function has no way to tell if 'x' points to a single value of type double, or an array containing a million values of type double; so it let's you use it either way.

    If, within this function, you have the statement:

    Code ( (Unknown Language)):
    1.  
    2. y = x[13];
    3.  
    then the compiler will assume that 'x' is an array of doubles. Knowing that a double requires 8 bytes of memory (on most machines, but if it is different on yours, your compiler knows what the size is), it can calculate what the memory address of x[13] is, so it can go get it. In fact, the following two lines are identical:

    Code ( (Unknown Language)):
    1.  
    2. y = x[13];
    3. y = x+13;
    4.  
    Any time you add a pointer to an integer, the compiler implicitly multiplies the integer by the size of whatever type of item that the pointer points to is.

    In fact, the following is also the same as above:

    Code ( (Unknown Language)):
    1.  
    2. y = 13[x];
    3.  
    Look strange? That's because it is. Look illegal? Looks can be deceiving. Try it. The reason that it is valid is because array indexing using square brackets is a shorthand for the pointer arithmetic expression, and since pointer addition is commutative, it doesn't matter what is inside and what is outside the brackets.

    With all of this said, we can now talk about 2D arrays and the problems they cause the compiler.

    A 2D array is stored in memory (in C) using "row major" ordering, meaning that the first row is stored starting at the base address and then the second row, and so on.

    What this means is that if you declare and array such as:

    Code ( (Unknown Language)):
    1.  
    2. #define ROWS (10)
    3. #define COLS (20)
    4. ...
    5. double array_2D[ROWS][COLS];
    6.  
    THen the first row starts at address 'array_2D' and the second starts at 'array_2D'+COLS and the third at 'array_2D'+2*COLS and so on.

    Notice that the number of columns per row is needed to calculate the offset, but that the number of rows is not. That the latter is not needed is for the exact same reason that the number of elements was not needed by the function to compute the offsets to the 1D array elements -- the compiler was willing to assume that if you reference the 10th element, that the 10th element exists. While it can still assume that if you reference something int he 10th row of a 2D array that the 10th row exists, it has to know how many columns are in each row in order to find anything 9that is not in the first row).

    Thus, when you declare a function like:

    Code ( (Unknown Language)):
    1.  
    2. double sum2D(double x[][COLS])
    3.  
    You are giving the compiler the minimum information it needs in order to figure out what memory location a given element is at -- if those elements are indexed in the 2D fashion (namely y = x[r][c];).

    But any array is ultimately a 1D array in memory and you can perform the pointer arithmetic yourself. If you've followed the above descriptions, you have everything you need to understand the following code:

    Code ( (Unknown Language)):
    1.  
    2. double x[10][30];
    3. double y[5][50];
    4. ...
    5. total1 = sum(x, 10, 30);
    6. total2 = sum(x, 5, 50);
    7. ...
    8.  
    where

    Code ( (Unknown Language)):
    1.  
    2. double sum(double *d, int rows, int cols)
    3. {
    4.    int r, c;
    5.    double total;
    6.    
    7.    for (r = 0; r < rows; r++)
    8.       for (c = 0; c < cols; c++)
    9.          total += x[r*rows + c]; // x[r][c]
    10.  
    11.    return total;
    12. }
    13.  
     
Loading...