2

I've read many discussions on passing arrays to functions. They seem to be written for people who are fluent in C. I am a barbarian not yet fluent in C.

From what I've read in other discussions it should be possible to pass an array without pointer decay. Yet, I need help implementing this.

Please give me a concise example of how to pass an array as a pointer of a pointer and determine it's size within the function to which it is passed.

If the array is changed in the function, I want the source of the array to be changed. And I want to minimize additional allocation of memory. And, if at all possible, I'd like to size the array within the function.

This code works without passing.

// this works
j = sizeof(hourLongs)/sizeof(hourLongs[0]);
i = 0; while (now > hourLongs[i] && i < j){i++;}  
hour = --i; 

This works but fails to size the array within the function.

hour = compareToLongs(&now, hourLongs, (int)(sizeof(hourLongs)/sizeof(hourLongs[0])) ); 

// long *, long * , int -> int 
// compare time to array of times.
static int compareToLongs(long * time, long * timeList_pointer, int size){
  i = 0; while (*time> (timeList_pointer)[i] && i < size){i++;}  
  return --i;
}

I'd like to pass the array in a way that will allow me to find it's size within the function. Something like the following, minus my errors.

hour = compareToLongs(&now, &hourLongs); 

// long *, (long (*) [])*  -> int 
// compare time to array of times.
static int compareToLongs(long * time, long ** timeList_pointer){
  int size = (int)(sizeof(*timeList_pointer)/sizeof(*timeList_pointer[0]));
  i = 0; while (*time> (i < size && *timeList_pointer)[i]){i++;}
  free(size);  
  return --i;
}

Edit: hourLongs is an array of long integers.

Edit: Regarding the title, I used the word 'reference' in the vernacular so that other barbarians like me may find the question.

Edit: I'm really looking for a way to size an array of integers within a function.

sizeof(array)

gives a some address to sizeof() that allows the size to be determined. It is that address that I would like to pass to my function.

Is there a reason why I cant pass that which is passed to sizeof() to my function?

Edit: as the operation of sizeof() suggests it is possible for an array to be passed without "pointer decay". user529758 gives three examples in this discussion

in C99 there are three fundamental cases, namely:
1. when it's the argument of the & (address-of) operator.
2. when it's the argument of the sizeof operator.
3. When it's a string literal of type char [N + 1] or a wide string literal of type wchar_t [N + 1] (N is the length of the string) which is used to initialize an array, as in char str[] = "foo"; or wchar_t wstr[] = L"foo";.

What I seek to do should be possible using &array.

Community
  • 1
  • 1
kjl
  • 311
  • 3
  • 13
  • Why the second snipped is failing, again? It is the correct way to do things. The only thing, that I don't see a point of passing a pointer to `time` instead of just it's value. And.. the formatting. Don't save on newlines. – Eugene Sh. Aug 09 '16 at 19:09
  • 1
    You cannot directly pass an array as a function argument. The usual way to pass an array indirectly is to pass two arguments: the address of the 0th element, and the length of the array. (In some cases, you can omit the size argument if the size can be determined from the contents of the array; for example the end of a string is indicated by the `'\0'` null character.) I'm not sure why you'd need a pointer to a pointer. I note that you haven't shown us the definition of the array object you want to pass. – Keith Thompson Aug 09 '16 at 19:14
  • @KeithThompson I'm looking for way that will work without a character indicating the end of the array. – kjl Aug 09 '16 at 19:25
  • Remember that [C does *not* have references](http://stackoverflow.com/questions/4305673/does-c-have-references). There is an implicit array to pointer decay that occurs when passing arrays into functions. Check [this](http://en.cppreference.com/w/c/language/array) out for more on arrays in C. – callyalater Aug 09 '16 at 19:34
  • Re your edit: as an answer says, you cannot find the size of an array passed to a function. You have to tell it that too. Within the function, `sizeof` the pointer argument will never give anything but the size of the pointer itself (not the array) *even if you state the array dimension in the argument*. – Weather Vane Aug 09 '16 at 19:37
  • You are working off of a false premise. Yes, `sizeof` will prevent pointer decay *if the thing being passed to it is an array*. But as soon as you pass it into a function, it decays. Even if you take the address of the array and pass that into the function, it decays. Unless you handle the array right away, it will decay when you pass it to a function. If you can solve the halting problem, then we can help you make the function do magic. – callyalater Aug 09 '16 at 22:48

3 Answers3

4

In C, you cannot find the size of the array in the called function. Caller function has to pass one more argument stating the size of array.


Let us see some examples to see why this is not possbile.

First you will try to pass the array as an argument when you call the function. However, note that when you pass an array as an argument, it automatically deays to a pointer to the datatype of its element. And this will prevent the called function from calculating the size of array using sizeof operator.

For example,

int main(void)
{
    int arr_a[2] = {22, 33};
    int arr_b[5] = {6, 7, 8, 9, 10};

    foo(arr_a); // Here arr_a decays to an `int *`
    foo(arr_b); // Here arr_b decays to an `int *` too

    return 0;
}

void foo(int *arr)
{
    sizeof(arr); 
    /* Here sizeof(arr) will always give the size of 
     * an `int *` which maybe 4 or 8 bytes depending
     * on platform. It does not matter weather arr_a
     * or arr_b was passed from main.   
     * And because sizeof(arr) always gives the sameresult, 
     * we cannot use sizeof(arr)/sizeof(arr[0]) to calculate 
     * the size of array that was passed.
     */

     /* This value will always be the same, 
      * regardless of what was passed */
     sizeof(arr)/sizeof(arr[0]); 

}

Also, note that that:

void foo(int arr[]) { ... }

is equivalent to:

void foo(int *arr) { ... }

Compiler will silently change int arr[] into int *arr. So having int arr[] as a parameter will not make any difference.

Next, you may think of passing the address of array (by doing &arr_name). However, please note that, &arr_name is a pointer to array (of some DEFINITE size), which is different from a pointer to pointer to underlying datatype. This time you do something like this.

void foo(void) {
    int arr_a[2] = {22, 33};
    int arr_b[3] = {7, 8, 9};
     bar(&arr_a); // Note here that type of `&arr_a`  is `int (*)[2]`,
                  // i.e. a `pointer to int array of 2 elements`, which is
                  // different from a `pionter to pointer to int`

     bar(&arr_b); // Note here that type of `&arr_b`  is `int (*)[3]`,
                  // i.e. a `pointer to int array of 3 elements`, which is
                  // different from a `int (*)[2]`, 
                  // i.e a `pointer to int array of 2 elements`
                  // Note that this will give ERROR. See comments in bar()
    return 0;
}

void bar(int (*arr)[2]) {
     /* 
      * The caller of this function can ONLY pass int arrays with
      * 2 elements. Caller CANNOT pass int array with 3 elemens, or
      * 1 element, or 5 element.
      * This means you ALWAYS KNOW the size of arrays being passed,
      * and although you can calculate the size by doing
      * sizeof(*arr)/sizeof(*arr[0]); 
      * There is no point in calculating it - you alreay know the size 
      */
}

So, basically you cannot even pass pointer to the array by passing &array_name to solve this problem, because you will need different functions for accepting arrays of different sizes. Like, void bar_2(int (*arr)[2]) {...} to accept pointer to array of 2 integers, and void bar_3(int (*arr)[3]) {...} to accept pointer to array of 3 integers. Besides, there is no point in calculating the size in these functions, as you will already know it.

Lastly, you will try passing a pointer to pointer to the underlying datatype. So you do something like:

void foo() {
    int arr_a[2] = {22, 33};
    int arr_b[5] = {6, 7, 8, 9, 10};
    int *ptr;
    ptr = &arr_a[0];
    bar(&ptr); // Passing pointer to pointer to int (int **)
    ptr = &arr_b[0];
    bar(&ptr); // Passing pointer to pointer to int (int **)
}

void bar(int **pptr) {
    sizeof(*pptr);
    /* This will always be the same. 
     * i.e. the size of an integer pointer 
     * So here again you cannot use 
     * sizeof(*pptr)/sizeof((*pptr)[0]) to calculate the size
     * as it will always give the same result */

     sizeof(*pptr)/sizeof((*pptr)[0]) // Always same, no matter what was
                                      // passed by the caller
}

You see that passing a pointer to pointer to underlying datatype also does not solve this problem.

So, you see that no matter what you do, you CANNOT find the size of array by using sizeof() in the called function. The called function can know the size of the array only if the caller passes this info.


As a side note, there is an issue in your code. When you are doing

 i = 0; while (now > hourLongs[i] && i < j){i++;}  // AND
 i = 0; while (*time> (timeList_pointer)[i] && i < size){i++;} 

The order of your conditions in while loop should be other way round. It should be

 i = 0; while ( i < j && now > hourLongs[i]){i++;}  // AND
 i = 0; while (i < size && *time> (timeList_pointer)[i]){i++;} 

This is because, you have to first check that i is within the bounds of array, and then only evaluate hourLongs[i] or (timeList_pointer)[i].

sps
  • 2,720
  • 2
  • 19
  • 38
  • Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackoverflow.com/rooms/120553/discussion-on-answer-by-sps-how-to-pass-a-pointer-to-an-array-without-decay-and). – Flexo Aug 09 '16 at 22:51
  • @sps Again, you don't need to search something you already know :)) – Michi Aug 09 '16 at 22:52
  • @Michi OP wants to search for it in a function. You have still not understood the question. OP wants to write a function which calculates `10` for `char arr[10];` , `15` for `char arr[15];`, and `20` for `char arr[20];` by passing onle ONE argument to that function. Please reply if you do write that function. More preferable please post an answer with your solution for array of char type. – sps Aug 09 '16 at 23:00
1

As others have stated, you cannot pass an array to a function and get its size due to how arrays decay to a pointer to the first element when passed to a function. So you need to pass in the array and its size to your function.

There is a roundabout way of passing an array to a function so you can get its size, however it only works for arrays of a fixed size.

#include <stdio.h>
#include <stdlib.h>

void test1(int (*a)[10])
{
    int i;
    printf("size=%zu\n",sizeof(*a));
    for (i=0;i<sizeof(*a)/sizeof((*a)[0]);i++) {
        printf("%d\n",(*a)[i]);
    }
}

int main()
{
    int a[10] = {1,2,3,4,5,6,7,8,9,10};
    test1(&a);
    return 0;
}

The above code allow you to pass a pointer to an array of a fixed size. You can then dereference that pointer to get the number of elements as sizeof(*a)/sizeof((*a)[0]).

However, you can only pass a int [10] array to this function. You can't pass an array of any other size. So in this case, you can simply replace the above expression with 10 since the size is known.

dbush
  • 205,898
  • 23
  • 218
  • 273
0

When you declare an array, what c does in the background is basically just allocate enough memory for the size you want and then giving you a pointer. when you say

int arr[20];

what is effectively happening is that c is allocating enough memory for 20 ints and gives you a pointer named arr(there is some difference but for simplicity's sake I am explaining it this way), so if a function's signature requires a pointer to an int, you can pass it the array.

void foo(int *);
foo(arr);      */ works */

this works, so there is no need to use a pointer to a pointer, although you will have to specify the size, because a c array is basically just a pointer that points to some memory, so you can either receive the size as a parameter or mark the last element of the array in a manner similar to how the end of a string of chars is marked(using a null terminator). when you use the [] operator, c just offsets the pointer and dereferences it, so these two statements are equivalent(one being less readable)

arr[10] = somevalue;
*(arr+10) = somevalue;
// In general, arr[n] is equivalent to *(arr+n).

hope this helps you understand it a little better even though my explanation was a little simplistic.

callyalater
  • 3,102
  • 8
  • 20
  • 27
monkeyStix
  • 620
  • 5
  • 10
  • 1
    Just a nit, but the two statements are not equivalent. `*(arr+9)` is equivalent to `arr[9]`. The indices are zero based. i.e. `*(arr+0)` same as `arr[0]`. – ryyker Aug 09 '16 at 19:32