简体   繁体   中英

How to pass a pointer to an array without decay, and use the 'reference' in a function for barbarrians?

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.

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] .

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.

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 ac 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM